1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
240aa4a30SMark Brown /*
340aa4a30SMark Brown * wm8350.c -- WM8350 ALSA SoC audio driver
440aa4a30SMark Brown *
5656baaebSMark Brown * Copyright (C) 2007-12 Wolfson Microelectronics PLC.
640aa4a30SMark Brown *
764ca0404SLiam Girdwood * Author: Liam Girdwood <lrg@slimlogic.co.uk>
840aa4a30SMark Brown */
940aa4a30SMark Brown
1040aa4a30SMark Brown #include <linux/module.h>
1140aa4a30SMark Brown #include <linux/moduleparam.h>
1240aa4a30SMark Brown #include <linux/init.h>
135a0e3ad6STejun Heo #include <linux/slab.h>
1440aa4a30SMark Brown #include <linux/delay.h>
1540aa4a30SMark Brown #include <linux/pm.h>
1640aa4a30SMark Brown #include <linux/platform_device.h>
1740aa4a30SMark Brown #include <linux/mfd/wm8350/audio.h>
1840aa4a30SMark Brown #include <linux/mfd/wm8350/core.h>
1940aa4a30SMark Brown #include <linux/regulator/consumer.h>
2040aa4a30SMark Brown #include <sound/core.h>
2140aa4a30SMark Brown #include <sound/pcm.h>
2240aa4a30SMark Brown #include <sound/pcm_params.h>
2340aa4a30SMark Brown #include <sound/soc.h>
2440aa4a30SMark Brown #include <sound/initval.h>
2540aa4a30SMark Brown #include <sound/tlv.h>
262bbb5d66SMark Brown #include <trace/events/asoc.h>
2740aa4a30SMark Brown
2840aa4a30SMark Brown #include "wm8350.h"
2940aa4a30SMark Brown
3040aa4a30SMark Brown #define WM8350_OUTn_0dB 0x39
3140aa4a30SMark Brown
3240aa4a30SMark Brown #define WM8350_RAMP_NONE 0
3340aa4a30SMark Brown #define WM8350_RAMP_UP 1
3440aa4a30SMark Brown #define WM8350_RAMP_DOWN 2
3540aa4a30SMark Brown
3640aa4a30SMark Brown /* We only include the analogue supplies here; the digital supplies
3740aa4a30SMark Brown * need to be available well before this driver can be probed.
3840aa4a30SMark Brown */
3940aa4a30SMark Brown static const char *supply_names[] = {
4040aa4a30SMark Brown "AVDD",
4140aa4a30SMark Brown "HPVDD",
4240aa4a30SMark Brown };
4340aa4a30SMark Brown
4440aa4a30SMark Brown struct wm8350_output {
4540aa4a30SMark Brown u16 active;
4640aa4a30SMark Brown u16 left_vol;
4740aa4a30SMark Brown u16 right_vol;
4840aa4a30SMark Brown u16 ramp;
4940aa4a30SMark Brown u16 mute;
5040aa4a30SMark Brown };
5140aa4a30SMark Brown
52a6ba2b2dSMark Brown struct wm8350_jack_data {
53a6ba2b2dSMark Brown struct snd_soc_jack *jack;
546d3c26bcSMark Brown struct delayed_work work;
55a6ba2b2dSMark Brown int report;
562a0761a3SMark Brown int short_report;
57a6ba2b2dSMark Brown };
58a6ba2b2dSMark Brown
5940aa4a30SMark Brown struct wm8350_data {
6030facd4dSMark Brown struct wm8350 *wm8350;
6140aa4a30SMark Brown struct wm8350_output out1;
6240aa4a30SMark Brown struct wm8350_output out2;
63a6ba2b2dSMark Brown struct wm8350_jack_data hpl;
64a6ba2b2dSMark Brown struct wm8350_jack_data hpr;
652a0761a3SMark Brown struct wm8350_jack_data mic;
6640aa4a30SMark Brown struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)];
67f1e887deSMark Brown int fll_freq_out;
68f1e887deSMark Brown int fll_freq_in;
69cd5d8226SLars-Peter Clausen struct delayed_work pga_work;
7040aa4a30SMark Brown };
7140aa4a30SMark Brown
7240aa4a30SMark Brown /*
7340aa4a30SMark Brown * Ramp OUT1 PGA volume to minimise pops at stream startup and shutdown.
7440aa4a30SMark Brown */
wm8350_out1_ramp_step(struct wm8350_data * wm8350_data)75cd5d8226SLars-Peter Clausen static inline int wm8350_out1_ramp_step(struct wm8350_data *wm8350_data)
7640aa4a30SMark Brown {
7740aa4a30SMark Brown struct wm8350_output *out1 = &wm8350_data->out1;
78018a455aSMark Brown struct wm8350 *wm8350 = wm8350_data->wm8350;
7940aa4a30SMark Brown int left_complete = 0, right_complete = 0;
8040aa4a30SMark Brown u16 reg, val;
8140aa4a30SMark Brown
8240aa4a30SMark Brown /* left channel */
8340aa4a30SMark Brown reg = wm8350_reg_read(wm8350, WM8350_LOUT1_VOLUME);
8440aa4a30SMark Brown val = (reg & WM8350_OUT1L_VOL_MASK) >> WM8350_OUT1L_VOL_SHIFT;
8540aa4a30SMark Brown
8640aa4a30SMark Brown if (out1->ramp == WM8350_RAMP_UP) {
8740aa4a30SMark Brown /* ramp step up */
8840aa4a30SMark Brown if (val < out1->left_vol) {
8940aa4a30SMark Brown val++;
9040aa4a30SMark Brown reg &= ~WM8350_OUT1L_VOL_MASK;
9140aa4a30SMark Brown wm8350_reg_write(wm8350, WM8350_LOUT1_VOLUME,
9240aa4a30SMark Brown reg | (val << WM8350_OUT1L_VOL_SHIFT));
9340aa4a30SMark Brown } else
9440aa4a30SMark Brown left_complete = 1;
9540aa4a30SMark Brown } else if (out1->ramp == WM8350_RAMP_DOWN) {
9640aa4a30SMark Brown /* ramp step down */
9740aa4a30SMark Brown if (val > 0) {
9840aa4a30SMark Brown val--;
9940aa4a30SMark Brown reg &= ~WM8350_OUT1L_VOL_MASK;
10040aa4a30SMark Brown wm8350_reg_write(wm8350, WM8350_LOUT1_VOLUME,
10140aa4a30SMark Brown reg | (val << WM8350_OUT1L_VOL_SHIFT));
10240aa4a30SMark Brown } else
10340aa4a30SMark Brown left_complete = 1;
10440aa4a30SMark Brown } else
10540aa4a30SMark Brown return 1;
10640aa4a30SMark Brown
10740aa4a30SMark Brown /* right channel */
10840aa4a30SMark Brown reg = wm8350_reg_read(wm8350, WM8350_ROUT1_VOLUME);
10940aa4a30SMark Brown val = (reg & WM8350_OUT1R_VOL_MASK) >> WM8350_OUT1R_VOL_SHIFT;
11040aa4a30SMark Brown if (out1->ramp == WM8350_RAMP_UP) {
11140aa4a30SMark Brown /* ramp step up */
11240aa4a30SMark Brown if (val < out1->right_vol) {
11340aa4a30SMark Brown val++;
11440aa4a30SMark Brown reg &= ~WM8350_OUT1R_VOL_MASK;
11540aa4a30SMark Brown wm8350_reg_write(wm8350, WM8350_ROUT1_VOLUME,
11640aa4a30SMark Brown reg | (val << WM8350_OUT1R_VOL_SHIFT));
11740aa4a30SMark Brown } else
11840aa4a30SMark Brown right_complete = 1;
11940aa4a30SMark Brown } else if (out1->ramp == WM8350_RAMP_DOWN) {
12040aa4a30SMark Brown /* ramp step down */
12140aa4a30SMark Brown if (val > 0) {
12240aa4a30SMark Brown val--;
12340aa4a30SMark Brown reg &= ~WM8350_OUT1R_VOL_MASK;
12440aa4a30SMark Brown wm8350_reg_write(wm8350, WM8350_ROUT1_VOLUME,
12540aa4a30SMark Brown reg | (val << WM8350_OUT1R_VOL_SHIFT));
12640aa4a30SMark Brown } else
12740aa4a30SMark Brown right_complete = 1;
12840aa4a30SMark Brown }
12940aa4a30SMark Brown
13040aa4a30SMark Brown /* only hit the update bit if either volume has changed this step */
13140aa4a30SMark Brown if (!left_complete || !right_complete)
13240aa4a30SMark Brown wm8350_set_bits(wm8350, WM8350_LOUT1_VOLUME, WM8350_OUT1_VU);
13340aa4a30SMark Brown
13440aa4a30SMark Brown return left_complete & right_complete;
13540aa4a30SMark Brown }
13640aa4a30SMark Brown
13740aa4a30SMark Brown /*
13840aa4a30SMark Brown * Ramp OUT2 PGA volume to minimise pops at stream startup and shutdown.
13940aa4a30SMark Brown */
wm8350_out2_ramp_step(struct wm8350_data * wm8350_data)140cd5d8226SLars-Peter Clausen static inline int wm8350_out2_ramp_step(struct wm8350_data *wm8350_data)
14140aa4a30SMark Brown {
14240aa4a30SMark Brown struct wm8350_output *out2 = &wm8350_data->out2;
143018a455aSMark Brown struct wm8350 *wm8350 = wm8350_data->wm8350;
14440aa4a30SMark Brown int left_complete = 0, right_complete = 0;
14540aa4a30SMark Brown u16 reg, val;
14640aa4a30SMark Brown
14740aa4a30SMark Brown /* left channel */
14840aa4a30SMark Brown reg = wm8350_reg_read(wm8350, WM8350_LOUT2_VOLUME);
14940aa4a30SMark Brown val = (reg & WM8350_OUT2L_VOL_MASK) >> WM8350_OUT1L_VOL_SHIFT;
15040aa4a30SMark Brown if (out2->ramp == WM8350_RAMP_UP) {
15140aa4a30SMark Brown /* ramp step up */
15240aa4a30SMark Brown if (val < out2->left_vol) {
15340aa4a30SMark Brown val++;
15440aa4a30SMark Brown reg &= ~WM8350_OUT2L_VOL_MASK;
15540aa4a30SMark Brown wm8350_reg_write(wm8350, WM8350_LOUT2_VOLUME,
15640aa4a30SMark Brown reg | (val << WM8350_OUT1L_VOL_SHIFT));
15740aa4a30SMark Brown } else
15840aa4a30SMark Brown left_complete = 1;
15940aa4a30SMark Brown } else if (out2->ramp == WM8350_RAMP_DOWN) {
16040aa4a30SMark Brown /* ramp step down */
16140aa4a30SMark Brown if (val > 0) {
16240aa4a30SMark Brown val--;
16340aa4a30SMark Brown reg &= ~WM8350_OUT2L_VOL_MASK;
16440aa4a30SMark Brown wm8350_reg_write(wm8350, WM8350_LOUT2_VOLUME,
16540aa4a30SMark Brown reg | (val << WM8350_OUT1L_VOL_SHIFT));
16640aa4a30SMark Brown } else
16740aa4a30SMark Brown left_complete = 1;
16840aa4a30SMark Brown } else
16940aa4a30SMark Brown return 1;
17040aa4a30SMark Brown
17140aa4a30SMark Brown /* right channel */
17240aa4a30SMark Brown reg = wm8350_reg_read(wm8350, WM8350_ROUT2_VOLUME);
17340aa4a30SMark Brown val = (reg & WM8350_OUT2R_VOL_MASK) >> WM8350_OUT1R_VOL_SHIFT;
17440aa4a30SMark Brown if (out2->ramp == WM8350_RAMP_UP) {
17540aa4a30SMark Brown /* ramp step up */
17640aa4a30SMark Brown if (val < out2->right_vol) {
17740aa4a30SMark Brown val++;
17840aa4a30SMark Brown reg &= ~WM8350_OUT2R_VOL_MASK;
17940aa4a30SMark Brown wm8350_reg_write(wm8350, WM8350_ROUT2_VOLUME,
18040aa4a30SMark Brown reg | (val << WM8350_OUT1R_VOL_SHIFT));
18140aa4a30SMark Brown } else
18240aa4a30SMark Brown right_complete = 1;
18340aa4a30SMark Brown } else if (out2->ramp == WM8350_RAMP_DOWN) {
18440aa4a30SMark Brown /* ramp step down */
18540aa4a30SMark Brown if (val > 0) {
18640aa4a30SMark Brown val--;
18740aa4a30SMark Brown reg &= ~WM8350_OUT2R_VOL_MASK;
18840aa4a30SMark Brown wm8350_reg_write(wm8350, WM8350_ROUT2_VOLUME,
18940aa4a30SMark Brown reg | (val << WM8350_OUT1R_VOL_SHIFT));
19040aa4a30SMark Brown } else
19140aa4a30SMark Brown right_complete = 1;
19240aa4a30SMark Brown }
19340aa4a30SMark Brown
19440aa4a30SMark Brown /* only hit the update bit if either volume has changed this step */
19540aa4a30SMark Brown if (!left_complete || !right_complete)
19640aa4a30SMark Brown wm8350_set_bits(wm8350, WM8350_LOUT2_VOLUME, WM8350_OUT2_VU);
19740aa4a30SMark Brown
19840aa4a30SMark Brown return left_complete & right_complete;
19940aa4a30SMark Brown }
20040aa4a30SMark Brown
20140aa4a30SMark Brown /*
20240aa4a30SMark Brown * This work ramps both output PGAs at stream start/stop time to
20340aa4a30SMark Brown * minimise pop associated with DAPM power switching.
20440aa4a30SMark Brown * It's best to enable Zero Cross when ramping occurs to minimise any
20540aa4a30SMark Brown * zipper noises.
20640aa4a30SMark Brown */
wm8350_pga_work(struct work_struct * work)20740aa4a30SMark Brown static void wm8350_pga_work(struct work_struct *work)
20840aa4a30SMark Brown {
209cd5d8226SLars-Peter Clausen struct wm8350_data *wm8350_data =
210cd5d8226SLars-Peter Clausen container_of(work, struct wm8350_data, pga_work.work);
21140aa4a30SMark Brown struct wm8350_output *out1 = &wm8350_data->out1,
21240aa4a30SMark Brown *out2 = &wm8350_data->out2;
21340aa4a30SMark Brown int i, out1_complete, out2_complete;
21440aa4a30SMark Brown
21540aa4a30SMark Brown /* do we need to ramp at all ? */
21640aa4a30SMark Brown if (out1->ramp == WM8350_RAMP_NONE && out2->ramp == WM8350_RAMP_NONE)
21740aa4a30SMark Brown return;
21840aa4a30SMark Brown
21940aa4a30SMark Brown /* PGA volumes have 6 bits of resolution to ramp */
22040aa4a30SMark Brown for (i = 0; i <= 63; i++) {
2212db5fa77SJulia Lawall out1_complete = 1;
2222db5fa77SJulia Lawall out2_complete = 1;
22340aa4a30SMark Brown if (out1->ramp != WM8350_RAMP_NONE)
224cd5d8226SLars-Peter Clausen out1_complete = wm8350_out1_ramp_step(wm8350_data);
22540aa4a30SMark Brown if (out2->ramp != WM8350_RAMP_NONE)
226cd5d8226SLars-Peter Clausen out2_complete = wm8350_out2_ramp_step(wm8350_data);
22740aa4a30SMark Brown
22840aa4a30SMark Brown /* ramp finished ? */
22940aa4a30SMark Brown if (out1_complete && out2_complete)
23040aa4a30SMark Brown break;
23140aa4a30SMark Brown
23240aa4a30SMark Brown /* we need to delay longer on the up ramp */
23340aa4a30SMark Brown if (out1->ramp == WM8350_RAMP_UP ||
23440aa4a30SMark Brown out2->ramp == WM8350_RAMP_UP) {
23540aa4a30SMark Brown /* delay is longer over 0dB as increases are larger */
23640aa4a30SMark Brown if (i >= WM8350_OUTn_0dB)
23740aa4a30SMark Brown schedule_timeout_interruptible(msecs_to_jiffies
23840aa4a30SMark Brown (2));
23940aa4a30SMark Brown else
24040aa4a30SMark Brown schedule_timeout_interruptible(msecs_to_jiffies
24140aa4a30SMark Brown (1));
24240aa4a30SMark Brown } else
24340aa4a30SMark Brown udelay(50); /* doesn't matter if we delay longer */
24440aa4a30SMark Brown }
24540aa4a30SMark Brown
24640aa4a30SMark Brown out1->ramp = WM8350_RAMP_NONE;
24740aa4a30SMark Brown out2->ramp = WM8350_RAMP_NONE;
24840aa4a30SMark Brown }
24940aa4a30SMark Brown
25040aa4a30SMark Brown /*
25140aa4a30SMark Brown * WM8350 Controls
25240aa4a30SMark Brown */
25340aa4a30SMark Brown
pga_event(struct snd_soc_dapm_widget * w,struct snd_kcontrol * kcontrol,int event)25440aa4a30SMark Brown static int pga_event(struct snd_soc_dapm_widget *w,
25540aa4a30SMark Brown struct snd_kcontrol *kcontrol, int event)
25640aa4a30SMark Brown {
2572621a9a4SKuninori Morimoto struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
2582621a9a4SKuninori Morimoto struct wm8350_data *wm8350_data = snd_soc_component_get_drvdata(component);
25940aa4a30SMark Brown struct wm8350_output *out;
26040aa4a30SMark Brown
26140aa4a30SMark Brown switch (w->shift) {
26240aa4a30SMark Brown case 0:
26340aa4a30SMark Brown case 1:
26440aa4a30SMark Brown out = &wm8350_data->out1;
26540aa4a30SMark Brown break;
26640aa4a30SMark Brown case 2:
26740aa4a30SMark Brown case 3:
26840aa4a30SMark Brown out = &wm8350_data->out2;
26940aa4a30SMark Brown break;
27040aa4a30SMark Brown
27140aa4a30SMark Brown default:
272a361f452STakashi Iwai WARN(1, "Invalid shift %d\n", w->shift);
27340aa4a30SMark Brown return -1;
27440aa4a30SMark Brown }
27540aa4a30SMark Brown
27640aa4a30SMark Brown switch (event) {
27740aa4a30SMark Brown case SND_SOC_DAPM_POST_PMU:
27840aa4a30SMark Brown out->ramp = WM8350_RAMP_UP;
27940aa4a30SMark Brown out->active = 1;
28040aa4a30SMark Brown
281cd5d8226SLars-Peter Clausen schedule_delayed_work(&wm8350_data->pga_work,
28240aa4a30SMark Brown msecs_to_jiffies(1));
28340aa4a30SMark Brown break;
28440aa4a30SMark Brown
28540aa4a30SMark Brown case SND_SOC_DAPM_PRE_PMD:
28640aa4a30SMark Brown out->ramp = WM8350_RAMP_DOWN;
28740aa4a30SMark Brown out->active = 0;
28840aa4a30SMark Brown
289cd5d8226SLars-Peter Clausen schedule_delayed_work(&wm8350_data->pga_work,
29040aa4a30SMark Brown msecs_to_jiffies(1));
29140aa4a30SMark Brown break;
29240aa4a30SMark Brown }
29340aa4a30SMark Brown
29440aa4a30SMark Brown return 0;
29540aa4a30SMark Brown }
29640aa4a30SMark Brown
wm8350_put_volsw_2r_vu(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)29740aa4a30SMark Brown static int wm8350_put_volsw_2r_vu(struct snd_kcontrol *kcontrol,
29840aa4a30SMark Brown struct snd_ctl_elem_value *ucontrol)
29940aa4a30SMark Brown {
3002621a9a4SKuninori Morimoto struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
3012621a9a4SKuninori Morimoto struct wm8350_data *wm8350_priv = snd_soc_component_get_drvdata(component);
30240aa4a30SMark Brown struct wm8350_output *out = NULL;
30340aa4a30SMark Brown struct soc_mixer_control *mc =
30440aa4a30SMark Brown (struct soc_mixer_control *)kcontrol->private_value;
30540aa4a30SMark Brown int ret;
30640aa4a30SMark Brown unsigned int reg = mc->reg;
30740aa4a30SMark Brown u16 val;
30840aa4a30SMark Brown
30940aa4a30SMark Brown /* For OUT1 and OUT2 we shadow the values and only actually write
31040aa4a30SMark Brown * them out when active in order to ensure the amplifier comes on
31140aa4a30SMark Brown * as quietly as possible. */
31240aa4a30SMark Brown switch (reg) {
31340aa4a30SMark Brown case WM8350_LOUT1_VOLUME:
31440aa4a30SMark Brown out = &wm8350_priv->out1;
31540aa4a30SMark Brown break;
31640aa4a30SMark Brown case WM8350_LOUT2_VOLUME:
31740aa4a30SMark Brown out = &wm8350_priv->out2;
31840aa4a30SMark Brown break;
31940aa4a30SMark Brown default:
32040aa4a30SMark Brown break;
32140aa4a30SMark Brown }
32240aa4a30SMark Brown
32340aa4a30SMark Brown if (out) {
32440aa4a30SMark Brown out->left_vol = ucontrol->value.integer.value[0];
32540aa4a30SMark Brown out->right_vol = ucontrol->value.integer.value[1];
32640aa4a30SMark Brown if (!out->active)
32740aa4a30SMark Brown return 1;
32840aa4a30SMark Brown }
32940aa4a30SMark Brown
330c4671a95SPeter Ujfalusi ret = snd_soc_put_volsw(kcontrol, ucontrol);
33140aa4a30SMark Brown if (ret < 0)
33240aa4a30SMark Brown return ret;
33340aa4a30SMark Brown
33440aa4a30SMark Brown /* now hit the volume update bits (always bit 8) */
3356d75dfc3SKuninori Morimoto val = snd_soc_component_read(component, reg);
3362621a9a4SKuninori Morimoto snd_soc_component_write(component, reg, val | WM8350_OUT1_VU);
33740aa4a30SMark Brown return 1;
33840aa4a30SMark Brown }
33940aa4a30SMark Brown
wm8350_get_volsw_2r(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)34040aa4a30SMark Brown static int wm8350_get_volsw_2r(struct snd_kcontrol *kcontrol,
34140aa4a30SMark Brown struct snd_ctl_elem_value *ucontrol)
34240aa4a30SMark Brown {
3432621a9a4SKuninori Morimoto struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
3442621a9a4SKuninori Morimoto struct wm8350_data *wm8350_priv = snd_soc_component_get_drvdata(component);
34540aa4a30SMark Brown struct wm8350_output *out1 = &wm8350_priv->out1;
34640aa4a30SMark Brown struct wm8350_output *out2 = &wm8350_priv->out2;
34740aa4a30SMark Brown struct soc_mixer_control *mc =
34840aa4a30SMark Brown (struct soc_mixer_control *)kcontrol->private_value;
34940aa4a30SMark Brown unsigned int reg = mc->reg;
35040aa4a30SMark Brown
35140aa4a30SMark Brown /* If these are cached registers use the cache */
35240aa4a30SMark Brown switch (reg) {
35340aa4a30SMark Brown case WM8350_LOUT1_VOLUME:
35440aa4a30SMark Brown ucontrol->value.integer.value[0] = out1->left_vol;
35540aa4a30SMark Brown ucontrol->value.integer.value[1] = out1->right_vol;
35640aa4a30SMark Brown return 0;
35740aa4a30SMark Brown
35840aa4a30SMark Brown case WM8350_LOUT2_VOLUME:
35940aa4a30SMark Brown ucontrol->value.integer.value[0] = out2->left_vol;
36040aa4a30SMark Brown ucontrol->value.integer.value[1] = out2->right_vol;
36140aa4a30SMark Brown return 0;
36240aa4a30SMark Brown
36340aa4a30SMark Brown default:
36440aa4a30SMark Brown break;
36540aa4a30SMark Brown }
36640aa4a30SMark Brown
367c4671a95SPeter Ujfalusi return snd_soc_get_volsw(kcontrol, ucontrol);
36840aa4a30SMark Brown }
36940aa4a30SMark Brown
37040aa4a30SMark Brown static const char *wm8350_deemp[] = { "None", "32kHz", "44.1kHz", "48kHz" };
37140aa4a30SMark Brown static const char *wm8350_pol[] = { "Normal", "Inv R", "Inv L", "Inv L & R" };
37240aa4a30SMark Brown static const char *wm8350_dacmutem[] = { "Normal", "Soft" };
37340aa4a30SMark Brown static const char *wm8350_dacmutes[] = { "Fast", "Slow" };
37440aa4a30SMark Brown static const char *wm8350_adcfilter[] = { "None", "High Pass" };
37540aa4a30SMark Brown static const char *wm8350_adchp[] = { "44.1kHz", "8kHz", "16kHz", "32kHz" };
37640aa4a30SMark Brown static const char *wm8350_lr[] = { "Left", "Right" };
37740aa4a30SMark Brown
37840aa4a30SMark Brown static const struct soc_enum wm8350_enum[] = {
37940aa4a30SMark Brown SOC_ENUM_SINGLE(WM8350_DAC_CONTROL, 4, 4, wm8350_deemp),
38040aa4a30SMark Brown SOC_ENUM_SINGLE(WM8350_DAC_CONTROL, 0, 4, wm8350_pol),
38140aa4a30SMark Brown SOC_ENUM_SINGLE(WM8350_DAC_MUTE_VOLUME, 14, 2, wm8350_dacmutem),
38240aa4a30SMark Brown SOC_ENUM_SINGLE(WM8350_DAC_MUTE_VOLUME, 13, 2, wm8350_dacmutes),
38340aa4a30SMark Brown SOC_ENUM_SINGLE(WM8350_ADC_CONTROL, 15, 2, wm8350_adcfilter),
38440aa4a30SMark Brown SOC_ENUM_SINGLE(WM8350_ADC_CONTROL, 8, 4, wm8350_adchp),
38540aa4a30SMark Brown SOC_ENUM_SINGLE(WM8350_ADC_CONTROL, 0, 4, wm8350_pol),
38640aa4a30SMark Brown SOC_ENUM_SINGLE(WM8350_INPUT_MIXER_VOLUME, 15, 2, wm8350_lr),
38740aa4a30SMark Brown };
38840aa4a30SMark Brown
389e6a08c5aSMark Brown static DECLARE_TLV_DB_SCALE(pre_amp_tlv, -1200, 3525, 0);
390e6a08c5aSMark Brown static DECLARE_TLV_DB_SCALE(out_pga_tlv, -5700, 600, 0);
39140aa4a30SMark Brown static DECLARE_TLV_DB_SCALE(dac_pcm_tlv, -7163, 36, 1);
39240aa4a30SMark Brown static DECLARE_TLV_DB_SCALE(adc_pcm_tlv, -12700, 50, 1);
39340aa4a30SMark Brown static DECLARE_TLV_DB_SCALE(out_mix_tlv, -1500, 300, 1);
39440aa4a30SMark Brown
395d3d383baSLars-Peter Clausen static const DECLARE_TLV_DB_RANGE(capture_sd_tlv,
39640aa4a30SMark Brown 0, 12, TLV_DB_SCALE_ITEM(-3600, 300, 1),
397d3d383baSLars-Peter Clausen 13, 15, TLV_DB_SCALE_ITEM(0, 0, 0)
398d3d383baSLars-Peter Clausen );
39940aa4a30SMark Brown
40040aa4a30SMark Brown static const struct snd_kcontrol_new wm8350_snd_controls[] = {
40140aa4a30SMark Brown SOC_ENUM("Playback Deemphasis", wm8350_enum[0]),
40240aa4a30SMark Brown SOC_ENUM("Playback DAC Inversion", wm8350_enum[1]),
4030f9887d1SPeter Ujfalusi SOC_DOUBLE_R_EXT_TLV("Playback PCM Volume",
40440aa4a30SMark Brown WM8350_DAC_DIGITAL_VOLUME_L,
40540aa4a30SMark Brown WM8350_DAC_DIGITAL_VOLUME_R,
4060f9887d1SPeter Ujfalusi 0, 255, 0, wm8350_get_volsw_2r,
4070f9887d1SPeter Ujfalusi wm8350_put_volsw_2r_vu, dac_pcm_tlv),
40840aa4a30SMark Brown SOC_ENUM("Playback PCM Mute Function", wm8350_enum[2]),
40940aa4a30SMark Brown SOC_ENUM("Playback PCM Mute Speed", wm8350_enum[3]),
41061943999SMark Brown SOC_ENUM("Capture PCM Filter", wm8350_enum[4]),
41161943999SMark Brown SOC_ENUM("Capture PCM HP Filter", wm8350_enum[5]),
41261943999SMark Brown SOC_ENUM("Capture ADC Inversion", wm8350_enum[6]),
4130f9887d1SPeter Ujfalusi SOC_DOUBLE_R_EXT_TLV("Capture PCM Volume",
41440aa4a30SMark Brown WM8350_ADC_DIGITAL_VOLUME_L,
41540aa4a30SMark Brown WM8350_ADC_DIGITAL_VOLUME_R,
4160f9887d1SPeter Ujfalusi 0, 255, 0, wm8350_get_volsw_2r,
4170f9887d1SPeter Ujfalusi wm8350_put_volsw_2r_vu, adc_pcm_tlv),
41840aa4a30SMark Brown SOC_DOUBLE_TLV("Capture Sidetone Volume",
41940aa4a30SMark Brown WM8350_ADC_DIVIDER,
42040aa4a30SMark Brown 8, 4, 15, 1, capture_sd_tlv),
4210f9887d1SPeter Ujfalusi SOC_DOUBLE_R_EXT_TLV("Capture Volume",
42240aa4a30SMark Brown WM8350_LEFT_INPUT_VOLUME,
42340aa4a30SMark Brown WM8350_RIGHT_INPUT_VOLUME,
4240f9887d1SPeter Ujfalusi 2, 63, 0, wm8350_get_volsw_2r,
4250f9887d1SPeter Ujfalusi wm8350_put_volsw_2r_vu, pre_amp_tlv),
42640aa4a30SMark Brown SOC_DOUBLE_R("Capture ZC Switch",
42740aa4a30SMark Brown WM8350_LEFT_INPUT_VOLUME,
42840aa4a30SMark Brown WM8350_RIGHT_INPUT_VOLUME, 13, 1, 0),
42940aa4a30SMark Brown SOC_SINGLE_TLV("Left Input Left Sidetone Volume",
43040aa4a30SMark Brown WM8350_OUTPUT_LEFT_MIXER_VOLUME, 1, 7, 0, out_mix_tlv),
43140aa4a30SMark Brown SOC_SINGLE_TLV("Left Input Right Sidetone Volume",
43240aa4a30SMark Brown WM8350_OUTPUT_LEFT_MIXER_VOLUME,
43340aa4a30SMark Brown 5, 7, 0, out_mix_tlv),
43440aa4a30SMark Brown SOC_SINGLE_TLV("Left Input Bypass Volume",
43540aa4a30SMark Brown WM8350_OUTPUT_LEFT_MIXER_VOLUME,
43640aa4a30SMark Brown 9, 7, 0, out_mix_tlv),
43740aa4a30SMark Brown SOC_SINGLE_TLV("Right Input Left Sidetone Volume",
43840aa4a30SMark Brown WM8350_OUTPUT_RIGHT_MIXER_VOLUME,
43940aa4a30SMark Brown 1, 7, 0, out_mix_tlv),
44040aa4a30SMark Brown SOC_SINGLE_TLV("Right Input Right Sidetone Volume",
44140aa4a30SMark Brown WM8350_OUTPUT_RIGHT_MIXER_VOLUME,
44240aa4a30SMark Brown 5, 7, 0, out_mix_tlv),
44340aa4a30SMark Brown SOC_SINGLE_TLV("Right Input Bypass Volume",
44440aa4a30SMark Brown WM8350_OUTPUT_RIGHT_MIXER_VOLUME,
44540aa4a30SMark Brown 13, 7, 0, out_mix_tlv),
44640aa4a30SMark Brown SOC_SINGLE("Left Input Mixer +20dB Switch",
44740aa4a30SMark Brown WM8350_INPUT_MIXER_VOLUME_L, 0, 1, 0),
44840aa4a30SMark Brown SOC_SINGLE("Right Input Mixer +20dB Switch",
44940aa4a30SMark Brown WM8350_INPUT_MIXER_VOLUME_R, 0, 1, 0),
45040aa4a30SMark Brown SOC_SINGLE_TLV("Out4 Capture Volume",
45140aa4a30SMark Brown WM8350_INPUT_MIXER_VOLUME,
45240aa4a30SMark Brown 1, 7, 0, out_mix_tlv),
4530f9887d1SPeter Ujfalusi SOC_DOUBLE_R_EXT_TLV("Out1 Playback Volume",
45440aa4a30SMark Brown WM8350_LOUT1_VOLUME,
45540aa4a30SMark Brown WM8350_ROUT1_VOLUME,
4560f9887d1SPeter Ujfalusi 2, 63, 0, wm8350_get_volsw_2r,
4570f9887d1SPeter Ujfalusi wm8350_put_volsw_2r_vu, out_pga_tlv),
45840aa4a30SMark Brown SOC_DOUBLE_R("Out1 Playback ZC Switch",
45940aa4a30SMark Brown WM8350_LOUT1_VOLUME,
46040aa4a30SMark Brown WM8350_ROUT1_VOLUME, 13, 1, 0),
4610f9887d1SPeter Ujfalusi SOC_DOUBLE_R_EXT_TLV("Out2 Playback Volume",
46240aa4a30SMark Brown WM8350_LOUT2_VOLUME,
46340aa4a30SMark Brown WM8350_ROUT2_VOLUME,
4640f9887d1SPeter Ujfalusi 2, 63, 0, wm8350_get_volsw_2r,
4650f9887d1SPeter Ujfalusi wm8350_put_volsw_2r_vu, out_pga_tlv),
46640aa4a30SMark Brown SOC_DOUBLE_R("Out2 Playback ZC Switch", WM8350_LOUT2_VOLUME,
46740aa4a30SMark Brown WM8350_ROUT2_VOLUME, 13, 1, 0),
46840aa4a30SMark Brown SOC_SINGLE("Out2 Right Invert Switch", WM8350_ROUT2_VOLUME, 10, 1, 0),
46940aa4a30SMark Brown SOC_SINGLE_TLV("Out2 Beep Volume", WM8350_BEEP_VOLUME,
47040aa4a30SMark Brown 5, 7, 0, out_mix_tlv),
47140aa4a30SMark Brown
47240aa4a30SMark Brown SOC_DOUBLE_R("Out1 Playback Switch",
47340aa4a30SMark Brown WM8350_LOUT1_VOLUME,
47440aa4a30SMark Brown WM8350_ROUT1_VOLUME,
47540aa4a30SMark Brown 14, 1, 1),
47640aa4a30SMark Brown SOC_DOUBLE_R("Out2 Playback Switch",
47740aa4a30SMark Brown WM8350_LOUT2_VOLUME,
47840aa4a30SMark Brown WM8350_ROUT2_VOLUME,
47940aa4a30SMark Brown 14, 1, 1),
48040aa4a30SMark Brown };
48140aa4a30SMark Brown
48240aa4a30SMark Brown /*
48340aa4a30SMark Brown * DAPM Controls
48440aa4a30SMark Brown */
48540aa4a30SMark Brown
48640aa4a30SMark Brown /* Left Playback Mixer */
48740aa4a30SMark Brown static const struct snd_kcontrol_new wm8350_left_play_mixer_controls[] = {
48840aa4a30SMark Brown SOC_DAPM_SINGLE("Playback Switch",
48940aa4a30SMark Brown WM8350_LEFT_MIXER_CONTROL, 11, 1, 0),
49040aa4a30SMark Brown SOC_DAPM_SINGLE("Left Bypass Switch",
49140aa4a30SMark Brown WM8350_LEFT_MIXER_CONTROL, 2, 1, 0),
49240aa4a30SMark Brown SOC_DAPM_SINGLE("Right Playback Switch",
49340aa4a30SMark Brown WM8350_LEFT_MIXER_CONTROL, 12, 1, 0),
49440aa4a30SMark Brown SOC_DAPM_SINGLE("Left Sidetone Switch",
49540aa4a30SMark Brown WM8350_LEFT_MIXER_CONTROL, 0, 1, 0),
49640aa4a30SMark Brown SOC_DAPM_SINGLE("Right Sidetone Switch",
49740aa4a30SMark Brown WM8350_LEFT_MIXER_CONTROL, 1, 1, 0),
49840aa4a30SMark Brown };
49940aa4a30SMark Brown
50040aa4a30SMark Brown /* Right Playback Mixer */
50140aa4a30SMark Brown static const struct snd_kcontrol_new wm8350_right_play_mixer_controls[] = {
50240aa4a30SMark Brown SOC_DAPM_SINGLE("Playback Switch",
50340aa4a30SMark Brown WM8350_RIGHT_MIXER_CONTROL, 12, 1, 0),
50440aa4a30SMark Brown SOC_DAPM_SINGLE("Right Bypass Switch",
50540aa4a30SMark Brown WM8350_RIGHT_MIXER_CONTROL, 3, 1, 0),
50640aa4a30SMark Brown SOC_DAPM_SINGLE("Left Playback Switch",
50740aa4a30SMark Brown WM8350_RIGHT_MIXER_CONTROL, 11, 1, 0),
50840aa4a30SMark Brown SOC_DAPM_SINGLE("Left Sidetone Switch",
50940aa4a30SMark Brown WM8350_RIGHT_MIXER_CONTROL, 0, 1, 0),
51040aa4a30SMark Brown SOC_DAPM_SINGLE("Right Sidetone Switch",
51140aa4a30SMark Brown WM8350_RIGHT_MIXER_CONTROL, 1, 1, 0),
51240aa4a30SMark Brown };
51340aa4a30SMark Brown
51440aa4a30SMark Brown /* Out4 Mixer */
51540aa4a30SMark Brown static const struct snd_kcontrol_new wm8350_out4_mixer_controls[] = {
51640aa4a30SMark Brown SOC_DAPM_SINGLE("Right Playback Switch",
51740aa4a30SMark Brown WM8350_OUT4_MIXER_CONTROL, 12, 1, 0),
51840aa4a30SMark Brown SOC_DAPM_SINGLE("Left Playback Switch",
51940aa4a30SMark Brown WM8350_OUT4_MIXER_CONTROL, 11, 1, 0),
52040aa4a30SMark Brown SOC_DAPM_SINGLE("Right Capture Switch",
52140aa4a30SMark Brown WM8350_OUT4_MIXER_CONTROL, 9, 1, 0),
52240aa4a30SMark Brown SOC_DAPM_SINGLE("Out3 Playback Switch",
52340aa4a30SMark Brown WM8350_OUT4_MIXER_CONTROL, 2, 1, 0),
52440aa4a30SMark Brown SOC_DAPM_SINGLE("Right Mixer Switch",
52540aa4a30SMark Brown WM8350_OUT4_MIXER_CONTROL, 1, 1, 0),
52640aa4a30SMark Brown SOC_DAPM_SINGLE("Left Mixer Switch",
52740aa4a30SMark Brown WM8350_OUT4_MIXER_CONTROL, 0, 1, 0),
52840aa4a30SMark Brown };
52940aa4a30SMark Brown
53040aa4a30SMark Brown /* Out3 Mixer */
53140aa4a30SMark Brown static const struct snd_kcontrol_new wm8350_out3_mixer_controls[] = {
53240aa4a30SMark Brown SOC_DAPM_SINGLE("Left Playback Switch",
53340aa4a30SMark Brown WM8350_OUT3_MIXER_CONTROL, 11, 1, 0),
53440aa4a30SMark Brown SOC_DAPM_SINGLE("Left Capture Switch",
53540aa4a30SMark Brown WM8350_OUT3_MIXER_CONTROL, 8, 1, 0),
53640aa4a30SMark Brown SOC_DAPM_SINGLE("Out4 Playback Switch",
53740aa4a30SMark Brown WM8350_OUT3_MIXER_CONTROL, 3, 1, 0),
53840aa4a30SMark Brown SOC_DAPM_SINGLE("Left Mixer Switch",
53940aa4a30SMark Brown WM8350_OUT3_MIXER_CONTROL, 0, 1, 0),
54040aa4a30SMark Brown };
54140aa4a30SMark Brown
54240aa4a30SMark Brown /* Left Input Mixer */
54340aa4a30SMark Brown static const struct snd_kcontrol_new wm8350_left_capt_mixer_controls[] = {
54440aa4a30SMark Brown SOC_DAPM_SINGLE_TLV("L2 Capture Volume",
54540aa4a30SMark Brown WM8350_INPUT_MIXER_VOLUME_L, 1, 7, 0, out_mix_tlv),
54640aa4a30SMark Brown SOC_DAPM_SINGLE_TLV("L3 Capture Volume",
54740aa4a30SMark Brown WM8350_INPUT_MIXER_VOLUME_L, 9, 7, 0, out_mix_tlv),
54840aa4a30SMark Brown SOC_DAPM_SINGLE("PGA Capture Switch",
5495b7dde34SMark Brown WM8350_LEFT_INPUT_VOLUME, 14, 1, 1),
55040aa4a30SMark Brown };
55140aa4a30SMark Brown
55240aa4a30SMark Brown /* Right Input Mixer */
55340aa4a30SMark Brown static const struct snd_kcontrol_new wm8350_right_capt_mixer_controls[] = {
55440aa4a30SMark Brown SOC_DAPM_SINGLE_TLV("L2 Capture Volume",
55540aa4a30SMark Brown WM8350_INPUT_MIXER_VOLUME_R, 5, 7, 0, out_mix_tlv),
55640aa4a30SMark Brown SOC_DAPM_SINGLE_TLV("L3 Capture Volume",
55740aa4a30SMark Brown WM8350_INPUT_MIXER_VOLUME_R, 13, 7, 0, out_mix_tlv),
55840aa4a30SMark Brown SOC_DAPM_SINGLE("PGA Capture Switch",
5595b7dde34SMark Brown WM8350_RIGHT_INPUT_VOLUME, 14, 1, 1),
56040aa4a30SMark Brown };
56140aa4a30SMark Brown
56240aa4a30SMark Brown /* Left Mic Mixer */
56340aa4a30SMark Brown static const struct snd_kcontrol_new wm8350_left_mic_mixer_controls[] = {
56440aa4a30SMark Brown SOC_DAPM_SINGLE("INN Capture Switch", WM8350_INPUT_CONTROL, 1, 1, 0),
56540aa4a30SMark Brown SOC_DAPM_SINGLE("INP Capture Switch", WM8350_INPUT_CONTROL, 0, 1, 0),
56640aa4a30SMark Brown SOC_DAPM_SINGLE("IN2 Capture Switch", WM8350_INPUT_CONTROL, 2, 1, 0),
56740aa4a30SMark Brown };
56840aa4a30SMark Brown
56940aa4a30SMark Brown /* Right Mic Mixer */
57040aa4a30SMark Brown static const struct snd_kcontrol_new wm8350_right_mic_mixer_controls[] = {
57140aa4a30SMark Brown SOC_DAPM_SINGLE("INN Capture Switch", WM8350_INPUT_CONTROL, 9, 1, 0),
57240aa4a30SMark Brown SOC_DAPM_SINGLE("INP Capture Switch", WM8350_INPUT_CONTROL, 8, 1, 0),
57340aa4a30SMark Brown SOC_DAPM_SINGLE("IN2 Capture Switch", WM8350_INPUT_CONTROL, 10, 1, 0),
57440aa4a30SMark Brown };
57540aa4a30SMark Brown
57640aa4a30SMark Brown /* Beep Switch */
57740aa4a30SMark Brown static const struct snd_kcontrol_new wm8350_beep_switch_controls =
57840aa4a30SMark Brown SOC_DAPM_SINGLE("Switch", WM8350_BEEP_VOLUME, 15, 1, 1);
57940aa4a30SMark Brown
58040aa4a30SMark Brown /* Out4 Capture Mux */
58140aa4a30SMark Brown static const struct snd_kcontrol_new wm8350_out4_capture_controls =
58287831cb6SMark Brown SOC_DAPM_ENUM("Route", wm8350_enum[7]);
58340aa4a30SMark Brown
58440aa4a30SMark Brown static const struct snd_soc_dapm_widget wm8350_dapm_widgets[] = {
58540aa4a30SMark Brown
58640aa4a30SMark Brown SND_SOC_DAPM_PGA("IN3R PGA", WM8350_POWER_MGMT_2, 11, 0, NULL, 0),
58740aa4a30SMark Brown SND_SOC_DAPM_PGA("IN3L PGA", WM8350_POWER_MGMT_2, 10, 0, NULL, 0),
58840aa4a30SMark Brown SND_SOC_DAPM_PGA_E("Right Out2 PGA", WM8350_POWER_MGMT_3, 3, 0, NULL,
58940aa4a30SMark Brown 0, pga_event,
59040aa4a30SMark Brown SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
59140aa4a30SMark Brown SND_SOC_DAPM_PGA_E("Left Out2 PGA", WM8350_POWER_MGMT_3, 2, 0, NULL, 0,
59240aa4a30SMark Brown pga_event,
59340aa4a30SMark Brown SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
59440aa4a30SMark Brown SND_SOC_DAPM_PGA_E("Right Out1 PGA", WM8350_POWER_MGMT_3, 1, 0, NULL,
59540aa4a30SMark Brown 0, pga_event,
59640aa4a30SMark Brown SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
59740aa4a30SMark Brown SND_SOC_DAPM_PGA_E("Left Out1 PGA", WM8350_POWER_MGMT_3, 0, 0, NULL, 0,
59840aa4a30SMark Brown pga_event,
59940aa4a30SMark Brown SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
60040aa4a30SMark Brown
60140aa4a30SMark Brown SND_SOC_DAPM_MIXER("Right Capture Mixer", WM8350_POWER_MGMT_2,
60240aa4a30SMark Brown 7, 0, &wm8350_right_capt_mixer_controls[0],
60340aa4a30SMark Brown ARRAY_SIZE(wm8350_right_capt_mixer_controls)),
60440aa4a30SMark Brown
60540aa4a30SMark Brown SND_SOC_DAPM_MIXER("Left Capture Mixer", WM8350_POWER_MGMT_2,
60640aa4a30SMark Brown 6, 0, &wm8350_left_capt_mixer_controls[0],
60740aa4a30SMark Brown ARRAY_SIZE(wm8350_left_capt_mixer_controls)),
60840aa4a30SMark Brown
60940aa4a30SMark Brown SND_SOC_DAPM_MIXER("Out4 Mixer", WM8350_POWER_MGMT_2, 5, 0,
61040aa4a30SMark Brown &wm8350_out4_mixer_controls[0],
61140aa4a30SMark Brown ARRAY_SIZE(wm8350_out4_mixer_controls)),
61240aa4a30SMark Brown
61340aa4a30SMark Brown SND_SOC_DAPM_MIXER("Out3 Mixer", WM8350_POWER_MGMT_2, 4, 0,
61440aa4a30SMark Brown &wm8350_out3_mixer_controls[0],
61540aa4a30SMark Brown ARRAY_SIZE(wm8350_out3_mixer_controls)),
61640aa4a30SMark Brown
61740aa4a30SMark Brown SND_SOC_DAPM_MIXER("Right Playback Mixer", WM8350_POWER_MGMT_2, 1, 0,
61840aa4a30SMark Brown &wm8350_right_play_mixer_controls[0],
61940aa4a30SMark Brown ARRAY_SIZE(wm8350_right_play_mixer_controls)),
62040aa4a30SMark Brown
62140aa4a30SMark Brown SND_SOC_DAPM_MIXER("Left Playback Mixer", WM8350_POWER_MGMT_2, 0, 0,
62240aa4a30SMark Brown &wm8350_left_play_mixer_controls[0],
62340aa4a30SMark Brown ARRAY_SIZE(wm8350_left_play_mixer_controls)),
62440aa4a30SMark Brown
62540aa4a30SMark Brown SND_SOC_DAPM_MIXER("Left Mic Mixer", WM8350_POWER_MGMT_2, 8, 0,
62640aa4a30SMark Brown &wm8350_left_mic_mixer_controls[0],
62740aa4a30SMark Brown ARRAY_SIZE(wm8350_left_mic_mixer_controls)),
62840aa4a30SMark Brown
62940aa4a30SMark Brown SND_SOC_DAPM_MIXER("Right Mic Mixer", WM8350_POWER_MGMT_2, 9, 0,
63040aa4a30SMark Brown &wm8350_right_mic_mixer_controls[0],
63140aa4a30SMark Brown ARRAY_SIZE(wm8350_right_mic_mixer_controls)),
63240aa4a30SMark Brown
63340aa4a30SMark Brown /* virtual mixer for Beep and Out2R */
63440aa4a30SMark Brown SND_SOC_DAPM_MIXER("Out2 Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
63540aa4a30SMark Brown
63640aa4a30SMark Brown SND_SOC_DAPM_SWITCH("Beep", WM8350_POWER_MGMT_3, 7, 0,
63740aa4a30SMark Brown &wm8350_beep_switch_controls),
63840aa4a30SMark Brown
63940aa4a30SMark Brown SND_SOC_DAPM_ADC("Right ADC", "Right Capture",
64040aa4a30SMark Brown WM8350_POWER_MGMT_4, 3, 0),
64140aa4a30SMark Brown SND_SOC_DAPM_ADC("Left ADC", "Left Capture",
64240aa4a30SMark Brown WM8350_POWER_MGMT_4, 2, 0),
64340aa4a30SMark Brown SND_SOC_DAPM_DAC("Right DAC", "Right Playback",
64440aa4a30SMark Brown WM8350_POWER_MGMT_4, 5, 0),
64540aa4a30SMark Brown SND_SOC_DAPM_DAC("Left DAC", "Left Playback",
64640aa4a30SMark Brown WM8350_POWER_MGMT_4, 4, 0),
64740aa4a30SMark Brown
64840aa4a30SMark Brown SND_SOC_DAPM_MICBIAS("Mic Bias", WM8350_POWER_MGMT_1, 4, 0),
64940aa4a30SMark Brown
65040aa4a30SMark Brown SND_SOC_DAPM_MUX("Out4 Capture Channel", SND_SOC_NOPM, 0, 0,
65140aa4a30SMark Brown &wm8350_out4_capture_controls),
65240aa4a30SMark Brown
65340aa4a30SMark Brown SND_SOC_DAPM_OUTPUT("OUT1R"),
65440aa4a30SMark Brown SND_SOC_DAPM_OUTPUT("OUT1L"),
65540aa4a30SMark Brown SND_SOC_DAPM_OUTPUT("OUT2R"),
65640aa4a30SMark Brown SND_SOC_DAPM_OUTPUT("OUT2L"),
65740aa4a30SMark Brown SND_SOC_DAPM_OUTPUT("OUT3"),
65840aa4a30SMark Brown SND_SOC_DAPM_OUTPUT("OUT4"),
65940aa4a30SMark Brown
66040aa4a30SMark Brown SND_SOC_DAPM_INPUT("IN1RN"),
66140aa4a30SMark Brown SND_SOC_DAPM_INPUT("IN1RP"),
66240aa4a30SMark Brown SND_SOC_DAPM_INPUT("IN2R"),
66340aa4a30SMark Brown SND_SOC_DAPM_INPUT("IN1LP"),
66440aa4a30SMark Brown SND_SOC_DAPM_INPUT("IN1LN"),
66540aa4a30SMark Brown SND_SOC_DAPM_INPUT("IN2L"),
66640aa4a30SMark Brown SND_SOC_DAPM_INPUT("IN3R"),
66740aa4a30SMark Brown SND_SOC_DAPM_INPUT("IN3L"),
66840aa4a30SMark Brown };
66940aa4a30SMark Brown
670e6c94e9fSMark Brown static const struct snd_soc_dapm_route wm8350_dapm_routes[] = {
67140aa4a30SMark Brown
67240aa4a30SMark Brown /* left playback mixer */
67340aa4a30SMark Brown {"Left Playback Mixer", "Playback Switch", "Left DAC"},
67440aa4a30SMark Brown {"Left Playback Mixer", "Left Bypass Switch", "IN3L PGA"},
67540aa4a30SMark Brown {"Left Playback Mixer", "Right Playback Switch", "Right DAC"},
67640aa4a30SMark Brown {"Left Playback Mixer", "Left Sidetone Switch", "Left Mic Mixer"},
67740aa4a30SMark Brown {"Left Playback Mixer", "Right Sidetone Switch", "Right Mic Mixer"},
67840aa4a30SMark Brown
67940aa4a30SMark Brown /* right playback mixer */
68040aa4a30SMark Brown {"Right Playback Mixer", "Playback Switch", "Right DAC"},
68140aa4a30SMark Brown {"Right Playback Mixer", "Right Bypass Switch", "IN3R PGA"},
68240aa4a30SMark Brown {"Right Playback Mixer", "Left Playback Switch", "Left DAC"},
68340aa4a30SMark Brown {"Right Playback Mixer", "Left Sidetone Switch", "Left Mic Mixer"},
68440aa4a30SMark Brown {"Right Playback Mixer", "Right Sidetone Switch", "Right Mic Mixer"},
68540aa4a30SMark Brown
68640aa4a30SMark Brown /* out4 playback mixer */
68740aa4a30SMark Brown {"Out4 Mixer", "Right Playback Switch", "Right DAC"},
68840aa4a30SMark Brown {"Out4 Mixer", "Left Playback Switch", "Left DAC"},
68940aa4a30SMark Brown {"Out4 Mixer", "Right Capture Switch", "Right Capture Mixer"},
69040aa4a30SMark Brown {"Out4 Mixer", "Out3 Playback Switch", "Out3 Mixer"},
69140aa4a30SMark Brown {"Out4 Mixer", "Right Mixer Switch", "Right Playback Mixer"},
69240aa4a30SMark Brown {"Out4 Mixer", "Left Mixer Switch", "Left Playback Mixer"},
69340aa4a30SMark Brown {"OUT4", NULL, "Out4 Mixer"},
69440aa4a30SMark Brown
69540aa4a30SMark Brown /* out3 playback mixer */
69640aa4a30SMark Brown {"Out3 Mixer", "Left Playback Switch", "Left DAC"},
69740aa4a30SMark Brown {"Out3 Mixer", "Left Capture Switch", "Left Capture Mixer"},
69840aa4a30SMark Brown {"Out3 Mixer", "Left Mixer Switch", "Left Playback Mixer"},
69940aa4a30SMark Brown {"Out3 Mixer", "Out4 Playback Switch", "Out4 Mixer"},
70040aa4a30SMark Brown {"OUT3", NULL, "Out3 Mixer"},
70140aa4a30SMark Brown
70240aa4a30SMark Brown /* out2 */
70340aa4a30SMark Brown {"Right Out2 PGA", NULL, "Right Playback Mixer"},
70440aa4a30SMark Brown {"Left Out2 PGA", NULL, "Left Playback Mixer"},
70540aa4a30SMark Brown {"OUT2L", NULL, "Left Out2 PGA"},
70640aa4a30SMark Brown {"OUT2R", NULL, "Right Out2 PGA"},
70740aa4a30SMark Brown
70840aa4a30SMark Brown /* out1 */
70940aa4a30SMark Brown {"Right Out1 PGA", NULL, "Right Playback Mixer"},
71040aa4a30SMark Brown {"Left Out1 PGA", NULL, "Left Playback Mixer"},
71140aa4a30SMark Brown {"OUT1L", NULL, "Left Out1 PGA"},
71240aa4a30SMark Brown {"OUT1R", NULL, "Right Out1 PGA"},
71340aa4a30SMark Brown
71440aa4a30SMark Brown /* ADCs */
71540aa4a30SMark Brown {"Left ADC", NULL, "Left Capture Mixer"},
71640aa4a30SMark Brown {"Right ADC", NULL, "Right Capture Mixer"},
71740aa4a30SMark Brown
71840aa4a30SMark Brown /* Left capture mixer */
71940aa4a30SMark Brown {"Left Capture Mixer", "L2 Capture Volume", "IN2L"},
72040aa4a30SMark Brown {"Left Capture Mixer", "L3 Capture Volume", "IN3L PGA"},
72140aa4a30SMark Brown {"Left Capture Mixer", "PGA Capture Switch", "Left Mic Mixer"},
72240aa4a30SMark Brown {"Left Capture Mixer", NULL, "Out4 Capture Channel"},
72340aa4a30SMark Brown
72440aa4a30SMark Brown /* Right capture mixer */
72540aa4a30SMark Brown {"Right Capture Mixer", "L2 Capture Volume", "IN2R"},
72640aa4a30SMark Brown {"Right Capture Mixer", "L3 Capture Volume", "IN3R PGA"},
72740aa4a30SMark Brown {"Right Capture Mixer", "PGA Capture Switch", "Right Mic Mixer"},
72840aa4a30SMark Brown {"Right Capture Mixer", NULL, "Out4 Capture Channel"},
72940aa4a30SMark Brown
73040aa4a30SMark Brown /* L3 Inputs */
73140aa4a30SMark Brown {"IN3L PGA", NULL, "IN3L"},
73240aa4a30SMark Brown {"IN3R PGA", NULL, "IN3R"},
73340aa4a30SMark Brown
73440aa4a30SMark Brown /* Left Mic mixer */
73540aa4a30SMark Brown {"Left Mic Mixer", "INN Capture Switch", "IN1LN"},
73640aa4a30SMark Brown {"Left Mic Mixer", "INP Capture Switch", "IN1LP"},
73740aa4a30SMark Brown {"Left Mic Mixer", "IN2 Capture Switch", "IN2L"},
73840aa4a30SMark Brown
73940aa4a30SMark Brown /* Right Mic mixer */
74040aa4a30SMark Brown {"Right Mic Mixer", "INN Capture Switch", "IN1RN"},
74140aa4a30SMark Brown {"Right Mic Mixer", "INP Capture Switch", "IN1RP"},
74240aa4a30SMark Brown {"Right Mic Mixer", "IN2 Capture Switch", "IN2R"},
74340aa4a30SMark Brown
74440aa4a30SMark Brown /* out 4 capture */
74540aa4a30SMark Brown {"Out4 Capture Channel", NULL, "Out4 Mixer"},
74640aa4a30SMark Brown
74740aa4a30SMark Brown /* Beep */
74840aa4a30SMark Brown {"Beep", NULL, "IN3R PGA"},
74940aa4a30SMark Brown };
75040aa4a30SMark Brown
wm8350_set_dai_sysclk(struct snd_soc_dai * codec_dai,int clk_id,unsigned int freq,int dir)75140aa4a30SMark Brown static int wm8350_set_dai_sysclk(struct snd_soc_dai *codec_dai,
75240aa4a30SMark Brown int clk_id, unsigned int freq, int dir)
75340aa4a30SMark Brown {
7542621a9a4SKuninori Morimoto struct snd_soc_component *component = codec_dai->component;
7552621a9a4SKuninori Morimoto struct wm8350_data *wm8350_data = snd_soc_component_get_drvdata(component);
756018a455aSMark Brown struct wm8350 *wm8350 = wm8350_data->wm8350;
75740aa4a30SMark Brown u16 fll_4;
75840aa4a30SMark Brown
75940aa4a30SMark Brown switch (clk_id) {
76040aa4a30SMark Brown case WM8350_MCLK_SEL_MCLK:
76140aa4a30SMark Brown wm8350_clear_bits(wm8350, WM8350_CLOCK_CONTROL_1,
76240aa4a30SMark Brown WM8350_MCLK_SEL);
76340aa4a30SMark Brown break;
76440aa4a30SMark Brown case WM8350_MCLK_SEL_PLL_MCLK:
76540aa4a30SMark Brown case WM8350_MCLK_SEL_PLL_DAC:
76640aa4a30SMark Brown case WM8350_MCLK_SEL_PLL_ADC:
76740aa4a30SMark Brown case WM8350_MCLK_SEL_PLL_32K:
76840aa4a30SMark Brown wm8350_set_bits(wm8350, WM8350_CLOCK_CONTROL_1,
76940aa4a30SMark Brown WM8350_MCLK_SEL);
7706d75dfc3SKuninori Morimoto fll_4 = snd_soc_component_read(component, WM8350_FLL_CONTROL_4) &
77140aa4a30SMark Brown ~WM8350_FLL_CLK_SRC_MASK;
7722621a9a4SKuninori Morimoto snd_soc_component_write(component, WM8350_FLL_CONTROL_4, fll_4 | clk_id);
77340aa4a30SMark Brown break;
77440aa4a30SMark Brown }
77540aa4a30SMark Brown
77640aa4a30SMark Brown /* MCLK direction */
777c28a9926SMark Brown if (dir == SND_SOC_CLOCK_OUT)
77840aa4a30SMark Brown wm8350_set_bits(wm8350, WM8350_CLOCK_CONTROL_2,
77940aa4a30SMark Brown WM8350_MCLK_DIR);
78040aa4a30SMark Brown else
78140aa4a30SMark Brown wm8350_clear_bits(wm8350, WM8350_CLOCK_CONTROL_2,
78240aa4a30SMark Brown WM8350_MCLK_DIR);
78340aa4a30SMark Brown
78440aa4a30SMark Brown return 0;
78540aa4a30SMark Brown }
78640aa4a30SMark Brown
wm8350_set_clkdiv(struct snd_soc_dai * codec_dai,int div_id,int div)78740aa4a30SMark Brown static int wm8350_set_clkdiv(struct snd_soc_dai *codec_dai, int div_id, int div)
78840aa4a30SMark Brown {
7892621a9a4SKuninori Morimoto struct snd_soc_component *component = codec_dai->component;
79040aa4a30SMark Brown u16 val;
79140aa4a30SMark Brown
79240aa4a30SMark Brown switch (div_id) {
79340aa4a30SMark Brown case WM8350_ADC_CLKDIV:
7946d75dfc3SKuninori Morimoto val = snd_soc_component_read(component, WM8350_ADC_DIVIDER) &
79540aa4a30SMark Brown ~WM8350_ADC_CLKDIV_MASK;
7962621a9a4SKuninori Morimoto snd_soc_component_write(component, WM8350_ADC_DIVIDER, val | div);
79740aa4a30SMark Brown break;
79840aa4a30SMark Brown case WM8350_DAC_CLKDIV:
7996d75dfc3SKuninori Morimoto val = snd_soc_component_read(component, WM8350_DAC_CLOCK_CONTROL) &
80040aa4a30SMark Brown ~WM8350_DAC_CLKDIV_MASK;
8012621a9a4SKuninori Morimoto snd_soc_component_write(component, WM8350_DAC_CLOCK_CONTROL, val | div);
80240aa4a30SMark Brown break;
80340aa4a30SMark Brown case WM8350_BCLK_CLKDIV:
8046d75dfc3SKuninori Morimoto val = snd_soc_component_read(component, WM8350_CLOCK_CONTROL_1) &
80540aa4a30SMark Brown ~WM8350_BCLK_DIV_MASK;
8062621a9a4SKuninori Morimoto snd_soc_component_write(component, WM8350_CLOCK_CONTROL_1, val | div);
80740aa4a30SMark Brown break;
80840aa4a30SMark Brown case WM8350_OPCLK_CLKDIV:
8096d75dfc3SKuninori Morimoto val = snd_soc_component_read(component, WM8350_CLOCK_CONTROL_1) &
81040aa4a30SMark Brown ~WM8350_OPCLK_DIV_MASK;
8112621a9a4SKuninori Morimoto snd_soc_component_write(component, WM8350_CLOCK_CONTROL_1, val | div);
81240aa4a30SMark Brown break;
81340aa4a30SMark Brown case WM8350_SYS_CLKDIV:
8146d75dfc3SKuninori Morimoto val = snd_soc_component_read(component, WM8350_CLOCK_CONTROL_1) &
81540aa4a30SMark Brown ~WM8350_MCLK_DIV_MASK;
8162621a9a4SKuninori Morimoto snd_soc_component_write(component, WM8350_CLOCK_CONTROL_1, val | div);
81740aa4a30SMark Brown break;
81840aa4a30SMark Brown case WM8350_DACLR_CLKDIV:
8196d75dfc3SKuninori Morimoto val = snd_soc_component_read(component, WM8350_DAC_LR_RATE) &
82040aa4a30SMark Brown ~WM8350_DACLRC_RATE_MASK;
8212621a9a4SKuninori Morimoto snd_soc_component_write(component, WM8350_DAC_LR_RATE, val | div);
82240aa4a30SMark Brown break;
82340aa4a30SMark Brown case WM8350_ADCLR_CLKDIV:
8246d75dfc3SKuninori Morimoto val = snd_soc_component_read(component, WM8350_ADC_LR_RATE) &
82540aa4a30SMark Brown ~WM8350_ADCLRC_RATE_MASK;
8262621a9a4SKuninori Morimoto snd_soc_component_write(component, WM8350_ADC_LR_RATE, val | div);
82740aa4a30SMark Brown break;
82840aa4a30SMark Brown default:
82940aa4a30SMark Brown return -EINVAL;
83040aa4a30SMark Brown }
83140aa4a30SMark Brown
83240aa4a30SMark Brown return 0;
83340aa4a30SMark Brown }
83440aa4a30SMark Brown
wm8350_set_dai_fmt(struct snd_soc_dai * codec_dai,unsigned int fmt)83540aa4a30SMark Brown static int wm8350_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
83640aa4a30SMark Brown {
8372621a9a4SKuninori Morimoto struct snd_soc_component *component = codec_dai->component;
8386d75dfc3SKuninori Morimoto u16 iface = snd_soc_component_read(component, WM8350_AI_FORMATING) &
83940aa4a30SMark Brown ~(WM8350_AIF_BCLK_INV | WM8350_AIF_LRCLK_INV | WM8350_AIF_FMT_MASK);
8406d75dfc3SKuninori Morimoto u16 master = snd_soc_component_read(component, WM8350_AI_DAC_CONTROL) &
84140aa4a30SMark Brown ~WM8350_BCLK_MSTR;
8426d75dfc3SKuninori Morimoto u16 dac_lrc = snd_soc_component_read(component, WM8350_DAC_LR_RATE) &
84340aa4a30SMark Brown ~WM8350_DACLRC_ENA;
8446d75dfc3SKuninori Morimoto u16 adc_lrc = snd_soc_component_read(component, WM8350_ADC_LR_RATE) &
84540aa4a30SMark Brown ~WM8350_ADCLRC_ENA;
84640aa4a30SMark Brown
84740aa4a30SMark Brown /* set master/slave audio interface */
84840aa4a30SMark Brown switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
84940aa4a30SMark Brown case SND_SOC_DAIFMT_CBM_CFM:
85040aa4a30SMark Brown master |= WM8350_BCLK_MSTR;
85140aa4a30SMark Brown dac_lrc |= WM8350_DACLRC_ENA;
85240aa4a30SMark Brown adc_lrc |= WM8350_ADCLRC_ENA;
85340aa4a30SMark Brown break;
85440aa4a30SMark Brown case SND_SOC_DAIFMT_CBS_CFS:
85540aa4a30SMark Brown break;
85640aa4a30SMark Brown default:
85740aa4a30SMark Brown return -EINVAL;
85840aa4a30SMark Brown }
85940aa4a30SMark Brown
86040aa4a30SMark Brown /* interface format */
86140aa4a30SMark Brown switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
86240aa4a30SMark Brown case SND_SOC_DAIFMT_I2S:
86340aa4a30SMark Brown iface |= 0x2 << 8;
86440aa4a30SMark Brown break;
86540aa4a30SMark Brown case SND_SOC_DAIFMT_RIGHT_J:
86640aa4a30SMark Brown break;
86740aa4a30SMark Brown case SND_SOC_DAIFMT_LEFT_J:
86840aa4a30SMark Brown iface |= 0x1 << 8;
86940aa4a30SMark Brown break;
87040aa4a30SMark Brown case SND_SOC_DAIFMT_DSP_A:
87140aa4a30SMark Brown iface |= 0x3 << 8;
87240aa4a30SMark Brown break;
87340aa4a30SMark Brown case SND_SOC_DAIFMT_DSP_B:
8745ee518ecSMark Brown iface |= 0x3 << 8 | WM8350_AIF_LRCLK_INV;
87540aa4a30SMark Brown break;
87640aa4a30SMark Brown default:
87740aa4a30SMark Brown return -EINVAL;
87840aa4a30SMark Brown }
87940aa4a30SMark Brown
88040aa4a30SMark Brown /* clock inversion */
88140aa4a30SMark Brown switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
88240aa4a30SMark Brown case SND_SOC_DAIFMT_NB_NF:
88340aa4a30SMark Brown break;
88440aa4a30SMark Brown case SND_SOC_DAIFMT_IB_IF:
88540aa4a30SMark Brown iface |= WM8350_AIF_LRCLK_INV | WM8350_AIF_BCLK_INV;
88640aa4a30SMark Brown break;
88740aa4a30SMark Brown case SND_SOC_DAIFMT_IB_NF:
88840aa4a30SMark Brown iface |= WM8350_AIF_BCLK_INV;
88940aa4a30SMark Brown break;
89040aa4a30SMark Brown case SND_SOC_DAIFMT_NB_IF:
89140aa4a30SMark Brown iface |= WM8350_AIF_LRCLK_INV;
89240aa4a30SMark Brown break;
89340aa4a30SMark Brown default:
89440aa4a30SMark Brown return -EINVAL;
89540aa4a30SMark Brown }
89640aa4a30SMark Brown
8972621a9a4SKuninori Morimoto snd_soc_component_write(component, WM8350_AI_FORMATING, iface);
8982621a9a4SKuninori Morimoto snd_soc_component_write(component, WM8350_AI_DAC_CONTROL, master);
8992621a9a4SKuninori Morimoto snd_soc_component_write(component, WM8350_DAC_LR_RATE, dac_lrc);
9002621a9a4SKuninori Morimoto snd_soc_component_write(component, WM8350_ADC_LR_RATE, adc_lrc);
90140aa4a30SMark Brown return 0;
90240aa4a30SMark Brown }
90340aa4a30SMark Brown
wm8350_pcm_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params,struct snd_soc_dai * codec_dai)90440aa4a30SMark Brown static int wm8350_pcm_hw_params(struct snd_pcm_substream *substream,
90540aa4a30SMark Brown struct snd_pcm_hw_params *params,
90640aa4a30SMark Brown struct snd_soc_dai *codec_dai)
90740aa4a30SMark Brown {
9082621a9a4SKuninori Morimoto struct snd_soc_component *component = codec_dai->component;
9092621a9a4SKuninori Morimoto struct wm8350_data *wm8350_data = snd_soc_component_get_drvdata(component);
910018a455aSMark Brown struct wm8350 *wm8350 = wm8350_data->wm8350;
9116d75dfc3SKuninori Morimoto u16 iface = snd_soc_component_read(component, WM8350_AI_FORMATING) &
91240aa4a30SMark Brown ~WM8350_AIF_WL_MASK;
91340aa4a30SMark Brown
91440aa4a30SMark Brown /* bit size */
9151e6453acSMark Brown switch (params_width(params)) {
9161e6453acSMark Brown case 16:
91740aa4a30SMark Brown break;
9181e6453acSMark Brown case 20:
91940aa4a30SMark Brown iface |= 0x1 << 10;
92040aa4a30SMark Brown break;
9211e6453acSMark Brown case 24:
92240aa4a30SMark Brown iface |= 0x2 << 10;
92340aa4a30SMark Brown break;
9241e6453acSMark Brown case 32:
92540aa4a30SMark Brown iface |= 0x3 << 10;
92640aa4a30SMark Brown break;
92740aa4a30SMark Brown }
92840aa4a30SMark Brown
9292621a9a4SKuninori Morimoto snd_soc_component_write(component, WM8350_AI_FORMATING, iface);
93061943999SMark Brown
93161943999SMark Brown /* The sloping stopband filter is recommended for use with
93261943999SMark Brown * lower sample rates to improve performance.
93361943999SMark Brown */
93461943999SMark Brown if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
93561943999SMark Brown if (params_rate(params) < 24000)
93661943999SMark Brown wm8350_set_bits(wm8350, WM8350_DAC_MUTE_VOLUME,
93761943999SMark Brown WM8350_DAC_SB_FILT);
93861943999SMark Brown else
93961943999SMark Brown wm8350_clear_bits(wm8350, WM8350_DAC_MUTE_VOLUME,
94061943999SMark Brown WM8350_DAC_SB_FILT);
94161943999SMark Brown }
94261943999SMark Brown
94340aa4a30SMark Brown return 0;
94440aa4a30SMark Brown }
94540aa4a30SMark Brown
wm8350_mute(struct snd_soc_dai * dai,int mute,int direction)94626d3c16eSKuninori Morimoto static int wm8350_mute(struct snd_soc_dai *dai, int mute, int direction)
94740aa4a30SMark Brown {
9482621a9a4SKuninori Morimoto struct snd_soc_component *component = dai->component;
949018a455aSMark Brown unsigned int val;
95040aa4a30SMark Brown
95140aa4a30SMark Brown if (mute)
952018a455aSMark Brown val = WM8350_DAC_MUTE_ENA;
95340aa4a30SMark Brown else
954018a455aSMark Brown val = 0;
955018a455aSMark Brown
9562621a9a4SKuninori Morimoto snd_soc_component_update_bits(component, WM8350_DAC_MUTE, WM8350_DAC_MUTE_ENA, val);
957018a455aSMark Brown
95840aa4a30SMark Brown return 0;
95940aa4a30SMark Brown }
96040aa4a30SMark Brown
96140aa4a30SMark Brown /* FLL divisors */
96240aa4a30SMark Brown struct _fll_div {
96340aa4a30SMark Brown int div; /* FLL_OUTDIV */
96440aa4a30SMark Brown int n;
96540aa4a30SMark Brown int k;
96640aa4a30SMark Brown int ratio; /* FLL_FRATIO */
96740aa4a30SMark Brown };
96840aa4a30SMark Brown
96940aa4a30SMark Brown /* The size in bits of the fll divide multiplied by 10
97040aa4a30SMark Brown * to allow rounding later */
97140aa4a30SMark Brown #define FIXED_FLL_SIZE ((1 << 16) * 10)
97240aa4a30SMark Brown
fll_factors(struct _fll_div * fll_div,unsigned int input,unsigned int output)97340aa4a30SMark Brown static inline int fll_factors(struct _fll_div *fll_div, unsigned int input,
97440aa4a30SMark Brown unsigned int output)
97540aa4a30SMark Brown {
97640aa4a30SMark Brown u64 Kpart;
97740aa4a30SMark Brown unsigned int t1, t2, K, Nmod;
97840aa4a30SMark Brown
97940aa4a30SMark Brown if (output >= 2815250 && output <= 3125000)
98040aa4a30SMark Brown fll_div->div = 0x4;
98140aa4a30SMark Brown else if (output >= 5625000 && output <= 6250000)
98240aa4a30SMark Brown fll_div->div = 0x3;
98340aa4a30SMark Brown else if (output >= 11250000 && output <= 12500000)
98440aa4a30SMark Brown fll_div->div = 0x2;
98540aa4a30SMark Brown else if (output >= 22500000 && output <= 25000000)
98640aa4a30SMark Brown fll_div->div = 0x1;
98740aa4a30SMark Brown else {
98840aa4a30SMark Brown printk(KERN_ERR "wm8350: fll freq %d out of range\n", output);
98940aa4a30SMark Brown return -EINVAL;
99040aa4a30SMark Brown }
99140aa4a30SMark Brown
99240aa4a30SMark Brown if (input > 48000)
99340aa4a30SMark Brown fll_div->ratio = 1;
99440aa4a30SMark Brown else
99540aa4a30SMark Brown fll_div->ratio = 8;
99640aa4a30SMark Brown
99740aa4a30SMark Brown t1 = output * (1 << (fll_div->div + 1));
99840aa4a30SMark Brown t2 = input * fll_div->ratio;
99940aa4a30SMark Brown
100040aa4a30SMark Brown fll_div->n = t1 / t2;
100140aa4a30SMark Brown Nmod = t1 % t2;
100240aa4a30SMark Brown
100340aa4a30SMark Brown if (Nmod) {
100440aa4a30SMark Brown Kpart = FIXED_FLL_SIZE * (long long)Nmod;
100540aa4a30SMark Brown do_div(Kpart, t2);
100640aa4a30SMark Brown K = Kpart & 0xFFFFFFFF;
100740aa4a30SMark Brown
100840aa4a30SMark Brown /* Check if we need to round */
100940aa4a30SMark Brown if ((K % 10) >= 5)
101040aa4a30SMark Brown K += 5;
101140aa4a30SMark Brown
101240aa4a30SMark Brown /* Move down to proper range now rounding is done */
101340aa4a30SMark Brown K /= 10;
101440aa4a30SMark Brown fll_div->k = K;
101540aa4a30SMark Brown } else
101640aa4a30SMark Brown fll_div->k = 0;
101740aa4a30SMark Brown
101840aa4a30SMark Brown return 0;
101940aa4a30SMark Brown }
102040aa4a30SMark Brown
wm8350_set_fll(struct snd_soc_dai * codec_dai,int pll_id,int source,unsigned int freq_in,unsigned int freq_out)102140aa4a30SMark Brown static int wm8350_set_fll(struct snd_soc_dai *codec_dai,
102285488037SMark Brown int pll_id, int source, unsigned int freq_in,
102340aa4a30SMark Brown unsigned int freq_out)
102440aa4a30SMark Brown {
10252621a9a4SKuninori Morimoto struct snd_soc_component *component = codec_dai->component;
10262621a9a4SKuninori Morimoto struct wm8350_data *priv = snd_soc_component_get_drvdata(component);
1027018a455aSMark Brown struct wm8350 *wm8350 = priv->wm8350;
102840aa4a30SMark Brown struct _fll_div fll_div;
102940aa4a30SMark Brown int ret = 0;
103040aa4a30SMark Brown u16 fll_1, fll_4;
103140aa4a30SMark Brown
1032f1e887deSMark Brown if (freq_in == priv->fll_freq_in && freq_out == priv->fll_freq_out)
1033f1e887deSMark Brown return 0;
1034f1e887deSMark Brown
103540aa4a30SMark Brown /* power down FLL - we need to do this for reconfiguration */
103640aa4a30SMark Brown wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_4,
103740aa4a30SMark Brown WM8350_FLL_ENA | WM8350_FLL_OSC_ENA);
103840aa4a30SMark Brown
103940aa4a30SMark Brown if (freq_out == 0 || freq_in == 0)
104040aa4a30SMark Brown return ret;
104140aa4a30SMark Brown
104240aa4a30SMark Brown ret = fll_factors(&fll_div, freq_in, freq_out);
104340aa4a30SMark Brown if (ret < 0)
104440aa4a30SMark Brown return ret;
104540aa4a30SMark Brown dev_dbg(wm8350->dev,
1046449bd54dSRoel Kluin "FLL in %u FLL out %u N 0x%x K 0x%x div %d ratio %d",
104740aa4a30SMark Brown freq_in, freq_out, fll_div.n, fll_div.k, fll_div.div,
104840aa4a30SMark Brown fll_div.ratio);
104940aa4a30SMark Brown
105040aa4a30SMark Brown /* set up N.K & dividers */
10516d75dfc3SKuninori Morimoto fll_1 = snd_soc_component_read(component, WM8350_FLL_CONTROL_1) &
105240aa4a30SMark Brown ~(WM8350_FLL_OUTDIV_MASK | WM8350_FLL_RSP_RATE_MASK | 0xc000);
10532621a9a4SKuninori Morimoto snd_soc_component_write(component, WM8350_FLL_CONTROL_1,
105440aa4a30SMark Brown fll_1 | (fll_div.div << 8) | 0x50);
10552621a9a4SKuninori Morimoto snd_soc_component_write(component, WM8350_FLL_CONTROL_2,
105640aa4a30SMark Brown (fll_div.ratio << 11) | (fll_div.
105740aa4a30SMark Brown n & WM8350_FLL_N_MASK));
10582621a9a4SKuninori Morimoto snd_soc_component_write(component, WM8350_FLL_CONTROL_3, fll_div.k);
10596d75dfc3SKuninori Morimoto fll_4 = snd_soc_component_read(component, WM8350_FLL_CONTROL_4) &
106040aa4a30SMark Brown ~(WM8350_FLL_FRAC | WM8350_FLL_SLOW_LOCK_REF);
10612621a9a4SKuninori Morimoto snd_soc_component_write(component, WM8350_FLL_CONTROL_4,
106240aa4a30SMark Brown fll_4 | (fll_div.k ? WM8350_FLL_FRAC : 0) |
106340aa4a30SMark Brown (fll_div.ratio == 8 ? WM8350_FLL_SLOW_LOCK_REF : 0));
106440aa4a30SMark Brown
106540aa4a30SMark Brown /* power FLL on */
106640aa4a30SMark Brown wm8350_set_bits(wm8350, WM8350_POWER_MGMT_4, WM8350_FLL_OSC_ENA);
106740aa4a30SMark Brown wm8350_set_bits(wm8350, WM8350_POWER_MGMT_4, WM8350_FLL_ENA);
106840aa4a30SMark Brown
1069f1e887deSMark Brown priv->fll_freq_out = freq_out;
1070f1e887deSMark Brown priv->fll_freq_in = freq_in;
1071f1e887deSMark Brown
107240aa4a30SMark Brown return 0;
107340aa4a30SMark Brown }
107440aa4a30SMark Brown
wm8350_set_bias_level(struct snd_soc_component * component,enum snd_soc_bias_level level)10752621a9a4SKuninori Morimoto static int wm8350_set_bias_level(struct snd_soc_component *component,
107640aa4a30SMark Brown enum snd_soc_bias_level level)
107740aa4a30SMark Brown {
10782621a9a4SKuninori Morimoto struct wm8350_data *priv = snd_soc_component_get_drvdata(component);
1079018a455aSMark Brown struct wm8350 *wm8350 = priv->wm8350;
108040aa4a30SMark Brown struct wm8350_audio_platform_data *platform =
108140aa4a30SMark Brown wm8350->codec.platform_data;
108240aa4a30SMark Brown u16 pm1;
108340aa4a30SMark Brown int ret;
108440aa4a30SMark Brown
108540aa4a30SMark Brown switch (level) {
108640aa4a30SMark Brown case SND_SOC_BIAS_ON:
108740aa4a30SMark Brown pm1 = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_1) &
108840aa4a30SMark Brown ~(WM8350_VMID_MASK | WM8350_CODEC_ISEL_MASK);
108940aa4a30SMark Brown wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1,
109040aa4a30SMark Brown pm1 | WM8350_VMID_50K |
109140aa4a30SMark Brown platform->codec_current_on << 14);
109240aa4a30SMark Brown break;
109340aa4a30SMark Brown
109440aa4a30SMark Brown case SND_SOC_BIAS_PREPARE:
109540aa4a30SMark Brown pm1 = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_1);
109640aa4a30SMark Brown pm1 &= ~WM8350_VMID_MASK;
109740aa4a30SMark Brown wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1,
109840aa4a30SMark Brown pm1 | WM8350_VMID_50K);
109940aa4a30SMark Brown break;
110040aa4a30SMark Brown
110140aa4a30SMark Brown case SND_SOC_BIAS_STANDBY:
11022621a9a4SKuninori Morimoto if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
110340aa4a30SMark Brown ret = regulator_bulk_enable(ARRAY_SIZE(priv->supplies),
110440aa4a30SMark Brown priv->supplies);
110540aa4a30SMark Brown if (ret != 0)
110640aa4a30SMark Brown return ret;
110740aa4a30SMark Brown
110840aa4a30SMark Brown /* Enable the system clock */
110940aa4a30SMark Brown wm8350_set_bits(wm8350, WM8350_POWER_MGMT_4,
111040aa4a30SMark Brown WM8350_SYSCLK_ENA);
111140aa4a30SMark Brown
111240aa4a30SMark Brown /* mute DAC & outputs */
111340aa4a30SMark Brown wm8350_set_bits(wm8350, WM8350_DAC_MUTE,
111440aa4a30SMark Brown WM8350_DAC_MUTE_ENA);
111540aa4a30SMark Brown
111640aa4a30SMark Brown /* discharge cap memory */
111740aa4a30SMark Brown wm8350_reg_write(wm8350, WM8350_ANTI_POP_CONTROL,
111840aa4a30SMark Brown platform->dis_out1 |
111940aa4a30SMark Brown (platform->dis_out2 << 2) |
112040aa4a30SMark Brown (platform->dis_out3 << 4) |
112140aa4a30SMark Brown (platform->dis_out4 << 6));
112240aa4a30SMark Brown
112340aa4a30SMark Brown /* wait for discharge */
112440aa4a30SMark Brown schedule_timeout_interruptible(msecs_to_jiffies
112540aa4a30SMark Brown (platform->
112640aa4a30SMark Brown cap_discharge_msecs));
112740aa4a30SMark Brown
112840aa4a30SMark Brown /* enable antipop */
112940aa4a30SMark Brown wm8350_reg_write(wm8350, WM8350_ANTI_POP_CONTROL,
113040aa4a30SMark Brown (platform->vmid_s_curve << 8));
113140aa4a30SMark Brown
113240aa4a30SMark Brown /* ramp up vmid */
113340aa4a30SMark Brown wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1,
113440aa4a30SMark Brown (platform->
113540aa4a30SMark Brown codec_current_charge << 14) |
113640aa4a30SMark Brown WM8350_VMID_5K | WM8350_VMIDEN |
113740aa4a30SMark Brown WM8350_VBUFEN);
113840aa4a30SMark Brown
113940aa4a30SMark Brown /* wait for vmid */
114040aa4a30SMark Brown schedule_timeout_interruptible(msecs_to_jiffies
114140aa4a30SMark Brown (platform->
114240aa4a30SMark Brown vmid_charge_msecs));
114340aa4a30SMark Brown
114440aa4a30SMark Brown /* turn on vmid 300k */
114540aa4a30SMark Brown pm1 = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_1) &
114640aa4a30SMark Brown ~(WM8350_VMID_MASK | WM8350_CODEC_ISEL_MASK);
114740aa4a30SMark Brown pm1 |= WM8350_VMID_300K |
114840aa4a30SMark Brown (platform->codec_current_standby << 14);
114940aa4a30SMark Brown wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1,
115040aa4a30SMark Brown pm1);
115140aa4a30SMark Brown
115240aa4a30SMark Brown
115340aa4a30SMark Brown /* enable analogue bias */
115440aa4a30SMark Brown pm1 |= WM8350_BIASEN;
115540aa4a30SMark Brown wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1, pm1);
115640aa4a30SMark Brown
115740aa4a30SMark Brown /* disable antipop */
115840aa4a30SMark Brown wm8350_reg_write(wm8350, WM8350_ANTI_POP_CONTROL, 0);
115940aa4a30SMark Brown
116040aa4a30SMark Brown } else {
116140aa4a30SMark Brown /* turn on vmid 300k and reduce current */
116240aa4a30SMark Brown pm1 = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_1) &
116340aa4a30SMark Brown ~(WM8350_VMID_MASK | WM8350_CODEC_ISEL_MASK);
116440aa4a30SMark Brown wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1,
116540aa4a30SMark Brown pm1 | WM8350_VMID_300K |
116640aa4a30SMark Brown (platform->
116740aa4a30SMark Brown codec_current_standby << 14));
116840aa4a30SMark Brown
116940aa4a30SMark Brown }
117040aa4a30SMark Brown break;
117140aa4a30SMark Brown
117240aa4a30SMark Brown case SND_SOC_BIAS_OFF:
117340aa4a30SMark Brown
117440aa4a30SMark Brown /* mute DAC & enable outputs */
117540aa4a30SMark Brown wm8350_set_bits(wm8350, WM8350_DAC_MUTE, WM8350_DAC_MUTE_ENA);
117640aa4a30SMark Brown
117740aa4a30SMark Brown wm8350_set_bits(wm8350, WM8350_POWER_MGMT_3,
117840aa4a30SMark Brown WM8350_OUT1L_ENA | WM8350_OUT1R_ENA |
117940aa4a30SMark Brown WM8350_OUT2L_ENA | WM8350_OUT2R_ENA);
118040aa4a30SMark Brown
118140aa4a30SMark Brown /* enable anti pop S curve */
118240aa4a30SMark Brown wm8350_reg_write(wm8350, WM8350_ANTI_POP_CONTROL,
118340aa4a30SMark Brown (platform->vmid_s_curve << 8));
118440aa4a30SMark Brown
118540aa4a30SMark Brown /* turn off vmid */
118640aa4a30SMark Brown pm1 = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_1) &
118740aa4a30SMark Brown ~WM8350_VMIDEN;
118840aa4a30SMark Brown wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1, pm1);
118940aa4a30SMark Brown
119040aa4a30SMark Brown /* wait */
119140aa4a30SMark Brown schedule_timeout_interruptible(msecs_to_jiffies
119240aa4a30SMark Brown (platform->
119340aa4a30SMark Brown vmid_discharge_msecs));
119440aa4a30SMark Brown
119540aa4a30SMark Brown wm8350_reg_write(wm8350, WM8350_ANTI_POP_CONTROL,
119640aa4a30SMark Brown (platform->vmid_s_curve << 8) |
119740aa4a30SMark Brown platform->dis_out1 |
119840aa4a30SMark Brown (platform->dis_out2 << 2) |
119940aa4a30SMark Brown (platform->dis_out3 << 4) |
120040aa4a30SMark Brown (platform->dis_out4 << 6));
120140aa4a30SMark Brown
120240aa4a30SMark Brown /* turn off VBuf and drain */
120340aa4a30SMark Brown pm1 = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_1) &
120440aa4a30SMark Brown ~(WM8350_VBUFEN | WM8350_VMID_MASK);
120540aa4a30SMark Brown wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1,
120640aa4a30SMark Brown pm1 | WM8350_OUTPUT_DRAIN_EN);
120740aa4a30SMark Brown
120840aa4a30SMark Brown /* wait */
120940aa4a30SMark Brown schedule_timeout_interruptible(msecs_to_jiffies
121040aa4a30SMark Brown (platform->drain_msecs));
121140aa4a30SMark Brown
121240aa4a30SMark Brown pm1 &= ~WM8350_BIASEN;
121340aa4a30SMark Brown wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1, pm1);
121440aa4a30SMark Brown
121540aa4a30SMark Brown /* disable anti-pop */
121640aa4a30SMark Brown wm8350_reg_write(wm8350, WM8350_ANTI_POP_CONTROL, 0);
121740aa4a30SMark Brown
121840aa4a30SMark Brown wm8350_clear_bits(wm8350, WM8350_LOUT1_VOLUME,
121940aa4a30SMark Brown WM8350_OUT1L_ENA);
122040aa4a30SMark Brown wm8350_clear_bits(wm8350, WM8350_ROUT1_VOLUME,
122140aa4a30SMark Brown WM8350_OUT1R_ENA);
122240aa4a30SMark Brown wm8350_clear_bits(wm8350, WM8350_LOUT2_VOLUME,
122340aa4a30SMark Brown WM8350_OUT2L_ENA);
122440aa4a30SMark Brown wm8350_clear_bits(wm8350, WM8350_ROUT2_VOLUME,
122540aa4a30SMark Brown WM8350_OUT2R_ENA);
122640aa4a30SMark Brown
122740aa4a30SMark Brown /* disable clock gen */
122840aa4a30SMark Brown wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_4,
122940aa4a30SMark Brown WM8350_SYSCLK_ENA);
123040aa4a30SMark Brown
123140aa4a30SMark Brown regulator_bulk_disable(ARRAY_SIZE(priv->supplies),
123240aa4a30SMark Brown priv->supplies);
123340aa4a30SMark Brown break;
123440aa4a30SMark Brown }
123540aa4a30SMark Brown return 0;
123640aa4a30SMark Brown }
123740aa4a30SMark Brown
wm8350_hp_work(struct wm8350_data * priv,struct wm8350_jack_data * jack,u16 mask)12386d3c26bcSMark Brown static void wm8350_hp_work(struct wm8350_data *priv,
12396d3c26bcSMark Brown struct wm8350_jack_data *jack,
12406d3c26bcSMark Brown u16 mask)
1241a6ba2b2dSMark Brown {
124230facd4dSMark Brown struct wm8350 *wm8350 = priv->wm8350;
1243a6ba2b2dSMark Brown u16 reg;
1244a6ba2b2dSMark Brown int report;
1245a6ba2b2dSMark Brown
1246a6ba2b2dSMark Brown reg = wm8350_reg_read(wm8350, WM8350_JACK_PIN_STATUS);
1247a6ba2b2dSMark Brown if (reg & mask)
1248a6ba2b2dSMark Brown report = jack->report;
1249a6ba2b2dSMark Brown else
1250a6ba2b2dSMark Brown report = 0;
1251a6ba2b2dSMark Brown
1252a6ba2b2dSMark Brown snd_soc_jack_report(jack->jack, report, jack->report);
12535a65edbcSMark Brown
12546d3c26bcSMark Brown }
12556d3c26bcSMark Brown
wm8350_hpl_work(struct work_struct * work)12566d3c26bcSMark Brown static void wm8350_hpl_work(struct work_struct *work)
12576d3c26bcSMark Brown {
12586d3c26bcSMark Brown struct wm8350_data *priv =
12596d3c26bcSMark Brown container_of(work, struct wm8350_data, hpl.work.work);
12606d3c26bcSMark Brown
12616d3c26bcSMark Brown wm8350_hp_work(priv, &priv->hpl, WM8350_JACK_L_LVL);
12626d3c26bcSMark Brown }
12636d3c26bcSMark Brown
wm8350_hpr_work(struct work_struct * work)12646d3c26bcSMark Brown static void wm8350_hpr_work(struct work_struct *work)
12656d3c26bcSMark Brown {
12666d3c26bcSMark Brown struct wm8350_data *priv =
12676d3c26bcSMark Brown container_of(work, struct wm8350_data, hpr.work.work);
12686d3c26bcSMark Brown
12696d3c26bcSMark Brown wm8350_hp_work(priv, &priv->hpr, WM8350_JACK_R_LVL);
12706d3c26bcSMark Brown }
12716d3c26bcSMark Brown
wm8350_hpl_jack_handler(int irq,void * data)1272f43f2db7SMark Brown static irqreturn_t wm8350_hpl_jack_handler(int irq, void *data)
12736d3c26bcSMark Brown {
12746d3c26bcSMark Brown struct wm8350_data *priv = data;
127530facd4dSMark Brown struct wm8350 *wm8350 = priv->wm8350;
12766d3c26bcSMark Brown
12771435b940SMark Brown #ifndef CONFIG_SND_SOC_WM8350_MODULE
12782bbb5d66SMark Brown trace_snd_soc_jack_irq("WM8350 HPL");
12791435b940SMark Brown #endif
12806d3c26bcSMark Brown
12816d3c26bcSMark Brown if (device_may_wakeup(wm8350->dev))
12826d3c26bcSMark Brown pm_wakeup_event(wm8350->dev, 250);
12836d3c26bcSMark Brown
12842c5920a7SMark Brown queue_delayed_work(system_power_efficient_wq,
12852c5920a7SMark Brown &priv->hpl.work, msecs_to_jiffies(200));
1286f43f2db7SMark Brown
1287f43f2db7SMark Brown return IRQ_HANDLED;
1288f43f2db7SMark Brown }
1289f43f2db7SMark Brown
wm8350_hpr_jack_handler(int irq,void * data)1290f43f2db7SMark Brown static irqreturn_t wm8350_hpr_jack_handler(int irq, void *data)
1291f43f2db7SMark Brown {
1292f43f2db7SMark Brown struct wm8350_data *priv = data;
1293f43f2db7SMark Brown struct wm8350 *wm8350 = priv->wm8350;
1294f43f2db7SMark Brown
1295f43f2db7SMark Brown #ifndef CONFIG_SND_SOC_WM8350_MODULE
1296f43f2db7SMark Brown trace_snd_soc_jack_irq("WM8350 HPR");
1297f43f2db7SMark Brown #endif
1298f43f2db7SMark Brown
1299f43f2db7SMark Brown if (device_may_wakeup(wm8350->dev))
1300f43f2db7SMark Brown pm_wakeup_event(wm8350->dev, 250);
1301f43f2db7SMark Brown
13022c5920a7SMark Brown queue_delayed_work(system_power_efficient_wq,
13032c5920a7SMark Brown &priv->hpr.work, msecs_to_jiffies(200));
13046d3c26bcSMark Brown
13055a65edbcSMark Brown return IRQ_HANDLED;
1306a6ba2b2dSMark Brown }
1307a6ba2b2dSMark Brown
1308a6ba2b2dSMark Brown /**
1309a6ba2b2dSMark Brown * wm8350_hp_jack_detect - Enable headphone jack detection.
1310a6ba2b2dSMark Brown *
13112621a9a4SKuninori Morimoto * @component: WM8350 component
1312a6ba2b2dSMark Brown * @which: left or right jack detect signal
1313a6ba2b2dSMark Brown * @jack: jack to report detection events on
1314a6ba2b2dSMark Brown * @report: value to report
1315a6ba2b2dSMark Brown *
1316f06bce9cSMark Brown * Enables the headphone jack detection of the WM8350. If no report
1317f06bce9cSMark Brown * is specified then detection is disabled.
1318a6ba2b2dSMark Brown */
wm8350_hp_jack_detect(struct snd_soc_component * component,enum wm8350_jack which,struct snd_soc_jack * jack,int report)13192621a9a4SKuninori Morimoto int wm8350_hp_jack_detect(struct snd_soc_component *component, enum wm8350_jack which,
1320a6ba2b2dSMark Brown struct snd_soc_jack *jack, int report)
1321a6ba2b2dSMark Brown {
13222621a9a4SKuninori Morimoto struct wm8350_data *priv = snd_soc_component_get_drvdata(component);
1323018a455aSMark Brown struct wm8350 *wm8350 = priv->wm8350;
1324a6ba2b2dSMark Brown int ena;
1325a6ba2b2dSMark Brown
1326a6ba2b2dSMark Brown switch (which) {
1327a6ba2b2dSMark Brown case WM8350_JDL:
1328a6ba2b2dSMark Brown priv->hpl.jack = jack;
1329a6ba2b2dSMark Brown priv->hpl.report = report;
1330a6ba2b2dSMark Brown ena = WM8350_JDL_ENA;
1331a6ba2b2dSMark Brown break;
1332a6ba2b2dSMark Brown
1333a6ba2b2dSMark Brown case WM8350_JDR:
1334a6ba2b2dSMark Brown priv->hpr.jack = jack;
1335a6ba2b2dSMark Brown priv->hpr.report = report;
1336a6ba2b2dSMark Brown ena = WM8350_JDR_ENA;
1337a6ba2b2dSMark Brown break;
1338a6ba2b2dSMark Brown
1339a6ba2b2dSMark Brown default:
1340a6ba2b2dSMark Brown return -EINVAL;
1341a6ba2b2dSMark Brown }
1342a6ba2b2dSMark Brown
1343f06bce9cSMark Brown if (report) {
1344a6ba2b2dSMark Brown wm8350_set_bits(wm8350, WM8350_POWER_MGMT_4, WM8350_TOCLK_ENA);
1345a6ba2b2dSMark Brown wm8350_set_bits(wm8350, WM8350_JACK_DETECT, ena);
1346f06bce9cSMark Brown } else {
1347f06bce9cSMark Brown wm8350_clear_bits(wm8350, WM8350_JACK_DETECT, ena);
1348f06bce9cSMark Brown }
1349a6ba2b2dSMark Brown
1350a6ba2b2dSMark Brown /* Sync status */
1351f43f2db7SMark Brown switch (which) {
1352f43f2db7SMark Brown case WM8350_JDL:
1353f43f2db7SMark Brown wm8350_hpl_jack_handler(0, priv);
1354f43f2db7SMark Brown break;
1355f43f2db7SMark Brown case WM8350_JDR:
1356f43f2db7SMark Brown wm8350_hpr_jack_handler(0, priv);
1357f43f2db7SMark Brown break;
1358f43f2db7SMark Brown }
1359a6ba2b2dSMark Brown
1360a6ba2b2dSMark Brown return 0;
1361a6ba2b2dSMark Brown }
1362a6ba2b2dSMark Brown EXPORT_SYMBOL_GPL(wm8350_hp_jack_detect);
1363a6ba2b2dSMark Brown
wm8350_mic_handler(int irq,void * data)13642a0761a3SMark Brown static irqreturn_t wm8350_mic_handler(int irq, void *data)
13652a0761a3SMark Brown {
13662a0761a3SMark Brown struct wm8350_data *priv = data;
136730facd4dSMark Brown struct wm8350 *wm8350 = priv->wm8350;
13682a0761a3SMark Brown u16 reg;
13692a0761a3SMark Brown int report = 0;
13702a0761a3SMark Brown
13717116f452SMark Brown #ifndef CONFIG_SND_SOC_WM8350_MODULE
13722bbb5d66SMark Brown trace_snd_soc_jack_irq("WM8350 mic");
13737116f452SMark Brown #endif
13742bbb5d66SMark Brown
13752a0761a3SMark Brown reg = wm8350_reg_read(wm8350, WM8350_JACK_PIN_STATUS);
13762a0761a3SMark Brown if (reg & WM8350_JACK_MICSCD_LVL)
13772a0761a3SMark Brown report |= priv->mic.short_report;
13782a0761a3SMark Brown if (reg & WM8350_JACK_MICSD_LVL)
13792a0761a3SMark Brown report |= priv->mic.report;
13802a0761a3SMark Brown
13812a0761a3SMark Brown snd_soc_jack_report(priv->mic.jack, report,
13822a0761a3SMark Brown priv->mic.report | priv->mic.short_report);
13832a0761a3SMark Brown
13842a0761a3SMark Brown return IRQ_HANDLED;
13852a0761a3SMark Brown }
13862a0761a3SMark Brown
13872a0761a3SMark Brown /**
13882a0761a3SMark Brown * wm8350_mic_jack_detect - Enable microphone jack detection.
13892a0761a3SMark Brown *
13902621a9a4SKuninori Morimoto * @component: WM8350 component
13912a0761a3SMark Brown * @jack: jack to report detection events on
13922a0761a3SMark Brown * @detect_report: value to report when presence detected
13932a0761a3SMark Brown * @short_report: value to report when microphone short detected
13942a0761a3SMark Brown *
1395f06bce9cSMark Brown * Enables the microphone jack detection of the WM8350. If both reports
1396f06bce9cSMark Brown * are specified as zero then detection is disabled.
13972a0761a3SMark Brown */
wm8350_mic_jack_detect(struct snd_soc_component * component,struct snd_soc_jack * jack,int detect_report,int short_report)13982621a9a4SKuninori Morimoto int wm8350_mic_jack_detect(struct snd_soc_component *component,
13992a0761a3SMark Brown struct snd_soc_jack *jack,
14002a0761a3SMark Brown int detect_report, int short_report)
14012a0761a3SMark Brown {
14022621a9a4SKuninori Morimoto struct wm8350_data *priv = snd_soc_component_get_drvdata(component);
1403018a455aSMark Brown struct wm8350 *wm8350 = priv->wm8350;
14042a0761a3SMark Brown
14052a0761a3SMark Brown priv->mic.jack = jack;
14062a0761a3SMark Brown priv->mic.report = detect_report;
14072a0761a3SMark Brown priv->mic.short_report = short_report;
14082a0761a3SMark Brown
1409f06bce9cSMark Brown if (detect_report || short_report) {
14102a0761a3SMark Brown wm8350_set_bits(wm8350, WM8350_POWER_MGMT_4, WM8350_TOCLK_ENA);
1411f06bce9cSMark Brown wm8350_set_bits(wm8350, WM8350_POWER_MGMT_1,
1412f06bce9cSMark Brown WM8350_MIC_DET_ENA);
1413f06bce9cSMark Brown } else {
1414f06bce9cSMark Brown wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_1,
1415f06bce9cSMark Brown WM8350_MIC_DET_ENA);
1416f06bce9cSMark Brown }
14172a0761a3SMark Brown
14182a0761a3SMark Brown return 0;
14192a0761a3SMark Brown }
14202a0761a3SMark Brown EXPORT_SYMBOL_GPL(wm8350_mic_jack_detect);
14212a0761a3SMark Brown
1422f0fba2adSLiam Girdwood #define WM8350_RATES (SNDRV_PCM_RATE_8000_96000)
142340aa4a30SMark Brown
1424f0fba2adSLiam Girdwood #define WM8350_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
1425f0fba2adSLiam Girdwood SNDRV_PCM_FMTBIT_S20_3LE |\
1426f0fba2adSLiam Girdwood SNDRV_PCM_FMTBIT_S24_LE)
1427f0fba2adSLiam Girdwood
142885e7652dSLars-Peter Clausen static const struct snd_soc_dai_ops wm8350_dai_ops = {
1429f0fba2adSLiam Girdwood .hw_params = wm8350_pcm_hw_params,
143026d3c16eSKuninori Morimoto .mute_stream = wm8350_mute,
1431f0fba2adSLiam Girdwood .set_fmt = wm8350_set_dai_fmt,
1432f0fba2adSLiam Girdwood .set_sysclk = wm8350_set_dai_sysclk,
1433f0fba2adSLiam Girdwood .set_pll = wm8350_set_fll,
1434f0fba2adSLiam Girdwood .set_clkdiv = wm8350_set_clkdiv,
143526d3c16eSKuninori Morimoto .no_capture_mute = 1,
1436f0fba2adSLiam Girdwood };
1437f0fba2adSLiam Girdwood
1438f0fba2adSLiam Girdwood static struct snd_soc_dai_driver wm8350_dai = {
1439f0fba2adSLiam Girdwood .name = "wm8350-hifi",
1440f0fba2adSLiam Girdwood .playback = {
1441f0fba2adSLiam Girdwood .stream_name = "Playback",
1442f0fba2adSLiam Girdwood .channels_min = 1,
1443f0fba2adSLiam Girdwood .channels_max = 2,
1444f0fba2adSLiam Girdwood .rates = WM8350_RATES,
1445f0fba2adSLiam Girdwood .formats = WM8350_FORMATS,
1446f0fba2adSLiam Girdwood },
1447f0fba2adSLiam Girdwood .capture = {
1448f0fba2adSLiam Girdwood .stream_name = "Capture",
1449f0fba2adSLiam Girdwood .channels_min = 1,
1450f0fba2adSLiam Girdwood .channels_max = 2,
1451f0fba2adSLiam Girdwood .rates = WM8350_RATES,
1452f0fba2adSLiam Girdwood .formats = WM8350_FORMATS,
1453f0fba2adSLiam Girdwood },
1454f0fba2adSLiam Girdwood .ops = &wm8350_dai_ops,
1455f0fba2adSLiam Girdwood };
1456f0fba2adSLiam Girdwood
wm8350_component_probe(struct snd_soc_component * component)14572621a9a4SKuninori Morimoto static int wm8350_component_probe(struct snd_soc_component *component)
145840aa4a30SMark Brown {
14592621a9a4SKuninori Morimoto struct wm8350 *wm8350 = dev_get_platdata(component->dev);
146040aa4a30SMark Brown struct wm8350_data *priv;
146140aa4a30SMark Brown struct wm8350_output *out1;
146240aa4a30SMark Brown struct wm8350_output *out2;
1463f0fba2adSLiam Girdwood int ret, i;
146440aa4a30SMark Brown
1465f0fba2adSLiam Girdwood if (wm8350->codec.platform_data == NULL) {
14662621a9a4SKuninori Morimoto dev_err(component->dev, "No audio platform data supplied\n");
1467f0fba2adSLiam Girdwood return -EINVAL;
1468f0fba2adSLiam Girdwood }
146940aa4a30SMark Brown
14702621a9a4SKuninori Morimoto priv = devm_kzalloc(component->dev, sizeof(struct wm8350_data),
14710d1fe0d4SMark Brown GFP_KERNEL);
1472f0fba2adSLiam Girdwood if (priv == NULL)
1473f0fba2adSLiam Girdwood return -ENOMEM;
147440b84884SKuninori Morimoto
14752621a9a4SKuninori Morimoto snd_soc_component_init_regmap(component, wm8350->regmap);
14762621a9a4SKuninori Morimoto snd_soc_component_set_drvdata(component, priv);
1477f0fba2adSLiam Girdwood
147830facd4dSMark Brown priv->wm8350 = wm8350;
147930facd4dSMark Brown
1480f0fba2adSLiam Girdwood for (i = 0; i < ARRAY_SIZE(supply_names); i++)
1481f0fba2adSLiam Girdwood priv->supplies[i].supply = supply_names[i];
1482f0fba2adSLiam Girdwood
14835851e9b8SSachin Kamat ret = devm_regulator_bulk_get(wm8350->dev, ARRAY_SIZE(priv->supplies),
1484f0fba2adSLiam Girdwood priv->supplies);
1485f0fba2adSLiam Girdwood if (ret != 0)
14860d1fe0d4SMark Brown return ret;
1487f0fba2adSLiam Girdwood
1488f0fba2adSLiam Girdwood /* Put the codec into reset if it wasn't already */
1489f0fba2adSLiam Girdwood wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_5, WM8350_CODEC_ENA);
1490f0fba2adSLiam Girdwood
1491cd5d8226SLars-Peter Clausen INIT_DELAYED_WORK(&priv->pga_work, wm8350_pga_work);
14926d3c26bcSMark Brown INIT_DELAYED_WORK(&priv->hpl.work, wm8350_hpl_work);
14936d3c26bcSMark Brown INIT_DELAYED_WORK(&priv->hpr.work, wm8350_hpr_work);
149440aa4a30SMark Brown
149540aa4a30SMark Brown /* Enable the codec */
149640aa4a30SMark Brown wm8350_set_bits(wm8350, WM8350_POWER_MGMT_5, WM8350_CODEC_ENA);
149740aa4a30SMark Brown
149840aa4a30SMark Brown /* Enable robust clocking mode in ADC */
14992621a9a4SKuninori Morimoto snd_soc_component_write(component, WM8350_SECURITY, 0xa7);
15002621a9a4SKuninori Morimoto snd_soc_component_write(component, 0xde, 0x13);
15012621a9a4SKuninori Morimoto snd_soc_component_write(component, WM8350_SECURITY, 0);
150240aa4a30SMark Brown
150340aa4a30SMark Brown /* read OUT1 & OUT2 volumes */
150440aa4a30SMark Brown out1 = &priv->out1;
150540aa4a30SMark Brown out2 = &priv->out2;
150640aa4a30SMark Brown out1->left_vol = (wm8350_reg_read(wm8350, WM8350_LOUT1_VOLUME) &
150740aa4a30SMark Brown WM8350_OUT1L_VOL_MASK) >> WM8350_OUT1L_VOL_SHIFT;
150840aa4a30SMark Brown out1->right_vol = (wm8350_reg_read(wm8350, WM8350_ROUT1_VOLUME) &
150940aa4a30SMark Brown WM8350_OUT1R_VOL_MASK) >> WM8350_OUT1R_VOL_SHIFT;
151040aa4a30SMark Brown out2->left_vol = (wm8350_reg_read(wm8350, WM8350_LOUT2_VOLUME) &
151140aa4a30SMark Brown WM8350_OUT2L_VOL_MASK) >> WM8350_OUT1L_VOL_SHIFT;
151240aa4a30SMark Brown out2->right_vol = (wm8350_reg_read(wm8350, WM8350_ROUT2_VOLUME) &
151340aa4a30SMark Brown WM8350_OUT2R_VOL_MASK) >> WM8350_OUT1R_VOL_SHIFT;
151440aa4a30SMark Brown wm8350_reg_write(wm8350, WM8350_LOUT1_VOLUME, 0);
151540aa4a30SMark Brown wm8350_reg_write(wm8350, WM8350_ROUT1_VOLUME, 0);
151640aa4a30SMark Brown wm8350_reg_write(wm8350, WM8350_LOUT2_VOLUME, 0);
151740aa4a30SMark Brown wm8350_reg_write(wm8350, WM8350_ROUT2_VOLUME, 0);
151840aa4a30SMark Brown
151940aa4a30SMark Brown /* Latch VU bits & mute */
152040aa4a30SMark Brown wm8350_set_bits(wm8350, WM8350_LOUT1_VOLUME,
152140aa4a30SMark Brown WM8350_OUT1_VU | WM8350_OUT1L_MUTE);
152240aa4a30SMark Brown wm8350_set_bits(wm8350, WM8350_LOUT2_VOLUME,
152340aa4a30SMark Brown WM8350_OUT2_VU | WM8350_OUT2L_MUTE);
152440aa4a30SMark Brown wm8350_set_bits(wm8350, WM8350_ROUT1_VOLUME,
152540aa4a30SMark Brown WM8350_OUT1_VU | WM8350_OUT1R_MUTE);
152640aa4a30SMark Brown wm8350_set_bits(wm8350, WM8350_ROUT2_VOLUME,
152740aa4a30SMark Brown WM8350_OUT2_VU | WM8350_OUT2R_MUTE);
152840aa4a30SMark Brown
15290049317eSMark Brown /* Make sure AIF tristating is disabled by default */
15300049317eSMark Brown wm8350_clear_bits(wm8350, WM8350_AI_FORMATING, WM8350_AIF_TRI);
15310049317eSMark Brown
15320049317eSMark Brown /* Make sure we've got a sane companding setup too */
15330049317eSMark Brown wm8350_clear_bits(wm8350, WM8350_ADC_DAC_COMP,
15340049317eSMark Brown WM8350_DAC_COMP | WM8350_LOOPBACK);
15350049317eSMark Brown
15366a612746SMark Brown /* Make sure jack detect is disabled to start off with */
15376a612746SMark Brown wm8350_clear_bits(wm8350, WM8350_JACK_DETECT,
15386a612746SMark Brown WM8350_JDL_ENA | WM8350_JDR_ENA);
15396a612746SMark Brown
1540*db0350daSJiasheng Jiang ret = wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_L,
1541f43f2db7SMark Brown wm8350_hpl_jack_handler, 0, "Left jack detect",
15425a65edbcSMark Brown priv);
1543*db0350daSJiasheng Jiang if (ret != 0)
1544*db0350daSJiasheng Jiang goto err;
1545*db0350daSJiasheng Jiang
1546*db0350daSJiasheng Jiang ret = wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R,
1547f43f2db7SMark Brown wm8350_hpr_jack_handler, 0, "Right jack detect",
15485a65edbcSMark Brown priv);
1549*db0350daSJiasheng Jiang if (ret != 0)
1550*db0350daSJiasheng Jiang goto free_jck_det_l;
1551*db0350daSJiasheng Jiang
1552*db0350daSJiasheng Jiang ret = wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_MICSCD,
15532a0761a3SMark Brown wm8350_mic_handler, 0, "Microphone short", priv);
1554*db0350daSJiasheng Jiang if (ret != 0)
1555*db0350daSJiasheng Jiang goto free_jck_det_r;
1556*db0350daSJiasheng Jiang
1557*db0350daSJiasheng Jiang ret = wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_MICD,
15582a0761a3SMark Brown wm8350_mic_handler, 0, "Microphone detect", priv);
1559*db0350daSJiasheng Jiang if (ret != 0)
1560*db0350daSJiasheng Jiang goto free_micscd;
1561a6ba2b2dSMark Brown
156240aa4a30SMark Brown return 0;
1563*db0350daSJiasheng Jiang
1564*db0350daSJiasheng Jiang free_micscd:
1565*db0350daSJiasheng Jiang wm8350_free_irq(wm8350, WM8350_IRQ_CODEC_MICSCD, priv);
1566*db0350daSJiasheng Jiang free_jck_det_r:
1567*db0350daSJiasheng Jiang wm8350_free_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R, priv);
1568*db0350daSJiasheng Jiang free_jck_det_l:
1569*db0350daSJiasheng Jiang wm8350_free_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_L, priv);
1570*db0350daSJiasheng Jiang err:
1571*db0350daSJiasheng Jiang return ret;
157240aa4a30SMark Brown }
157340aa4a30SMark Brown
wm8350_component_remove(struct snd_soc_component * component)15742621a9a4SKuninori Morimoto static void wm8350_component_remove(struct snd_soc_component *component)
157540aa4a30SMark Brown {
15762621a9a4SKuninori Morimoto struct wm8350_data *priv = snd_soc_component_get_drvdata(component);
15772621a9a4SKuninori Morimoto struct wm8350 *wm8350 = dev_get_platdata(component->dev);
157840aa4a30SMark Brown
1579a6ba2b2dSMark Brown wm8350_clear_bits(wm8350, WM8350_JACK_DETECT,
1580a6ba2b2dSMark Brown WM8350_JDL_ENA | WM8350_JDR_ENA);
1581a6ba2b2dSMark Brown wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_4, WM8350_TOCLK_ENA);
1582a6ba2b2dSMark Brown
15832a0761a3SMark Brown wm8350_free_irq(wm8350, WM8350_IRQ_CODEC_MICD, priv);
15842a0761a3SMark Brown wm8350_free_irq(wm8350, WM8350_IRQ_CODEC_MICSCD, priv);
1585f99344fcSMark Brown wm8350_free_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_L, priv);
1586f99344fcSMark Brown wm8350_free_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R, priv);
1587a6ba2b2dSMark Brown
1588a6ba2b2dSMark Brown priv->hpl.jack = NULL;
1589a6ba2b2dSMark Brown priv->hpr.jack = NULL;
15902a0761a3SMark Brown priv->mic.jack = NULL;
1591a6ba2b2dSMark Brown
15926d3c26bcSMark Brown cancel_delayed_work_sync(&priv->hpl.work);
15936d3c26bcSMark Brown cancel_delayed_work_sync(&priv->hpr.work);
15946d3c26bcSMark Brown
159540aa4a30SMark Brown /* if there was any work waiting then we run it now and
159640aa4a30SMark Brown * wait for its completion */
1597cd5d8226SLars-Peter Clausen flush_delayed_work(&priv->pga_work);
159840aa4a30SMark Brown
159940aa4a30SMark Brown wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_5, WM8350_CODEC_ENA);
160040aa4a30SMark Brown }
160140aa4a30SMark Brown
16022621a9a4SKuninori Morimoto static const struct snd_soc_component_driver soc_component_dev_wm8350 = {
16032621a9a4SKuninori Morimoto .probe = wm8350_component_probe,
16042621a9a4SKuninori Morimoto .remove = wm8350_component_remove,
1605f0fba2adSLiam Girdwood .set_bias_level = wm8350_set_bias_level,
1606e6c94e9fSMark Brown .controls = wm8350_snd_controls,
1607e6c94e9fSMark Brown .num_controls = ARRAY_SIZE(wm8350_snd_controls),
1608e6c94e9fSMark Brown .dapm_widgets = wm8350_dapm_widgets,
1609e6c94e9fSMark Brown .num_dapm_widgets = ARRAY_SIZE(wm8350_dapm_widgets),
1610e6c94e9fSMark Brown .dapm_routes = wm8350_dapm_routes,
1611e6c94e9fSMark Brown .num_dapm_routes = ARRAY_SIZE(wm8350_dapm_routes),
16122621a9a4SKuninori Morimoto .suspend_bias_off = 1,
16132621a9a4SKuninori Morimoto .idle_bias_on = 1,
16142621a9a4SKuninori Morimoto .use_pmdown_time = 1,
16152621a9a4SKuninori Morimoto .endianness = 1,
161640aa4a30SMark Brown };
161740aa4a30SMark Brown
wm8350_probe(struct platform_device * pdev)16187a79e94eSBill Pemberton static int wm8350_probe(struct platform_device *pdev)
161940aa4a30SMark Brown {
16202621a9a4SKuninori Morimoto return devm_snd_soc_register_component(&pdev->dev,
16212621a9a4SKuninori Morimoto &soc_component_dev_wm8350,
1622f0fba2adSLiam Girdwood &wm8350_dai, 1);
162340aa4a30SMark Brown }
162440aa4a30SMark Brown
162540aa4a30SMark Brown static struct platform_driver wm8350_codec_driver = {
162640aa4a30SMark Brown .driver = {
162740aa4a30SMark Brown .name = "wm8350-codec",
162840aa4a30SMark Brown },
1629f0fba2adSLiam Girdwood .probe = wm8350_probe,
163040aa4a30SMark Brown };
163140aa4a30SMark Brown
16325bbcc3c0SMark Brown module_platform_driver(wm8350_codec_driver);
163340aa4a30SMark Brown
163440aa4a30SMark Brown MODULE_DESCRIPTION("ASoC WM8350 driver");
163540aa4a30SMark Brown MODULE_AUTHOR("Liam Girdwood");
163640aa4a30SMark Brown MODULE_LICENSE("GPL");
163740aa4a30SMark Brown MODULE_ALIAS("platform:wm8350-codec");
1638