be49e1dfb97a61bc0072c39f538f1c97f9cdb298
[firefly-linux-kernel-4.4.55.git] / sound / soc / rockchip / rk_rk616.c
1 /*
2  * rk_rk616.c  --  SoC audio for rockchip
3  *
4  * Driver for rockchip rk616 audio
5  *
6  *  This program is free software; you can redistribute  it and/or modify it
7  *  under  the terms of  the GNU General  Public License as published by the
8  *  Free Software Foundation;  either version 2 of the  License, or (at your
9  *  option) any later version.
10  *
11  *
12  */
13
14 #include <linux/module.h>
15 #include <linux/device.h>
16 #include <linux/of.h>
17 #include <linux/of_gpio.h>
18 #include <sound/core.h>
19 #include <sound/pcm.h>
20 #include <sound/soc.h>
21 #include <sound/soc-dapm.h>
22
23 #include "../codecs/rk616_codec.h"
24 #include "card_info.h"
25 #include "rk_pcm.h"
26 #include "rk_i2s.h"
27
28 #if 1
29 #define DBG(x...)       printk(KERN_INFO x)
30 #else
31 #define DBG(x...)
32 #endif
33
34 static const struct snd_soc_dapm_widget rk_dapm_widgets[] = {
35         SND_SOC_DAPM_MIC("Mic Jack", NULL),
36         SND_SOC_DAPM_MIC("Headset Jack", NULL),
37         SND_SOC_DAPM_SPK("Ext Spk", NULL),
38         SND_SOC_DAPM_HP("Headphone Jack", NULL),
39 };
40
41 static const struct snd_soc_dapm_route rk_audio_map[]={
42
43         /* Mic Jack --> MIC_IN*/
44         {"Mic1 Bias", NULL, "Mic Jack"},
45         {"MIC1P", NULL, "Mic1 Bias"},
46         {"MIC1N", NULL, "Mic1 Bias"},
47
48         // HP MIC
49         {"Mic2 Bias", NULL, "Headset Jack"},
50         {"MIC2P", NULL, "Mic2 Bias"},
51         {"MIC2N", NULL, "Mic2 Bias"},
52
53         {"Ext Spk", NULL, "SPKOUTR"},
54         {"Ext Spk", NULL, "SPKOUTL"},
55
56         {"Headphone Jack", NULL, "HPOUTR"},
57         {"Headphone Jack", NULL, "HPOUTL"},
58 } ;
59
60 static const struct snd_kcontrol_new rk_controls[] = {
61         SOC_DAPM_PIN_SWITCH("Mic Jack"),
62         SOC_DAPM_PIN_SWITCH("Headset Jack"),
63         SOC_DAPM_PIN_SWITCH("Ext Spk"),
64         SOC_DAPM_PIN_SWITCH("Headphone Jack"),
65 };
66
67 static int rk616_init(struct snd_soc_pcm_runtime *rtd)
68 {
69         struct snd_soc_codec *codec = rtd->codec;
70         struct snd_soc_dapm_context *dapm = &codec->dapm;
71
72         // if is for mid that using tiny alsa, 
73         // it don't need this controls and route, so return.
74         if (rk616_get_for_mid())
75                 return 0;
76
77         DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
78
79         snd_soc_add_codec_controls(codec, rk_controls,
80                         ARRAY_SIZE(rk_controls));
81
82         /* Add specific widgets */
83         snd_soc_dapm_new_controls(dapm, rk_dapm_widgets,
84                                   ARRAY_SIZE(rk_dapm_widgets));
85         /* Set up specific audio path audio_mapnects */
86         snd_soc_dapm_add_routes(dapm, rk_audio_map, ARRAY_SIZE(rk_audio_map));
87
88         snd_soc_dapm_enable_pin(dapm, "Mic Jack");
89         snd_soc_dapm_enable_pin(dapm, "Headset Jack");
90         snd_soc_dapm_enable_pin(dapm, "Ext Spk");
91         snd_soc_dapm_enable_pin(dapm, "Headphone Jack");
92
93         snd_soc_dapm_sync(dapm);
94
95         return 0;
96 }
97
98 static int rk_hifi_hw_params(struct snd_pcm_substream *substream,
99         struct snd_pcm_hw_params *params)
100 {
101         struct snd_soc_pcm_runtime *rtd = substream->private_data;
102         struct snd_soc_dai *codec_dai = rtd->codec_dai;
103         struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
104         unsigned int pll_out = 0, div = 4, dai_fmt = rtd->dai_link->dai_fmt;
105         int ret;
106
107         DBG("Enter::%s----%d\n", __FUNCTION__, __LINE__);
108
109         /* set codec DAI configuration */
110         ret = snd_soc_dai_set_fmt(codec_dai, dai_fmt);
111         if (ret < 0) {
112                 printk("%s():failed to set the format for codec side\n", __FUNCTION__);
113                 return ret;
114         }
115
116         /* set cpu DAI configuration */
117         ret = snd_soc_dai_set_fmt(cpu_dai, dai_fmt);
118         if (ret < 0) {
119                 printk("%s():failed to set the format for cpu side\n", __FUNCTION__);
120                 return ret;
121         }
122
123         switch(params_rate(params)) {
124                 case 16000:
125                 case 24000:
126                 case 32000:
127                 case 48000:
128                         pll_out = 12288000;
129                         break;
130                 case 11025:
131                 case 22050:
132                 case 44100:
133                         pll_out = 11289600;
134                         break;
135                 case 8000:
136                         pll_out = 12000000;
137                         div = 6;
138                         break;
139                 default:
140                         DBG("Enter:%s, %d, Error rate=%d\n", __FUNCTION__, __LINE__, params_rate(params));
141                         return -EINVAL;
142                         break;
143         }
144
145         DBG("Enter:%s, %d, rate=%d\n", __FUNCTION__, __LINE__, params_rate(params));
146
147         #if defined(CONFIG_RK616_USE_MCLK_12M)
148         /* MCLK must be 12M when RK616 HDMI is in */
149         if (get_hdmi_state() && pll_out != 12000000) {
150                 DBG("%s : HDMI is in, don't set sys clk %u\n",__FUNCTION__, pll_out);
151                 goto __setdiv;
152         }
153         #endif
154
155         /* Set the system clk for codec
156            mclk will be setted in set_sysclk of codec_dai*/
157         ret = snd_soc_dai_set_sysclk(codec_dai, 0, pll_out, SND_SOC_CLOCK_IN);
158         if (ret < 0) {
159                 DBG("rk_hifi_hw_params:failed to set the sysclk for codec side\n");
160                 return ret;
161         }
162 #if defined(CONFIG_RK616_USE_MCLK_12M)
163 __setdiv:
164 #endif
165         snd_soc_dai_set_clkdiv(cpu_dai, ROCKCHIP_DIV_BCLK, (pll_out / div)/params_rate(params)-1);
166         snd_soc_dai_set_clkdiv(cpu_dai, ROCKCHIP_DIV_MCLK, div - 1);
167
168         DBG("Enter:%s, %d, pll_out/div/params_rate(params) = %d \n", __FUNCTION__, __LINE__, (pll_out/div)/params_rate(params));
169
170         return 0;
171 }
172
173 static int rk_voice_hw_params(struct snd_pcm_substream *substream,
174         struct snd_pcm_hw_params *params)
175 {
176         struct snd_soc_pcm_runtime *rtd = substream->private_data;
177         struct snd_soc_dai *codec_dai = rtd->codec_dai;
178         struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
179         unsigned int pll_out = 0, dai_fmt = rtd->dai_link->dai_fmt;
180         int ret;
181
182         DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
183
184         /* set codec DAI configuration */
185         ret = snd_soc_dai_set_fmt(codec_dai, dai_fmt);
186         if (ret < 0) {
187                 printk("rk_voice_hw_params:failed to set the format for codec side\n");
188                 return ret;
189         }
190
191         switch(params_rate(params)) {
192                 case 8000:
193                 case 16000:
194                 case 24000:
195                 case 32000:
196                 case 48000:
197                         pll_out = 12288000;
198                         break;
199                 case 11025:
200                 case 22050:
201                 case 44100:
202                         pll_out = 11289600;
203                         break;
204                 default:
205                         DBG("Enter:%s, %d, Error rate=%d\n", __FUNCTION__, __LINE__, params_rate(params));
206                         return -EINVAL;
207                         break;
208         }
209
210         /* MCLK must be 12M when RK616 HDMI is in */
211         #if defined(CONFIG_RK616_USE_MCLK_12M)
212         if (get_hdmi_state() && pll_out != 12000000) {
213                 DBG("%s : HDMI is in, set mclk to 12Mn",__FUNCTION__);
214                 pll_out = 12000000;
215         }
216         #endif
217
218         //snd_soc_dai_set_pll(codec_dai, RT5625_PLL_MCLK_TO_VSYSCLK, 0, pll_out, 24576000);
219
220         /*Set the system clk for codec*/
221         ret = snd_soc_dai_set_sysclk(codec_dai, 0, pll_out, SND_SOC_CLOCK_IN);
222         if (ret < 0) {
223                 printk("rk_voice_hw_params:failed to set the sysclk for codec side\n");
224                 return ret;
225         }
226
227         ret = snd_soc_dai_set_sysclk(cpu_dai, 0, pll_out, 0);
228         if (ret < 0) {
229                 printk("rk_voice_hw_params:failed to set the sysclk for cpu side\n");
230                 return ret;
231         }
232
233         return 0;
234 }
235
236 static struct snd_soc_ops rk616_hifi_ops = {
237         .hw_params = rk_hifi_hw_params,
238 };
239
240 static struct snd_soc_ops rk616_voice_ops = {
241         .hw_params = rk_voice_hw_params,
242 };
243
244 static struct snd_soc_dai_link rk_dai[] = {
245         {
246                 .name = "RK616 I2S1",
247                 .stream_name = "RK616 PCM",
248                 .codec_dai_name = "rk616-hifi",
249                 .init = rk616_init,
250                 .ops = &rk616_hifi_ops,
251         },
252         {
253                 .name = "RK616 I2S2",
254                 .stream_name = "RK616 PCM",
255                 .codec_dai_name = "rk616-voice",
256                 .ops = &rk616_voice_ops,
257                 //.no_pcm = 1,
258         },
259 };
260
261 static struct snd_soc_card rockchip_rk616_snd_card = {
262         .name = "RK_RK616",
263         .dai_link = rk_dai,
264         .num_links = 2,
265 };
266
267 static int rockchip_rk616_audio_probe(struct platform_device *pdev)
268 {
269         int ret;
270         struct snd_soc_card *card = &rockchip_rk616_snd_card;
271
272         card->dev = &pdev->dev;
273
274         ret = rockchip_of_get_sound_card_info(card);
275         if (ret) {
276                 printk("%s() get sound card info failed:%d\n", __FUNCTION__, ret);
277                 return ret;
278         }
279
280         ret = snd_soc_register_card(card);
281         if (ret)
282                 printk("%s() register card failed:%d\n", __FUNCTION__, ret);
283
284         return ret;
285 }
286
287 static int rockchip_rk616_audio_remove(struct platform_device *pdev)
288 {
289         struct snd_soc_card *card = platform_get_drvdata(pdev);
290
291         snd_soc_unregister_card(card);
292
293         return 0;
294 }
295
296 #ifdef CONFIG_OF
297 static const struct of_device_id rockchip_rk616_of_match[] = {
298         { .compatible = "rockchip-rk616", },
299         {},
300 };
301 MODULE_DEVICE_TABLE(of, rockchip_rk616_of_match);
302 #endif /* CONFIG_OF */
303
304 static struct platform_driver rockchip_rk616_audio_driver = {
305         .driver         = {
306                 .name   = "rockchip-rk616",
307                 .owner  = THIS_MODULE,
308                 .of_match_table = of_match_ptr(rockchip_rk616_of_match),
309         },
310         .probe          = rockchip_rk616_audio_probe,
311         .remove         = rockchip_rk616_audio_remove,
312 };
313
314 module_platform_driver(rockchip_rk616_audio_driver);
315
316 /* Module information */
317 MODULE_AUTHOR("rockchip");
318 MODULE_DESCRIPTION("ROCKCHIP i2s ASoC Interface");
319 MODULE_LICENSE("GPL");