2 * rk29_wm8994.c -- SoC audio for rockchip
4 * Driver for rockchip wm8994 audio
5 * Copyright (C) 2009 lhh
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2 of the License, or (at your
10 * option) any later version.
15 #include <linux/module.h>
16 #include <linux/device.h>
18 #include <linux/of_gpio.h>
19 #include <sound/core.h>
20 #include <sound/pcm.h>
21 #include <sound/soc.h>
22 #include <sound/soc-dapm.h>
24 #include "../codecs/wm8994.h"
27 #include <linux/clk.h>
30 #define DBG(x...) printk(KERN_INFO x)
35 static int rk29_aif1_hw_params(struct snd_pcm_substream *substream,
36 struct snd_pcm_hw_params *params)
38 struct snd_soc_pcm_runtime *rtd = substream->private_data;
39 struct snd_soc_dai *codec_dai = rtd->codec_dai;
40 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
41 unsigned int pll_out = 0;
42 int div_bclk,div_mclk;
44 struct clk *general_pll;
47 DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
49 /* set codec DAI configuration */
50 #if defined (CONFIG_SND_RK_CODEC_SOC_SLAVE)
51 DBG("Set codec_dai slave\n");
52 ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
53 SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
55 #if defined (CONFIG_SND_RK_CODEC_SOC_MASTER)
56 ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
57 SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
58 DBG("Set codec_dai master\n");
63 /* set cpu DAI configuration */
64 #if defined (CONFIG_SND_RK_CODEC_SOC_SLAVE)
65 DBG("Set cpu_dai master\n");
66 ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
67 SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
69 #if defined (CONFIG_SND_RK_CODEC_SOC_MASTER)
70 ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
71 SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
72 DBG("Set cpu_dai slave\n");
77 switch(params_rate(params)) {
91 DBG("Enter:%s, %d, Error rate=%d\n",__FUNCTION__,__LINE__,params_rate(params));
95 // DBG("Enter:%s, %d, rate=%d,pll_out = %d\n",__FUNCTION__,__LINE__,params_rate(params),pll_out);
96 #ifdef CONFIG_ARCH_RK29
97 general_pll=clk_get(NULL, "general_pll");
98 if(clk_get_rate(general_pll)>260000000)
100 div_bclk=(pll_out/4)/params_rate(params)-1;
103 else if(clk_get_rate(general_pll)>130000000)
105 div_bclk=(pll_out/2)/params_rate(params)-1;
111 div_bclk=(pll_out)/params_rate(params)-1;
115 div_bclk=(pll_out/4)/params_rate(params)-1;
119 DBG("func is%s,gpll=%ld,pll_out=%d,div_mclk=%d\n",__FUNCTION__,clk_get_rate(general_pll),pll_out,div_mclk);
120 ret = snd_soc_dai_set_sysclk(cpu_dai, 0, pll_out, 0);
123 DBG("rk29_hw_params_wm8994:failed to set the cpu sysclk for codec side\n");
126 snd_soc_dai_set_clkdiv(cpu_dai, ROCKCHIP_DIV_BCLK,div_bclk);
127 snd_soc_dai_set_clkdiv(cpu_dai, ROCKCHIP_DIV_MCLK, div_mclk);
128 DBG("Enter:%s, %d, LRCK=%d\n",__FUNCTION__,__LINE__,(pll_out/4)/params_rate(params));
131 {//MCLK == 11289600 or 12288000
132 ret = snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_MCLK1, pll_out, 0);
134 DBG("rk29_hw_params_wm8994:failed to set the sysclk for codec side\n");
140 /* set the codec FLL */
141 ret = snd_soc_dai_set_pll(codec_dai, WM8994_FLL1, WM8994_FLL_SRC_MCLK1, pll_out,
142 params_rate(params) * 256);
145 printk("%s: snd_soc_dai_set_pll err =%d\n",__FUNCTION__,ret);
148 /* set the codec system clock */
149 ret = snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_FLL1,
150 params_rate(params) * 256, SND_SOC_CLOCK_IN);
153 printk("%s: snd_soc_dai_set_sysclk err =%d\n",__FUNCTION__,ret);
161 static int rk29_aif2_hw_params(struct snd_pcm_substream *substream,
162 struct snd_pcm_hw_params *params)
164 struct snd_soc_pcm_runtime *rtd = substream->private_data;
165 struct snd_soc_dai *codec_dai = rtd->codec_dai;
166 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
167 unsigned int pll_out = 0;
168 int div_bclk,div_mclk;
170 struct clk *general_pll;
173 // params->intervals[SNDRV_PCM_HW_PARAM_RATE - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL].min = 8000;
175 DBG("Enter:%s, %d, rate=%d\n",__FUNCTION__,__LINE__,params_rate(params));
177 // if (params_rate(params) != 8000)
180 /* set codec DAI configuration */
181 ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_A |
182 SND_SOC_DAIFMT_IB_NF | SND_SOC_DAIFMT_CBM_CFM);
185 printk("%s: snd_soc_dai_set_fmt err =%d\n",__FUNCTION__,ret);
188 switch(params_rate(params)) {
202 DBG("Enter:%s, %d, Error rate=%d\n",__FUNCTION__,__LINE__,params_rate(params));
206 general_pll=clk_get(NULL, "general_pll");
207 if(clk_get_rate(general_pll)>260000000)
209 div_bclk=(pll_out/4)/params_rate(params)-1;
212 else if(clk_get_rate(general_pll)>130000000)
214 div_bclk=(pll_out/2)/params_rate(params)-1;
220 div_bclk=(pll_out)/params_rate(params)-1;
224 DBG("func is%s,gpll=%ld,pll_out=%d,div_mclk=%d\n",
225 __FUNCTION__,clk_get_rate(general_pll),pll_out,div_mclk);
227 ret = snd_soc_dai_set_sysclk(cpu_dai, 0, pll_out, 0);
230 DBG("rk29_hw_params_wm8994:failed to set the cpu sysclk for codec side\n");
233 snd_soc_dai_set_clkdiv(cpu_dai, ROCKCHIP_DIV_BCLK,div_bclk);
234 snd_soc_dai_set_clkdiv(cpu_dai, ROCKCHIP_DIV_MCLK, div_mclk);
235 DBG("Enter:%s, %d, LRCK=%d\n",__FUNCTION__,__LINE__,(pll_out/4)/params_rate(params));
237 /* set the codec FLL */
238 ret = snd_soc_dai_set_pll(codec_dai, WM8994_FLL2, WM8994_FLL_SRC_MCLK1, pll_out,
242 printk("%s: snd_soc_dai_set_pll err =%d\n",__FUNCTION__,ret);
245 /* set the codec system clock */
246 ret = snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_FLL2,
247 8000 * 256, SND_SOC_CLOCK_IN);
250 printk("%s: snd_soc_dai_set_sysclk err =%d\n",__FUNCTION__,ret);
258 static const struct snd_soc_dapm_widget rk29_dapm_widgets[] = {
259 SND_SOC_DAPM_SPK("Ext Left Spk", NULL),
260 SND_SOC_DAPM_SPK("Ext Right Spk", NULL),
261 SND_SOC_DAPM_SPK("Ext Rcv", NULL),
262 SND_SOC_DAPM_HP("Headset Stereophone", NULL),
263 SND_SOC_DAPM_MIC("Headset Mic", NULL),
264 SND_SOC_DAPM_MIC("Main Mic", NULL),
265 SND_SOC_DAPM_MIC("2nd Mic", NULL),
266 // SND_SOC_DAPM_LINE("Radio In", NULL),
267 SND_SOC_DAPM_LINE("Line In", NULL),
268 SND_SOC_DAPM_LINE("Line Out", NULL),
272 static const struct snd_soc_dapm_route rk29_dapm_routes[] = {
273 {"Ext Left Spk", NULL, "SPKOUTLP"},
274 {"Ext Left Spk", NULL, "SPKOUTLN"},
276 {"Ext Right Spk", NULL, "SPKOUTRP"},
277 {"Ext Right Spk", NULL, "SPKOUTRN"},
279 {"Ext Rcv", NULL, "HPOUT2N"},
280 {"Ext Rcv", NULL, "HPOUT2P"},
282 {"Headset Stereophone", NULL, "HPOUT1L"},
283 {"Headset Stereophone", NULL, "HPOUT1R"},
285 {"IN1LN", NULL, "Headset Mic"},
286 {"IN1LP", NULL, "Headset Mic"},
288 {"IN1LN", NULL, "2nd Mic"},
289 {"IN1LP", NULL, "2nd Mic"},
291 {"IN1RN", NULL, "Main Mic"},
292 {"IN1RP", NULL, "Main Mic"},
294 // {"IN2LN", NULL, "Radio In"},
295 // {"IN2RN", NULL, "Radio In"},
297 {"IN2LP:VXRN", NULL, "Line In"},
298 {"IN2RP:VXRP", NULL, "Line In"},
300 {"Line Out", NULL, "LINEOUT1N"},
301 {"Line Out", NULL, "LINEOUT1P"},
305 static int rk29_wm8994_init(struct snd_soc_pcm_runtime *rtd)
307 struct snd_soc_codec *codec = rtd->codec;
308 struct snd_soc_dapm_context *dapm = &codec->dapm;
310 DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
312 /* add goni specific widgets */
313 snd_soc_dapm_new_controls(dapm, rk29_dapm_widgets,
314 ARRAY_SIZE(rk29_dapm_widgets));
316 /* set up goni specific audio routes */
317 snd_soc_dapm_add_routes(dapm, rk29_dapm_routes,
318 ARRAY_SIZE(rk29_dapm_routes));
320 /* set endpoints to not connected */
321 // snd_soc_dapm_nc_pin(dapm, "IN2LP:VXRN");
322 // snd_soc_dapm_nc_pin(dapm, "IN2RP:VXRP");
323 snd_soc_dapm_nc_pin(dapm, "IN2LN");
324 snd_soc_dapm_nc_pin(dapm, "IN2RN");
325 // snd_soc_dapm_nc_pin(dapm, "LINEOUT1N");
326 // snd_soc_dapm_nc_pin(dapm, "LINEOUT1P");
327 snd_soc_dapm_nc_pin(dapm, "LINEOUT2N");
328 snd_soc_dapm_nc_pin(dapm, "LINEOUT2P");
330 extern int hdmi_is_insert(void);
331 extern void codec_set_spk(bool on);
333 codec_set_spk(false);
335 snd_soc_dapm_sync(dapm);
337 /* Headset jack detection */
338 /* ret = snd_soc_jack_new(codec, "Headset Jack",
339 SND_JACK_HEADSET | SND_JACK_MECHANICAL | SND_JACK_AVOUT,
344 ret = snd_soc_jack_add_pins(&jack, ARRAY_SIZE(jack_pins), jack_pins);
348 ret = snd_soc_jack_add_gpios(&jack, ARRAY_SIZE(jack_gpios), jack_gpios);
356 static struct snd_soc_ops rk29_aif1_ops = {
357 .hw_params = rk29_aif1_hw_params,
360 static struct snd_soc_ops rk29_aif2_ops = {
361 .hw_params = rk29_aif2_hw_params,
364 static struct snd_soc_dai_driver voice_dai = {
365 .name = "rk29-voice-dai",
370 .rates = SNDRV_PCM_RATE_8000,
371 .formats = SNDRV_PCM_FMTBIT_S16_LE,},
375 .rates = SNDRV_PCM_RATE_8000,
376 .formats = SNDRV_PCM_FMTBIT_S16_LE,},
379 static struct snd_soc_dai_link rk29_dai[] = {
381 .name = "WM8994 I2S1",
382 .stream_name = "WM8994 PCM",
383 .codec_name = "wm8994-codec",
384 .platform_name = "rockchip-pcm",
385 #if defined(CONFIG_SND_RK_SOC_I2S_8CH)
386 .cpu_dai_name = "rockchip-i2s.0",
387 #elif defined(CONFIG_SND_RK_SOC_I2S_2CH)
388 .cpu_dai_name = "rockchip-i2s.1",
390 .codec_dai_name = "wm8994-aif1",
391 .ops = &rk29_aif1_ops,
392 .init = rk29_wm8994_init,
395 .name = "WM8994 I2S2",
396 .stream_name = "WM8994 PCM",
397 .codec_name = "wm8994-codec",
398 .platform_name = "rockchip-pcm",
399 #if defined(CONFIG_SND_RK_SOC_I2S_8CH)
400 .cpu_dai_name = "rockchip-i2s.0",
401 #elif defined(CONFIG_SND_RK_SOC_I2S_2CH)
402 .cpu_dai_name = "rockchip-i2s.1",
404 .codec_dai_name = "wm8994-aif2",
405 .ops = &rk29_aif2_ops,
409 static struct snd_soc_card rockchip_wm8994_snd_card = {
411 .dai_link = rk29_dai,
412 .num_links = ARRAY_SIZE(rk29_dai),
415 static int rockchip_wm8994_audio_probe(struct platform_device *pdev)
418 struct snd_soc_card *card = &rockchip_wm8994_snd_card;
420 card->dev = &pdev->dev;
422 ret = snd_soc_register_card(card);
425 printk("%s() register card failed:%d\n", __FUNCTION__, ret);
430 static int rockchip_wm8994_audio_remove(struct platform_device *pdev)
432 struct snd_soc_card *card = platform_get_drvdata(pdev);
434 snd_soc_unregister_card(card);
440 static const struct of_device_id rockchip_wm8994_of_match[] = {
441 { .compatible = "rockchip-wm8994", },
444 MODULE_DEVICE_TABLE(of, rockchip_wm8994_of_match);
445 #endif /* CONFIG_OF */
447 static struct platform_driver rockchip_wm8994_audio_driver = {
449 .name = "rockchip-wm8994",
450 .owner = THIS_MODULE,
451 .of_match_table = of_match_ptr(rockchip_wm8994_of_match),
453 .probe = rockchip_wm8994_audio_probe,
454 .remove = rockchip_wm8994_audio_remove,
457 module_platform_driver(rockchip_wm8994_audio_driver);
459 /* Module information */
460 MODULE_AUTHOR("rockchip");
461 MODULE_DESCRIPTION("ROCKCHIP i2s ASoC Interface");
462 MODULE_LICENSE("GPL");