1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * cht_bsw_rt5672.c - ASoc Machine driver for Intel Cherryview-based platforms
4*4882a593Smuzhiyun * Cherrytrail and Braswell, with RT5672 codec.
5*4882a593Smuzhiyun *
6*4882a593Smuzhiyun * Copyright (C) 2014 Intel Corp
7*4882a593Smuzhiyun * Author: Subhransu S. Prusty <subhransu.s.prusty@intel.com>
8*4882a593Smuzhiyun * Mengdong Lin <mengdong.lin@intel.com>
9*4882a593Smuzhiyun */
10*4882a593Smuzhiyun
11*4882a593Smuzhiyun #include <linux/gpio/consumer.h>
12*4882a593Smuzhiyun #include <linux/input.h>
13*4882a593Smuzhiyun #include <linux/module.h>
14*4882a593Smuzhiyun #include <linux/platform_device.h>
15*4882a593Smuzhiyun #include <linux/slab.h>
16*4882a593Smuzhiyun #include <linux/clk.h>
17*4882a593Smuzhiyun #include <sound/pcm.h>
18*4882a593Smuzhiyun #include <sound/pcm_params.h>
19*4882a593Smuzhiyun #include <sound/soc.h>
20*4882a593Smuzhiyun #include <sound/jack.h>
21*4882a593Smuzhiyun #include <sound/soc-acpi.h>
22*4882a593Smuzhiyun #include "../../codecs/rt5670.h"
23*4882a593Smuzhiyun #include "../atom/sst-atom-controls.h"
24*4882a593Smuzhiyun
25*4882a593Smuzhiyun
26*4882a593Smuzhiyun /* The platform clock #3 outputs 19.2Mhz clock to codec as I2S MCLK */
27*4882a593Smuzhiyun #define CHT_PLAT_CLK_3_HZ 19200000
28*4882a593Smuzhiyun #define CHT_CODEC_DAI "rt5670-aif1"
29*4882a593Smuzhiyun
30*4882a593Smuzhiyun struct cht_mc_private {
31*4882a593Smuzhiyun struct snd_soc_jack headset;
32*4882a593Smuzhiyun char codec_name[SND_ACPI_I2C_ID_LEN];
33*4882a593Smuzhiyun struct clk *mclk;
34*4882a593Smuzhiyun };
35*4882a593Smuzhiyun
36*4882a593Smuzhiyun /* Headset jack detection DAPM pins */
37*4882a593Smuzhiyun static struct snd_soc_jack_pin cht_bsw_headset_pins[] = {
38*4882a593Smuzhiyun {
39*4882a593Smuzhiyun .pin = "Headset Mic",
40*4882a593Smuzhiyun .mask = SND_JACK_MICROPHONE,
41*4882a593Smuzhiyun },
42*4882a593Smuzhiyun {
43*4882a593Smuzhiyun .pin = "Headphone",
44*4882a593Smuzhiyun .mask = SND_JACK_HEADPHONE,
45*4882a593Smuzhiyun },
46*4882a593Smuzhiyun };
47*4882a593Smuzhiyun
platform_clock_control(struct snd_soc_dapm_widget * w,struct snd_kcontrol * k,int event)48*4882a593Smuzhiyun static int platform_clock_control(struct snd_soc_dapm_widget *w,
49*4882a593Smuzhiyun struct snd_kcontrol *k, int event)
50*4882a593Smuzhiyun {
51*4882a593Smuzhiyun struct snd_soc_dapm_context *dapm = w->dapm;
52*4882a593Smuzhiyun struct snd_soc_card *card = dapm->card;
53*4882a593Smuzhiyun struct snd_soc_dai *codec_dai;
54*4882a593Smuzhiyun struct cht_mc_private *ctx = snd_soc_card_get_drvdata(card);
55*4882a593Smuzhiyun int ret;
56*4882a593Smuzhiyun
57*4882a593Smuzhiyun codec_dai = snd_soc_card_get_codec_dai(card, CHT_CODEC_DAI);
58*4882a593Smuzhiyun if (!codec_dai) {
59*4882a593Smuzhiyun dev_err(card->dev, "Codec dai not found; Unable to set platform clock\n");
60*4882a593Smuzhiyun return -EIO;
61*4882a593Smuzhiyun }
62*4882a593Smuzhiyun
63*4882a593Smuzhiyun if (SND_SOC_DAPM_EVENT_ON(event)) {
64*4882a593Smuzhiyun if (ctx->mclk) {
65*4882a593Smuzhiyun ret = clk_prepare_enable(ctx->mclk);
66*4882a593Smuzhiyun if (ret < 0) {
67*4882a593Smuzhiyun dev_err(card->dev,
68*4882a593Smuzhiyun "could not configure MCLK state");
69*4882a593Smuzhiyun return ret;
70*4882a593Smuzhiyun }
71*4882a593Smuzhiyun }
72*4882a593Smuzhiyun
73*4882a593Smuzhiyun /* set codec PLL source to the 19.2MHz platform clock (MCLK) */
74*4882a593Smuzhiyun ret = snd_soc_dai_set_pll(codec_dai, 0, RT5670_PLL1_S_MCLK,
75*4882a593Smuzhiyun CHT_PLAT_CLK_3_HZ, 48000 * 512);
76*4882a593Smuzhiyun if (ret < 0) {
77*4882a593Smuzhiyun dev_err(card->dev, "can't set codec pll: %d\n", ret);
78*4882a593Smuzhiyun return ret;
79*4882a593Smuzhiyun }
80*4882a593Smuzhiyun
81*4882a593Smuzhiyun /* set codec sysclk source to PLL */
82*4882a593Smuzhiyun ret = snd_soc_dai_set_sysclk(codec_dai, RT5670_SCLK_S_PLL1,
83*4882a593Smuzhiyun 48000 * 512, SND_SOC_CLOCK_IN);
84*4882a593Smuzhiyun if (ret < 0) {
85*4882a593Smuzhiyun dev_err(card->dev, "can't set codec sysclk: %d\n", ret);
86*4882a593Smuzhiyun return ret;
87*4882a593Smuzhiyun }
88*4882a593Smuzhiyun } else {
89*4882a593Smuzhiyun /* Set codec sysclk source to its internal clock because codec
90*4882a593Smuzhiyun * PLL will be off when idle and MCLK will also be off by ACPI
91*4882a593Smuzhiyun * when codec is runtime suspended. Codec needs clock for jack
92*4882a593Smuzhiyun * detection and button press.
93*4882a593Smuzhiyun */
94*4882a593Smuzhiyun snd_soc_dai_set_sysclk(codec_dai, RT5670_SCLK_S_RCCLK,
95*4882a593Smuzhiyun 48000 * 512, SND_SOC_CLOCK_IN);
96*4882a593Smuzhiyun
97*4882a593Smuzhiyun if (ctx->mclk)
98*4882a593Smuzhiyun clk_disable_unprepare(ctx->mclk);
99*4882a593Smuzhiyun }
100*4882a593Smuzhiyun return 0;
101*4882a593Smuzhiyun }
102*4882a593Smuzhiyun
103*4882a593Smuzhiyun static const struct snd_soc_dapm_widget cht_dapm_widgets[] = {
104*4882a593Smuzhiyun SND_SOC_DAPM_HP("Headphone", NULL),
105*4882a593Smuzhiyun SND_SOC_DAPM_MIC("Headset Mic", NULL),
106*4882a593Smuzhiyun SND_SOC_DAPM_MIC("Int Mic", NULL),
107*4882a593Smuzhiyun SND_SOC_DAPM_SPK("Ext Spk", NULL),
108*4882a593Smuzhiyun SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
109*4882a593Smuzhiyun platform_clock_control, SND_SOC_DAPM_PRE_PMU |
110*4882a593Smuzhiyun SND_SOC_DAPM_POST_PMD),
111*4882a593Smuzhiyun };
112*4882a593Smuzhiyun
113*4882a593Smuzhiyun static const struct snd_soc_dapm_route cht_audio_map[] = {
114*4882a593Smuzhiyun {"IN1P", NULL, "Headset Mic"},
115*4882a593Smuzhiyun {"IN1N", NULL, "Headset Mic"},
116*4882a593Smuzhiyun {"DMIC L1", NULL, "Int Mic"},
117*4882a593Smuzhiyun {"DMIC R1", NULL, "Int Mic"},
118*4882a593Smuzhiyun {"Headphone", NULL, "HPOL"},
119*4882a593Smuzhiyun {"Headphone", NULL, "HPOR"},
120*4882a593Smuzhiyun {"Ext Spk", NULL, "SPOLP"},
121*4882a593Smuzhiyun {"Ext Spk", NULL, "SPOLN"},
122*4882a593Smuzhiyun {"Ext Spk", NULL, "SPORP"},
123*4882a593Smuzhiyun {"Ext Spk", NULL, "SPORN"},
124*4882a593Smuzhiyun {"AIF1 Playback", NULL, "ssp2 Tx"},
125*4882a593Smuzhiyun {"ssp2 Tx", NULL, "codec_out0"},
126*4882a593Smuzhiyun {"ssp2 Tx", NULL, "codec_out1"},
127*4882a593Smuzhiyun {"codec_in0", NULL, "ssp2 Rx"},
128*4882a593Smuzhiyun {"codec_in1", NULL, "ssp2 Rx"},
129*4882a593Smuzhiyun {"ssp2 Rx", NULL, "AIF1 Capture"},
130*4882a593Smuzhiyun {"Headphone", NULL, "Platform Clock"},
131*4882a593Smuzhiyun {"Headset Mic", NULL, "Platform Clock"},
132*4882a593Smuzhiyun {"Int Mic", NULL, "Platform Clock"},
133*4882a593Smuzhiyun {"Ext Spk", NULL, "Platform Clock"},
134*4882a593Smuzhiyun };
135*4882a593Smuzhiyun
136*4882a593Smuzhiyun static const struct snd_kcontrol_new cht_mc_controls[] = {
137*4882a593Smuzhiyun SOC_DAPM_PIN_SWITCH("Headphone"),
138*4882a593Smuzhiyun SOC_DAPM_PIN_SWITCH("Headset Mic"),
139*4882a593Smuzhiyun SOC_DAPM_PIN_SWITCH("Int Mic"),
140*4882a593Smuzhiyun SOC_DAPM_PIN_SWITCH("Ext Spk"),
141*4882a593Smuzhiyun };
142*4882a593Smuzhiyun
cht_aif1_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params)143*4882a593Smuzhiyun static int cht_aif1_hw_params(struct snd_pcm_substream *substream,
144*4882a593Smuzhiyun struct snd_pcm_hw_params *params)
145*4882a593Smuzhiyun {
146*4882a593Smuzhiyun struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
147*4882a593Smuzhiyun struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
148*4882a593Smuzhiyun int ret;
149*4882a593Smuzhiyun
150*4882a593Smuzhiyun /* set codec PLL source to the 19.2MHz platform clock (MCLK) */
151*4882a593Smuzhiyun ret = snd_soc_dai_set_pll(codec_dai, 0, RT5670_PLL1_S_MCLK,
152*4882a593Smuzhiyun CHT_PLAT_CLK_3_HZ, params_rate(params) * 512);
153*4882a593Smuzhiyun if (ret < 0) {
154*4882a593Smuzhiyun dev_err(rtd->dev, "can't set codec pll: %d\n", ret);
155*4882a593Smuzhiyun return ret;
156*4882a593Smuzhiyun }
157*4882a593Smuzhiyun
158*4882a593Smuzhiyun /* set codec sysclk source to PLL */
159*4882a593Smuzhiyun ret = snd_soc_dai_set_sysclk(codec_dai, RT5670_SCLK_S_PLL1,
160*4882a593Smuzhiyun params_rate(params) * 512,
161*4882a593Smuzhiyun SND_SOC_CLOCK_IN);
162*4882a593Smuzhiyun if (ret < 0) {
163*4882a593Smuzhiyun dev_err(rtd->dev, "can't set codec sysclk: %d\n", ret);
164*4882a593Smuzhiyun return ret;
165*4882a593Smuzhiyun }
166*4882a593Smuzhiyun return 0;
167*4882a593Smuzhiyun }
168*4882a593Smuzhiyun
169*4882a593Smuzhiyun static const struct acpi_gpio_params headset_gpios = { 0, 0, false };
170*4882a593Smuzhiyun
171*4882a593Smuzhiyun static const struct acpi_gpio_mapping cht_rt5672_gpios[] = {
172*4882a593Smuzhiyun { "headset-gpios", &headset_gpios, 1 },
173*4882a593Smuzhiyun {},
174*4882a593Smuzhiyun };
175*4882a593Smuzhiyun
cht_codec_init(struct snd_soc_pcm_runtime * runtime)176*4882a593Smuzhiyun static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)
177*4882a593Smuzhiyun {
178*4882a593Smuzhiyun int ret;
179*4882a593Smuzhiyun struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(runtime, 0);
180*4882a593Smuzhiyun struct snd_soc_component *component = codec_dai->component;
181*4882a593Smuzhiyun struct cht_mc_private *ctx = snd_soc_card_get_drvdata(runtime->card);
182*4882a593Smuzhiyun
183*4882a593Smuzhiyun if (devm_acpi_dev_add_driver_gpios(component->dev, cht_rt5672_gpios))
184*4882a593Smuzhiyun dev_warn(runtime->dev, "Unable to add GPIO mapping table\n");
185*4882a593Smuzhiyun
186*4882a593Smuzhiyun /* Select codec ASRC clock source to track I2S1 clock, because codec
187*4882a593Smuzhiyun * is in slave mode and 100fs I2S format (BCLK = 100 * LRCLK) cannot
188*4882a593Smuzhiyun * be supported by RT5672. Otherwise, ASRC will be disabled and cause
189*4882a593Smuzhiyun * noise.
190*4882a593Smuzhiyun */
191*4882a593Smuzhiyun rt5670_sel_asrc_clk_src(component,
192*4882a593Smuzhiyun RT5670_DA_STEREO_FILTER
193*4882a593Smuzhiyun | RT5670_DA_MONO_L_FILTER
194*4882a593Smuzhiyun | RT5670_DA_MONO_R_FILTER
195*4882a593Smuzhiyun | RT5670_AD_STEREO_FILTER
196*4882a593Smuzhiyun | RT5670_AD_MONO_L_FILTER
197*4882a593Smuzhiyun | RT5670_AD_MONO_R_FILTER,
198*4882a593Smuzhiyun RT5670_CLK_SEL_I2S1_ASRC);
199*4882a593Smuzhiyun
200*4882a593Smuzhiyun ret = snd_soc_card_jack_new(runtime->card, "Headset",
201*4882a593Smuzhiyun SND_JACK_HEADSET | SND_JACK_BTN_0 |
202*4882a593Smuzhiyun SND_JACK_BTN_1 | SND_JACK_BTN_2,
203*4882a593Smuzhiyun &ctx->headset,
204*4882a593Smuzhiyun cht_bsw_headset_pins,
205*4882a593Smuzhiyun ARRAY_SIZE(cht_bsw_headset_pins));
206*4882a593Smuzhiyun if (ret)
207*4882a593Smuzhiyun return ret;
208*4882a593Smuzhiyun
209*4882a593Smuzhiyun snd_jack_set_key(ctx->headset.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
210*4882a593Smuzhiyun snd_jack_set_key(ctx->headset.jack, SND_JACK_BTN_1, KEY_VOLUMEUP);
211*4882a593Smuzhiyun snd_jack_set_key(ctx->headset.jack, SND_JACK_BTN_2, KEY_VOLUMEDOWN);
212*4882a593Smuzhiyun
213*4882a593Smuzhiyun rt5670_set_jack_detect(component, &ctx->headset);
214*4882a593Smuzhiyun if (ctx->mclk) {
215*4882a593Smuzhiyun /*
216*4882a593Smuzhiyun * The firmware might enable the clock at
217*4882a593Smuzhiyun * boot (this information may or may not
218*4882a593Smuzhiyun * be reflected in the enable clock register).
219*4882a593Smuzhiyun * To change the rate we must disable the clock
220*4882a593Smuzhiyun * first to cover these cases. Due to common
221*4882a593Smuzhiyun * clock framework restrictions that do not allow
222*4882a593Smuzhiyun * to disable a clock that has not been enabled,
223*4882a593Smuzhiyun * we need to enable the clock first.
224*4882a593Smuzhiyun */
225*4882a593Smuzhiyun ret = clk_prepare_enable(ctx->mclk);
226*4882a593Smuzhiyun if (!ret)
227*4882a593Smuzhiyun clk_disable_unprepare(ctx->mclk);
228*4882a593Smuzhiyun
229*4882a593Smuzhiyun ret = clk_set_rate(ctx->mclk, CHT_PLAT_CLK_3_HZ);
230*4882a593Smuzhiyun
231*4882a593Smuzhiyun if (ret) {
232*4882a593Smuzhiyun dev_err(runtime->dev, "unable to set MCLK rate\n");
233*4882a593Smuzhiyun return ret;
234*4882a593Smuzhiyun }
235*4882a593Smuzhiyun }
236*4882a593Smuzhiyun return 0;
237*4882a593Smuzhiyun }
238*4882a593Smuzhiyun
cht_codec_fixup(struct snd_soc_pcm_runtime * rtd,struct snd_pcm_hw_params * params)239*4882a593Smuzhiyun static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd,
240*4882a593Smuzhiyun struct snd_pcm_hw_params *params)
241*4882a593Smuzhiyun {
242*4882a593Smuzhiyun struct snd_interval *rate = hw_param_interval(params,
243*4882a593Smuzhiyun SNDRV_PCM_HW_PARAM_RATE);
244*4882a593Smuzhiyun struct snd_interval *channels = hw_param_interval(params,
245*4882a593Smuzhiyun SNDRV_PCM_HW_PARAM_CHANNELS);
246*4882a593Smuzhiyun int ret;
247*4882a593Smuzhiyun
248*4882a593Smuzhiyun /* The DSP will covert the FE rate to 48k, stereo, 24bits */
249*4882a593Smuzhiyun rate->min = rate->max = 48000;
250*4882a593Smuzhiyun channels->min = channels->max = 2;
251*4882a593Smuzhiyun
252*4882a593Smuzhiyun /* set SSP2 to 24-bit */
253*4882a593Smuzhiyun params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
254*4882a593Smuzhiyun
255*4882a593Smuzhiyun /*
256*4882a593Smuzhiyun * The default mode for the cpu-dai is TDM 4 slot. The default mode
257*4882a593Smuzhiyun * for the codec-dai is I2S. So we need to either set the cpu-dai to
258*4882a593Smuzhiyun * I2S mode to match the codec-dai, or set the codec-dai to TDM 4 slot
259*4882a593Smuzhiyun * (or program both to yet another mode).
260*4882a593Smuzhiyun * One board, the Lenovo Miix 2 10, uses not 1 but 2 codecs connected
261*4882a593Smuzhiyun * to SSP2. The second piggy-backed, output-only codec is inside the
262*4882a593Smuzhiyun * keyboard-dock (which has extra speakers). Unlike the main rt5672
263*4882a593Smuzhiyun * codec, we cannot configure this codec, it is hard coded to use
264*4882a593Smuzhiyun * 2 channel 24 bit I2S. For this to work we must use I2S mode on this
265*4882a593Smuzhiyun * board. Since we only support 2 channels anyways, there is no need
266*4882a593Smuzhiyun * for TDM on any cht-bsw-rt5672 designs. So we use I2S 2ch everywhere.
267*4882a593Smuzhiyun */
268*4882a593Smuzhiyun ret = snd_soc_dai_set_fmt(asoc_rtd_to_cpu(rtd, 0),
269*4882a593Smuzhiyun SND_SOC_DAIFMT_I2S |
270*4882a593Smuzhiyun SND_SOC_DAIFMT_NB_NF |
271*4882a593Smuzhiyun SND_SOC_DAIFMT_CBS_CFS);
272*4882a593Smuzhiyun if (ret < 0) {
273*4882a593Smuzhiyun dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret);
274*4882a593Smuzhiyun return ret;
275*4882a593Smuzhiyun }
276*4882a593Smuzhiyun
277*4882a593Smuzhiyun return 0;
278*4882a593Smuzhiyun }
279*4882a593Smuzhiyun
cht_aif1_startup(struct snd_pcm_substream * substream)280*4882a593Smuzhiyun static int cht_aif1_startup(struct snd_pcm_substream *substream)
281*4882a593Smuzhiyun {
282*4882a593Smuzhiyun return snd_pcm_hw_constraint_single(substream->runtime,
283*4882a593Smuzhiyun SNDRV_PCM_HW_PARAM_RATE, 48000);
284*4882a593Smuzhiyun }
285*4882a593Smuzhiyun
286*4882a593Smuzhiyun static const struct snd_soc_ops cht_aif1_ops = {
287*4882a593Smuzhiyun .startup = cht_aif1_startup,
288*4882a593Smuzhiyun };
289*4882a593Smuzhiyun
290*4882a593Smuzhiyun static const struct snd_soc_ops cht_be_ssp2_ops = {
291*4882a593Smuzhiyun .hw_params = cht_aif1_hw_params,
292*4882a593Smuzhiyun };
293*4882a593Smuzhiyun
294*4882a593Smuzhiyun SND_SOC_DAILINK_DEF(dummy,
295*4882a593Smuzhiyun DAILINK_COMP_ARRAY(COMP_DUMMY()));
296*4882a593Smuzhiyun
297*4882a593Smuzhiyun SND_SOC_DAILINK_DEF(media,
298*4882a593Smuzhiyun DAILINK_COMP_ARRAY(COMP_CPU("media-cpu-dai")));
299*4882a593Smuzhiyun
300*4882a593Smuzhiyun SND_SOC_DAILINK_DEF(deepbuffer,
301*4882a593Smuzhiyun DAILINK_COMP_ARRAY(COMP_CPU("deepbuffer-cpu-dai")));
302*4882a593Smuzhiyun
303*4882a593Smuzhiyun SND_SOC_DAILINK_DEF(ssp2_port,
304*4882a593Smuzhiyun DAILINK_COMP_ARRAY(COMP_CPU("ssp2-port")));
305*4882a593Smuzhiyun SND_SOC_DAILINK_DEF(ssp2_codec,
306*4882a593Smuzhiyun DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10EC5670:00",
307*4882a593Smuzhiyun "rt5670-aif1")));
308*4882a593Smuzhiyun
309*4882a593Smuzhiyun SND_SOC_DAILINK_DEF(platform,
310*4882a593Smuzhiyun DAILINK_COMP_ARRAY(COMP_PLATFORM("sst-mfld-platform")));
311*4882a593Smuzhiyun
312*4882a593Smuzhiyun static struct snd_soc_dai_link cht_dailink[] = {
313*4882a593Smuzhiyun /* Front End DAI links */
314*4882a593Smuzhiyun [MERR_DPCM_AUDIO] = {
315*4882a593Smuzhiyun .name = "Audio Port",
316*4882a593Smuzhiyun .stream_name = "Audio",
317*4882a593Smuzhiyun .nonatomic = true,
318*4882a593Smuzhiyun .dynamic = 1,
319*4882a593Smuzhiyun .dpcm_playback = 1,
320*4882a593Smuzhiyun .dpcm_capture = 1,
321*4882a593Smuzhiyun .ops = &cht_aif1_ops,
322*4882a593Smuzhiyun SND_SOC_DAILINK_REG(media, dummy, platform),
323*4882a593Smuzhiyun },
324*4882a593Smuzhiyun [MERR_DPCM_DEEP_BUFFER] = {
325*4882a593Smuzhiyun .name = "Deep-Buffer Audio Port",
326*4882a593Smuzhiyun .stream_name = "Deep-Buffer Audio",
327*4882a593Smuzhiyun .nonatomic = true,
328*4882a593Smuzhiyun .dynamic = 1,
329*4882a593Smuzhiyun .dpcm_playback = 1,
330*4882a593Smuzhiyun .ops = &cht_aif1_ops,
331*4882a593Smuzhiyun SND_SOC_DAILINK_REG(deepbuffer, dummy, platform),
332*4882a593Smuzhiyun },
333*4882a593Smuzhiyun
334*4882a593Smuzhiyun /* Back End DAI links */
335*4882a593Smuzhiyun {
336*4882a593Smuzhiyun /* SSP2 - Codec */
337*4882a593Smuzhiyun .name = "SSP2-Codec",
338*4882a593Smuzhiyun .id = 0,
339*4882a593Smuzhiyun .no_pcm = 1,
340*4882a593Smuzhiyun .nonatomic = true,
341*4882a593Smuzhiyun .init = cht_codec_init,
342*4882a593Smuzhiyun .be_hw_params_fixup = cht_codec_fixup,
343*4882a593Smuzhiyun .dpcm_playback = 1,
344*4882a593Smuzhiyun .dpcm_capture = 1,
345*4882a593Smuzhiyun .ops = &cht_be_ssp2_ops,
346*4882a593Smuzhiyun SND_SOC_DAILINK_REG(ssp2_port, ssp2_codec, platform),
347*4882a593Smuzhiyun },
348*4882a593Smuzhiyun };
349*4882a593Smuzhiyun
cht_suspend_pre(struct snd_soc_card * card)350*4882a593Smuzhiyun static int cht_suspend_pre(struct snd_soc_card *card)
351*4882a593Smuzhiyun {
352*4882a593Smuzhiyun struct snd_soc_component *component;
353*4882a593Smuzhiyun struct cht_mc_private *ctx = snd_soc_card_get_drvdata(card);
354*4882a593Smuzhiyun
355*4882a593Smuzhiyun for_each_card_components(card, component) {
356*4882a593Smuzhiyun if (!strncmp(component->name,
357*4882a593Smuzhiyun ctx->codec_name, sizeof(ctx->codec_name))) {
358*4882a593Smuzhiyun
359*4882a593Smuzhiyun dev_dbg(component->dev, "disabling jack detect before going to suspend.\n");
360*4882a593Smuzhiyun rt5670_jack_suspend(component);
361*4882a593Smuzhiyun break;
362*4882a593Smuzhiyun }
363*4882a593Smuzhiyun }
364*4882a593Smuzhiyun return 0;
365*4882a593Smuzhiyun }
366*4882a593Smuzhiyun
cht_resume_post(struct snd_soc_card * card)367*4882a593Smuzhiyun static int cht_resume_post(struct snd_soc_card *card)
368*4882a593Smuzhiyun {
369*4882a593Smuzhiyun struct snd_soc_component *component;
370*4882a593Smuzhiyun struct cht_mc_private *ctx = snd_soc_card_get_drvdata(card);
371*4882a593Smuzhiyun
372*4882a593Smuzhiyun for_each_card_components(card, component) {
373*4882a593Smuzhiyun if (!strncmp(component->name,
374*4882a593Smuzhiyun ctx->codec_name, sizeof(ctx->codec_name))) {
375*4882a593Smuzhiyun
376*4882a593Smuzhiyun dev_dbg(component->dev, "enabling jack detect for resume.\n");
377*4882a593Smuzhiyun rt5670_jack_resume(component);
378*4882a593Smuzhiyun break;
379*4882a593Smuzhiyun }
380*4882a593Smuzhiyun }
381*4882a593Smuzhiyun
382*4882a593Smuzhiyun return 0;
383*4882a593Smuzhiyun }
384*4882a593Smuzhiyun
385*4882a593Smuzhiyun #if IS_ENABLED(CONFIG_SND_SOC_SOF_BAYTRAIL)
386*4882a593Smuzhiyun /* use space before codec name to simplify card ID, and simplify driver name */
387*4882a593Smuzhiyun #define CARD_NAME "bytcht rt5672" /* card name will be 'sof-bytcht rt5672' */
388*4882a593Smuzhiyun #define DRIVER_NAME "SOF"
389*4882a593Smuzhiyun #else
390*4882a593Smuzhiyun #define CARD_NAME "cht-bsw-rt5672"
391*4882a593Smuzhiyun #define DRIVER_NAME NULL /* card name will be used for driver name */
392*4882a593Smuzhiyun #endif
393*4882a593Smuzhiyun
394*4882a593Smuzhiyun /* SoC card */
395*4882a593Smuzhiyun static struct snd_soc_card snd_soc_card_cht = {
396*4882a593Smuzhiyun .name = CARD_NAME,
397*4882a593Smuzhiyun .driver_name = DRIVER_NAME,
398*4882a593Smuzhiyun .owner = THIS_MODULE,
399*4882a593Smuzhiyun .dai_link = cht_dailink,
400*4882a593Smuzhiyun .num_links = ARRAY_SIZE(cht_dailink),
401*4882a593Smuzhiyun .dapm_widgets = cht_dapm_widgets,
402*4882a593Smuzhiyun .num_dapm_widgets = ARRAY_SIZE(cht_dapm_widgets),
403*4882a593Smuzhiyun .dapm_routes = cht_audio_map,
404*4882a593Smuzhiyun .num_dapm_routes = ARRAY_SIZE(cht_audio_map),
405*4882a593Smuzhiyun .controls = cht_mc_controls,
406*4882a593Smuzhiyun .num_controls = ARRAY_SIZE(cht_mc_controls),
407*4882a593Smuzhiyun .suspend_pre = cht_suspend_pre,
408*4882a593Smuzhiyun .resume_post = cht_resume_post,
409*4882a593Smuzhiyun };
410*4882a593Smuzhiyun
411*4882a593Smuzhiyun #define RT5672_I2C_DEFAULT "i2c-10EC5670:00"
412*4882a593Smuzhiyun
snd_cht_mc_probe(struct platform_device * pdev)413*4882a593Smuzhiyun static int snd_cht_mc_probe(struct platform_device *pdev)
414*4882a593Smuzhiyun {
415*4882a593Smuzhiyun int ret_val = 0;
416*4882a593Smuzhiyun struct cht_mc_private *drv;
417*4882a593Smuzhiyun struct snd_soc_acpi_mach *mach = pdev->dev.platform_data;
418*4882a593Smuzhiyun const char *platform_name;
419*4882a593Smuzhiyun struct acpi_device *adev;
420*4882a593Smuzhiyun int i;
421*4882a593Smuzhiyun
422*4882a593Smuzhiyun drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
423*4882a593Smuzhiyun if (!drv)
424*4882a593Smuzhiyun return -ENOMEM;
425*4882a593Smuzhiyun
426*4882a593Smuzhiyun strcpy(drv->codec_name, RT5672_I2C_DEFAULT);
427*4882a593Smuzhiyun
428*4882a593Smuzhiyun /* fixup codec name based on HID */
429*4882a593Smuzhiyun adev = acpi_dev_get_first_match_dev(mach->id, NULL, -1);
430*4882a593Smuzhiyun if (adev) {
431*4882a593Smuzhiyun snprintf(drv->codec_name, sizeof(drv->codec_name),
432*4882a593Smuzhiyun "i2c-%s", acpi_dev_name(adev));
433*4882a593Smuzhiyun put_device(&adev->dev);
434*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(cht_dailink); i++) {
435*4882a593Smuzhiyun if (!strcmp(cht_dailink[i].codecs->name,
436*4882a593Smuzhiyun RT5672_I2C_DEFAULT)) {
437*4882a593Smuzhiyun cht_dailink[i].codecs->name = drv->codec_name;
438*4882a593Smuzhiyun break;
439*4882a593Smuzhiyun }
440*4882a593Smuzhiyun }
441*4882a593Smuzhiyun }
442*4882a593Smuzhiyun
443*4882a593Smuzhiyun /* override plaform name, if required */
444*4882a593Smuzhiyun snd_soc_card_cht.dev = &pdev->dev;
445*4882a593Smuzhiyun platform_name = mach->mach_params.platform;
446*4882a593Smuzhiyun
447*4882a593Smuzhiyun ret_val = snd_soc_fixup_dai_links_platform_name(&snd_soc_card_cht,
448*4882a593Smuzhiyun platform_name);
449*4882a593Smuzhiyun if (ret_val)
450*4882a593Smuzhiyun return ret_val;
451*4882a593Smuzhiyun
452*4882a593Smuzhiyun drv->mclk = devm_clk_get(&pdev->dev, "pmc_plt_clk_3");
453*4882a593Smuzhiyun if (IS_ERR(drv->mclk)) {
454*4882a593Smuzhiyun dev_err(&pdev->dev,
455*4882a593Smuzhiyun "Failed to get MCLK from pmc_plt_clk_3: %ld\n",
456*4882a593Smuzhiyun PTR_ERR(drv->mclk));
457*4882a593Smuzhiyun return PTR_ERR(drv->mclk);
458*4882a593Smuzhiyun }
459*4882a593Smuzhiyun snd_soc_card_set_drvdata(&snd_soc_card_cht, drv);
460*4882a593Smuzhiyun
461*4882a593Smuzhiyun /* register the soc card */
462*4882a593Smuzhiyun ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_cht);
463*4882a593Smuzhiyun if (ret_val) {
464*4882a593Smuzhiyun dev_err(&pdev->dev,
465*4882a593Smuzhiyun "snd_soc_register_card failed %d\n", ret_val);
466*4882a593Smuzhiyun return ret_val;
467*4882a593Smuzhiyun }
468*4882a593Smuzhiyun platform_set_drvdata(pdev, &snd_soc_card_cht);
469*4882a593Smuzhiyun return ret_val;
470*4882a593Smuzhiyun }
471*4882a593Smuzhiyun
472*4882a593Smuzhiyun static struct platform_driver snd_cht_mc_driver = {
473*4882a593Smuzhiyun .driver = {
474*4882a593Smuzhiyun .name = "cht-bsw-rt5672",
475*4882a593Smuzhiyun #if IS_ENABLED(CONFIG_SND_SOC_SOF_BAYTRAIL)
476*4882a593Smuzhiyun .pm = &snd_soc_pm_ops,
477*4882a593Smuzhiyun #endif
478*4882a593Smuzhiyun },
479*4882a593Smuzhiyun .probe = snd_cht_mc_probe,
480*4882a593Smuzhiyun };
481*4882a593Smuzhiyun
482*4882a593Smuzhiyun module_platform_driver(snd_cht_mc_driver);
483*4882a593Smuzhiyun
484*4882a593Smuzhiyun MODULE_DESCRIPTION("ASoC Intel(R) Baytrail CR Machine driver");
485*4882a593Smuzhiyun MODULE_AUTHOR("Subhransu S. Prusty, Mengdong Lin");
486*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
487*4882a593Smuzhiyun MODULE_ALIAS("platform:cht-bsw-rt5672");
488