Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/soc/intel/boards/cht_bsw_max98090_ti.c
54408 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/*
3
* cht-bsw-max98090.c - ASoc Machine driver for Intel Cherryview-based
4
* platforms Cherrytrail and Braswell, with max98090 & TI codec.
5
*
6
* Copyright (C) 2015 Intel Corp
7
* Author: Fang, Yang A <[email protected]>
8
* This file is modified from cht_bsw_rt5645.c
9
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
10
*
11
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
12
*/
13
14
#include <linux/dmi.h>
15
#include <linux/gpio/consumer.h>
16
#include <linux/module.h>
17
#include <linux/platform_device.h>
18
#include <linux/slab.h>
19
#include <linux/acpi.h>
20
#include <linux/clk.h>
21
#include <sound/pcm.h>
22
#include <sound/pcm_params.h>
23
#include <sound/soc.h>
24
#include <sound/soc-acpi.h>
25
#include <sound/jack.h>
26
#include "../../codecs/max98090.h"
27
#include "../atom/sst-atom-controls.h"
28
#include "../../codecs/ts3a227e.h"
29
30
#define CHT_PLAT_CLK_3_HZ 19200000
31
#define CHT_CODEC_DAI "HiFi"
32
33
#define QUIRK_PMC_PLT_CLK_0 0x01
34
35
struct cht_mc_private {
36
struct clk *mclk;
37
struct snd_soc_jack jack;
38
bool ts3a227e_present;
39
int quirks;
40
};
41
42
static int platform_clock_control(struct snd_soc_dapm_widget *w,
43
struct snd_kcontrol *k, int event)
44
{
45
struct snd_soc_card *card = snd_soc_dapm_to_card(w->dapm);
46
struct snd_soc_dai *codec_dai;
47
struct cht_mc_private *ctx = snd_soc_card_get_drvdata(card);
48
int ret;
49
50
/* See the comment in snd_cht_mc_probe() */
51
if (ctx->quirks & QUIRK_PMC_PLT_CLK_0)
52
return 0;
53
54
codec_dai = snd_soc_card_get_codec_dai(card, CHT_CODEC_DAI);
55
if (!codec_dai) {
56
dev_err(card->dev, "Codec dai not found; Unable to set platform clock\n");
57
return -EIO;
58
}
59
60
if (SND_SOC_DAPM_EVENT_ON(event)) {
61
ret = clk_prepare_enable(ctx->mclk);
62
if (ret < 0) {
63
dev_err(card->dev,
64
"could not configure MCLK state");
65
return ret;
66
}
67
} else {
68
clk_disable_unprepare(ctx->mclk);
69
}
70
71
return 0;
72
}
73
74
static const struct snd_soc_dapm_widget cht_dapm_widgets[] = {
75
SND_SOC_DAPM_HP("Headphone", NULL),
76
SND_SOC_DAPM_MIC("Headset Mic", NULL),
77
SND_SOC_DAPM_MIC("Int Mic", NULL),
78
SND_SOC_DAPM_SPK("Ext Spk", NULL),
79
SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
80
platform_clock_control, SND_SOC_DAPM_PRE_PMU |
81
SND_SOC_DAPM_POST_PMD),
82
};
83
84
static const struct snd_soc_dapm_route cht_audio_map[] = {
85
{"IN34", NULL, "Headset Mic"},
86
{"Headset Mic", NULL, "MICBIAS"},
87
{"DMICL", NULL, "Int Mic"},
88
{"Headphone", NULL, "HPL"},
89
{"Headphone", NULL, "HPR"},
90
{"Ext Spk", NULL, "SPKL"},
91
{"Ext Spk", NULL, "SPKR"},
92
{"HiFi Playback", NULL, "ssp2 Tx"},
93
{"ssp2 Tx", NULL, "codec_out0"},
94
{"ssp2 Tx", NULL, "codec_out1"},
95
{"codec_in0", NULL, "ssp2 Rx" },
96
{"codec_in1", NULL, "ssp2 Rx" },
97
{"ssp2 Rx", NULL, "HiFi Capture"},
98
{"Headphone", NULL, "Platform Clock"},
99
{"Headset Mic", NULL, "Platform Clock"},
100
{"Int Mic", NULL, "Platform Clock"},
101
{"Ext Spk", NULL, "Platform Clock"},
102
};
103
104
static const struct snd_kcontrol_new cht_mc_controls[] = {
105
SOC_DAPM_PIN_SWITCH("Headphone"),
106
SOC_DAPM_PIN_SWITCH("Headset Mic"),
107
SOC_DAPM_PIN_SWITCH("Int Mic"),
108
SOC_DAPM_PIN_SWITCH("Ext Spk"),
109
};
110
111
static int cht_aif1_hw_params(struct snd_pcm_substream *substream,
112
struct snd_pcm_hw_params *params)
113
{
114
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
115
struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
116
int ret;
117
118
ret = snd_soc_dai_set_sysclk(codec_dai, M98090_REG_SYSTEM_CLOCK,
119
CHT_PLAT_CLK_3_HZ, SND_SOC_CLOCK_IN);
120
if (ret < 0) {
121
dev_err(rtd->dev, "can't set codec sysclk: %d\n", ret);
122
return ret;
123
}
124
125
return 0;
126
}
127
128
static int cht_ti_jack_event(struct notifier_block *nb,
129
unsigned long event, void *data)
130
{
131
struct snd_soc_jack *jack = (struct snd_soc_jack *)data;
132
struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(jack->card);
133
134
if (event & SND_JACK_MICROPHONE) {
135
snd_soc_dapm_force_enable_pin(dapm, "SHDN");
136
snd_soc_dapm_force_enable_pin(dapm, "MICBIAS");
137
snd_soc_dapm_sync(dapm);
138
} else {
139
snd_soc_dapm_disable_pin(dapm, "MICBIAS");
140
snd_soc_dapm_disable_pin(dapm, "SHDN");
141
snd_soc_dapm_sync(dapm);
142
}
143
144
return 0;
145
}
146
147
static struct notifier_block cht_jack_nb = {
148
.notifier_call = cht_ti_jack_event,
149
};
150
151
static struct snd_soc_jack_pin hs_jack_pins[] = {
152
{
153
.pin = "Headphone",
154
.mask = SND_JACK_HEADPHONE,
155
},
156
{
157
.pin = "Headset Mic",
158
.mask = SND_JACK_MICROPHONE,
159
},
160
};
161
162
static struct snd_soc_jack_gpio hs_jack_gpios[] = {
163
{
164
.name = "hp",
165
.report = SND_JACK_HEADPHONE | SND_JACK_LINEOUT,
166
.debounce_time = 200,
167
},
168
{
169
.name = "mic",
170
.invert = 1,
171
.report = SND_JACK_MICROPHONE,
172
.debounce_time = 200,
173
},
174
};
175
176
static const struct acpi_gpio_params hp_gpios = { 0, 0, false };
177
static const struct acpi_gpio_params mic_gpios = { 1, 0, false };
178
179
static const struct acpi_gpio_mapping acpi_max98090_gpios[] = {
180
{ "hp-gpios", &hp_gpios, 1 },
181
{ "mic-gpios", &mic_gpios, 1 },
182
{},
183
};
184
185
static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)
186
{
187
int ret;
188
int jack_type;
189
struct cht_mc_private *ctx = snd_soc_card_get_drvdata(runtime->card);
190
struct snd_soc_jack *jack = &ctx->jack;
191
192
if (ctx->ts3a227e_present) {
193
/*
194
* The jack has already been created in the
195
* cht_max98090_headset_init() function.
196
*/
197
snd_soc_jack_notifier_register(jack, &cht_jack_nb);
198
return 0;
199
}
200
201
jack_type = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE;
202
203
ret = snd_soc_card_jack_new_pins(runtime->card, "Headset Jack",
204
jack_type, jack,
205
hs_jack_pins,
206
ARRAY_SIZE(hs_jack_pins));
207
if (ret) {
208
dev_err(runtime->dev, "Headset Jack creation failed %d\n", ret);
209
return ret;
210
}
211
212
ret = snd_soc_jack_add_gpiods(runtime->card->dev->parent, jack,
213
ARRAY_SIZE(hs_jack_gpios),
214
hs_jack_gpios);
215
if (ret) {
216
/*
217
* flag error but don't bail if jack detect is broken
218
* due to platform issues or bad BIOS/configuration
219
*/
220
dev_err(runtime->dev,
221
"jack detection gpios not added, error %d\n", ret);
222
}
223
224
/* See the comment in snd_cht_mc_probe() */
225
if (ctx->quirks & QUIRK_PMC_PLT_CLK_0)
226
return 0;
227
228
/*
229
* The firmware might enable the clock at
230
* boot (this information may or may not
231
* be reflected in the enable clock register).
232
* To change the rate we must disable the clock
233
* first to cover these cases. Due to common
234
* clock framework restrictions that do not allow
235
* to disable a clock that has not been enabled,
236
* we need to enable the clock first.
237
*/
238
ret = clk_prepare_enable(ctx->mclk);
239
if (!ret)
240
clk_disable_unprepare(ctx->mclk);
241
242
ret = clk_set_rate(ctx->mclk, CHT_PLAT_CLK_3_HZ);
243
244
if (ret)
245
dev_err(runtime->dev, "unable to set MCLK rate\n");
246
247
return ret;
248
}
249
250
static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd,
251
struct snd_pcm_hw_params *params)
252
{
253
struct snd_interval *rate = hw_param_interval(params,
254
SNDRV_PCM_HW_PARAM_RATE);
255
struct snd_interval *channels = hw_param_interval(params,
256
SNDRV_PCM_HW_PARAM_CHANNELS);
257
int ret = 0;
258
unsigned int fmt = 0;
259
260
ret = snd_soc_dai_set_tdm_slot(snd_soc_rtd_to_cpu(rtd, 0), 0x3, 0x3, 2, 16);
261
if (ret < 0) {
262
dev_err(rtd->dev, "can't set cpu_dai slot fmt: %d\n", ret);
263
return ret;
264
}
265
266
fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_BP_FP;
267
268
ret = snd_soc_dai_set_fmt(snd_soc_rtd_to_cpu(rtd, 0), fmt);
269
if (ret < 0) {
270
dev_err(rtd->dev, "can't set cpu_dai set fmt: %d\n", ret);
271
return ret;
272
}
273
274
/* The DSP will convert the FE rate to 48k, stereo, 24bits */
275
rate->min = rate->max = 48000;
276
channels->min = channels->max = 2;
277
278
/* set SSP2 to 16-bit */
279
params_set_format(params, SNDRV_PCM_FORMAT_S16_LE);
280
return 0;
281
}
282
283
static int cht_aif1_startup(struct snd_pcm_substream *substream)
284
{
285
return snd_pcm_hw_constraint_single(substream->runtime,
286
SNDRV_PCM_HW_PARAM_RATE, 48000);
287
}
288
289
static int cht_max98090_headset_init(struct snd_soc_component *component)
290
{
291
struct snd_soc_card *card = component->card;
292
struct cht_mc_private *ctx = snd_soc_card_get_drvdata(card);
293
struct snd_soc_jack *jack = &ctx->jack;
294
int jack_type;
295
int ret;
296
297
/*
298
* TI supports 4 buttons headset detection
299
* KEY_MEDIA
300
* KEY_VOICECOMMAND
301
* KEY_VOLUMEUP
302
* KEY_VOLUMEDOWN
303
*/
304
jack_type = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE |
305
SND_JACK_BTN_0 | SND_JACK_BTN_1 |
306
SND_JACK_BTN_2 | SND_JACK_BTN_3;
307
308
ret = snd_soc_card_jack_new(card, "Headset Jack", jack_type, jack);
309
if (ret) {
310
dev_err(card->dev, "Headset Jack creation failed %d\n", ret);
311
return ret;
312
}
313
314
return ts3a227e_enable_jack_detect(component, jack);
315
}
316
317
static const struct snd_soc_ops cht_aif1_ops = {
318
.startup = cht_aif1_startup,
319
};
320
321
static const struct snd_soc_ops cht_be_ssp2_ops = {
322
.hw_params = cht_aif1_hw_params,
323
};
324
325
static struct snd_soc_aux_dev cht_max98090_headset_dev = {
326
.dlc = COMP_AUX("i2c-104C227E:00"),
327
.init = cht_max98090_headset_init,
328
};
329
330
SND_SOC_DAILINK_DEF(dummy,
331
DAILINK_COMP_ARRAY(COMP_DUMMY()));
332
333
SND_SOC_DAILINK_DEF(media,
334
DAILINK_COMP_ARRAY(COMP_CPU("media-cpu-dai")));
335
336
SND_SOC_DAILINK_DEF(deepbuffer,
337
DAILINK_COMP_ARRAY(COMP_CPU("deepbuffer-cpu-dai")));
338
339
SND_SOC_DAILINK_DEF(ssp2_port,
340
DAILINK_COMP_ARRAY(COMP_CPU("ssp2-port")));
341
SND_SOC_DAILINK_DEF(ssp2_codec,
342
DAILINK_COMP_ARRAY(COMP_CODEC("i2c-193C9890:00", "HiFi")));
343
344
SND_SOC_DAILINK_DEF(platform,
345
DAILINK_COMP_ARRAY(COMP_PLATFORM("sst-mfld-platform")));
346
347
static struct snd_soc_dai_link cht_dailink[] = {
348
[MERR_DPCM_AUDIO] = {
349
.name = "Audio Port",
350
.stream_name = "Audio",
351
.nonatomic = true,
352
.dynamic = 1,
353
.ops = &cht_aif1_ops,
354
SND_SOC_DAILINK_REG(media, dummy, platform),
355
},
356
[MERR_DPCM_DEEP_BUFFER] = {
357
.name = "Deep-Buffer Audio Port",
358
.stream_name = "Deep-Buffer Audio",
359
.nonatomic = true,
360
.dynamic = 1,
361
.playback_only = 1,
362
.ops = &cht_aif1_ops,
363
SND_SOC_DAILINK_REG(deepbuffer, dummy, platform),
364
},
365
/* back ends */
366
{
367
.name = "SSP2-Codec",
368
.id = 0,
369
.no_pcm = 1,
370
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
371
| SND_SOC_DAIFMT_CBC_CFC,
372
.init = cht_codec_init,
373
.be_hw_params_fixup = cht_codec_fixup,
374
.ops = &cht_be_ssp2_ops,
375
SND_SOC_DAILINK_REG(ssp2_port, ssp2_codec, platform),
376
},
377
};
378
379
/* use space before codec name to simplify card ID, and simplify driver name */
380
#define SOF_CARD_NAME "bytcht max98090" /* card name will be 'sof-bytcht max98090 */
381
#define SOF_DRIVER_NAME "SOF"
382
383
#define CARD_NAME "chtmax98090"
384
#define DRIVER_NAME NULL /* card name will be used for driver name */
385
386
/* SoC card */
387
static struct snd_soc_card snd_soc_card_cht = {
388
.owner = THIS_MODULE,
389
.dai_link = cht_dailink,
390
.num_links = ARRAY_SIZE(cht_dailink),
391
.aux_dev = &cht_max98090_headset_dev,
392
.num_aux_devs = 1,
393
.dapm_widgets = cht_dapm_widgets,
394
.num_dapm_widgets = ARRAY_SIZE(cht_dapm_widgets),
395
.dapm_routes = cht_audio_map,
396
.num_dapm_routes = ARRAY_SIZE(cht_audio_map),
397
.controls = cht_mc_controls,
398
.num_controls = ARRAY_SIZE(cht_mc_controls),
399
};
400
401
static const struct dmi_system_id cht_max98090_quirk_table[] = {
402
{
403
/* Banjo model Chromebook */
404
.matches = {
405
DMI_MATCH(DMI_PRODUCT_NAME, "Banjo"),
406
},
407
.driver_data = (void *)QUIRK_PMC_PLT_CLK_0,
408
},
409
{
410
/* Candy model Chromebook */
411
.matches = {
412
DMI_MATCH(DMI_PRODUCT_NAME, "Candy"),
413
},
414
.driver_data = (void *)QUIRK_PMC_PLT_CLK_0,
415
},
416
{
417
/* Clapper model Chromebook */
418
.matches = {
419
DMI_MATCH(DMI_PRODUCT_NAME, "Clapper"),
420
},
421
.driver_data = (void *)QUIRK_PMC_PLT_CLK_0,
422
},
423
{
424
/* Cyan model Chromebook */
425
.matches = {
426
DMI_MATCH(DMI_PRODUCT_NAME, "Cyan"),
427
},
428
.driver_data = (void *)QUIRK_PMC_PLT_CLK_0,
429
},
430
{
431
/* Enguarde model Chromebook */
432
.matches = {
433
DMI_MATCH(DMI_PRODUCT_NAME, "Enguarde"),
434
},
435
.driver_data = (void *)QUIRK_PMC_PLT_CLK_0,
436
},
437
{
438
/* Glimmer model Chromebook */
439
.matches = {
440
DMI_MATCH(DMI_PRODUCT_NAME, "Glimmer"),
441
},
442
.driver_data = (void *)QUIRK_PMC_PLT_CLK_0,
443
},
444
{
445
/* Gnawty model Chromebook (Acer Chromebook CB3-111) */
446
.matches = {
447
DMI_MATCH(DMI_PRODUCT_NAME, "Gnawty"),
448
},
449
.driver_data = (void *)QUIRK_PMC_PLT_CLK_0,
450
},
451
{
452
/* Heli model Chromebook */
453
.matches = {
454
DMI_MATCH(DMI_PRODUCT_NAME, "Heli"),
455
},
456
.driver_data = (void *)QUIRK_PMC_PLT_CLK_0,
457
},
458
{
459
/* Kip model Chromebook */
460
.matches = {
461
DMI_MATCH(DMI_PRODUCT_NAME, "Kip"),
462
},
463
.driver_data = (void *)QUIRK_PMC_PLT_CLK_0,
464
},
465
{
466
/* Ninja model Chromebook */
467
.matches = {
468
DMI_MATCH(DMI_PRODUCT_NAME, "Ninja"),
469
},
470
.driver_data = (void *)QUIRK_PMC_PLT_CLK_0,
471
},
472
{
473
/* Orco model Chromebook */
474
.matches = {
475
DMI_MATCH(DMI_PRODUCT_NAME, "Orco"),
476
},
477
.driver_data = (void *)QUIRK_PMC_PLT_CLK_0,
478
},
479
{
480
/* Quawks model Chromebook */
481
.matches = {
482
DMI_MATCH(DMI_PRODUCT_NAME, "Quawks"),
483
},
484
.driver_data = (void *)QUIRK_PMC_PLT_CLK_0,
485
},
486
{
487
/* Rambi model Chromebook */
488
.matches = {
489
DMI_MATCH(DMI_PRODUCT_NAME, "Rambi"),
490
},
491
.driver_data = (void *)QUIRK_PMC_PLT_CLK_0,
492
},
493
{
494
/* Squawks model Chromebook */
495
.matches = {
496
DMI_MATCH(DMI_PRODUCT_NAME, "Squawks"),
497
},
498
.driver_data = (void *)QUIRK_PMC_PLT_CLK_0,
499
},
500
{
501
/* Sumo model Chromebook */
502
.matches = {
503
DMI_MATCH(DMI_PRODUCT_NAME, "Sumo"),
504
},
505
.driver_data = (void *)QUIRK_PMC_PLT_CLK_0,
506
},
507
{
508
/* Swanky model Chromebook (Toshiba Chromebook 2) */
509
.matches = {
510
DMI_MATCH(DMI_PRODUCT_NAME, "Swanky"),
511
},
512
.driver_data = (void *)QUIRK_PMC_PLT_CLK_0,
513
},
514
{
515
/* Winky model Chromebook */
516
.matches = {
517
DMI_MATCH(DMI_PRODUCT_NAME, "Winky"),
518
},
519
.driver_data = (void *)QUIRK_PMC_PLT_CLK_0,
520
},
521
{}
522
};
523
524
static int snd_cht_mc_probe(struct platform_device *pdev)
525
{
526
const struct dmi_system_id *dmi_id;
527
struct device *dev = &pdev->dev;
528
int ret_val = 0;
529
struct cht_mc_private *drv;
530
const char *mclk_name;
531
struct snd_soc_acpi_mach *mach;
532
const char *platform_name;
533
bool sof_parent;
534
535
drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL);
536
if (!drv)
537
return -ENOMEM;
538
539
dmi_id = dmi_first_match(cht_max98090_quirk_table);
540
if (dmi_id)
541
drv->quirks = (unsigned long)dmi_id->driver_data;
542
543
drv->ts3a227e_present = acpi_dev_found("104C227E");
544
if (!drv->ts3a227e_present) {
545
/* no need probe TI jack detection chip */
546
snd_soc_card_cht.aux_dev = NULL;
547
snd_soc_card_cht.num_aux_devs = 0;
548
549
ret_val = devm_acpi_dev_add_driver_gpios(dev->parent,
550
acpi_max98090_gpios);
551
if (ret_val)
552
dev_dbg(dev, "Unable to add GPIO mapping table\n");
553
}
554
555
/* override platform name, if required */
556
snd_soc_card_cht.dev = dev;
557
mach = dev->platform_data;
558
platform_name = mach->mach_params.platform;
559
560
ret_val = snd_soc_fixup_dai_links_platform_name(&snd_soc_card_cht,
561
platform_name);
562
if (ret_val)
563
return ret_val;
564
565
/* register the soc card */
566
snd_soc_card_set_drvdata(&snd_soc_card_cht, drv);
567
568
if (drv->quirks & QUIRK_PMC_PLT_CLK_0)
569
mclk_name = "pmc_plt_clk_0";
570
else
571
mclk_name = "pmc_plt_clk_3";
572
573
drv->mclk = devm_clk_get(dev, mclk_name);
574
if (IS_ERR(drv->mclk)) {
575
dev_err(dev,
576
"Failed to get MCLK from %s: %ld\n",
577
mclk_name, PTR_ERR(drv->mclk));
578
return PTR_ERR(drv->mclk);
579
}
580
581
/*
582
* Boards which have the MAX98090's clk connected to clk_0 do not seem
583
* to like it if we muck with the clock. If we disable the clock when
584
* it is unused we get "max98090 i2c-193C9890:00: PLL unlocked" errors
585
* and the PLL never seems to lock again.
586
* So for these boards we enable it here once and leave it at that.
587
*/
588
if (drv->quirks & QUIRK_PMC_PLT_CLK_0) {
589
ret_val = clk_prepare_enable(drv->mclk);
590
if (ret_val < 0) {
591
dev_err(dev, "MCLK enable error: %d\n", ret_val);
592
return ret_val;
593
}
594
}
595
596
sof_parent = snd_soc_acpi_sof_parent(dev);
597
598
/* set card and driver name */
599
if (sof_parent) {
600
snd_soc_card_cht.name = SOF_CARD_NAME;
601
snd_soc_card_cht.driver_name = SOF_DRIVER_NAME;
602
} else {
603
snd_soc_card_cht.name = CARD_NAME;
604
snd_soc_card_cht.driver_name = DRIVER_NAME;
605
}
606
607
/* set pm ops */
608
if (sof_parent)
609
dev->driver->pm = &snd_soc_pm_ops;
610
611
ret_val = devm_snd_soc_register_card(dev, &snd_soc_card_cht);
612
if (ret_val) {
613
dev_err(dev,
614
"snd_soc_register_card failed %d\n", ret_val);
615
return ret_val;
616
}
617
platform_set_drvdata(pdev, &snd_soc_card_cht);
618
return ret_val;
619
}
620
621
static void snd_cht_mc_remove(struct platform_device *pdev)
622
{
623
struct snd_soc_card *card = platform_get_drvdata(pdev);
624
struct cht_mc_private *ctx = snd_soc_card_get_drvdata(card);
625
626
if (ctx->quirks & QUIRK_PMC_PLT_CLK_0)
627
clk_disable_unprepare(ctx->mclk);
628
}
629
630
static struct platform_driver snd_cht_mc_driver = {
631
.driver = {
632
.name = "cht-bsw-max98090",
633
},
634
.probe = snd_cht_mc_probe,
635
.remove = snd_cht_mc_remove,
636
};
637
638
module_platform_driver(snd_cht_mc_driver)
639
640
MODULE_DESCRIPTION("ASoC Intel(R) Braswell Machine driver");
641
MODULE_AUTHOR("Fang, Yang A <[email protected]>");
642
MODULE_LICENSE("GPL v2");
643
MODULE_ALIAS("platform:cht-bsw-max98090");
644
645