1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0+
2*4882a593Smuzhiyun //
3*4882a593Smuzhiyun // eukrea-tlv320.c -- SoC audio for eukrea_cpuimxXX in I2S mode
4*4882a593Smuzhiyun //
5*4882a593Smuzhiyun // Copyright 2010 Eric Bénard, Eukréa Electromatique <eric@eukrea.com>
6*4882a593Smuzhiyun //
7*4882a593Smuzhiyun // based on sound/soc/s3c24xx/s3c24xx_simtec_tlv320aic23.c
8*4882a593Smuzhiyun // which is Copyright 2009 Simtec Electronics
9*4882a593Smuzhiyun // and on sound/soc/imx/phycore-ac97.c which is
10*4882a593Smuzhiyun // Copyright 2009 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
11*4882a593Smuzhiyun
12*4882a593Smuzhiyun #include <linux/errno.h>
13*4882a593Smuzhiyun #include <linux/module.h>
14*4882a593Smuzhiyun #include <linux/moduleparam.h>
15*4882a593Smuzhiyun #include <linux/of.h>
16*4882a593Smuzhiyun #include <linux/of_platform.h>
17*4882a593Smuzhiyun #include <linux/device.h>
18*4882a593Smuzhiyun #include <linux/i2c.h>
19*4882a593Smuzhiyun #include <sound/core.h>
20*4882a593Smuzhiyun #include <sound/pcm.h>
21*4882a593Smuzhiyun #include <sound/soc.h>
22*4882a593Smuzhiyun #include <asm/mach-types.h>
23*4882a593Smuzhiyun
24*4882a593Smuzhiyun #include "../codecs/tlv320aic23.h"
25*4882a593Smuzhiyun #include "imx-ssi.h"
26*4882a593Smuzhiyun #include "imx-audmux.h"
27*4882a593Smuzhiyun
28*4882a593Smuzhiyun #define CODEC_CLOCK 12000000
29*4882a593Smuzhiyun
eukrea_tlv320_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params)30*4882a593Smuzhiyun static int eukrea_tlv320_hw_params(struct snd_pcm_substream *substream,
31*4882a593Smuzhiyun struct snd_pcm_hw_params *params)
32*4882a593Smuzhiyun {
33*4882a593Smuzhiyun struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
34*4882a593Smuzhiyun struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
35*4882a593Smuzhiyun struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
36*4882a593Smuzhiyun int ret;
37*4882a593Smuzhiyun
38*4882a593Smuzhiyun ret = snd_soc_dai_set_sysclk(codec_dai, 0,
39*4882a593Smuzhiyun CODEC_CLOCK, SND_SOC_CLOCK_OUT);
40*4882a593Smuzhiyun if (ret) {
41*4882a593Smuzhiyun dev_err(cpu_dai->dev,
42*4882a593Smuzhiyun "Failed to set the codec sysclk.\n");
43*4882a593Smuzhiyun return ret;
44*4882a593Smuzhiyun }
45*4882a593Smuzhiyun
46*4882a593Smuzhiyun snd_soc_dai_set_tdm_slot(cpu_dai, 0x3, 0x3, 2, 0);
47*4882a593Smuzhiyun
48*4882a593Smuzhiyun ret = snd_soc_dai_set_sysclk(cpu_dai, IMX_SSP_SYS_CLK, 0,
49*4882a593Smuzhiyun SND_SOC_CLOCK_IN);
50*4882a593Smuzhiyun /* fsl_ssi lacks the set_sysclk ops */
51*4882a593Smuzhiyun if (ret && ret != -EINVAL) {
52*4882a593Smuzhiyun dev_err(cpu_dai->dev,
53*4882a593Smuzhiyun "Can't set the IMX_SSP_SYS_CLK CPU system clock.\n");
54*4882a593Smuzhiyun return ret;
55*4882a593Smuzhiyun }
56*4882a593Smuzhiyun
57*4882a593Smuzhiyun return 0;
58*4882a593Smuzhiyun }
59*4882a593Smuzhiyun
60*4882a593Smuzhiyun static const struct snd_soc_ops eukrea_tlv320_snd_ops = {
61*4882a593Smuzhiyun .hw_params = eukrea_tlv320_hw_params,
62*4882a593Smuzhiyun };
63*4882a593Smuzhiyun
64*4882a593Smuzhiyun SND_SOC_DAILINK_DEFS(hifi,
65*4882a593Smuzhiyun DAILINK_COMP_ARRAY(COMP_EMPTY()),
66*4882a593Smuzhiyun DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "tlv320aic23-hifi")),
67*4882a593Smuzhiyun DAILINK_COMP_ARRAY(COMP_EMPTY()));
68*4882a593Smuzhiyun
69*4882a593Smuzhiyun static struct snd_soc_dai_link eukrea_tlv320_dai = {
70*4882a593Smuzhiyun .name = "tlv320aic23",
71*4882a593Smuzhiyun .stream_name = "TLV320AIC23",
72*4882a593Smuzhiyun .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
73*4882a593Smuzhiyun SND_SOC_DAIFMT_CBM_CFM,
74*4882a593Smuzhiyun .ops = &eukrea_tlv320_snd_ops,
75*4882a593Smuzhiyun SND_SOC_DAILINK_REG(hifi),
76*4882a593Smuzhiyun };
77*4882a593Smuzhiyun
78*4882a593Smuzhiyun static struct snd_soc_card eukrea_tlv320 = {
79*4882a593Smuzhiyun .owner = THIS_MODULE,
80*4882a593Smuzhiyun .dai_link = &eukrea_tlv320_dai,
81*4882a593Smuzhiyun .num_links = 1,
82*4882a593Smuzhiyun };
83*4882a593Smuzhiyun
eukrea_tlv320_probe(struct platform_device * pdev)84*4882a593Smuzhiyun static int eukrea_tlv320_probe(struct platform_device *pdev)
85*4882a593Smuzhiyun {
86*4882a593Smuzhiyun int ret;
87*4882a593Smuzhiyun int int_port = 0, ext_port;
88*4882a593Smuzhiyun struct device_node *np = pdev->dev.of_node;
89*4882a593Smuzhiyun struct device_node *ssi_np = NULL, *codec_np = NULL, *tmp_np = NULL;
90*4882a593Smuzhiyun
91*4882a593Smuzhiyun eukrea_tlv320.dev = &pdev->dev;
92*4882a593Smuzhiyun if (np) {
93*4882a593Smuzhiyun ret = snd_soc_of_parse_card_name(&eukrea_tlv320,
94*4882a593Smuzhiyun "eukrea,model");
95*4882a593Smuzhiyun if (ret) {
96*4882a593Smuzhiyun dev_err(&pdev->dev,
97*4882a593Smuzhiyun "eukrea,model node missing or invalid.\n");
98*4882a593Smuzhiyun goto err;
99*4882a593Smuzhiyun }
100*4882a593Smuzhiyun
101*4882a593Smuzhiyun ssi_np = of_parse_phandle(pdev->dev.of_node,
102*4882a593Smuzhiyun "ssi-controller", 0);
103*4882a593Smuzhiyun if (!ssi_np) {
104*4882a593Smuzhiyun dev_err(&pdev->dev,
105*4882a593Smuzhiyun "ssi-controller missing or invalid.\n");
106*4882a593Smuzhiyun ret = -ENODEV;
107*4882a593Smuzhiyun goto err;
108*4882a593Smuzhiyun }
109*4882a593Smuzhiyun
110*4882a593Smuzhiyun codec_np = of_parse_phandle(ssi_np, "codec-handle", 0);
111*4882a593Smuzhiyun if (codec_np)
112*4882a593Smuzhiyun eukrea_tlv320_dai.codecs->of_node = codec_np;
113*4882a593Smuzhiyun else
114*4882a593Smuzhiyun dev_err(&pdev->dev, "codec-handle node missing or invalid.\n");
115*4882a593Smuzhiyun
116*4882a593Smuzhiyun ret = of_property_read_u32(np, "fsl,mux-int-port", &int_port);
117*4882a593Smuzhiyun if (ret) {
118*4882a593Smuzhiyun dev_err(&pdev->dev,
119*4882a593Smuzhiyun "fsl,mux-int-port node missing or invalid.\n");
120*4882a593Smuzhiyun goto err;
121*4882a593Smuzhiyun }
122*4882a593Smuzhiyun ret = of_property_read_u32(np, "fsl,mux-ext-port", &ext_port);
123*4882a593Smuzhiyun if (ret) {
124*4882a593Smuzhiyun dev_err(&pdev->dev,
125*4882a593Smuzhiyun "fsl,mux-ext-port node missing or invalid.\n");
126*4882a593Smuzhiyun goto err;
127*4882a593Smuzhiyun }
128*4882a593Smuzhiyun
129*4882a593Smuzhiyun /*
130*4882a593Smuzhiyun * The port numbering in the hardware manual starts at 1, while
131*4882a593Smuzhiyun * the audmux API expects it starts at 0.
132*4882a593Smuzhiyun */
133*4882a593Smuzhiyun int_port--;
134*4882a593Smuzhiyun ext_port--;
135*4882a593Smuzhiyun
136*4882a593Smuzhiyun eukrea_tlv320_dai.cpus->of_node = ssi_np;
137*4882a593Smuzhiyun eukrea_tlv320_dai.platforms->of_node = ssi_np;
138*4882a593Smuzhiyun } else {
139*4882a593Smuzhiyun eukrea_tlv320_dai.cpus->dai_name = "imx-ssi.0";
140*4882a593Smuzhiyun eukrea_tlv320_dai.platforms->name = "imx-ssi.0";
141*4882a593Smuzhiyun eukrea_tlv320_dai.codecs->name = "tlv320aic23-codec.0-001a";
142*4882a593Smuzhiyun eukrea_tlv320.name = "cpuimx-audio";
143*4882a593Smuzhiyun }
144*4882a593Smuzhiyun
145*4882a593Smuzhiyun if (machine_is_eukrea_cpuimx27() ||
146*4882a593Smuzhiyun (tmp_np = of_find_compatible_node(NULL, NULL, "fsl,imx21-audmux"))) {
147*4882a593Smuzhiyun imx_audmux_v1_configure_port(MX27_AUDMUX_HPCR1_SSI0,
148*4882a593Smuzhiyun IMX_AUDMUX_V1_PCR_SYN |
149*4882a593Smuzhiyun IMX_AUDMUX_V1_PCR_TFSDIR |
150*4882a593Smuzhiyun IMX_AUDMUX_V1_PCR_TCLKDIR |
151*4882a593Smuzhiyun IMX_AUDMUX_V1_PCR_RFSDIR |
152*4882a593Smuzhiyun IMX_AUDMUX_V1_PCR_RCLKDIR |
153*4882a593Smuzhiyun IMX_AUDMUX_V1_PCR_TFCSEL(MX27_AUDMUX_HPCR3_SSI_PINS_4) |
154*4882a593Smuzhiyun IMX_AUDMUX_V1_PCR_RFCSEL(MX27_AUDMUX_HPCR3_SSI_PINS_4) |
155*4882a593Smuzhiyun IMX_AUDMUX_V1_PCR_RXDSEL(MX27_AUDMUX_HPCR3_SSI_PINS_4)
156*4882a593Smuzhiyun );
157*4882a593Smuzhiyun imx_audmux_v1_configure_port(MX27_AUDMUX_HPCR3_SSI_PINS_4,
158*4882a593Smuzhiyun IMX_AUDMUX_V1_PCR_SYN |
159*4882a593Smuzhiyun IMX_AUDMUX_V1_PCR_RXDSEL(MX27_AUDMUX_HPCR1_SSI0)
160*4882a593Smuzhiyun );
161*4882a593Smuzhiyun of_node_put(tmp_np);
162*4882a593Smuzhiyun } else if (machine_is_eukrea_cpuimx25sd() ||
163*4882a593Smuzhiyun machine_is_eukrea_cpuimx35sd() ||
164*4882a593Smuzhiyun machine_is_eukrea_cpuimx51sd() ||
165*4882a593Smuzhiyun (tmp_np = of_find_compatible_node(NULL, NULL, "fsl,imx31-audmux"))) {
166*4882a593Smuzhiyun if (!np)
167*4882a593Smuzhiyun ext_port = machine_is_eukrea_cpuimx25sd() ?
168*4882a593Smuzhiyun 4 : 3;
169*4882a593Smuzhiyun
170*4882a593Smuzhiyun imx_audmux_v2_configure_port(int_port,
171*4882a593Smuzhiyun IMX_AUDMUX_V2_PTCR_SYN |
172*4882a593Smuzhiyun IMX_AUDMUX_V2_PTCR_TFSDIR |
173*4882a593Smuzhiyun IMX_AUDMUX_V2_PTCR_TFSEL(ext_port) |
174*4882a593Smuzhiyun IMX_AUDMUX_V2_PTCR_TCLKDIR |
175*4882a593Smuzhiyun IMX_AUDMUX_V2_PTCR_TCSEL(ext_port),
176*4882a593Smuzhiyun IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port)
177*4882a593Smuzhiyun );
178*4882a593Smuzhiyun imx_audmux_v2_configure_port(ext_port,
179*4882a593Smuzhiyun IMX_AUDMUX_V2_PTCR_SYN,
180*4882a593Smuzhiyun IMX_AUDMUX_V2_PDCR_RXDSEL(int_port)
181*4882a593Smuzhiyun );
182*4882a593Smuzhiyun of_node_put(tmp_np);
183*4882a593Smuzhiyun } else {
184*4882a593Smuzhiyun if (np) {
185*4882a593Smuzhiyun /* The eukrea,asoc-tlv320 driver was explicitly
186*4882a593Smuzhiyun * requested (through the device tree).
187*4882a593Smuzhiyun */
188*4882a593Smuzhiyun dev_err(&pdev->dev,
189*4882a593Smuzhiyun "Missing or invalid audmux DT node.\n");
190*4882a593Smuzhiyun return -ENODEV;
191*4882a593Smuzhiyun } else {
192*4882a593Smuzhiyun /* Return happy.
193*4882a593Smuzhiyun * We might run on a totally different machine.
194*4882a593Smuzhiyun */
195*4882a593Smuzhiyun return 0;
196*4882a593Smuzhiyun }
197*4882a593Smuzhiyun }
198*4882a593Smuzhiyun
199*4882a593Smuzhiyun ret = snd_soc_register_card(&eukrea_tlv320);
200*4882a593Smuzhiyun err:
201*4882a593Smuzhiyun if (ret)
202*4882a593Smuzhiyun dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
203*4882a593Smuzhiyun of_node_put(ssi_np);
204*4882a593Smuzhiyun
205*4882a593Smuzhiyun return ret;
206*4882a593Smuzhiyun }
207*4882a593Smuzhiyun
eukrea_tlv320_remove(struct platform_device * pdev)208*4882a593Smuzhiyun static int eukrea_tlv320_remove(struct platform_device *pdev)
209*4882a593Smuzhiyun {
210*4882a593Smuzhiyun snd_soc_unregister_card(&eukrea_tlv320);
211*4882a593Smuzhiyun
212*4882a593Smuzhiyun return 0;
213*4882a593Smuzhiyun }
214*4882a593Smuzhiyun
215*4882a593Smuzhiyun static const struct of_device_id imx_tlv320_dt_ids[] = {
216*4882a593Smuzhiyun { .compatible = "eukrea,asoc-tlv320"},
217*4882a593Smuzhiyun { /* sentinel */ }
218*4882a593Smuzhiyun };
219*4882a593Smuzhiyun MODULE_DEVICE_TABLE(of, imx_tlv320_dt_ids);
220*4882a593Smuzhiyun
221*4882a593Smuzhiyun static struct platform_driver eukrea_tlv320_driver = {
222*4882a593Smuzhiyun .driver = {
223*4882a593Smuzhiyun .name = "eukrea_tlv320",
224*4882a593Smuzhiyun .of_match_table = imx_tlv320_dt_ids,
225*4882a593Smuzhiyun },
226*4882a593Smuzhiyun .probe = eukrea_tlv320_probe,
227*4882a593Smuzhiyun .remove = eukrea_tlv320_remove,
228*4882a593Smuzhiyun };
229*4882a593Smuzhiyun
230*4882a593Smuzhiyun module_platform_driver(eukrea_tlv320_driver);
231*4882a593Smuzhiyun
232*4882a593Smuzhiyun MODULE_AUTHOR("Eric Bénard <eric@eukrea.com>");
233*4882a593Smuzhiyun MODULE_DESCRIPTION("CPUIMX ALSA SoC driver");
234*4882a593Smuzhiyun MODULE_LICENSE("GPL");
235*4882a593Smuzhiyun MODULE_ALIAS("platform:eukrea_tlv320");
236