xref: /openbmc/linux/sound/soc/codecs/wm8350.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
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