ARM64: DTS: Add rk3399-firefly uart4 device, node as /dev/ttyS1
[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...)       pr_info(x)
30 #else
31 #define DBG(x...)
32 #endif
33
34 static bool get_hdmi_state(void)
35 {
36 #ifdef CONFIG_HDMI
37         if (hdmi_is_insert())
38                 return true;
39 #endif
40
41 #ifdef CONFIG_HDMI_RK30
42         /*HDMI_HPD_ACTIVED*/
43         if (hdmi_get_hotplug() == 2)
44                 return true;
45 #endif
46
47                         return false;
48 }
49
50 static const struct snd_soc_dapm_widget rk_rk616_dapm_widgets[] = {
51         SND_SOC_DAPM_MIC("Mic Jack", NULL),
52         SND_SOC_DAPM_MIC("Headset Jack", NULL),
53         SND_SOC_DAPM_SPK("Ext Spk", NULL),
54         SND_SOC_DAPM_HP("Headphone Jack", NULL),
55 };
56
57 static const struct snd_soc_dapm_route rk_rk616_audio_map[] = {
58
59         /* Mic Jack --> MIC_IN*/
60         {"Mic1 Bias", NULL, "Mic Jack"},
61         {"MIC1P", NULL, "Mic1 Bias"},
62         {"MIC1N", NULL, "Mic1 Bias"},
63
64         /* HP MIC */
65         {"Mic2 Bias", NULL, "Headset Jack"},
66         {"MIC2P", NULL, "Mic2 Bias"},
67         {"MIC2N", NULL, "Mic2 Bias"},
68
69         {"Ext Spk", NULL, "SPKOUTR"},
70         {"Ext Spk", NULL, "SPKOUTL"},
71
72         {"Headphone Jack", NULL, "HPOUTR"},
73         {"Headphone Jack", NULL, "HPOUTL"},
74 };
75
76 static const struct snd_kcontrol_new rk_rk616_controls[] = {
77         SOC_DAPM_PIN_SWITCH("Mic Jack"),
78         SOC_DAPM_PIN_SWITCH("Headset Jack"),
79         SOC_DAPM_PIN_SWITCH("Ext Spk"),
80         SOC_DAPM_PIN_SWITCH("Headphone Jack"),
81 };
82
83 static int rk616_init(struct snd_soc_pcm_runtime *rtd)
84 {
85         struct snd_soc_codec *codec = rtd->codec;
86         struct snd_soc_dapm_context *dapm = &codec->dapm;
87
88         /*
89         * if is for mid that using tiny alsa,
90         * it don't need this controls and route, so return.
91         */
92         if (rk616_get_for_mid())
93                 return 0;
94
95         DBG("%s() %d\n", __func__, __LINE__);
96
97         mutex_lock(&dapm->card->dapm_mutex);
98
99         snd_soc_dapm_enable_pin(dapm, "Mic Jack");
100         snd_soc_dapm_enable_pin(dapm, "Headset Jack");
101         snd_soc_dapm_enable_pin(dapm, "Ext Spk");
102         snd_soc_dapm_enable_pin(dapm, "Headphone Jack");
103
104         mutex_unlock(&dapm->card->dapm_mutex);
105
106         snd_soc_dapm_sync(dapm);
107
108         return 0;
109 }
110
111 static int rk_hifi_hw_params(struct snd_pcm_substream *substream,
112         struct snd_pcm_hw_params *params)
113 {
114         struct snd_soc_pcm_runtime *rtd = substream->private_data;
115         struct snd_soc_dai *codec_dai = rtd->codec_dai;
116         struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
117         unsigned int pll_out = 0, div = 4, dai_fmt = rtd->dai_link->dai_fmt;
118         int ret;
119
120         DBG("%s() %d\n", __func__, __LINE__);
121
122         /* set codec DAI configuration */
123         ret = snd_soc_dai_set_fmt(codec_dai, dai_fmt);
124         if (ret < 0) {
125                 pr_err("%s():failed to set the format for codec side\n",
126                         __func__);
127                 return ret;
128         }
129
130         /* set cpu DAI configuration */
131         ret = snd_soc_dai_set_fmt(cpu_dai, dai_fmt);
132         if (ret < 0) {
133                 pr_err("%s():failed to set the format for cpu side\n",
134                         __func__);
135                 return ret;
136         }
137
138         switch (params_rate(params)) {
139         case 16000:
140         case 24000:
141         case 32000:
142         case 48000:
143                 pll_out = 12288000;
144                 break;
145         case 11025:
146         case 22050:
147         case 44100:
148                 pll_out = 11289600;
149                 break;
150         case 8000:
151                 pll_out = 12000000;
152                 div = 6;
153                 break;
154         default:
155                 DBG("Enter:%s, %d, Error rate=%d\n",
156                         __func__, __LINE__,
157                         params_rate(params));
158                 return -EINVAL;
159                 break;
160         }
161
162         DBG("Enter:%s, %d, rate=%d\n",
163                 __func__, __LINE__,
164                 params_rate(params));
165
166         #if defined(CONFIG_RK616_USE_MCLK_12M)
167         /* MCLK must be 12M when RK616 HDMI is in */
168         if (get_hdmi_state() && pll_out != 12000000) {
169                 DBG("%s : HDMI is in, don't set sys clk %u\n",
170                         __func__, pll_out);
171                 goto __setdiv;
172         }
173         #endif
174
175         /* Set the system clk for codec
176            mclk will be setted in set_sysclk of codec_dai*/
177         ret = snd_soc_dai_set_sysclk(codec_dai, 0,
178                 pll_out, SND_SOC_CLOCK_IN);
179         if (ret < 0) {
180                 DBG("%s : failed to set the sysclk for codec side\n",
181                         __func__);
182                 return ret;
183         }
184 #if defined(CONFIG_RK616_USE_MCLK_12M)
185 __setdiv:
186 #endif
187         snd_soc_dai_set_clkdiv(cpu_dai,
188                 ROCKCHIP_DIV_BCLK,
189                 (pll_out / div)/params_rate(params)-1);
190         snd_soc_dai_set_clkdiv(cpu_dai,
191                 ROCKCHIP_DIV_MCLK, div - 1);
192
193         DBG("Enter:%s, %d, pll_out/div/params_rate(params) = %d\n",
194                 __func__, __LINE__, (pll_out/div)/params_rate(params));
195
196         return 0;
197 }
198
199 static int rk_voice_hw_params(struct snd_pcm_substream *substream,
200         struct snd_pcm_hw_params *params)
201 {
202         struct snd_soc_pcm_runtime *rtd = substream->private_data;
203         struct snd_soc_dai *codec_dai = rtd->codec_dai;
204         struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
205         unsigned int pll_out = 0, dai_fmt = rtd->dai_link->dai_fmt;
206         int ret;
207
208         DBG("%s() %d\n", __func__, __LINE__);
209
210         /* set codec DAI configuration */
211         ret = snd_soc_dai_set_fmt(codec_dai, dai_fmt);
212         if (ret < 0) {
213                 pr_err("rk_voice_hw_params:failed to set the format for codec side\n");
214                 return ret;
215         }
216
217         switch (params_rate(params)) {
218         case 8000:
219         case 16000:
220         case 24000:
221         case 32000:
222         case 48000:
223                 pll_out = 12288000;
224                 break;
225         case 11025:
226         case 22050:
227         case 44100:
228                 pll_out = 11289600;
229                 break;
230         default:
231                 DBG("Enter:%s, %d, Error rate=%d\n",
232                         __func__, __LINE__,
233                         params_rate(params));
234                 return -EINVAL;
235                 break;
236         }
237
238         /* MCLK must be 12M when RK616 HDMI is in */
239         #if defined(CONFIG_RK616_USE_MCLK_12M)
240         if (get_hdmi_state() && pll_out != 12000000) {
241                 DBG("%s : HDMI is in, set mclk to 12Mn", __func__);
242                 pll_out = 12000000;
243         }
244         #endif
245
246         /*Set the system clk for codec*/
247         ret = snd_soc_dai_set_sysclk(codec_dai, 0,
248                 pll_out, SND_SOC_CLOCK_IN);
249         if (ret < 0) {
250                 pr_err("rk_voice_hw_params:failed to set the sysclk for codec side\n");
251                 return ret;
252         }
253
254         ret = snd_soc_dai_set_sysclk(cpu_dai, 0, pll_out, 0);
255         if (ret < 0) {
256                 pr_err("rk_voice_hw_params:failed to set the sysclk for cpu side\n");
257                 return ret;
258         }
259
260         return 0;
261 }
262
263 static struct snd_soc_ops rk616_hifi_ops = {
264         .hw_params = rk_hifi_hw_params,
265 };
266
267 static struct snd_soc_ops rk616_voice_ops = {
268         .hw_params = rk_voice_hw_params,
269 };
270
271 static struct snd_soc_dai_link rk_dai[] = {
272         {
273                 .name = "RK616 I2S1",
274                 .stream_name = "RK616 PCM",
275                 .codec_dai_name = "rk616-hifi",
276                 .init = rk616_init,
277                 .ops = &rk616_hifi_ops,
278         },
279         {
280                 .name = "RK616 I2S2",
281                 .stream_name = "RK616 PCM",
282                 .codec_dai_name = "rk616-voice",
283                 .ops = &rk616_voice_ops,
284         },
285 };
286
287 static struct snd_soc_card rockchip_rk616_snd_card = {
288         .name = "RK_RK616",
289         .dai_link = rk_dai,
290         .num_links = 2,
291         .controls = rk_rk616_controls,
292         .num_controls = ARRAY_SIZE(rk_rk616_controls),
293         .dapm_widgets    = rk_rk616_dapm_widgets,
294         .num_dapm_widgets = ARRAY_SIZE(rk_rk616_dapm_widgets),
295         .dapm_routes    = rk_rk616_audio_map,
296         .num_dapm_routes = ARRAY_SIZE(rk_rk616_audio_map),
297 };
298 /*
299 * dts:
300 * rockchip-rk616 {
301 *       compatible = "rockchip-rk616";
302 *       dais {
303 *               dai0 {
304 *                       audio-codec = <&rk616>;
305 *                       audio-controller = <&i2s0>;
306 *                       format = "i2s";
307 *                       //continuous-clock;
308 *                       //bitclock-inversion;
309 *                       //frame-inversion;
310 *                       //bitclock-master;
311 *                       //frame-master;
312 *               };
313 *
314 *               dai1 {
315 *                       audio-codec = <&rk616>;
316 *                       audio-controller = <&i2s0>;
317 *                       format = "dsp_a";
318 *                       //continuous-clock;
319 *                       bitclock-inversion;
320 *                       //frame-inversion;
321 *                       //bitclock-master;
322 *                       //frame-master;
323 *               };
324 *       };
325 * };
326 */
327 static int rockchip_rk616_audio_probe(struct platform_device *pdev)
328 {
329         int ret;
330         struct snd_soc_card *card = &rockchip_rk616_snd_card;
331
332         card->dev = &pdev->dev;
333
334         ret = rockchip_of_get_sound_card_info(card);
335         if (ret) {
336                 pr_err("%s() get sound card info failed:%d\n",
337                         __func__, ret);
338                 return ret;
339         }
340
341         ret = snd_soc_register_card(card);
342         if (ret)
343                 pr_err("%s() register card failed:%d\n",
344                         __func__, ret);
345
346         return ret;
347 }
348
349 static int rockchip_rk616_audio_remove(struct platform_device *pdev)
350 {
351         struct snd_soc_card *card = platform_get_drvdata(pdev);
352
353         snd_soc_unregister_card(card);
354
355         return 0;
356 }
357
358 #ifdef CONFIG_OF
359 static const struct of_device_id rockchip_rk616_of_match[] = {
360         { .compatible = "rockchip-rk616", },
361         {},
362 };
363 MODULE_DEVICE_TABLE(of, rockchip_rk616_of_match);
364 #endif /* CONFIG_OF */
365
366 static struct platform_driver rockchip_rk616_audio_driver = {
367         .driver         = {
368                 .name   = "rockchip-rk616",
369                 .owner  = THIS_MODULE,
370                 .pm = &snd_soc_pm_ops,
371                 .of_match_table = of_match_ptr(rockchip_rk616_of_match),
372         },
373         .probe          = rockchip_rk616_audio_probe,
374         .remove         = rockchip_rk616_audio_remove,
375 };
376
377 module_platform_driver(rockchip_rk616_audio_driver);
378
379 /* Module information */
380 MODULE_AUTHOR("rockchip");
381 MODULE_DESCRIPTION("ROCKCHIP i2s ASoC Interface");
382 MODULE_LICENSE("GPL");