Audio: add dts property(i2s format) and delete them from menuconfig, add sound card...
[firefly-linux-kernel-4.4.55.git] / sound / soc / rockchip / rk_rt5640.c
1 /*
2  * rk29_rt5640.c  --  SoC audio for rockchip
3  *
4  * Driver for rockchip rt5640 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/rt5640.h"
24 #include "card_info.h"
25 #include "rk_pcm.h"
26 #include "rk_i2s.h"
27
28
29 #if 0
30 #define DBG(x...)       printk(KERN_INFO x)
31 #else
32 #define DBG(x...)
33 #endif
34
35 static int rk29_hw_params(struct snd_pcm_substream *substream,
36         struct snd_pcm_hw_params *params)
37 {
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 ret;
43
44         DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);    
45
46         switch(params_rate(params)) {
47                 case 8000:
48                 case 16000:
49                 case 24000:
50                 case 32000:
51                 case 48000:
52                         pll_out = 12288000;
53                         break;
54                 case 11025:
55                 case 22050:
56                 case 44100:
57                         pll_out = 11289600;
58                         break;
59                 default:
60                         DBG("Enter:%s, %d, Error rate=%d\n", __FUNCTION__, __LINE__, params_rate(params));
61                         return -EINVAL;
62                         break;
63         }
64
65         DBG("Enter:%s, %d, rate=%d\n", __FUNCTION__, __LINE__, params_rate(params));
66
67         /*Set the system clk for codec*/
68         ret = snd_soc_dai_set_sysclk(codec_dai, 0, pll_out, SND_SOC_CLOCK_IN);
69         if (ret < 0)
70         {
71                 DBG("rk29_hw_params_rt5640:failed to set the sysclk for codec side\n"); 
72                 return ret;
73         }
74
75         snd_soc_dai_set_sysclk(cpu_dai, 0, pll_out, 0);
76         snd_soc_dai_set_clkdiv(cpu_dai, ROCKCHIP_DIV_BCLK, (pll_out/4)/params_rate(params)-1);
77         snd_soc_dai_set_clkdiv(cpu_dai, ROCKCHIP_DIV_MCLK, 3);
78
79         DBG("Enter:%s, %d, pll_out/4/params_rate(params) = %d \n", __FUNCTION__, __LINE__, (pll_out/4)/params_rate(params));
80  
81         return 0;
82 }
83
84 static int rt5640_voice_hw_params(struct snd_pcm_substream *substream,
85         struct snd_pcm_hw_params *params)
86 {
87         struct snd_soc_pcm_runtime *rtd = substream->private_data;
88         struct snd_soc_dai *codec_dai = rtd->codec_dai;
89         struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
90         unsigned int pll_out = 0;
91         int ret;
92
93         DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);    
94        
95         ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_A |
96                 SND_SOC_DAIFMT_IB_NF | SND_SOC_DAIFMT_CBS_CFS ); 
97
98         switch(params_rate(params)) {
99                 case 8000:
100                 case 16000:
101                 case 24000:
102                 case 32000:
103                 case 48000:
104                         pll_out = 12288000;
105                         break;
106                 case 11025:
107                 case 22050:
108                 case 44100:
109                         pll_out = 11289600;
110                         break;
111                 default:
112                         DBG("Enter:%s, %d, Error rate=%d\n", __FUNCTION__, __LINE__, params_rate(params));
113                         return -EINVAL;
114                         break;
115         }
116
117         DBG("Enter:%s, %d, rate=%d\n", __FUNCTION__, __LINE__, params_rate(params));
118
119         /*Set the system clk for codec*/
120         snd_soc_dai_set_pll(codec_dai, 0, RT5640_PLL1_S_MCLK, pll_out, 24576000);
121
122         ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_PLL1, 24576000, SND_SOC_CLOCK_IN);
123
124
125         if (ret < 0) {
126                 printk("rk29_hw_params_rt5640:failed to set the sysclk for codec side\n"); 
127                 return ret;
128         }
129
130         snd_soc_dai_set_sysclk(cpu_dai, 0, pll_out, 0);
131         snd_soc_dai_set_clkdiv(cpu_dai, ROCKCHIP_DIV_BCLK, (pll_out/4)/params_rate(params)-1);
132         snd_soc_dai_set_clkdiv(cpu_dai, ROCKCHIP_DIV_MCLK, 3);
133
134         DBG("Enter:%s, %d, pll_out/4/params_rate(params) = %d \n", __FUNCTION__, __LINE__, (pll_out/4)/params_rate(params));
135  
136         return 0;
137 }
138
139 static const struct snd_soc_dapm_widget rt5640_dapm_widgets[] = {
140         SND_SOC_DAPM_MIC("Mic Jack", NULL),
141         SND_SOC_DAPM_MIC("Headset Jack", NULL), 
142         SND_SOC_DAPM_SPK("Ext Spk", NULL),
143         SND_SOC_DAPM_HP("Headphone Jack", NULL),
144 };
145
146 static const struct snd_soc_dapm_route audio_map[]={
147
148         /* Mic Jack --> MIC_IN*/
149         {"micbias1", NULL, "Mic Jack"},
150         {"MIC1", NULL, "micbias1"},
151         
152         // HP MIC
153         {"micbias1", NULL, "Headset Jack"},
154         {"MIC3", NULL, "micbias1"},
155
156         {"Ext Spk", NULL, "SPOLP"},
157         {"Ext Spk", NULL, "SPOLN"},
158         {"Ext Spk", NULL, "SPORP"},
159         {"Ext Spk", NULL, "SPORN"},
160
161         {"Headphone Jack", NULL, "HPOL"},
162         {"Headphone Jack", NULL, "HPOR"},
163 } ;
164
165 static const struct snd_kcontrol_new rk_controls[] = {
166         SOC_DAPM_PIN_SWITCH("Mic Jack"),
167         SOC_DAPM_PIN_SWITCH("Headset Jack"),
168         SOC_DAPM_PIN_SWITCH("Ext Spk"),
169         SOC_DAPM_PIN_SWITCH("Headphone Jack"),
170 };
171
172 /*
173  * Logic for a rt5640 as connected on a rockchip board.
174  */
175 static int rk29_rt5640_init(struct snd_soc_pcm_runtime *rtd)
176 {
177         struct snd_soc_codec *codec = rtd->codec;
178         struct snd_soc_dapm_context *dapm = &codec->dapm;
179
180         DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
181
182         snd_soc_add_codec_controls(codec, rk_controls,
183                         ARRAY_SIZE(rk_controls));
184
185         /* Add specific widgets */
186         snd_soc_dapm_new_controls(dapm, rt5640_dapm_widgets,
187                                   ARRAY_SIZE(rt5640_dapm_widgets));
188         /* Set up specific audio path audio_mapnects */
189         snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
190
191         snd_soc_dapm_enable_pin(dapm, "Mic Jack");
192         snd_soc_dapm_enable_pin(dapm, "Headset Jack");
193         snd_soc_dapm_enable_pin(dapm, "Ext Spk");
194         snd_soc_dapm_enable_pin(dapm, "Headphone Jack");
195 #ifdef CONFIG_HDMI
196         extern int hdmi_is_insert(void);
197         extern void codec_set_spk(bool on);         
198         if(hdmi_is_insert())                 
199                 codec_set_spk(false);
200 #endif
201
202 #ifdef CONFIG_HDMI_RK30
203         extern int hdmi_get_hotplug(void);
204         if(hdmi_get_hotplug() == 2/*HDMI_HPD_ACTIVED*/)
205                 codec_set_spk(false);
206 #endif
207
208         snd_soc_dapm_sync(dapm);
209
210         return 0;
211 }
212
213 static struct snd_soc_ops rk29_ops = {
214         .hw_params = rk29_hw_params,
215 };
216
217 static struct snd_soc_ops rt5640_voice_ops = {
218         .hw_params = rt5640_voice_hw_params,
219 };
220
221 static struct snd_soc_dai_link rk29_dai[] = {
222         {
223                 .name = "RT5640 I2S1",
224                 .stream_name = "RT5640 PCM",
225                 .codec_dai_name = "rt5640-aif1",
226                 .init = rk29_rt5640_init,
227                 .ops = &rk29_ops,
228         },
229         {
230                 .name = "RT5640 I2S2",
231                 .stream_name = "RT5640 PCM",
232                 .codec_dai_name = "rt5640-aif2",
233                 .ops = &rt5640_voice_ops,
234         },
235 };
236
237 static struct snd_soc_card rockchip_rt5640_snd_card = {
238         .name = "RK_RT5640",
239         .dai_link = rk29_dai,
240         .num_links = 2,
241 };
242
243 static int rockchip_rt5640_audio_probe(struct platform_device *pdev)
244 {
245         int ret;
246         struct snd_soc_card *card = &rockchip_rt5640_snd_card;
247
248         card->dev = &pdev->dev;
249
250         ret = rockchip_of_get_sound_card_info(card);
251         if (ret) {
252                 printk("%s() get sound card info failed:%d\n", __FUNCTION__, ret);
253                 return ret;
254         }
255
256         ret = snd_soc_register_card(card);
257         if (ret)
258                 printk("%s() register card failed:%d\n", __FUNCTION__, ret);
259
260         return ret;
261 }
262
263 static int rockchip_rt5640_audio_remove(struct platform_device *pdev)
264 {
265         struct snd_soc_card *card = platform_get_drvdata(pdev);
266
267         snd_soc_unregister_card(card);
268
269         return 0;
270 }
271
272 #ifdef CONFIG_OF
273 static const struct of_device_id rockchip_rt5640_of_match[] = {
274         { .compatible = "rockchip-rt5640", },
275         {},
276 };
277 MODULE_DEVICE_TABLE(of, rockchip_rt5640_of_match);
278 #endif /* CONFIG_OF */
279
280 static struct platform_driver rockchip_rt5640_audio_driver = {
281         .driver         = {
282                 .name   = "rockchip-rt5640",
283                 .owner  = THIS_MODULE,
284                 .of_match_table = of_match_ptr(rockchip_rt5640_of_match),
285         },
286         .probe          = rockchip_rt5640_audio_probe,
287         .remove         = rockchip_rt5640_audio_remove,
288 };
289
290 module_platform_driver(rockchip_rt5640_audio_driver);
291
292 /* Module information */
293 MODULE_AUTHOR("rockchip");
294 MODULE_DESCRIPTION("ROCKCHIP i2s ASoC Interface");
295 MODULE_LICENSE("GPL");