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"
25 #include "card_info.h"
28 #include <linux/clk.h>
31 #define DBG(x...) printk(KERN_INFO x)
36 static int rk29_aif1_hw_params(struct snd_pcm_substream *substream,
37 struct snd_pcm_hw_params *params)
39 struct snd_soc_pcm_runtime *rtd = substream->private_data;
40 struct snd_soc_dai *codec_dai = rtd->codec_dai;
41 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
42 unsigned int pll_out = 0, dai_fmt = rtd->dai_link->dai_fmt;
43 int div_bclk,div_mclk;
45 struct clk *general_pll;
47 DBG("Enter::%s----%d\n", __FUNCTION__, __LINE__);
49 /* set codec DAI configuration */
50 ret = snd_soc_dai_set_fmt(codec_dai, dai_fmt);
52 printk("%s():failed to set the format for codec side\n", __FUNCTION__);
56 /* set cpu DAI configuration */
57 ret = snd_soc_dai_set_fmt(cpu_dai, dai_fmt);
59 printk("%s():failed to set the format for cpu side\n", __FUNCTION__);
63 switch(params_rate(params)) {
77 DBG("Enter:%s, %d, Error rate=%d\n",__FUNCTION__,__LINE__,params_rate(params));
81 // DBG("Enter:%s, %d, rate=%d,pll_out = %d\n",__FUNCTION__,__LINE__,params_rate(params),pll_out);
82 #ifdef CONFIG_ARCH_RK29
83 general_pll=clk_get(NULL, "general_pll");
84 if(clk_get_rate(general_pll)>260000000)
86 div_bclk=(pll_out/4)/params_rate(params)-1;
89 else if(clk_get_rate(general_pll)>130000000)
91 div_bclk=(pll_out/2)/params_rate(params)-1;
97 div_bclk=(pll_out)/params_rate(params)-1;
101 div_bclk=(pll_out/4)/params_rate(params)-1;
105 DBG("func is%s,gpll=%ld,pll_out=%d,div_mclk=%d\n",__FUNCTION__,clk_get_rate(general_pll),pll_out,div_mclk);
106 ret = snd_soc_dai_set_sysclk(cpu_dai, 0, pll_out, 0);
109 DBG("rk29_hw_params_wm8994:failed to set the cpu sysclk for codec side\n");
112 snd_soc_dai_set_clkdiv(cpu_dai, ROCKCHIP_DIV_BCLK,div_bclk);
113 snd_soc_dai_set_clkdiv(cpu_dai, ROCKCHIP_DIV_MCLK, div_mclk);
114 DBG("Enter:%s, %d, LRCK=%d\n",__FUNCTION__,__LINE__,(pll_out/4)/params_rate(params));
117 {//MCLK == 11289600 or 12288000
118 ret = snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_MCLK1, pll_out, 0);
120 DBG("rk29_hw_params_wm8994:failed to set the sysclk for codec side\n");
126 /* set the codec FLL */
127 ret = snd_soc_dai_set_pll(codec_dai, WM8994_FLL1, WM8994_FLL_SRC_MCLK1, pll_out,
128 params_rate(params) * 256);
131 printk("%s: snd_soc_dai_set_pll err =%d\n",__FUNCTION__,ret);
134 /* set the codec system clock */
135 ret = snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_FLL1,
136 params_rate(params) * 256, SND_SOC_CLOCK_IN);
139 printk("%s: snd_soc_dai_set_sysclk err =%d\n",__FUNCTION__,ret);
147 static int rk29_aif2_hw_params(struct snd_pcm_substream *substream,
148 struct snd_pcm_hw_params *params)
150 struct snd_soc_pcm_runtime *rtd = substream->private_data;
151 struct snd_soc_dai *codec_dai = rtd->codec_dai;
152 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
153 unsigned int pll_out = 0;
154 int div_bclk,div_mclk;
156 struct clk *general_pll;
159 // params->intervals[SNDRV_PCM_HW_PARAM_RATE - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL].min = 8000;
161 DBG("Enter:%s, %d, rate=%d\n",__FUNCTION__,__LINE__,params_rate(params));
163 // if (params_rate(params) != 8000)
166 /* set codec DAI configuration */
167 ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_A |
168 SND_SOC_DAIFMT_IB_NF | SND_SOC_DAIFMT_CBM_CFM);
171 printk("%s: snd_soc_dai_set_fmt err =%d\n",__FUNCTION__,ret);
174 switch(params_rate(params)) {
188 DBG("Enter:%s, %d, Error rate=%d\n",__FUNCTION__,__LINE__,params_rate(params));
192 general_pll=clk_get(NULL, "general_pll");
193 if(clk_get_rate(general_pll)>260000000)
195 div_bclk=(pll_out/4)/params_rate(params)-1;
198 else if(clk_get_rate(general_pll)>130000000)
200 div_bclk=(pll_out/2)/params_rate(params)-1;
206 div_bclk=(pll_out)/params_rate(params)-1;
210 DBG("func is%s,gpll=%ld,pll_out=%d,div_mclk=%d\n",
211 __FUNCTION__,clk_get_rate(general_pll),pll_out,div_mclk);
213 ret = snd_soc_dai_set_sysclk(cpu_dai, 0, pll_out, 0);
216 DBG("rk29_hw_params_wm8994:failed to set the cpu sysclk for codec side\n");
219 snd_soc_dai_set_clkdiv(cpu_dai, ROCKCHIP_DIV_BCLK,div_bclk);
220 snd_soc_dai_set_clkdiv(cpu_dai, ROCKCHIP_DIV_MCLK, div_mclk);
221 DBG("Enter:%s, %d, LRCK=%d\n",__FUNCTION__,__LINE__,(pll_out/4)/params_rate(params));
223 /* set the codec FLL */
224 ret = snd_soc_dai_set_pll(codec_dai, WM8994_FLL2, WM8994_FLL_SRC_MCLK1, pll_out,
228 printk("%s: snd_soc_dai_set_pll err =%d\n",__FUNCTION__,ret);
231 /* set the codec system clock */
232 ret = snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_FLL2,
233 8000 * 256, SND_SOC_CLOCK_IN);
236 printk("%s: snd_soc_dai_set_sysclk err =%d\n",__FUNCTION__,ret);
244 static const struct snd_soc_dapm_widget rk29_dapm_widgets[] = {
245 SND_SOC_DAPM_SPK("Ext Left Spk", NULL),
246 SND_SOC_DAPM_SPK("Ext Right Spk", NULL),
247 SND_SOC_DAPM_SPK("Ext Rcv", NULL),
248 SND_SOC_DAPM_HP("Headset Stereophone", NULL),
249 SND_SOC_DAPM_MIC("Headset Mic", NULL),
250 SND_SOC_DAPM_MIC("Main Mic", NULL),
251 SND_SOC_DAPM_MIC("2nd Mic", NULL),
252 // SND_SOC_DAPM_LINE("Radio In", NULL),
253 SND_SOC_DAPM_LINE("Line In", NULL),
254 SND_SOC_DAPM_LINE("Line Out", NULL),
258 static const struct snd_soc_dapm_route rk29_dapm_routes[] = {
259 {"Ext Left Spk", NULL, "SPKOUTLP"},
260 {"Ext Left Spk", NULL, "SPKOUTLN"},
262 {"Ext Right Spk", NULL, "SPKOUTRP"},
263 {"Ext Right Spk", NULL, "SPKOUTRN"},
265 {"Ext Rcv", NULL, "HPOUT2N"},
266 {"Ext Rcv", NULL, "HPOUT2P"},
268 {"Headset Stereophone", NULL, "HPOUT1L"},
269 {"Headset Stereophone", NULL, "HPOUT1R"},
271 {"IN1LN", NULL, "Headset Mic"},
272 {"IN1LP", NULL, "Headset Mic"},
274 {"IN1LN", NULL, "2nd Mic"},
275 {"IN1LP", NULL, "2nd Mic"},
277 {"IN1RN", NULL, "Main Mic"},
278 {"IN1RP", NULL, "Main Mic"},
280 // {"IN2LN", NULL, "Radio In"},
281 // {"IN2RN", NULL, "Radio In"},
283 {"IN2LP:VXRN", NULL, "Line In"},
284 {"IN2RP:VXRP", NULL, "Line In"},
286 {"Line Out", NULL, "LINEOUT1N"},
287 {"Line Out", NULL, "LINEOUT1P"},
291 static int rk29_wm8994_init(struct snd_soc_pcm_runtime *rtd)
293 struct snd_soc_codec *codec = rtd->codec;
294 struct snd_soc_dapm_context *dapm = &codec->dapm;
296 DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
298 /* add goni specific widgets */
299 snd_soc_dapm_new_controls(dapm, rk29_dapm_widgets,
300 ARRAY_SIZE(rk29_dapm_widgets));
302 /* set up goni specific audio routes */
303 snd_soc_dapm_add_routes(dapm, rk29_dapm_routes,
304 ARRAY_SIZE(rk29_dapm_routes));
306 /* set endpoints to not connected */
307 // snd_soc_dapm_nc_pin(dapm, "IN2LP:VXRN");
308 // snd_soc_dapm_nc_pin(dapm, "IN2RP:VXRP");
309 snd_soc_dapm_nc_pin(dapm, "IN2LN");
310 snd_soc_dapm_nc_pin(dapm, "IN2RN");
311 // snd_soc_dapm_nc_pin(dapm, "LINEOUT1N");
312 // snd_soc_dapm_nc_pin(dapm, "LINEOUT1P");
313 snd_soc_dapm_nc_pin(dapm, "LINEOUT2N");
314 snd_soc_dapm_nc_pin(dapm, "LINEOUT2P");
316 snd_soc_dapm_sync(dapm);
318 /* Headset jack detection */
319 /* ret = snd_soc_jack_new(codec, "Headset Jack",
320 SND_JACK_HEADSET | SND_JACK_MECHANICAL | SND_JACK_AVOUT,
325 ret = snd_soc_jack_add_pins(&jack, ARRAY_SIZE(jack_pins), jack_pins);
329 ret = snd_soc_jack_add_gpios(&jack, ARRAY_SIZE(jack_gpios), jack_gpios);
337 static struct snd_soc_ops rk29_aif1_ops = {
338 .hw_params = rk29_aif1_hw_params,
341 static struct snd_soc_ops rk29_aif2_ops = {
342 .hw_params = rk29_aif2_hw_params,
345 static struct snd_soc_dai_driver voice_dai = {
346 .name = "rk29-voice-dai",
351 .rates = SNDRV_PCM_RATE_8000,
352 .formats = SNDRV_PCM_FMTBIT_S16_LE,},
356 .rates = SNDRV_PCM_RATE_8000,
357 .formats = SNDRV_PCM_FMTBIT_S16_LE,},
360 static struct snd_soc_dai_link rk29_dai[] = {
362 .name = "WM8994 I2S1",
363 .stream_name = "WM8994 PCM",
364 .codec_dai_name = "wm8994-aif1",
365 .ops = &rk29_aif1_ops,
366 .init = rk29_wm8994_init,
369 .name = "WM8994 I2S2",
370 .stream_name = "WM8994 PCM",
371 .codec_dai_name = "wm8994-aif2",
372 .ops = &rk29_aif2_ops,
376 static struct snd_soc_card rockchip_wm8994_snd_card = {
378 .dai_link = rk29_dai,
379 .num_links = ARRAY_SIZE(rk29_dai),
382 static int rockchip_wm8994_audio_probe(struct platform_device *pdev)
385 struct snd_soc_card *card = &rockchip_wm8994_snd_card;
387 card->dev = &pdev->dev;
389 ret = rockchip_of_get_sound_card_info(card);
391 printk("%s() get sound card info failed:%d\n", __FUNCTION__, ret);
395 ret = snd_soc_register_card(card);
397 printk("%s() register card failed:%d\n", __FUNCTION__, ret);
402 static int rockchip_wm8994_audio_remove(struct platform_device *pdev)
404 struct snd_soc_card *card = platform_get_drvdata(pdev);
406 snd_soc_unregister_card(card);
412 static const struct of_device_id rockchip_wm8994_of_match[] = {
413 { .compatible = "rockchip-wm8994", },
416 MODULE_DEVICE_TABLE(of, rockchip_wm8994_of_match);
417 #endif /* CONFIG_OF */
419 static struct platform_driver rockchip_wm8994_audio_driver = {
421 .name = "rockchip-wm8994",
422 .owner = THIS_MODULE,
423 .of_match_table = of_match_ptr(rockchip_wm8994_of_match),
425 .probe = rockchip_wm8994_audio_probe,
426 .remove = rockchip_wm8994_audio_remove,
429 module_platform_driver(rockchip_wm8994_audio_driver);
431 /* Module information */
432 MODULE_AUTHOR("rockchip");
433 MODULE_DESCRIPTION("ROCKCHIP i2s ASoC Interface");
434 MODULE_LICENSE("GPL");