34824792757513ddae068fdfe57265375b75ce7f
[firefly-linux-kernel-4.4.55.git] / sound / soc / rockchip / rk_rt5512.c
1 /*
2  *  odroid_rt5512.c
3  *
4  *  This program is free software; you can redistribute  it and/or modify it
5  *  under  the terms of  the GNU General  Public License as published by the
6  *  Free Software Foundation;  either version 2 of the  License, or (at your
7  *  option) any later version.
8  */
9 #include <linux/module.h>
10 #include <linux/device.h>
11 #include <linux/of.h>
12 #include <linux/of_gpio.h>
13 #include <sound/core.h>
14 #include <sound/pcm.h>
15 #include <sound/soc.h>
16 #include <sound/soc-dapm.h>
17 #include <sound/jack.h>
18 #include <linux/delay.h>    
19 #include "rk_pcm.h"
20 #include "rk29_i2s.h"
21 #if 1
22 #define DBG(x...)       printk(KERN_INFO x)
23 #else
24 #define DBG(x...)
25 #endif
26
27 #include "../codecs/rt5512.h"
28
29 static struct platform_device *rk29_snd_device;
30
31
32 static int rk29_hw_params(struct snd_pcm_substream *substream,
33         struct snd_pcm_hw_params *params)
34 {
35         struct snd_soc_pcm_runtime *rtd = substream->private_data;
36         struct snd_soc_dai *codec_dai = rtd->codec_dai;
37         struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
38         unsigned int pll_out = 0;
39         unsigned int pll_div;
40         int ret;
41
42         DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);    
43         
44                 /* set cpu DAI configuration */
45                 #if defined (CONFIG_SND_RK_CODEC_SOC_SLAVE) 
46                      ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
47                                      SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
48                 #endif  
49                 #if defined (CONFIG_SND_RK_CODEC_SOC_MASTER) 
50                      ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
51                                      SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); 
52                 #endif      
53                      if (ret < 0)
54                        return ret;
55         
56                 
57                 /* set codec DAI configuration */
58                 #if defined (CONFIG_SND_RK_CODEC_SOC_SLAVE) 
59                 ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
60                                 SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
61                 #endif  
62                 #if defined (CONFIG_SND_RK_CODEC_SOC_MASTER) 
63                 ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
64                                 SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM ); 
65                 #endif
66                 if (ret < 0)
67                   return ret; 
68
69              
70
71
72         switch(params_rate(params)) {
73         case 8000:
74         case 16000:
75         case 24000:
76         case 32000:
77         case 48000:
78                 pll_out = 12288000;
79                 break;
80         case 11025:
81         case 22050:
82         case 44100:
83                 pll_out = 11289600;
84                 break;
85         case 96000:
86         case 192000:    
87                 pll_out = 12288000*2;
88                 break;          
89         case 88200:
90         case 176400:    
91                 pll_out = 11289600*2;
92                 break;          
93         default:
94                 DBG("Enter:%s, %d, Error rate=%d\n",__FUNCTION__,__LINE__,params_rate(params));
95                 return -EINVAL;
96                 break;
97         }
98 #if defined (CONFIG_SND_RK_CODEC_SOC_SLAVE)
99         snd_soc_dai_set_sysclk(cpu_dai, 0, pll_out, 0);
100         snd_soc_dai_set_clkdiv(cpu_dai, ROCKCHIP_DIV_BCLK, 64-1);//bclk = 2*32*lrck; 2*32fs
101         switch(params_rate(params)) {
102         case 176400:            
103                 case 192000:
104                         snd_soc_dai_set_clkdiv(cpu_dai, ROCKCHIP_DIV_MCLK, 1);  
105         DBG("Enter:%s, %d, MCLK=%d BCLK=%d LRCK=%d\n",
106                 __FUNCTION__,__LINE__,pll_out,pll_out/2,params_rate(params));                   
107                         break;
108                 default:
109                         snd_soc_dai_set_clkdiv(cpu_dai, ROCKCHIP_DIV_MCLK, 3);  
110         DBG("default:%s, %d, MCLK=%d BCLK=%d LRCK=%d\n",
111                 __FUNCTION__,__LINE__,pll_out,pll_out/4,params_rate(params));                   
112                         break;
113         }
114
115     /*Set the system clk for codec*/
116         ret=snd_soc_dai_set_sysclk(codec_dai, 0,pll_out,SND_SOC_CLOCK_IN);
117         if (ret < 0)
118         {
119                 DBG("rk29_hw_params_rt5512:failed to set the sysclk for codec side\n"); 
120                 return ret;
121         }           
122
123     switch (params_rate(params))
124         {
125                 case 8000:
126                         pll_div = 12;
127                         break;
128                 case 16000:
129                         pll_div = 6;
130                         break;
131                 case 32000:
132                         pll_div = 3;
133                         break;
134                 case 48000:
135                         pll_div = 2;
136                         break;
137                 case 96000:
138                         pll_div = 1;
139                         break;
140                 case 11025:
141                         pll_div = 8;
142                         break;
143                 case 22050:
144                         pll_div = 4;
145                         break;
146                 case 44100:
147                         pll_div = 2;
148                         break;
149                 case 88200:
150                         pll_div = 1;
151                         break;
152                 default:
153                         printk("Not yet supported!\n");
154                         return -EINVAL;
155         }
156         ret = snd_soc_dai_set_clkdiv(codec_dai, RT5512_CLK_DIV_ID, pll_div*4);
157         if (ret < 0)
158                 return ret;     
159 #endif
160
161 #if defined (CONFIG_SND_RK_CODEC_SOC_MASTER) 
162         //snd_soc_dai_set_pll(codec_dai,0,pll_out, 22579200);
163         snd_soc_dai_set_sysclk(codec_dai,0,pll_out, SND_SOC_CLOCK_IN);                                                
164 #endif       
165         return 0;
166 }
167
168 //---------------------------------------------------------------------------------
169 /*
170  * rt5512 DAI operations.
171  */
172 static struct snd_soc_ops rk29_ops = {
173         .hw_params = rk29_hw_params,
174 };
175
176 static const struct snd_soc_dapm_widget rt5512_dapm_widgets[] = {
177         // Input
178         SND_SOC_DAPM_MIC("Main Mic", NULL),
179         SND_SOC_DAPM_LINE("LineIn", NULL),
180         // Output
181         SND_SOC_DAPM_SPK("Ext Spk", NULL),
182         SND_SOC_DAPM_HP("Headphone Jack", NULL),
183 };
184
185 static const struct snd_soc_dapm_route rt5512_audio_map[] = {
186         // Input
187         {"MicBias1", NULL,"Main Mic"},
188         {"Mic2", NULL, "MicBias1"},
189     {"MicBias2", NULL, "LineIn"},
190     {"Aux", NULL, "MicBias2"},
191         // Output
192         {"Ext Spk", NULL, "LSpeaker"},
193         {"Ext Spk", NULL, "RSpeaker"},
194         {"Headphone Jack", NULL, "LHeadphone"},
195         {"Headphone Jack", NULL, "RHeadphone"},
196 };
197
198 #if 0
199
200 static struct snd_soc_jack rk29_soc_jack;
201
202
203
204 static struct snd_soc_jack_gpio odroid_soc_jack_gpio[] = {
205         {
206                 .gpio = 28,
207                 .name "headset event",
208                 .report = SND_JACK_HEADSET,
209                 .debounce_time = 200,
210         },
211 };
212 #endif
213
214 #if 0
215 static int rt5512_headset_keys(struct snd_soc_jack *jack)
216 {
217         int err = 0;
218
219         err = snd_jack_set_key(jack->jack, SND_JACK_BTN_0, 0x80);
220         if (err)
221                 return err;
222
223         err = snd_jack_set_key(jack->jack, SND_JACK_BTN_1, 0x81);
224         if (err)
225                 return err;
226
227         err = snd_jack_set_key(jack->jack, SND_JACK_BTN_2, 0x82);
228         if (err)
229                 return err;
230
231         return 0;
232 }
233 #endif
234
235 static int rt5512_init(struct snd_soc_pcm_runtime *rtd)
236 {
237         struct snd_soc_codec *codec = rtd->codec;
238         struct snd_soc_dapm_context *dapm = &codec->dapm;
239         //struct rt5512_codec_chip *chip = snd_soc_codec_get_drvdata(codec);
240         //int err = 0;
241
242         snd_soc_dapm_new_controls(dapm, rt5512_dapm_widgets,
243                                 ARRAY_SIZE(rt5512_dapm_widgets));
244
245         snd_soc_dapm_add_routes(dapm, rt5512_audio_map,
246                                 ARRAY_SIZE(rt5512_audio_map));
247 #if FOR_MID
248     snd_soc_dapm_disable_pin(dapm, "Main Mic");
249         snd_soc_dapm_disable_pin(dapm, "LineIn");
250         snd_soc_dapm_disable_pin(dapm, "Ext Spk");
251         snd_soc_dapm_disable_pin(dapm, "Headphone Jack");
252 #endif
253
254 #if 0
255         if (!chip->rt_jack)
256         {
257                 err = snd_soc_jack_new(codec, "Headset Jack" , SND_JACK_HEADSET, &rk29_soc_jack);
258                 if (err)
259                         return err;
260
261                 #if 0
262                 // How-to use gpio, just declare snd_soc_jack_gpios, then it will
263                 // help you to register a interrupt and set wakeup, and delayed schedule
264                 // work
265                 err = snd_soc_jack_add_gpios(&odroid_soc_jack, gpio_count, odroid_soc_jack_gpios);
266                 if (err)
267                         return err;
268
269                 // If  use this, when trigger, just use snd_soc_jack_get_type
270                 // then snd_soc_jack_report to send the event to upper layer
271                 err = snd_soc_jack_add_zones(&odroid_soc_jack, zone_count, tcc_soc_zones);
272                 if (err)
273                         return err;
274                 #endif
275
276                 err = rt5512_headset_keys(&rk29_soc_jack);
277                 if (err)
278                         return err;
279
280                 chip->rt_jack = &rk29_soc_jack;
281         }
282 #endif
283         snd_soc_dapm_sync(dapm);
284         return 0;
285 }
286
287 static struct snd_soc_dai_link rk29_dai[] = {
288         { /* Primary DAI i/f */
289                 .name = "RT5512 AIF1",
290                 .stream_name = "RT5512 PCM",
291                 .cpu_dai_name = "rockchip-i2s.1",
292                 .codec_dai_name = "RT5512-aif1",
293                 .platform_name = "rockchip-pcm",
294                 .codec_name = "rt5512.1-0018",
295                 .init = rt5512_init,
296                 .ops = &rk29_ops,
297         },
298 };
299
300 static struct snd_soc_card rockchip_rt5512_snd_card = {
301         .name = "RK_RT5512",
302         .dai_link = rk29_dai,
303
304         /* If you want to use sec_fifo device,
305          * changes the num_link = 2 or ARRAY_SIZE(odroid_dai). */
306         .num_links = ARRAY_SIZE(rk29_dai),
307 };
308
309 static int rockchip_rt5512_audio_probe(struct platform_device *pdev)
310 {
311         int ret;
312         struct snd_soc_card *card = &rockchip_rt5512_snd_card;
313
314         card->dev = &pdev->dev;
315
316         ret = snd_soc_register_card(card);
317
318         if (ret)
319                 printk("%s() register card failed:%d\n", __FUNCTION__, ret);
320
321         return ret;
322 }
323
324 static int rockchip_rt5512_audio_remove(struct platform_device *pdev)
325 {
326         struct snd_soc_card *card = platform_get_drvdata(pdev);
327
328         snd_soc_unregister_card(card);
329
330         return 0;
331 }
332
333 #ifdef CONFIG_OF
334 static const struct of_device_id rockchip_rt5512_of_match[] = {
335         { .compatible = "rockchip-rt5512", },
336         {},
337 };
338 MODULE_DEVICE_TABLE(of, rockchip_rt5512_of_match);
339 #endif /* CONFIG_OF */
340
341 static struct platform_driver rockchip_rt5512_audio_driver = {
342         .driver         = {
343                 .name   = "rockchip-rt5512",
344                 .owner  = THIS_MODULE,
345                 .of_match_table = of_match_ptr(rockchip_rt5512_of_match),
346         },
347         .probe          = rockchip_rt5512_audio_probe,
348         .remove         = rockchip_rt5512_audio_remove,
349 };
350
351 module_platform_driver(rockchip_rt5512_audio_driver);
352
353 MODULE_DESCRIPTION("ROCKCHIP i2s ASoC Interface");
354 MODULE_AUTHOR("cy_huang <cy_huang@richtek.com>");
355 MODULE_LICENSE("GPL");