ASoC: Intel: move sysclk source setting to platform_clock_control for balance.
[firefly-linux-kernel-4.4.55.git] / sound / soc / intel / cht_bsw_rt5672.c
1 /*
2  *  cht_bsw_rt5672.c - ASoc Machine driver for Intel Cherryview-based platforms
3  *                     Cherrytrail and Braswell, with RT5672 codec.
4  *
5  *  Copyright (C) 2014 Intel Corp
6  *  Author: Subhransu S. Prusty <subhransu.s.prusty@intel.com>
7  *          Mengdong Lin <mengdong.lin@intel.com>
8  *
9  *  This program is free software; you can redistribute it and/or modify
10  *  it under the terms of the GNU General Public License as published by
11  *  the Free Software Foundation; version 2 of the License.
12  *
13  *  This program is distributed in the hope that it will be useful, but
14  *  WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  *  General Public License for more details.
17  */
18
19 #include <linux/module.h>
20 #include <linux/platform_device.h>
21 #include <linux/slab.h>
22 #include <sound/pcm.h>
23 #include <sound/pcm_params.h>
24 #include <sound/soc.h>
25 #include "../codecs/rt5670.h"
26 #include "sst-atom-controls.h"
27
28 /* The platform clock #3 outputs 19.2Mhz clock to codec as I2S MCLK */
29 #define CHT_PLAT_CLK_3_HZ       19200000
30 #define CHT_CODEC_DAI   "rt5670-aif1"
31
32 static inline struct snd_soc_dai *cht_get_codec_dai(struct snd_soc_card *card)
33 {
34         int i;
35
36         for (i = 0; i < card->num_rtd; i++) {
37                 struct snd_soc_pcm_runtime *rtd;
38
39                 rtd = card->rtd + i;
40                 if (!strncmp(rtd->codec_dai->name, CHT_CODEC_DAI,
41                              strlen(CHT_CODEC_DAI)))
42                         return rtd->codec_dai;
43         }
44         return NULL;
45 }
46
47 static int platform_clock_control(struct snd_soc_dapm_widget *w,
48                 struct snd_kcontrol *k, int  event)
49 {
50         struct snd_soc_dapm_context *dapm = w->dapm;
51         struct snd_soc_card *card = dapm->card;
52         struct snd_soc_dai *codec_dai;
53         int ret;
54
55         codec_dai = cht_get_codec_dai(card);
56         if (!codec_dai) {
57                 dev_err(card->dev, "Codec dai not found; Unable to set platform clock\n");
58                 return -EIO;
59         }
60
61         if (SND_SOC_DAPM_EVENT_ON(event)) {
62                 /* set codec PLL source to the 19.2MHz platform clock (MCLK) */
63                 ret = snd_soc_dai_set_pll(codec_dai, 0, RT5670_PLL1_S_MCLK,
64                                 CHT_PLAT_CLK_3_HZ, 48000 * 512);
65                 if (ret < 0) {
66                         dev_err(card->dev, "can't set codec pll: %d\n", ret);
67                         return ret;
68                 }
69
70                 /* set codec sysclk source to PLL */
71                 ret = snd_soc_dai_set_sysclk(codec_dai, RT5670_SCLK_S_PLL1,
72                         48000 * 512, SND_SOC_CLOCK_IN);
73                 if (ret < 0) {
74                         dev_err(card->dev, "can't set codec sysclk: %d\n", ret);
75                         return ret;
76                 }
77         } else {
78                 /* Set codec sysclk source to its internal clock because codec
79                  * PLL will be off when idle and MCLK will also be off by ACPI
80                  * when codec is runtime suspended. Codec needs clock for jack
81                  * detection and button press.
82                  */
83                 snd_soc_dai_set_sysclk(codec_dai, RT5670_SCLK_S_RCCLK,
84                                        48000 * 512, SND_SOC_CLOCK_IN);
85         }
86         return 0;
87 }
88
89 static const struct snd_soc_dapm_widget cht_dapm_widgets[] = {
90         SND_SOC_DAPM_HP("Headphone", NULL),
91         SND_SOC_DAPM_MIC("Headset Mic", NULL),
92         SND_SOC_DAPM_MIC("Int Mic", NULL),
93         SND_SOC_DAPM_SPK("Ext Spk", NULL),
94         SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
95                         platform_clock_control, SND_SOC_DAPM_PRE_PMU |
96                         SND_SOC_DAPM_POST_PMD),
97 };
98
99 static const struct snd_soc_dapm_route cht_audio_map[] = {
100         {"IN1P", NULL, "Headset Mic"},
101         {"IN1N", NULL, "Headset Mic"},
102         {"DMIC L1", NULL, "Int Mic"},
103         {"DMIC R1", NULL, "Int Mic"},
104         {"Headphone", NULL, "HPOL"},
105         {"Headphone", NULL, "HPOR"},
106         {"Ext Spk", NULL, "SPOLP"},
107         {"Ext Spk", NULL, "SPOLN"},
108         {"Ext Spk", NULL, "SPORP"},
109         {"Ext Spk", NULL, "SPORN"},
110         {"AIF1 Playback", NULL, "ssp2 Tx"},
111         {"ssp2 Tx", NULL, "codec_out0"},
112         {"ssp2 Tx", NULL, "codec_out1"},
113         {"codec_in0", NULL, "ssp2 Rx"},
114         {"codec_in1", NULL, "ssp2 Rx"},
115         {"ssp2 Rx", NULL, "AIF1 Capture"},
116         {"Headphone", NULL, "Platform Clock"},
117         {"Headset Mic", NULL, "Platform Clock"},
118         {"Int Mic", NULL, "Platform Clock"},
119         {"Ext Spk", NULL, "Platform Clock"},
120 };
121
122 static const struct snd_kcontrol_new cht_mc_controls[] = {
123         SOC_DAPM_PIN_SWITCH("Headphone"),
124         SOC_DAPM_PIN_SWITCH("Headset Mic"),
125         SOC_DAPM_PIN_SWITCH("Int Mic"),
126         SOC_DAPM_PIN_SWITCH("Ext Spk"),
127 };
128
129 static int cht_aif1_hw_params(struct snd_pcm_substream *substream,
130                                         struct snd_pcm_hw_params *params)
131 {
132         struct snd_soc_pcm_runtime *rtd = substream->private_data;
133         struct snd_soc_dai *codec_dai = rtd->codec_dai;
134         int ret;
135
136         /* set codec PLL source to the 19.2MHz platform clock (MCLK) */
137         ret = snd_soc_dai_set_pll(codec_dai, 0, RT5670_PLL1_S_MCLK,
138                                   CHT_PLAT_CLK_3_HZ, params_rate(params) * 512);
139         if (ret < 0) {
140                 dev_err(rtd->dev, "can't set codec pll: %d\n", ret);
141                 return ret;
142         }
143
144         /* set codec sysclk source to PLL */
145         ret = snd_soc_dai_set_sysclk(codec_dai, RT5670_SCLK_S_PLL1,
146                                      params_rate(params) * 512,
147                                      SND_SOC_CLOCK_IN);
148         if (ret < 0) {
149                 dev_err(rtd->dev, "can't set codec sysclk: %d\n", ret);
150                 return ret;
151         }
152         return 0;
153 }
154
155 static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)
156 {
157         int ret;
158         struct snd_soc_dai *codec_dai = runtime->codec_dai;
159         struct snd_soc_codec *codec = codec_dai->codec;
160
161         /* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */
162         ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xF, 0xF, 4, 24);
163         if (ret < 0) {
164                 dev_err(runtime->dev, "can't set codec TDM slot %d\n", ret);
165                 return ret;
166         }
167
168         /* Select codec ASRC clock source to track I2S1 clock, because codec
169          * is in slave mode and 100fs I2S format (BCLK = 100 * LRCLK) cannot
170          * be supported by RT5672. Otherwise, ASRC will be disabled and cause
171          * noise.
172          */
173         rt5670_sel_asrc_clk_src(codec,
174                                 RT5670_DA_STEREO_FILTER
175                                 | RT5670_DA_MONO_L_FILTER
176                                 | RT5670_DA_MONO_R_FILTER
177                                 | RT5670_AD_STEREO_FILTER
178                                 | RT5670_AD_MONO_L_FILTER
179                                 | RT5670_AD_MONO_R_FILTER,
180                                 RT5670_CLK_SEL_I2S1_ASRC);
181         return 0;
182 }
183
184 static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd,
185                             struct snd_pcm_hw_params *params)
186 {
187         struct snd_interval *rate = hw_param_interval(params,
188                         SNDRV_PCM_HW_PARAM_RATE);
189         struct snd_interval *channels = hw_param_interval(params,
190                                                 SNDRV_PCM_HW_PARAM_CHANNELS);
191
192         /* The DSP will covert the FE rate to 48k, stereo, 24bits */
193         rate->min = rate->max = 48000;
194         channels->min = channels->max = 2;
195
196         /* set SSP2 to 24-bit */
197         params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
198         return 0;
199 }
200
201 static unsigned int rates_48000[] = {
202         48000,
203 };
204
205 static struct snd_pcm_hw_constraint_list constraints_48000 = {
206         .count = ARRAY_SIZE(rates_48000),
207         .list  = rates_48000,
208 };
209
210 static int cht_aif1_startup(struct snd_pcm_substream *substream)
211 {
212         return snd_pcm_hw_constraint_list(substream->runtime, 0,
213                         SNDRV_PCM_HW_PARAM_RATE,
214                         &constraints_48000);
215 }
216
217 static struct snd_soc_ops cht_aif1_ops = {
218         .startup = cht_aif1_startup,
219 };
220
221 static struct snd_soc_ops cht_be_ssp2_ops = {
222         .hw_params = cht_aif1_hw_params,
223 };
224
225 static struct snd_soc_dai_link cht_dailink[] = {
226         /* Front End DAI links */
227         [MERR_DPCM_AUDIO] = {
228                 .name = "Audio Port",
229                 .stream_name = "Audio",
230                 .cpu_dai_name = "media-cpu-dai",
231                 .codec_dai_name = "snd-soc-dummy-dai",
232                 .codec_name = "snd-soc-dummy",
233                 .platform_name = "sst-mfld-platform",
234                 .nonatomic = true,
235                 .dynamic = 1,
236                 .dpcm_playback = 1,
237                 .dpcm_capture = 1,
238                 .ops = &cht_aif1_ops,
239         },
240         [MERR_DPCM_COMPR] = {
241                 .name = "Compressed Port",
242                 .stream_name = "Compress",
243                 .cpu_dai_name = "compress-cpu-dai",
244                 .codec_dai_name = "snd-soc-dummy-dai",
245                 .codec_name = "snd-soc-dummy",
246                 .platform_name = "sst-mfld-platform",
247         },
248
249         /* Back End DAI links */
250         {
251                 /* SSP2 - Codec */
252                 .name = "SSP2-Codec",
253                 .be_id = 1,
254                 .cpu_dai_name = "ssp2-port",
255                 .platform_name = "sst-mfld-platform",
256                 .no_pcm = 1,
257                 .nonatomic = true,
258                 .codec_dai_name = "rt5670-aif1",
259                 .codec_name = "i2c-10EC5670:00",
260                 .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF
261                                         | SND_SOC_DAIFMT_CBS_CFS,
262                 .init = cht_codec_init,
263                 .be_hw_params_fixup = cht_codec_fixup,
264                 .dpcm_playback = 1,
265                 .dpcm_capture = 1,
266                 .ops = &cht_be_ssp2_ops,
267         },
268 };
269
270 /* SoC card */
271 static struct snd_soc_card snd_soc_card_cht = {
272         .name = "cherrytrailcraudio",
273         .dai_link = cht_dailink,
274         .num_links = ARRAY_SIZE(cht_dailink),
275         .dapm_widgets = cht_dapm_widgets,
276         .num_dapm_widgets = ARRAY_SIZE(cht_dapm_widgets),
277         .dapm_routes = cht_audio_map,
278         .num_dapm_routes = ARRAY_SIZE(cht_audio_map),
279         .controls = cht_mc_controls,
280         .num_controls = ARRAY_SIZE(cht_mc_controls),
281 };
282
283 static int snd_cht_mc_probe(struct platform_device *pdev)
284 {
285         int ret_val = 0;
286
287         /* register the soc card */
288         snd_soc_card_cht.dev = &pdev->dev;
289         ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_cht);
290         if (ret_val) {
291                 dev_err(&pdev->dev,
292                         "snd_soc_register_card failed %d\n", ret_val);
293                 return ret_val;
294         }
295         platform_set_drvdata(pdev, &snd_soc_card_cht);
296         return ret_val;
297 }
298
299 static struct platform_driver snd_cht_mc_driver = {
300         .driver = {
301                 .name = "cht-bsw-rt5672",
302         },
303         .probe = snd_cht_mc_probe,
304 };
305
306 module_platform_driver(snd_cht_mc_driver);
307
308 MODULE_DESCRIPTION("ASoC Intel(R) Baytrail CR Machine driver");
309 MODULE_AUTHOR("Subhransu S. Prusty, Mengdong Lin");
310 MODULE_LICENSE("GPL v2");
311 MODULE_ALIAS("platform:cht-bsw-rt5672");