Linux-libre 5.7.5-gnu
[librecmc/linux-libre.git] / sound / soc / intel / boards / bdw-rt5650.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * ASoC machine driver for Intel Broadwell platforms with RT5650 codec
4  *
5  * Copyright 2019, The Chromium OS Authors.  All rights reserved.
6  */
7
8 #include <linux/delay.h>
9 #include <linux/gpio/consumer.h>
10 #include <linux/module.h>
11 #include <linux/platform_device.h>
12 #include <sound/core.h>
13 #include <sound/jack.h>
14 #include <sound/pcm.h>
15 #include <sound/pcm_params.h>
16 #include <sound/soc.h>
17 #include <sound/soc-acpi.h>
18
19 #include "../common/sst-dsp.h"
20 #include "../haswell/sst-haswell-ipc.h"
21
22 #include "../../codecs/rt5645.h"
23
24 struct bdw_rt5650_priv {
25         struct gpio_desc *gpio_hp_en;
26         struct snd_soc_component *component;
27 };
28
29 static const struct snd_soc_dapm_widget bdw_rt5650_widgets[] = {
30         SND_SOC_DAPM_HP("Headphone", NULL),
31         SND_SOC_DAPM_SPK("Speaker", NULL),
32         SND_SOC_DAPM_MIC("Headset Mic", NULL),
33         SND_SOC_DAPM_MIC("DMIC Pair1", NULL),
34         SND_SOC_DAPM_MIC("DMIC Pair2", NULL),
35 };
36
37 static const struct snd_soc_dapm_route bdw_rt5650_map[] = {
38         /* Speakers */
39         {"Speaker", NULL, "SPOL"},
40         {"Speaker", NULL, "SPOR"},
41
42         /* Headset jack connectors */
43         {"Headphone", NULL, "HPOL"},
44         {"Headphone", NULL, "HPOR"},
45         {"IN1P", NULL, "Headset Mic"},
46         {"IN1N", NULL, "Headset Mic"},
47
48         /* Digital MICs
49          * DMIC Pair1 are the two DMICs connected on the DMICN1 connector.
50          * DMIC Pair2 are the two DMICs connected on the DMICN2 connector.
51          * Facing the camera, DMIC Pair1 are on the left side, DMIC Pair2
52          * are on the right side.
53          */
54         {"DMIC L1", NULL, "DMIC Pair1"},
55         {"DMIC R1", NULL, "DMIC Pair1"},
56         {"DMIC L2", NULL, "DMIC Pair2"},
57         {"DMIC R2", NULL, "DMIC Pair2"},
58
59         /* CODEC BE connections */
60         {"SSP0 CODEC IN", NULL, "AIF1 Capture"},
61         {"AIF1 Playback", NULL, "SSP0 CODEC OUT"},
62 };
63
64 static const struct snd_kcontrol_new bdw_rt5650_controls[] = {
65         SOC_DAPM_PIN_SWITCH("Speaker"),
66         SOC_DAPM_PIN_SWITCH("Headphone"),
67         SOC_DAPM_PIN_SWITCH("Headset Mic"),
68         SOC_DAPM_PIN_SWITCH("DMIC Pair1"),
69         SOC_DAPM_PIN_SWITCH("DMIC Pair2"),
70 };
71
72
73 static struct snd_soc_jack headphone_jack;
74 static struct snd_soc_jack mic_jack;
75
76 static struct snd_soc_jack_pin headphone_jack_pin = {
77         .pin    = "Headphone",
78         .mask   = SND_JACK_HEADPHONE,
79 };
80
81 static struct snd_soc_jack_pin mic_jack_pin = {
82         .pin    = "Headset Mic",
83         .mask   = SND_JACK_MICROPHONE,
84 };
85
86 static int broadwell_ssp0_fixup(struct snd_soc_pcm_runtime *rtd,
87                         struct snd_pcm_hw_params *params)
88 {
89         struct snd_interval *rate = hw_param_interval(params,
90                         SNDRV_PCM_HW_PARAM_RATE);
91         struct snd_interval *channels = hw_param_interval(params,
92                                                 SNDRV_PCM_HW_PARAM_CHANNELS);
93
94         /* The ADSP will covert the FE rate to 48k, max 4-channels */
95         rate->min = rate->max = 48000;
96         channels->min = 2;
97         channels->max = 4;
98
99         /* set SSP0 to 24 bit */
100         snd_mask_set_format(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT),
101                             SNDRV_PCM_FORMAT_S24_LE);
102
103         return 0;
104 }
105
106 static int bdw_rt5650_hw_params(struct snd_pcm_substream *substream,
107         struct snd_pcm_hw_params *params)
108 {
109         struct snd_soc_pcm_runtime *rtd = substream->private_data;
110         struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
111         int ret;
112
113         /* Workaround: set codec PLL to 19.2MHz that PLL source is
114          * from MCLK(24MHz) to conform 2.4MHz DMIC clock.
115          */
116         ret = snd_soc_dai_set_pll(codec_dai, 0, RT5645_PLL1_S_MCLK,
117                 24000000, 19200000);
118         if (ret < 0) {
119                 dev_err(rtd->dev, "can't set codec pll: %d\n", ret);
120                 return ret;
121         }
122
123         /* The actual MCLK freq is 24MHz. The codec is told that MCLK is
124          * 24.576MHz to satisfy the requirement of rl6231_get_clk_info.
125          * ASRC is enabled on AD and DA filters to ensure good audio quality.
126          */
127         ret = snd_soc_dai_set_sysclk(codec_dai, RT5645_SCLK_S_PLL1, 24576000,
128                 SND_SOC_CLOCK_IN);
129         if (ret < 0) {
130                 dev_err(rtd->dev, "can't set codec sysclk configuration\n");
131                 return ret;
132         }
133
134         return ret;
135 }
136
137 static struct snd_soc_ops bdw_rt5650_ops = {
138         .hw_params = bdw_rt5650_hw_params,
139 };
140
141 #if !IS_ENABLED(CONFIG_SND_SOC_SOF_BROADWELL)
142 static int bdw_rt5650_rtd_init(struct snd_soc_pcm_runtime *rtd)
143 {
144         struct snd_soc_component *component =
145                 snd_soc_rtdcom_lookup(rtd, DRV_NAME);
146         struct sst_pdata *pdata = dev_get_platdata(component->dev);
147         struct sst_hsw *broadwell = pdata->dsp;
148         int ret;
149
150         /* Set ADSP SSP port settings
151          * clock_divider = 4 means BCLK = MCLK/5 = 24MHz/5 = 4.8MHz
152          */
153         ret = sst_hsw_device_set_config(broadwell, SST_HSW_DEVICE_SSP_0,
154                 SST_HSW_DEVICE_MCLK_FREQ_24_MHZ,
155                 SST_HSW_DEVICE_TDM_CLOCK_MASTER, 4);
156         if (ret < 0) {
157                 dev_err(rtd->dev, "error: failed to set device config\n");
158                 return ret;
159         }
160
161         return 0;
162 }
163 #endif
164
165 static int bdw_rt5650_init(struct snd_soc_pcm_runtime *rtd)
166 {
167         struct bdw_rt5650_priv *bdw_rt5650 =
168                 snd_soc_card_get_drvdata(rtd->card);
169         struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
170         struct snd_soc_component *component = codec_dai->component;
171         int ret;
172
173         /* Enable codec ASRC function for Stereo DAC/Stereo1 ADC/DMIC/I2S1.
174          * The ASRC clock source is clk_i2s1_asrc.
175          */
176         rt5645_sel_asrc_clk_src(component,
177                                 RT5645_DA_STEREO_FILTER |
178                                 RT5645_DA_MONO_L_FILTER |
179                                 RT5645_DA_MONO_R_FILTER |
180                                 RT5645_AD_STEREO_FILTER |
181                                 RT5645_AD_MONO_L_FILTER |
182                                 RT5645_AD_MONO_R_FILTER,
183                                 RT5645_CLK_SEL_I2S1_ASRC);
184
185         /* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */
186         ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xF, 0xF, 4, 24);
187
188         if (ret < 0) {
189                 dev_err(rtd->dev, "can't set codec TDM slot %d\n", ret);
190                 return ret;
191         }
192
193         /* Create and initialize headphone jack */
194         if (snd_soc_card_jack_new(rtd->card, "Headphone Jack",
195                         SND_JACK_HEADPHONE, &headphone_jack,
196                         &headphone_jack_pin, 1)) {
197                 dev_err(component->dev, "Can't create headphone jack\n");
198         }
199
200         /* Create and initialize mic jack */
201         if (snd_soc_card_jack_new(rtd->card, "Mic Jack", SND_JACK_MICROPHONE,
202                         &mic_jack, &mic_jack_pin, 1)) {
203                 dev_err(component->dev, "Can't create mic jack\n");
204         }
205
206         rt5645_set_jack_detect(component, &headphone_jack, &mic_jack, NULL);
207
208         bdw_rt5650->component = component;
209
210         return 0;
211 }
212
213 /* broadwell digital audio interface glue - connects codec <--> CPU */
214 SND_SOC_DAILINK_DEF(dummy,
215         DAILINK_COMP_ARRAY(COMP_DUMMY()));
216
217 SND_SOC_DAILINK_DEF(fe,
218         DAILINK_COMP_ARRAY(COMP_CPU("System Pin")));
219
220 SND_SOC_DAILINK_DEF(platform,
221         DAILINK_COMP_ARRAY(COMP_PLATFORM("haswell-pcm-audio")));
222
223 SND_SOC_DAILINK_DEF(be,
224         DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10EC5650:00", "rt5645-aif1")));
225
226 #if IS_ENABLED(CONFIG_SND_SOC_SOF_BROADWELL)
227 SND_SOC_DAILINK_DEF(ssp0_port,
228             DAILINK_COMP_ARRAY(COMP_CPU("ssp0-port")));
229 #endif
230
231 static struct snd_soc_dai_link bdw_rt5650_dais[] = {
232         /* Front End DAI links */
233         {
234                 .name = "System PCM",
235                 .stream_name = "System Playback",
236                 .dynamic = 1,
237 #if !IS_ENABLED(CONFIG_SND_SOC_SOF_BROADWELL)
238                 .init = bdw_rt5650_rtd_init,
239 #endif
240                 .trigger = {
241                         SND_SOC_DPCM_TRIGGER_POST,
242                         SND_SOC_DPCM_TRIGGER_POST
243                 },
244                 .dpcm_playback = 1,
245                 .dpcm_capture = 1,
246                 SND_SOC_DAILINK_REG(fe, dummy, platform),
247         },
248
249         /* Back End DAI links */
250         {
251                 /* SSP0 - Codec */
252                 .name = "Codec",
253                 .id = 0,
254                 .no_pcm = 1,
255                 .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF |
256                         SND_SOC_DAIFMT_CBS_CFS,
257                 .ignore_pmdown_time = 1,
258                 .be_hw_params_fixup = broadwell_ssp0_fixup,
259                 .ops = &bdw_rt5650_ops,
260                 .dpcm_playback = 1,
261                 .dpcm_capture = 1,
262                 .init = bdw_rt5650_init,
263 #if !IS_ENABLED(CONFIG_SND_SOC_SOF_BROADWELL)
264                 SND_SOC_DAILINK_REG(dummy, be, dummy),
265 #else
266                 SND_SOC_DAILINK_REG(ssp0_port, be, platform),
267 #endif
268         },
269 };
270
271 /* ASoC machine driver for Broadwell DSP + RT5650 */
272 static struct snd_soc_card bdw_rt5650_card = {
273         .name = "bdw-rt5650",
274         .owner = THIS_MODULE,
275         .dai_link = bdw_rt5650_dais,
276         .num_links = ARRAY_SIZE(bdw_rt5650_dais),
277         .dapm_widgets = bdw_rt5650_widgets,
278         .num_dapm_widgets = ARRAY_SIZE(bdw_rt5650_widgets),
279         .dapm_routes = bdw_rt5650_map,
280         .num_dapm_routes = ARRAY_SIZE(bdw_rt5650_map),
281         .controls = bdw_rt5650_controls,
282         .num_controls = ARRAY_SIZE(bdw_rt5650_controls),
283         .fully_routed = true,
284 };
285
286 static int bdw_rt5650_probe(struct platform_device *pdev)
287 {
288         struct bdw_rt5650_priv *bdw_rt5650;
289         struct snd_soc_acpi_mach *mach;
290         int ret;
291
292         bdw_rt5650_card.dev = &pdev->dev;
293
294         /* Allocate driver private struct */
295         bdw_rt5650 = devm_kzalloc(&pdev->dev, sizeof(struct bdw_rt5650_priv),
296                 GFP_KERNEL);
297         if (!bdw_rt5650)
298                 return -ENOMEM;
299
300         /* override plaform name, if required */
301         mach = pdev->dev.platform_data;
302         ret = snd_soc_fixup_dai_links_platform_name(&bdw_rt5650_card,
303                                                     mach->mach_params.platform);
304
305         if (ret)
306                 return ret;
307
308         snd_soc_card_set_drvdata(&bdw_rt5650_card, bdw_rt5650);
309
310         return devm_snd_soc_register_card(&pdev->dev, &bdw_rt5650_card);
311 }
312
313 static struct platform_driver bdw_rt5650_audio = {
314         .probe = bdw_rt5650_probe,
315         .driver = {
316                 .name = "bdw-rt5650",
317                 .pm = &snd_soc_pm_ops,
318         },
319 };
320
321 module_platform_driver(bdw_rt5650_audio)
322
323 /* Module information */
324 MODULE_AUTHOR("Ben Zhang <benzh@chromium.org>");
325 MODULE_DESCRIPTION("Intel Broadwell RT5650 machine driver");
326 MODULE_LICENSE("GPL v2");
327 MODULE_ALIAS("platform:bdw-rt5650");