aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNicolin Chen <nicoleotsuka@gmail.com>2014-07-30 07:27:38 -0400
committerMark Brown <broonie@linaro.org>2014-08-16 18:03:50 -0400
commit708b4351f08c08ea93f773fb9197bdd3f3b08273 (patch)
tree98a0acdefc213833a69f64c85ef2c5dc60716a1a
parent7d1311b93e58ed55f3a31cc8f94c4b8fe988a2b9 (diff)
ASoC: fsl: Add Freescale Generic ASoC Sound Card with ASRC support
The Freescale Generic ASoC Sound Card is a general ASoC DAI Link driver that can be used, ideally, for all Freescale CPU DAI drivers and external CODECs. The idea of this generic sound card is a bit like ASoC Simple Card. However, for Freescale SoCs (especially those released in recent years), most of them have ASRC (Documentation/devicetree/bindings/sound/fsl,asrc.txt) inside. And this is a specific feature that might be painstakingly controlled and merged into the Simple Card driver. So having this driver will allow all Freescale SoC users to benefit from the simplification to support a new card and the capability of wide sample rates support through ASRC. The driver is initially designed for sound card using I2S or PCM DAI formats. However, it's also possible to merge those non-I2S/PCM type sound cards, such as S/PDIF audio and HDMI audio, into this card as long as the merge will not break the original function and as long as there is something redundant that can be abstracted along with I2S type sound cards. As an initial version, it only supports three cards that I can test: imx-audio-cs42888, a new card that links ESAI with CS42888 CODEC imx-audio-sgtl5000, just like the old imx-sgtl5000.c driver imx-audio-wm8962, just like the old imx-wm8962.c driver The driver is also compatible with the old Device Tree bindings of WM8962 and SGTL5000. So we may consider to remove those two drivers after this driver is totally enabled. (It needs to be added into defconfig) Signed-off-by: Nicolin Chen <nicoleotsuka@gmail.com> Signed-off-by: Mark Brown <broonie@linaro.org>
-rw-r--r--Documentation/devicetree/bindings/sound/fsl-asoc-card.txt82
-rw-r--r--sound/soc/fsl/Kconfig16
-rw-r--r--sound/soc/fsl/Makefile2
-rw-r--r--sound/soc/fsl/fsl-asoc-card.c573
4 files changed, 673 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/sound/fsl-asoc-card.txt b/Documentation/devicetree/bindings/sound/fsl-asoc-card.txt
new file mode 100644
index 000000000000..a96774c194c8
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/fsl-asoc-card.txt
@@ -0,0 +1,82 @@
1Freescale Generic ASoC Sound Card with ASRC support
2
3The Freescale Generic ASoC Sound Card can be used, ideally, for all Freescale
4SoCs connecting with external CODECs.
5
6The idea of this generic sound card is a bit like ASoC Simple Card. However,
7for Freescale SoCs (especially those released in recent years), most of them
8have ASRC (Documentation/devicetree/bindings/sound/fsl,asrc.txt) inside. And
9this is a specific feature that might be painstakingly controlled and merged
10into the Simple Card.
11
12So having this generic sound card allows all Freescale SoC users to benefit
13from the simplification of a new card support and the capability of the wide
14sample rates support through ASRC.
15
16Note: The card is initially designed for those sound cards who use I2S and
17 PCM DAI formats. However, it'll be also possible to support those non
18 I2S/PCM type sound cards, such as S/PDIF audio and HDMI audio, as long
19 as the driver has been properly upgraded.
20
21
22The compatible list for this generic sound card currently:
23 "fsl,imx-audio-cs42888"
24
25 "fsl,imx-audio-wm8962"
26 (compatible with Documentation/devicetree/bindings/sound/imx-audio-wm8962.txt)
27
28 "fsl,imx-audio-sgtl5000"
29 (compatible with Documentation/devicetree/bindings/sound/imx-audio-sgtl5000.txt)
30
31Required properties:
32
33 - compatible : Contains one of entries in the compatible list.
34
35 - model : The user-visible name of this sound complex
36
37 - audio-cpu : The phandle of an CPU DAI controller
38
39 - audio-codec : The phandle of an audio codec
40
41 - audio-routing : A list of the connections between audio components.
42 Each entry is a pair of strings, the first being the
43 connection's sink, the second being the connection's
44 source. There're a few pre-designed board connectors:
45 * Line Out Jack
46 * Line In Jack
47 * Headphone Jack
48 * Mic Jack
49 * Ext Spk
50 * AMIC (stands for Analog Microphone Jack)
51 * DMIC (stands for Digital Microphone Jack)
52
53 Note: The "Mic Jack" and "AMIC" are redundant while
54 coexsiting in order to support the old bindings
55 of wm8962 and sgtl5000.
56
57Optional properties:
58
59 - audio-asrc : The phandle of ASRC. It can be absent if there's no
60 need to add ASRC support via DPCM.
61
62Example:
63sound-cs42888 {
64 compatible = "fsl,imx-audio-cs42888";
65 model = "cs42888-audio";
66 audio-cpu = <&esai>;
67 audio-asrc = <&asrc>;
68 audio-codec = <&cs42888>;
69 audio-routing =
70 "Line Out Jack", "AOUT1L",
71 "Line Out Jack", "AOUT1R",
72 "Line Out Jack", "AOUT2L",
73 "Line Out Jack", "AOUT2R",
74 "Line Out Jack", "AOUT3L",
75 "Line Out Jack", "AOUT3R",
76 "Line Out Jack", "AOUT4L",
77 "Line Out Jack", "AOUT4R",
78 "AIN1L", "Line In Jack",
79 "AIN1R", "Line In Jack",
80 "AIN2L", "Line In Jack",
81 "AIN2R", "Line In Jack";
82};
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index f54a8fc99291..2b99a9e86899 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -59,6 +59,22 @@ config SND_SOC_FSL_ESAI
59config SND_SOC_FSL_UTILS 59config SND_SOC_FSL_UTILS
60 tristate 60 tristate
61 61
62config SND_SOC_FSL_ASOC_CARD
63 tristate "Generic ASoC Sound Card with ASRC support"
64 depends on OF && I2C
65 select SND_SOC_IMX_PCM_DMA
66 select SND_SOC_FSL_ESAI
67 select SND_SOC_FSL_SAI
68 select SND_SOC_FSL_SSI
69 select SND_SOC_CS42XX8_I2C
70 select SND_SOC_SGTL5000
71 select SND_SOC_WM8962
72 help
73 ALSA SoC Audio support with ASRC feature for Freescale SoCs that have
74 ESAI/SAI/SSI and connect with external CODECs such as WM8962, CS42888
75 and SGTL5000.
76 Say Y if you want to add support for Freescale Generic ASoC Sound Card.
77
62config SND_SOC_IMX_PCM_DMA 78config SND_SOC_IMX_PCM_DMA
63 tristate 79 tristate
64 select SND_SOC_GENERIC_DMAENGINE_PCM 80 select SND_SOC_GENERIC_DMAENGINE_PCM
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile
index 9ff59267eac9..8f6d84efa973 100644
--- a/sound/soc/fsl/Makefile
+++ b/sound/soc/fsl/Makefile
@@ -11,6 +11,7 @@ snd-soc-p1022-rdk-objs := p1022_rdk.o
11obj-$(CONFIG_SND_SOC_P1022_RDK) += snd-soc-p1022-rdk.o 11obj-$(CONFIG_SND_SOC_P1022_RDK) += snd-soc-p1022-rdk.o
12 12
13# Freescale SSI/DMA/SAI/SPDIF Support 13# Freescale SSI/DMA/SAI/SPDIF Support
14snd-soc-fsl-asoc-card-objs := fsl-asoc-card.o
14snd-soc-fsl-asrc-objs := fsl_asrc.o fsl_asrc_dma.o 15snd-soc-fsl-asrc-objs := fsl_asrc.o fsl_asrc_dma.o
15snd-soc-fsl-sai-objs := fsl_sai.o 16snd-soc-fsl-sai-objs := fsl_sai.o
16snd-soc-fsl-ssi-y := fsl_ssi.o 17snd-soc-fsl-ssi-y := fsl_ssi.o
@@ -19,6 +20,7 @@ snd-soc-fsl-spdif-objs := fsl_spdif.o
19snd-soc-fsl-esai-objs := fsl_esai.o 20snd-soc-fsl-esai-objs := fsl_esai.o
20snd-soc-fsl-utils-objs := fsl_utils.o 21snd-soc-fsl-utils-objs := fsl_utils.o
21snd-soc-fsl-dma-objs := fsl_dma.o 22snd-soc-fsl-dma-objs := fsl_dma.o
23obj-$(CONFIG_SND_SOC_FSL_ASOC_CARD) += snd-soc-fsl-asoc-card.o
22obj-$(CONFIG_SND_SOC_FSL_ASRC) += snd-soc-fsl-asrc.o 24obj-$(CONFIG_SND_SOC_FSL_ASRC) += snd-soc-fsl-asrc.o
23obj-$(CONFIG_SND_SOC_FSL_SAI) += snd-soc-fsl-sai.o 25obj-$(CONFIG_SND_SOC_FSL_SAI) += snd-soc-fsl-sai.o
24obj-$(CONFIG_SND_SOC_FSL_SSI) += snd-soc-fsl-ssi.o 26obj-$(CONFIG_SND_SOC_FSL_SSI) += snd-soc-fsl-ssi.o
diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c
new file mode 100644
index 000000000000..cf3f1f47f1e8
--- /dev/null
+++ b/sound/soc/fsl/fsl-asoc-card.c
@@ -0,0 +1,573 @@
1/*
2 * Freescale Generic ASoC Sound Card driver with ASRC
3 *
4 * Copyright (C) 2014 Freescale Semiconductor, Inc.
5 *
6 * Author: Nicolin Chen <nicoleotsuka@gmail.com>
7 *
8 * This file is licensed under the terms of the GNU General Public License
9 * version 2. This program is licensed "as is" without any warranty of any
10 * kind, whether express or implied.
11 */
12
13#include <linux/clk.h>
14#include <linux/i2c.h>
15#include <linux/module.h>
16#include <linux/of_platform.h>
17#include <sound/pcm_params.h>
18#include <sound/soc.h>
19
20#include "fsl_esai.h"
21#include "fsl_sai.h"
22#include "imx-audmux.h"
23
24#include "../codecs/sgtl5000.h"
25#include "../codecs/wm8962.h"
26
27#define RX 0
28#define TX 1
29
30/* Default DAI format without Master and Slave flag */
31#define DAI_FMT_BASE (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF)
32
33/**
34 * CODEC private data
35 *
36 * @mclk_freq: Clock rate of MCLK
37 * @mclk_id: MCLK (or main clock) id for set_sysclk()
38 * @fll_id: FLL (or secordary clock) id for set_sysclk()
39 * @pll_id: PLL id for set_pll()
40 */
41struct codec_priv {
42 unsigned long mclk_freq;
43 u32 mclk_id;
44 u32 fll_id;
45 u32 pll_id;
46};
47
48/**
49 * CPU private data
50 *
51 * @sysclk_freq[2]: SYSCLK rates for set_sysclk()
52 * @sysclk_dir[2]: SYSCLK directions for set_sysclk()
53 * @sysclk_id[2]: SYSCLK ids for set_sysclk()
54 *
55 * Note: [1] for tx and [0] for rx
56 */
57struct cpu_priv {
58 unsigned long sysclk_freq[2];
59 u32 sysclk_dir[2];
60 u32 sysclk_id[2];
61};
62
63/**
64 * Freescale Generic ASOC card private data
65 *
66 * @dai_link[3]: DAI link structure including normal one and DPCM link
67 * @pdev: platform device pointer
68 * @codec_priv: CODEC private data
69 * @cpu_priv: CPU private data
70 * @card: ASoC card structure
71 * @sample_rate: Current sample rate
72 * @sample_format: Current sample format
73 * @asrc_rate: ASRC sample rate used by Back-Ends
74 * @asrc_format: ASRC sample format used by Back-Ends
75 * @dai_fmt: DAI format between CPU and CODEC
76 * @name: Card name
77 */
78
79struct fsl_asoc_card_priv {
80 struct snd_soc_dai_link dai_link[3];
81 struct platform_device *pdev;
82 struct codec_priv codec_priv;
83 struct cpu_priv cpu_priv;
84 struct snd_soc_card card;
85 u32 sample_rate;
86 u32 sample_format;
87 u32 asrc_rate;
88 u32 asrc_format;
89 u32 dai_fmt;
90 char name[32];
91};
92
93/**
94 * This dapm route map exsits for DPCM link only.
95 * The other routes shall go through Device Tree.
96 */
97static const struct snd_soc_dapm_route audio_map[] = {
98 {"CPU-Playback", NULL, "ASRC-Playback"},
99 {"Playback", NULL, "CPU-Playback"},
100 {"ASRC-Capture", NULL, "CPU-Capture"},
101 {"CPU-Capture", NULL, "Capture"},
102};
103
104/* Add all possible widgets into here without being redundant */
105static const struct snd_soc_dapm_widget fsl_asoc_card_dapm_widgets[] = {
106 SND_SOC_DAPM_LINE("Line Out Jack", NULL),
107 SND_SOC_DAPM_LINE("Line In Jack", NULL),
108 SND_SOC_DAPM_HP("Headphone Jack", NULL),
109 SND_SOC_DAPM_SPK("Ext Spk", NULL),
110 SND_SOC_DAPM_MIC("Mic Jack", NULL),
111 SND_SOC_DAPM_MIC("AMIC", NULL),
112 SND_SOC_DAPM_MIC("DMIC", NULL),
113};
114
115static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream,
116 struct snd_pcm_hw_params *params)
117{
118 struct snd_soc_pcm_runtime *rtd = substream->private_data;
119 struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card);
120 bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
121 struct cpu_priv *cpu_priv = &priv->cpu_priv;
122 struct device *dev = rtd->card->dev;
123 int ret;
124
125 priv->sample_rate = params_rate(params);
126 priv->sample_format = params_format(params);
127
128 if (priv->card.set_bias_level)
129 return 0;
130
131 /* Specific configurations of DAIs starts from here */
132 ret = snd_soc_dai_set_sysclk(rtd->cpu_dai, cpu_priv->sysclk_id[tx],
133 cpu_priv->sysclk_freq[tx],
134 cpu_priv->sysclk_dir[tx]);
135 if (ret) {
136 dev_err(dev, "failed to set sysclk for cpu dai\n");
137 return ret;
138 }
139
140 return 0;
141}
142
143static struct snd_soc_ops fsl_asoc_card_ops = {
144 .hw_params = fsl_asoc_card_hw_params,
145};
146
147static int be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
148 struct snd_pcm_hw_params *params)
149{
150 struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card);
151 struct snd_interval *rate;
152 struct snd_mask *mask;
153
154 rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
155 rate->max = rate->min = priv->asrc_rate;
156
157 mask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
158 snd_mask_none(mask);
159 snd_mask_set(mask, priv->asrc_format);
160
161 return 0;
162}
163
164static struct snd_soc_dai_link fsl_asoc_card_dai[] = {
165 /* Default ASoC DAI Link*/
166 {
167 .name = "HiFi",
168 .stream_name = "HiFi",
169 .ops = &fsl_asoc_card_ops,
170 },
171 /* DPCM Link between Front-End and Back-End (Optional) */
172 {
173 .name = "HiFi-ASRC-FE",
174 .stream_name = "HiFi-ASRC-FE",
175 .codec_name = "snd-soc-dummy",
176 .codec_dai_name = "snd-soc-dummy-dai",
177 .dpcm_playback = 1,
178 .dpcm_capture = 1,
179 .dynamic = 1,
180 },
181 {
182 .name = "HiFi-ASRC-BE",
183 .stream_name = "HiFi-ASRC-BE",
184 .platform_name = "snd-soc-dummy",
185 .be_hw_params_fixup = be_hw_params_fixup,
186 .ops = &fsl_asoc_card_ops,
187 .dpcm_playback = 1,
188 .dpcm_capture = 1,
189 .no_pcm = 1,
190 },
191};
192
193static int fsl_asoc_card_set_bias_level(struct snd_soc_card *card,
194 struct snd_soc_dapm_context *dapm,
195 enum snd_soc_bias_level level)
196{
197 struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card);
198 struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
199 struct codec_priv *codec_priv = &priv->codec_priv;
200 struct device *dev = card->dev;
201 unsigned int pll_out;
202 int ret;
203
204 if (dapm->dev != codec_dai->dev)
205 return 0;
206
207 switch (level) {
208 case SND_SOC_BIAS_PREPARE:
209 if (dapm->bias_level != SND_SOC_BIAS_STANDBY)
210 break;
211
212 if (priv->sample_format == SNDRV_PCM_FORMAT_S24_LE)
213 pll_out = priv->sample_rate * 384;
214 else
215 pll_out = priv->sample_rate * 256;
216
217 ret = snd_soc_dai_set_pll(codec_dai, codec_priv->pll_id,
218 codec_priv->mclk_id,
219 codec_priv->mclk_freq, pll_out);
220 if (ret) {
221 dev_err(dev, "failed to start FLL: %d\n", ret);
222 return ret;
223 }
224
225 ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->fll_id,
226 pll_out, SND_SOC_CLOCK_IN);
227 if (ret) {
228 dev_err(dev, "failed to set SYSCLK: %d\n", ret);
229 return ret;
230 }
231 break;
232
233 case SND_SOC_BIAS_STANDBY:
234 if (dapm->bias_level != SND_SOC_BIAS_PREPARE)
235 break;
236
237 ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->mclk_id,
238 codec_priv->mclk_freq,
239 SND_SOC_CLOCK_IN);
240 if (ret) {
241 dev_err(dev, "failed to switch away from FLL: %d\n", ret);
242 return ret;
243 }
244
245 ret = snd_soc_dai_set_pll(codec_dai, codec_priv->pll_id, 0, 0, 0);
246 if (ret) {
247 dev_err(dev, "failed to stop FLL: %d\n", ret);
248 return ret;
249 }
250 break;
251
252 default:
253 break;
254 }
255
256 return 0;
257}
258
259static int fsl_asoc_card_audmux_init(struct device_node *np,
260 struct fsl_asoc_card_priv *priv)
261{
262 struct device *dev = &priv->pdev->dev;
263 u32 int_ptcr = 0, ext_ptcr = 0;
264 int int_port, ext_port;
265 int ret;
266
267 ret = of_property_read_u32(np, "mux-int-port", &int_port);
268 if (ret) {
269 dev_err(dev, "mux-int-port missing or invalid\n");
270 return ret;
271 }
272 ret = of_property_read_u32(np, "mux-ext-port", &ext_port);
273 if (ret) {
274 dev_err(dev, "mux-ext-port missing or invalid\n");
275 return ret;
276 }
277
278 /*
279 * The port numbering in the hardware manual starts at 1, while
280 * the AUDMUX API expects it starts at 0.
281 */
282 int_port--;
283 ext_port--;
284
285 /*
286 * Use asynchronous mode (6 wires) for all cases.
287 * If only 4 wires are needed, just set SSI into
288 * synchronous mode and enable 4 PADs in IOMUX.
289 */
290 switch (priv->dai_fmt & SND_SOC_DAIFMT_MASTER_MASK) {
291 case SND_SOC_DAIFMT_CBM_CFM:
292 int_ptcr = IMX_AUDMUX_V2_PTCR_RFSEL(8 | ext_port) |
293 IMX_AUDMUX_V2_PTCR_RCSEL(8 | ext_port) |
294 IMX_AUDMUX_V2_PTCR_TFSEL(ext_port) |
295 IMX_AUDMUX_V2_PTCR_TCSEL(ext_port) |
296 IMX_AUDMUX_V2_PTCR_RFSDIR |
297 IMX_AUDMUX_V2_PTCR_RCLKDIR |
298 IMX_AUDMUX_V2_PTCR_TFSDIR |
299 IMX_AUDMUX_V2_PTCR_TCLKDIR;
300 break;
301 case SND_SOC_DAIFMT_CBM_CFS:
302 int_ptcr = IMX_AUDMUX_V2_PTCR_RCSEL(8 | ext_port) |
303 IMX_AUDMUX_V2_PTCR_TCSEL(ext_port) |
304 IMX_AUDMUX_V2_PTCR_RCLKDIR |
305 IMX_AUDMUX_V2_PTCR_TCLKDIR;
306 ext_ptcr = IMX_AUDMUX_V2_PTCR_RFSEL(8 | int_port) |
307 IMX_AUDMUX_V2_PTCR_TFSEL(int_port) |
308 IMX_AUDMUX_V2_PTCR_RFSDIR |
309 IMX_AUDMUX_V2_PTCR_TFSDIR;
310 break;
311 case SND_SOC_DAIFMT_CBS_CFM:
312 int_ptcr = IMX_AUDMUX_V2_PTCR_RFSEL(8 | ext_port) |
313 IMX_AUDMUX_V2_PTCR_TFSEL(ext_port) |
314 IMX_AUDMUX_V2_PTCR_RFSDIR |
315 IMX_AUDMUX_V2_PTCR_TFSDIR;
316 ext_ptcr = IMX_AUDMUX_V2_PTCR_RCSEL(8 | int_port) |
317 IMX_AUDMUX_V2_PTCR_TCSEL(int_port) |
318 IMX_AUDMUX_V2_PTCR_RCLKDIR |
319 IMX_AUDMUX_V2_PTCR_TCLKDIR;
320 break;
321 case SND_SOC_DAIFMT_CBS_CFS:
322 ext_ptcr = IMX_AUDMUX_V2_PTCR_RFSEL(8 | int_port) |
323 IMX_AUDMUX_V2_PTCR_RCSEL(8 | int_port) |
324 IMX_AUDMUX_V2_PTCR_TFSEL(int_port) |
325 IMX_AUDMUX_V2_PTCR_TCSEL(int_port) |
326 IMX_AUDMUX_V2_PTCR_RFSDIR |
327 IMX_AUDMUX_V2_PTCR_RCLKDIR |
328 IMX_AUDMUX_V2_PTCR_TFSDIR |
329 IMX_AUDMUX_V2_PTCR_TCLKDIR;
330 break;
331 default:
332 return -EINVAL;
333 }
334
335 /* Asynchronous mode can not be set along with RCLKDIR */
336 ret = imx_audmux_v2_configure_port(int_port, 0,
337 IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port));
338 if (ret) {
339 dev_err(dev, "audmux internal port setup failed\n");
340 return ret;
341 }
342
343 ret = imx_audmux_v2_configure_port(int_port, int_ptcr,
344 IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port));
345 if (ret) {
346 dev_err(dev, "audmux internal port setup failed\n");
347 return ret;
348 }
349
350 ret = imx_audmux_v2_configure_port(ext_port, 0,
351 IMX_AUDMUX_V2_PDCR_RXDSEL(int_port));
352 if (ret) {
353 dev_err(dev, "audmux external port setup failed\n");
354 return ret;
355 }
356
357 ret = imx_audmux_v2_configure_port(ext_port, ext_ptcr,
358 IMX_AUDMUX_V2_PDCR_RXDSEL(int_port));
359 if (ret) {
360 dev_err(dev, "audmux external port setup failed\n");
361 return ret;
362 }
363
364 return 0;
365}
366
367static int fsl_asoc_card_late_probe(struct snd_soc_card *card)
368{
369 struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card);
370 struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
371 struct codec_priv *codec_priv = &priv->codec_priv;
372 struct device *dev = card->dev;
373 int ret;
374
375 ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->mclk_id,
376 codec_priv->mclk_freq, SND_SOC_CLOCK_IN);
377 if (ret) {
378 dev_err(dev, "failed to set sysclk in %s\n", __func__);
379 return ret;
380 }
381
382 return 0;
383}
384
385static int fsl_asoc_card_probe(struct platform_device *pdev)
386{
387 struct device_node *cpu_np, *codec_np, *asrc_np;
388 struct device_node *np = pdev->dev.of_node;
389 struct platform_device *asrc_pdev = NULL;
390 struct platform_device *cpu_pdev;
391 struct fsl_asoc_card_priv *priv;
392 struct i2c_client *codec_dev;
393 struct clk *codec_clk;
394 u32 width;
395 int ret;
396
397 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
398 if (!priv)
399 return -ENOMEM;
400
401 cpu_np = of_parse_phandle(np, "audio-cpu", 0);
402 /* Give a chance to old DT binding */
403 if (!cpu_np)
404 cpu_np = of_parse_phandle(np, "ssi-controller", 0);
405 codec_np = of_parse_phandle(np, "audio-codec", 0);
406 if (!cpu_np || !codec_np) {
407 dev_err(&pdev->dev, "phandle missing or invalid\n");
408 ret = -EINVAL;
409 goto fail;
410 }
411
412 cpu_pdev = of_find_device_by_node(cpu_np);
413 if (!cpu_pdev) {
414 dev_err(&pdev->dev, "failed to find CPU DAI device\n");
415 ret = -EINVAL;
416 goto fail;
417 }
418
419 codec_dev = of_find_i2c_device_by_node(codec_np);
420 if (!codec_dev) {
421 dev_err(&pdev->dev, "failed to find codec platform device\n");
422 ret = -EINVAL;
423 goto fail;
424 }
425
426 asrc_np = of_parse_phandle(np, "audio-asrc", 0);
427 if (asrc_np)
428 asrc_pdev = of_find_device_by_node(asrc_np);
429
430 /* Get the MCLK rate only, and leave it controlled by CODEC drivers */
431 codec_clk = clk_get(&codec_dev->dev, NULL);
432 if (!IS_ERR(codec_clk)) {
433 priv->codec_priv.mclk_freq = clk_get_rate(codec_clk);
434 clk_put(codec_clk);
435 }
436
437 /* Default sample rate and format, will be updated in hw_params() */
438 priv->sample_rate = 44100;
439 priv->sample_format = SNDRV_PCM_FORMAT_S16_LE;
440
441 /* Assign a default DAI format, and allow each card to overwrite it */
442 priv->dai_fmt = DAI_FMT_BASE;
443
444 /* Diversify the card configurations */
445 if (of_device_is_compatible(np, "fsl,imx-audio-cs42888")) {
446 priv->card.set_bias_level = NULL;
447 priv->cpu_priv.sysclk_freq[TX] = priv->codec_priv.mclk_freq;
448 priv->cpu_priv.sysclk_freq[RX] = priv->codec_priv.mclk_freq;
449 priv->cpu_priv.sysclk_dir[TX] = SND_SOC_CLOCK_OUT;
450 priv->cpu_priv.sysclk_dir[RX] = SND_SOC_CLOCK_OUT;
451 priv->dai_fmt |= SND_SOC_DAIFMT_CBS_CFS;
452 } else if (of_device_is_compatible(np, "fsl,imx-audio-sgtl5000")) {
453 priv->codec_priv.mclk_id = SGTL5000_SYSCLK;
454 priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM;
455 } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8962")) {
456 priv->card.set_bias_level = fsl_asoc_card_set_bias_level;
457 priv->codec_priv.mclk_id = WM8962_SYSCLK_MCLK;
458 priv->codec_priv.fll_id = WM8962_SYSCLK_FLL;
459 priv->codec_priv.pll_id = WM8962_FLL;
460 priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM;
461 } else {
462 dev_err(&pdev->dev, "unknown Device Tree compatible\n");
463 return -EINVAL;
464 }
465
466 /* Common settings for corresponding Freescale CPU DAI driver */
467 if (strstr(cpu_np->name, "ssi")) {
468 /* Only SSI needs to configure AUDMUX */
469 ret = fsl_asoc_card_audmux_init(np, priv);
470 if (ret) {
471 dev_err(&pdev->dev, "failed to init audmux\n");
472 goto fail;
473 }
474 } else if (strstr(cpu_np->name, "esai")) {
475 priv->cpu_priv.sysclk_id[1] = ESAI_HCKT_EXTAL;
476 priv->cpu_priv.sysclk_id[0] = ESAI_HCKR_EXTAL;
477 } else if (strstr(cpu_np->name, "sai")) {
478 priv->cpu_priv.sysclk_id[1] = FSL_SAI_CLK_MAST1;
479 priv->cpu_priv.sysclk_id[0] = FSL_SAI_CLK_MAST1;
480 }
481
482 sprintf(priv->name, "%s-audio", codec_dev->name);
483
484 /* Initialize sound card */
485 priv->pdev = pdev;
486 priv->card.dev = &pdev->dev;
487 priv->card.name = priv->name;
488 priv->card.dai_link = priv->dai_link;
489 priv->card.dapm_routes = audio_map;
490 priv->card.late_probe = fsl_asoc_card_late_probe;
491 priv->card.num_dapm_routes = ARRAY_SIZE(audio_map);
492 priv->card.dapm_widgets = fsl_asoc_card_dapm_widgets;
493 priv->card.num_dapm_widgets = ARRAY_SIZE(fsl_asoc_card_dapm_widgets);
494
495 memcpy(priv->dai_link, fsl_asoc_card_dai,
496 sizeof(struct snd_soc_dai_link) * ARRAY_SIZE(priv->dai_link));
497
498 /* Normal DAI Link */
499 priv->dai_link[0].cpu_of_node = cpu_np;
500 priv->dai_link[0].codec_of_node = codec_np;
501 priv->dai_link[0].codec_dai_name = codec_dev->name;
502 priv->dai_link[0].platform_of_node = cpu_np;
503 priv->dai_link[0].dai_fmt = priv->dai_fmt;
504 priv->card.num_links = 1;
505
506 if (asrc_pdev) {
507 /* DPCM DAI Links only if ASRC exsits */
508 priv->dai_link[1].cpu_of_node = asrc_np;
509 priv->dai_link[1].platform_of_node = asrc_np;
510 priv->dai_link[2].codec_dai_name = codec_dev->name;
511 priv->dai_link[2].codec_of_node = codec_np;
512 priv->dai_link[2].cpu_of_node = cpu_np;
513 priv->dai_link[2].dai_fmt = priv->dai_fmt;
514 priv->card.num_links = 3;
515
516 ret = of_property_read_u32(asrc_np, "fsl,asrc-rate",
517 &priv->asrc_rate);
518 if (ret) {
519 dev_err(&pdev->dev, "failed to get output rate\n");
520 ret = -EINVAL;
521 goto fail;
522 }
523
524 ret = of_property_read_u32(asrc_np, "fsl,asrc-width", &width);
525 if (ret) {
526 dev_err(&pdev->dev, "failed to get output rate\n");
527 ret = -EINVAL;
528 goto fail;
529 }
530
531 if (width == 24)
532 priv->asrc_format = SNDRV_PCM_FORMAT_S24_LE;
533 else
534 priv->asrc_format = SNDRV_PCM_FORMAT_S16_LE;
535 }
536
537 /* Finish card registering */
538 platform_set_drvdata(pdev, priv);
539 snd_soc_card_set_drvdata(&priv->card, priv);
540
541 ret = devm_snd_soc_register_card(&pdev->dev, &priv->card);
542 if (ret)
543 dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
544
545fail:
546 of_node_put(codec_np);
547 of_node_put(asrc_np);
548 of_node_put(cpu_np);
549
550 return ret;
551}
552
553static const struct of_device_id fsl_asoc_card_dt_ids[] = {
554 { .compatible = "fsl,imx-audio-cs42888", },
555 { .compatible = "fsl,imx-audio-sgtl5000", },
556 { .compatible = "fsl,imx-audio-wm8962", },
557 {}
558};
559
560static struct platform_driver fsl_asoc_card_driver = {
561 .probe = fsl_asoc_card_probe,
562 .driver = {
563 .name = "fsl-asoc-card",
564 .pm = &snd_soc_pm_ops,
565 .of_match_table = fsl_asoc_card_dt_ids,
566 },
567};
568module_platform_driver(fsl_asoc_card_driver);
569
570MODULE_DESCRIPTION("Freescale Generic ASoC Sound Card driver with ASRC");
571MODULE_AUTHOR("Nicolin Chen <nicoleotsuka@gmail.com>");
572MODULE_ALIAS("platform:fsl-asoc-card");
573MODULE_LICENSE("GPL");