xref: /linux/sound/soc/codecs/wm9090.c (revision 33e02dc69afbd8f1b85a51d74d72f139ba4ca623)
12b27bdccSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
239b8eab7SMark Brown /*
339b8eab7SMark Brown  * ALSA SoC WM9090 driver
439b8eab7SMark Brown  *
5656baaebSMark Brown  * Copyright 2009-12 Wolfson Microelectronics
639b8eab7SMark Brown  *
739b8eab7SMark Brown  * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
839b8eab7SMark Brown  */
939b8eab7SMark Brown 
1039b8eab7SMark Brown #include <linux/module.h>
1139b8eab7SMark Brown #include <linux/errno.h>
1239b8eab7SMark Brown #include <linux/device.h>
1339b8eab7SMark Brown #include <linux/i2c.h>
1439b8eab7SMark Brown #include <linux/delay.h>
15ec2c0fecSMark Brown #include <linux/regmap.h>
1639b8eab7SMark Brown #include <linux/slab.h>
1739b8eab7SMark Brown #include <sound/initval.h>
1839b8eab7SMark Brown #include <sound/soc.h>
1939b8eab7SMark Brown #include <sound/tlv.h>
2039b8eab7SMark Brown #include <sound/wm9090.h>
2139b8eab7SMark Brown 
2239b8eab7SMark Brown #include "wm9090.h"
2339b8eab7SMark Brown 
24ec2c0fecSMark Brown static const struct reg_default wm9090_reg_defaults[] = {
25ec2c0fecSMark Brown 	{ 1,  0x0006 },     /* R1   - Power Management (1) */
26ec2c0fecSMark Brown 	{ 2,  0x6000 },     /* R2   - Power Management (2) */
27ec2c0fecSMark Brown 	{ 3,  0x0000 },     /* R3   - Power Management (3) */
28ec2c0fecSMark Brown 	{ 6,  0x01C0 },     /* R6   - Clocking 1 */
29ec2c0fecSMark Brown 	{ 22, 0x0003 },     /* R22  - IN1 Line Control */
30ec2c0fecSMark Brown 	{ 23, 0x0003 },     /* R23  - IN2 Line Control */
31ec2c0fecSMark Brown 	{ 24, 0x0083 },     /* R24  - IN1 Line Input A Volume */
32ec2c0fecSMark Brown 	{ 25, 0x0083 },     /* R25  - IN1  Line Input B Volume */
33ec2c0fecSMark Brown 	{ 26, 0x0083 },     /* R26  - IN2 Line Input A Volume */
34ec2c0fecSMark Brown 	{ 27, 0x0083 },     /* R27  - IN2 Line Input B Volume */
35ec2c0fecSMark Brown 	{ 28, 0x002D },     /* R28  - Left Output Volume */
36ec2c0fecSMark Brown 	{ 29, 0x002D },     /* R29  - Right Output Volume */
37ec2c0fecSMark Brown 	{ 34, 0x0100 },     /* R34  - SPKMIXL Attenuation */
38ec2c0fecSMark Brown 	{ 35, 0x0010 },     /* R36  - SPKOUT Mixers */
39ec2c0fecSMark Brown 	{ 37, 0x0140 },     /* R37  - ClassD3 */
40ec2c0fecSMark Brown 	{ 38, 0x0039 },     /* R38  - Speaker Volume Left */
41ec2c0fecSMark Brown 	{ 45, 0x0000 },     /* R45  - Output Mixer1 */
42ec2c0fecSMark Brown 	{ 46, 0x0000 },     /* R46  - Output Mixer2 */
43ec2c0fecSMark Brown 	{ 47, 0x0100 },     /* R47  - Output Mixer3 */
44ec2c0fecSMark Brown 	{ 48, 0x0100 },     /* R48  - Output Mixer4 */
45ec2c0fecSMark Brown 	{ 54, 0x0000 },     /* R54  - Speaker Mixer */
46ec2c0fecSMark Brown 	{ 57, 0x000D },     /* R57  - AntiPOP2 */
47ec2c0fecSMark Brown 	{ 70, 0x0000 },     /* R70  - Write Sequencer 0 */
48ec2c0fecSMark Brown 	{ 71, 0x0000 },     /* R71  - Write Sequencer 1 */
49ec2c0fecSMark Brown 	{ 72, 0x0000 },     /* R72  - Write Sequencer 2 */
50ec2c0fecSMark Brown 	{ 73, 0x0000 },     /* R73  - Write Sequencer 3 */
51ec2c0fecSMark Brown 	{ 74, 0x0000 },     /* R74  - Write Sequencer 4 */
52ec2c0fecSMark Brown 	{ 75, 0x0000 },     /* R75  - Write Sequencer 5 */
53ec2c0fecSMark Brown 	{ 76, 0x1F25 },     /* R76  - Charge Pump 1 */
54ec2c0fecSMark Brown 	{ 85, 0x054A },     /* R85  - DC Servo 1 */
55ec2c0fecSMark Brown 	{ 87, 0x0000 },     /* R87  - DC Servo 3 */
56ec2c0fecSMark Brown 	{ 96, 0x0100 },     /* R96  - Analogue HP 0 */
57ec2c0fecSMark Brown 	{ 98, 0x8640 },     /* R98  - AGC Control 0 */
58ec2c0fecSMark Brown 	{ 99, 0xC000 },     /* R99  - AGC Control 1 */
59ec2c0fecSMark Brown 	{ 100, 0x0200 },     /* R100 - AGC Control 2 */
6039b8eab7SMark Brown };
6139b8eab7SMark Brown 
6239b8eab7SMark Brown /* This struct is used to save the context */
6339b8eab7SMark Brown struct wm9090_priv {
6439b8eab7SMark Brown 	struct wm9090_platform_data pdata;
65ec2c0fecSMark Brown 	struct regmap *regmap;
6639b8eab7SMark Brown };
6739b8eab7SMark Brown 
wm9090_volatile(struct device * dev,unsigned int reg)68ec2c0fecSMark Brown static bool wm9090_volatile(struct device *dev, unsigned int reg)
6939b8eab7SMark Brown {
7039b8eab7SMark Brown 	switch (reg) {
7139b8eab7SMark Brown 	case WM9090_SOFTWARE_RESET:
7239b8eab7SMark Brown 	case WM9090_DC_SERVO_0:
7339b8eab7SMark Brown 	case WM9090_DC_SERVO_READBACK_0:
7439b8eab7SMark Brown 	case WM9090_DC_SERVO_READBACK_1:
7539b8eab7SMark Brown 	case WM9090_DC_SERVO_READBACK_2:
76ec2c0fecSMark Brown 		return true;
7739b8eab7SMark Brown 
7839b8eab7SMark Brown 	default:
79ec2c0fecSMark Brown 		return false;
80ec2c0fecSMark Brown 	}
81ec2c0fecSMark Brown }
82ec2c0fecSMark Brown 
wm9090_readable(struct device * dev,unsigned int reg)83ec2c0fecSMark Brown static bool wm9090_readable(struct device *dev, unsigned int reg)
84ec2c0fecSMark Brown {
85ec2c0fecSMark Brown 	switch (reg) {
86ec2c0fecSMark Brown 	case WM9090_SOFTWARE_RESET:
87ec2c0fecSMark Brown 	case WM9090_POWER_MANAGEMENT_1:
88ec2c0fecSMark Brown 	case WM9090_POWER_MANAGEMENT_2:
89ec2c0fecSMark Brown 	case WM9090_POWER_MANAGEMENT_3:
90ec2c0fecSMark Brown 	case WM9090_CLOCKING_1:
91ec2c0fecSMark Brown 	case WM9090_IN1_LINE_CONTROL:
92ec2c0fecSMark Brown 	case WM9090_IN2_LINE_CONTROL:
93ec2c0fecSMark Brown 	case WM9090_IN1_LINE_INPUT_A_VOLUME:
94ec2c0fecSMark Brown 	case WM9090_IN1_LINE_INPUT_B_VOLUME:
95ec2c0fecSMark Brown 	case WM9090_IN2_LINE_INPUT_A_VOLUME:
96ec2c0fecSMark Brown 	case WM9090_IN2_LINE_INPUT_B_VOLUME:
97ec2c0fecSMark Brown 	case WM9090_LEFT_OUTPUT_VOLUME:
98ec2c0fecSMark Brown 	case WM9090_RIGHT_OUTPUT_VOLUME:
99ec2c0fecSMark Brown 	case WM9090_SPKMIXL_ATTENUATION:
100ec2c0fecSMark Brown 	case WM9090_SPKOUT_MIXERS:
101ec2c0fecSMark Brown 	case WM9090_CLASSD3:
102ec2c0fecSMark Brown 	case WM9090_SPEAKER_VOLUME_LEFT:
103ec2c0fecSMark Brown 	case WM9090_OUTPUT_MIXER1:
104ec2c0fecSMark Brown 	case WM9090_OUTPUT_MIXER2:
105ec2c0fecSMark Brown 	case WM9090_OUTPUT_MIXER3:
106ec2c0fecSMark Brown 	case WM9090_OUTPUT_MIXER4:
107ec2c0fecSMark Brown 	case WM9090_SPEAKER_MIXER:
108ec2c0fecSMark Brown 	case WM9090_ANTIPOP2:
109ec2c0fecSMark Brown 	case WM9090_WRITE_SEQUENCER_0:
110ec2c0fecSMark Brown 	case WM9090_WRITE_SEQUENCER_1:
111ec2c0fecSMark Brown 	case WM9090_WRITE_SEQUENCER_2:
112ec2c0fecSMark Brown 	case WM9090_WRITE_SEQUENCER_3:
113ec2c0fecSMark Brown 	case WM9090_WRITE_SEQUENCER_4:
114ec2c0fecSMark Brown 	case WM9090_WRITE_SEQUENCER_5:
115ec2c0fecSMark Brown 	case WM9090_CHARGE_PUMP_1:
116ec2c0fecSMark Brown 	case WM9090_DC_SERVO_0:
117ec2c0fecSMark Brown 	case WM9090_DC_SERVO_1:
118ec2c0fecSMark Brown 	case WM9090_DC_SERVO_3:
119ec2c0fecSMark Brown 	case WM9090_DC_SERVO_READBACK_0:
120ec2c0fecSMark Brown 	case WM9090_DC_SERVO_READBACK_1:
121ec2c0fecSMark Brown 	case WM9090_DC_SERVO_READBACK_2:
122ec2c0fecSMark Brown 	case WM9090_ANALOGUE_HP_0:
123ec2c0fecSMark Brown 	case WM9090_AGC_CONTROL_0:
124ec2c0fecSMark Brown 	case WM9090_AGC_CONTROL_1:
125ec2c0fecSMark Brown 	case WM9090_AGC_CONTROL_2:
126ec2c0fecSMark Brown 		return true;
127ec2c0fecSMark Brown 
128ec2c0fecSMark Brown 	default:
129ec2c0fecSMark Brown 		return false;
13039b8eab7SMark Brown 	}
13139b8eab7SMark Brown }
13239b8eab7SMark Brown 
wait_for_dc_servo(struct snd_soc_component * component)1332822e66bSKuninori Morimoto static void wait_for_dc_servo(struct snd_soc_component *component)
13439b8eab7SMark Brown {
13539b8eab7SMark Brown 	unsigned int reg;
13639b8eab7SMark Brown 	int count = 0;
13739b8eab7SMark Brown 
1382822e66bSKuninori Morimoto 	dev_dbg(component->dev, "Waiting for DC servo...\n");
13939b8eab7SMark Brown 	do {
14039b8eab7SMark Brown 		count++;
14139b8eab7SMark Brown 		msleep(1);
1426d75dfc3SKuninori Morimoto 		reg = snd_soc_component_read(component, WM9090_DC_SERVO_READBACK_0);
1432822e66bSKuninori Morimoto 		dev_dbg(component->dev, "DC servo status: %x\n", reg);
14439b8eab7SMark Brown 	} while ((reg & WM9090_DCS_CAL_COMPLETE_MASK)
14539b8eab7SMark Brown 		 != WM9090_DCS_CAL_COMPLETE_MASK && count < 1000);
14639b8eab7SMark Brown 
14739b8eab7SMark Brown 	if ((reg & WM9090_DCS_CAL_COMPLETE_MASK)
14839b8eab7SMark Brown 	    != WM9090_DCS_CAL_COMPLETE_MASK)
1492822e66bSKuninori Morimoto 		dev_err(component->dev, "Timed out waiting for DC Servo\n");
15039b8eab7SMark Brown }
15139b8eab7SMark Brown 
1520894e2b3SLars-Peter Clausen static const DECLARE_TLV_DB_RANGE(in_tlv,
15339b8eab7SMark Brown 	0, 0, TLV_DB_SCALE_ITEM(-600, 0, 0),
15439b8eab7SMark Brown 	1, 3, TLV_DB_SCALE_ITEM(-350, 350, 0),
1550894e2b3SLars-Peter Clausen 	4, 6, TLV_DB_SCALE_ITEM(600, 600, 0)
1560894e2b3SLars-Peter Clausen );
1570894e2b3SLars-Peter Clausen static const DECLARE_TLV_DB_RANGE(mix_tlv,
15839b8eab7SMark Brown 	0, 2, TLV_DB_SCALE_ITEM(-1200, 300, 0),
1590894e2b3SLars-Peter Clausen 	3, 3, TLV_DB_SCALE_ITEM(0, 0, 0)
1600894e2b3SLars-Peter Clausen );
16139b8eab7SMark Brown static const DECLARE_TLV_DB_SCALE(out_tlv, -5700, 100, 0);
1620894e2b3SLars-Peter Clausen static const DECLARE_TLV_DB_RANGE(spkboost_tlv,
16339b8eab7SMark Brown 	0, 6, TLV_DB_SCALE_ITEM(0, 150, 0),
1640894e2b3SLars-Peter Clausen 	7, 7, TLV_DB_SCALE_ITEM(1200, 0, 0)
1650894e2b3SLars-Peter Clausen );
16639b8eab7SMark Brown 
16739b8eab7SMark Brown static const struct snd_kcontrol_new wm9090_controls[] = {
16839b8eab7SMark Brown SOC_SINGLE_TLV("IN1A Volume", WM9090_IN1_LINE_INPUT_A_VOLUME, 0, 6, 0,
16939b8eab7SMark Brown 	       in_tlv),
17039b8eab7SMark Brown SOC_SINGLE("IN1A Switch", WM9090_IN1_LINE_INPUT_A_VOLUME, 7, 1, 1),
17139b8eab7SMark Brown SOC_SINGLE("IN1A ZC Switch", WM9090_IN1_LINE_INPUT_A_VOLUME, 6, 1, 0),
17239b8eab7SMark Brown 
17339b8eab7SMark Brown SOC_SINGLE_TLV("IN2A Volume", WM9090_IN2_LINE_INPUT_A_VOLUME, 0, 6, 0,
17439b8eab7SMark Brown 	       in_tlv),
17539b8eab7SMark Brown SOC_SINGLE("IN2A Switch", WM9090_IN2_LINE_INPUT_A_VOLUME, 7, 1, 1),
17639b8eab7SMark Brown SOC_SINGLE("IN2A ZC Switch", WM9090_IN2_LINE_INPUT_A_VOLUME, 6, 1, 0),
17739b8eab7SMark Brown 
17839b8eab7SMark Brown SOC_SINGLE("MIXOUTL Switch", WM9090_OUTPUT_MIXER3, 8, 1, 1),
17939b8eab7SMark Brown SOC_SINGLE_TLV("MIXOUTL IN1A Volume", WM9090_OUTPUT_MIXER3, 6, 3, 1,
18039b8eab7SMark Brown 	       mix_tlv),
18139b8eab7SMark Brown SOC_SINGLE_TLV("MIXOUTL IN2A Volume", WM9090_OUTPUT_MIXER3, 2, 3, 1,
18239b8eab7SMark Brown 	       mix_tlv),
18339b8eab7SMark Brown 
18439b8eab7SMark Brown SOC_SINGLE("MIXOUTR Switch", WM9090_OUTPUT_MIXER4, 8, 1, 1),
18539b8eab7SMark Brown SOC_SINGLE_TLV("MIXOUTR IN1A Volume", WM9090_OUTPUT_MIXER4, 6, 3, 1,
18639b8eab7SMark Brown 	       mix_tlv),
18739b8eab7SMark Brown SOC_SINGLE_TLV("MIXOUTR IN2A Volume", WM9090_OUTPUT_MIXER4, 2, 3, 1,
18839b8eab7SMark Brown 	       mix_tlv),
18939b8eab7SMark Brown 
19039b8eab7SMark Brown SOC_SINGLE("SPKMIX Switch", WM9090_SPKMIXL_ATTENUATION, 8, 1, 1),
19139b8eab7SMark Brown SOC_SINGLE_TLV("SPKMIX IN1A Volume", WM9090_SPKMIXL_ATTENUATION, 6, 3, 1,
19239b8eab7SMark Brown 	       mix_tlv),
19339b8eab7SMark Brown SOC_SINGLE_TLV("SPKMIX IN2A Volume", WM9090_SPKMIXL_ATTENUATION, 2, 3, 1,
19439b8eab7SMark Brown 	       mix_tlv),
19539b8eab7SMark Brown 
19639b8eab7SMark Brown SOC_DOUBLE_R_TLV("Headphone Volume", WM9090_LEFT_OUTPUT_VOLUME,
19739b8eab7SMark Brown 		 WM9090_RIGHT_OUTPUT_VOLUME, 0, 63, 0, out_tlv),
19839b8eab7SMark Brown SOC_DOUBLE_R("Headphone Switch", WM9090_LEFT_OUTPUT_VOLUME,
19939b8eab7SMark Brown 	     WM9090_RIGHT_OUTPUT_VOLUME, 6, 1, 1),
20039b8eab7SMark Brown SOC_DOUBLE_R("Headphone ZC Switch", WM9090_LEFT_OUTPUT_VOLUME,
20139b8eab7SMark Brown 	     WM9090_RIGHT_OUTPUT_VOLUME, 7, 1, 0),
20239b8eab7SMark Brown 
20339b8eab7SMark Brown SOC_SINGLE_TLV("Speaker Volume", WM9090_SPEAKER_VOLUME_LEFT, 0, 63, 0,
20439b8eab7SMark Brown 	       out_tlv),
20539b8eab7SMark Brown SOC_SINGLE("Speaker Switch", WM9090_SPEAKER_VOLUME_LEFT, 6, 1, 1),
20639b8eab7SMark Brown SOC_SINGLE("Speaker ZC Switch", WM9090_SPEAKER_VOLUME_LEFT, 7, 1, 0),
20739b8eab7SMark Brown SOC_SINGLE_TLV("Speaker Boost Volume", WM9090_CLASSD3, 3, 7, 0, spkboost_tlv),
20839b8eab7SMark Brown };
20939b8eab7SMark Brown 
21039b8eab7SMark Brown static const struct snd_kcontrol_new wm9090_in1_se_controls[] = {
21139b8eab7SMark Brown SOC_SINGLE_TLV("IN1B Volume", WM9090_IN1_LINE_INPUT_B_VOLUME, 0, 6, 0,
21239b8eab7SMark Brown 	       in_tlv),
21339b8eab7SMark Brown SOC_SINGLE("IN1B Switch", WM9090_IN1_LINE_INPUT_B_VOLUME, 7, 1, 1),
21439b8eab7SMark Brown SOC_SINGLE("IN1B ZC Switch", WM9090_IN1_LINE_INPUT_B_VOLUME, 6, 1, 0),
21539b8eab7SMark Brown 
21639b8eab7SMark Brown SOC_SINGLE_TLV("SPKMIX IN1B Volume", WM9090_SPKMIXL_ATTENUATION, 4, 3, 1,
21739b8eab7SMark Brown 	       mix_tlv),
21839b8eab7SMark Brown SOC_SINGLE_TLV("MIXOUTL IN1B Volume", WM9090_OUTPUT_MIXER3, 4, 3, 1,
21939b8eab7SMark Brown 	       mix_tlv),
22039b8eab7SMark Brown SOC_SINGLE_TLV("MIXOUTR IN1B Volume", WM9090_OUTPUT_MIXER4, 4, 3, 1,
22139b8eab7SMark Brown 	       mix_tlv),
22239b8eab7SMark Brown };
22339b8eab7SMark Brown 
22439b8eab7SMark Brown static const struct snd_kcontrol_new wm9090_in2_se_controls[] = {
22539b8eab7SMark Brown SOC_SINGLE_TLV("IN2B Volume", WM9090_IN2_LINE_INPUT_B_VOLUME, 0, 6, 0,
22639b8eab7SMark Brown 	       in_tlv),
22739b8eab7SMark Brown SOC_SINGLE("IN2B Switch", WM9090_IN2_LINE_INPUT_B_VOLUME, 7, 1, 1),
22839b8eab7SMark Brown SOC_SINGLE("IN2B ZC Switch", WM9090_IN2_LINE_INPUT_B_VOLUME, 6, 1, 0),
22939b8eab7SMark Brown 
23039b8eab7SMark Brown SOC_SINGLE_TLV("SPKMIX IN2B Volume", WM9090_SPKMIXL_ATTENUATION, 0, 3, 1,
23139b8eab7SMark Brown 	       mix_tlv),
23239b8eab7SMark Brown SOC_SINGLE_TLV("MIXOUTL IN2B Volume", WM9090_OUTPUT_MIXER3, 0, 3, 1,
23339b8eab7SMark Brown 	       mix_tlv),
23439b8eab7SMark Brown SOC_SINGLE_TLV("MIXOUTR IN2B Volume", WM9090_OUTPUT_MIXER4, 0, 3, 1,
23539b8eab7SMark Brown 	       mix_tlv),
23639b8eab7SMark Brown };
23739b8eab7SMark Brown 
hp_ev(struct snd_soc_dapm_widget * w,struct snd_kcontrol * kcontrol,int event)23839b8eab7SMark Brown static int hp_ev(struct snd_soc_dapm_widget *w,
23939b8eab7SMark Brown 		 struct snd_kcontrol *kcontrol, int event)
24039b8eab7SMark Brown {
2412822e66bSKuninori Morimoto 	struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
2426d75dfc3SKuninori Morimoto 	unsigned int reg = snd_soc_component_read(component, WM9090_ANALOGUE_HP_0);
24339b8eab7SMark Brown 
24439b8eab7SMark Brown 	switch (event) {
24539b8eab7SMark Brown 	case SND_SOC_DAPM_POST_PMU:
2462822e66bSKuninori Morimoto 		snd_soc_component_update_bits(component, WM9090_CHARGE_PUMP_1,
24739b8eab7SMark Brown 				    WM9090_CP_ENA, WM9090_CP_ENA);
24839b8eab7SMark Brown 
24939b8eab7SMark Brown 		msleep(5);
25039b8eab7SMark Brown 
2512822e66bSKuninori Morimoto 		snd_soc_component_update_bits(component, WM9090_POWER_MANAGEMENT_1,
25239b8eab7SMark Brown 				    WM9090_HPOUT1L_ENA | WM9090_HPOUT1R_ENA,
25339b8eab7SMark Brown 				    WM9090_HPOUT1L_ENA | WM9090_HPOUT1R_ENA);
25439b8eab7SMark Brown 
25539b8eab7SMark Brown 		reg |= WM9090_HPOUT1L_DLY | WM9090_HPOUT1R_DLY;
2562822e66bSKuninori Morimoto 		snd_soc_component_write(component, WM9090_ANALOGUE_HP_0, reg);
25739b8eab7SMark Brown 
25839b8eab7SMark Brown 		/* Start the DC servo.  We don't currently use the
25939b8eab7SMark Brown 		 * ability to save the state since we don't have full
26039b8eab7SMark Brown 		 * control of the analogue paths and they can change
26139b8eab7SMark Brown 		 * DC offsets; see the WM8904 driver for an example of
26239b8eab7SMark Brown 		 * doing so.
26339b8eab7SMark Brown 		 */
2642822e66bSKuninori Morimoto 		snd_soc_component_write(component, WM9090_DC_SERVO_0,
26539b8eab7SMark Brown 			      WM9090_DCS_ENA_CHAN_0 |
26639b8eab7SMark Brown 			      WM9090_DCS_ENA_CHAN_1 |
26739b8eab7SMark Brown 			      WM9090_DCS_TRIG_STARTUP_1 |
26839b8eab7SMark Brown 			      WM9090_DCS_TRIG_STARTUP_0);
2692822e66bSKuninori Morimoto 		wait_for_dc_servo(component);
27039b8eab7SMark Brown 
27139b8eab7SMark Brown 		reg |= WM9090_HPOUT1R_OUTP | WM9090_HPOUT1R_RMV_SHORT |
27239b8eab7SMark Brown 			WM9090_HPOUT1L_OUTP | WM9090_HPOUT1L_RMV_SHORT;
2732822e66bSKuninori Morimoto 		snd_soc_component_write(component, WM9090_ANALOGUE_HP_0, reg);
27439b8eab7SMark Brown 		break;
27539b8eab7SMark Brown 
27639b8eab7SMark Brown 	case SND_SOC_DAPM_PRE_PMD:
27739b8eab7SMark Brown 		reg &= ~(WM9090_HPOUT1L_RMV_SHORT |
27839b8eab7SMark Brown 			 WM9090_HPOUT1L_DLY |
27939b8eab7SMark Brown 			 WM9090_HPOUT1L_OUTP |
28039b8eab7SMark Brown 			 WM9090_HPOUT1R_RMV_SHORT |
28139b8eab7SMark Brown 			 WM9090_HPOUT1R_DLY |
28239b8eab7SMark Brown 			 WM9090_HPOUT1R_OUTP);
28339b8eab7SMark Brown 
2842822e66bSKuninori Morimoto 		snd_soc_component_write(component, WM9090_ANALOGUE_HP_0, reg);
28539b8eab7SMark Brown 
2862822e66bSKuninori Morimoto 		snd_soc_component_write(component, WM9090_DC_SERVO_0, 0);
28739b8eab7SMark Brown 
2882822e66bSKuninori Morimoto 		snd_soc_component_update_bits(component, WM9090_POWER_MANAGEMENT_1,
28939b8eab7SMark Brown 				    WM9090_HPOUT1L_ENA | WM9090_HPOUT1R_ENA,
29039b8eab7SMark Brown 				    0);
29139b8eab7SMark Brown 
2922822e66bSKuninori Morimoto 		snd_soc_component_update_bits(component, WM9090_CHARGE_PUMP_1,
29339b8eab7SMark Brown 				    WM9090_CP_ENA, 0);
29439b8eab7SMark Brown 		break;
29539b8eab7SMark Brown 	}
29639b8eab7SMark Brown 
29739b8eab7SMark Brown 	return 0;
29839b8eab7SMark Brown }
29939b8eab7SMark Brown 
30039b8eab7SMark Brown static const struct snd_kcontrol_new spkmix[] = {
30139b8eab7SMark Brown SOC_DAPM_SINGLE("IN1A Switch", WM9090_SPEAKER_MIXER, 6, 1, 0),
30239b8eab7SMark Brown SOC_DAPM_SINGLE("IN1B Switch", WM9090_SPEAKER_MIXER, 4, 1, 0),
30339b8eab7SMark Brown SOC_DAPM_SINGLE("IN2A Switch", WM9090_SPEAKER_MIXER, 2, 1, 0),
30439b8eab7SMark Brown SOC_DAPM_SINGLE("IN2B Switch", WM9090_SPEAKER_MIXER, 0, 1, 0),
30539b8eab7SMark Brown };
30639b8eab7SMark Brown 
30739b8eab7SMark Brown static const struct snd_kcontrol_new spkout[] = {
30839b8eab7SMark Brown SOC_DAPM_SINGLE("Mixer Switch", WM9090_SPKOUT_MIXERS, 4, 1, 0),
30939b8eab7SMark Brown };
31039b8eab7SMark Brown 
31139b8eab7SMark Brown static const struct snd_kcontrol_new mixoutl[] = {
31239b8eab7SMark Brown SOC_DAPM_SINGLE("IN1A Switch", WM9090_OUTPUT_MIXER1, 6, 1, 0),
31339b8eab7SMark Brown SOC_DAPM_SINGLE("IN1B Switch", WM9090_OUTPUT_MIXER1, 4, 1, 0),
31439b8eab7SMark Brown SOC_DAPM_SINGLE("IN2A Switch", WM9090_OUTPUT_MIXER1, 2, 1, 0),
31539b8eab7SMark Brown SOC_DAPM_SINGLE("IN2B Switch", WM9090_OUTPUT_MIXER1, 0, 1, 0),
31639b8eab7SMark Brown };
31739b8eab7SMark Brown 
31839b8eab7SMark Brown static const struct snd_kcontrol_new mixoutr[] = {
31939b8eab7SMark Brown SOC_DAPM_SINGLE("IN1A Switch", WM9090_OUTPUT_MIXER2, 6, 1, 0),
32039b8eab7SMark Brown SOC_DAPM_SINGLE("IN1B Switch", WM9090_OUTPUT_MIXER2, 4, 1, 0),
32139b8eab7SMark Brown SOC_DAPM_SINGLE("IN2A Switch", WM9090_OUTPUT_MIXER2, 2, 1, 0),
32239b8eab7SMark Brown SOC_DAPM_SINGLE("IN2B Switch", WM9090_OUTPUT_MIXER2, 0, 1, 0),
32339b8eab7SMark Brown };
32439b8eab7SMark Brown 
32539b8eab7SMark Brown static const struct snd_soc_dapm_widget wm9090_dapm_widgets[] = {
32639b8eab7SMark Brown SND_SOC_DAPM_INPUT("IN1+"),
32739b8eab7SMark Brown SND_SOC_DAPM_INPUT("IN1-"),
32839b8eab7SMark Brown SND_SOC_DAPM_INPUT("IN2+"),
32939b8eab7SMark Brown SND_SOC_DAPM_INPUT("IN2-"),
33039b8eab7SMark Brown 
33139b8eab7SMark Brown SND_SOC_DAPM_SUPPLY("OSC", WM9090_POWER_MANAGEMENT_1, 3, 0, NULL, 0),
33239b8eab7SMark Brown 
33339b8eab7SMark Brown SND_SOC_DAPM_PGA("IN1A PGA", WM9090_POWER_MANAGEMENT_2, 7, 0, NULL, 0),
33439b8eab7SMark Brown SND_SOC_DAPM_PGA("IN1B PGA", WM9090_POWER_MANAGEMENT_2, 6, 0, NULL, 0),
33539b8eab7SMark Brown SND_SOC_DAPM_PGA("IN2A PGA", WM9090_POWER_MANAGEMENT_2, 5, 0, NULL, 0),
33639b8eab7SMark Brown SND_SOC_DAPM_PGA("IN2B PGA", WM9090_POWER_MANAGEMENT_2, 4, 0, NULL, 0),
33739b8eab7SMark Brown 
33839b8eab7SMark Brown SND_SOC_DAPM_MIXER("SPKMIX", WM9090_POWER_MANAGEMENT_3, 3, 0,
33939b8eab7SMark Brown 		   spkmix, ARRAY_SIZE(spkmix)),
34039b8eab7SMark Brown SND_SOC_DAPM_MIXER("MIXOUTL", WM9090_POWER_MANAGEMENT_3, 5, 0,
34139b8eab7SMark Brown 		   mixoutl, ARRAY_SIZE(mixoutl)),
34239b8eab7SMark Brown SND_SOC_DAPM_MIXER("MIXOUTR", WM9090_POWER_MANAGEMENT_3, 4, 0,
34339b8eab7SMark Brown 		   mixoutr, ARRAY_SIZE(mixoutr)),
34439b8eab7SMark Brown 
34539b8eab7SMark Brown SND_SOC_DAPM_PGA_E("HP PGA", SND_SOC_NOPM, 0, 0, NULL, 0,
34639b8eab7SMark Brown 		   hp_ev, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
34739b8eab7SMark Brown 
34839b8eab7SMark Brown SND_SOC_DAPM_PGA("SPKPGA", WM9090_POWER_MANAGEMENT_3, 8, 0, NULL, 0),
34939b8eab7SMark Brown SND_SOC_DAPM_MIXER("SPKOUT", WM9090_POWER_MANAGEMENT_1, 12, 0,
35039b8eab7SMark Brown 		   spkout, ARRAY_SIZE(spkout)),
35139b8eab7SMark Brown 
35239b8eab7SMark Brown SND_SOC_DAPM_OUTPUT("HPR"),
35339b8eab7SMark Brown SND_SOC_DAPM_OUTPUT("HPL"),
35439b8eab7SMark Brown SND_SOC_DAPM_OUTPUT("Speaker"),
35539b8eab7SMark Brown };
35639b8eab7SMark Brown 
35739b8eab7SMark Brown static const struct snd_soc_dapm_route audio_map[] = {
35839b8eab7SMark Brown 	{ "IN1A PGA", NULL, "IN1+" },
35939b8eab7SMark Brown 	{ "IN2A PGA", NULL, "IN2+" },
36039b8eab7SMark Brown 
36139b8eab7SMark Brown 	{ "SPKMIX", "IN1A Switch", "IN1A PGA" },
36239b8eab7SMark Brown 	{ "SPKMIX", "IN2A Switch", "IN2A PGA" },
36339b8eab7SMark Brown 
36439b8eab7SMark Brown 	{ "MIXOUTL", "IN1A Switch", "IN1A PGA" },
36539b8eab7SMark Brown 	{ "MIXOUTL", "IN2A Switch", "IN2A PGA" },
36639b8eab7SMark Brown 
36739b8eab7SMark Brown 	{ "MIXOUTR", "IN1A Switch", "IN1A PGA" },
36839b8eab7SMark Brown 	{ "MIXOUTR", "IN2A Switch", "IN2A PGA" },
36939b8eab7SMark Brown 
37039b8eab7SMark Brown 	{ "HP PGA", NULL, "OSC" },
37139b8eab7SMark Brown 	{ "HP PGA", NULL, "MIXOUTL" },
37239b8eab7SMark Brown 	{ "HP PGA", NULL, "MIXOUTR" },
37339b8eab7SMark Brown 
37439b8eab7SMark Brown 	{ "HPL", NULL, "HP PGA" },
37539b8eab7SMark Brown 	{ "HPR", NULL, "HP PGA" },
37639b8eab7SMark Brown 
37739b8eab7SMark Brown 	{ "SPKPGA", NULL, "OSC" },
37839b8eab7SMark Brown 	{ "SPKPGA", NULL, "SPKMIX" },
37939b8eab7SMark Brown 
38039b8eab7SMark Brown 	{ "SPKOUT", "Mixer Switch", "SPKPGA" },
38139b8eab7SMark Brown 
38239b8eab7SMark Brown 	{ "Speaker", NULL, "SPKOUT" },
38339b8eab7SMark Brown };
38439b8eab7SMark Brown 
38539b8eab7SMark Brown static const struct snd_soc_dapm_route audio_map_in1_se[] = {
38639b8eab7SMark Brown 	{ "IN1B PGA", NULL, "IN1-" },
38739b8eab7SMark Brown 
38839b8eab7SMark Brown 	{ "SPKMIX", "IN1B Switch", "IN1B PGA" },
38939b8eab7SMark Brown 	{ "MIXOUTL", "IN1B Switch", "IN1B PGA" },
39039b8eab7SMark Brown 	{ "MIXOUTR", "IN1B Switch", "IN1B PGA" },
39139b8eab7SMark Brown };
39239b8eab7SMark Brown 
39339b8eab7SMark Brown static const struct snd_soc_dapm_route audio_map_in1_diff[] = {
39439b8eab7SMark Brown 	{ "IN1A PGA", NULL, "IN1-" },
39539b8eab7SMark Brown };
39639b8eab7SMark Brown 
39739b8eab7SMark Brown static const struct snd_soc_dapm_route audio_map_in2_se[] = {
39839b8eab7SMark Brown 	{ "IN2B PGA", NULL, "IN2-" },
39939b8eab7SMark Brown 
40039b8eab7SMark Brown 	{ "SPKMIX", "IN2B Switch", "IN2B PGA" },
40139b8eab7SMark Brown 	{ "MIXOUTL", "IN2B Switch", "IN2B PGA" },
40239b8eab7SMark Brown 	{ "MIXOUTR", "IN2B Switch", "IN2B PGA" },
40339b8eab7SMark Brown };
40439b8eab7SMark Brown 
40539b8eab7SMark Brown static const struct snd_soc_dapm_route audio_map_in2_diff[] = {
40639b8eab7SMark Brown 	{ "IN2A PGA", NULL, "IN2-" },
40739b8eab7SMark Brown };
40839b8eab7SMark Brown 
wm9090_add_controls(struct snd_soc_component * component)4092822e66bSKuninori Morimoto static int wm9090_add_controls(struct snd_soc_component *component)
41039b8eab7SMark Brown {
4112822e66bSKuninori Morimoto 	struct wm9090_priv *wm9090 = snd_soc_component_get_drvdata(component);
4122822e66bSKuninori Morimoto 	struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
41339b8eab7SMark Brown 	int i;
41439b8eab7SMark Brown 
415ce6120ccSLiam Girdwood 	snd_soc_dapm_new_controls(dapm, wm9090_dapm_widgets,
41639b8eab7SMark Brown 				  ARRAY_SIZE(wm9090_dapm_widgets));
41739b8eab7SMark Brown 
418ce6120ccSLiam Girdwood 	snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
41939b8eab7SMark Brown 
4202822e66bSKuninori Morimoto 	snd_soc_add_component_controls(component, wm9090_controls,
42139b8eab7SMark Brown 			     ARRAY_SIZE(wm9090_controls));
42239b8eab7SMark Brown 
42339b8eab7SMark Brown 	if (wm9090->pdata.lin1_diff) {
424ce6120ccSLiam Girdwood 		snd_soc_dapm_add_routes(dapm, audio_map_in1_diff,
42539b8eab7SMark Brown 					ARRAY_SIZE(audio_map_in1_diff));
42639b8eab7SMark Brown 	} else {
427ce6120ccSLiam Girdwood 		snd_soc_dapm_add_routes(dapm, audio_map_in1_se,
42839b8eab7SMark Brown 					ARRAY_SIZE(audio_map_in1_se));
4292822e66bSKuninori Morimoto 		snd_soc_add_component_controls(component, wm9090_in1_se_controls,
43039b8eab7SMark Brown 				     ARRAY_SIZE(wm9090_in1_se_controls));
43139b8eab7SMark Brown 	}
43239b8eab7SMark Brown 
43339b8eab7SMark Brown 	if (wm9090->pdata.lin2_diff) {
434ce6120ccSLiam Girdwood 		snd_soc_dapm_add_routes(dapm, audio_map_in2_diff,
43539b8eab7SMark Brown 					ARRAY_SIZE(audio_map_in2_diff));
43639b8eab7SMark Brown 	} else {
437ce6120ccSLiam Girdwood 		snd_soc_dapm_add_routes(dapm, audio_map_in2_se,
43839b8eab7SMark Brown 					ARRAY_SIZE(audio_map_in2_se));
4392822e66bSKuninori Morimoto 		snd_soc_add_component_controls(component, wm9090_in2_se_controls,
44039b8eab7SMark Brown 				     ARRAY_SIZE(wm9090_in2_se_controls));
44139b8eab7SMark Brown 	}
44239b8eab7SMark Brown 
44339b8eab7SMark Brown 	if (wm9090->pdata.agc_ena) {
44439b8eab7SMark Brown 		for (i = 0; i < ARRAY_SIZE(wm9090->pdata.agc); i++)
4452822e66bSKuninori Morimoto 			snd_soc_component_write(component, WM9090_AGC_CONTROL_0 + i,
44639b8eab7SMark Brown 				      wm9090->pdata.agc[i]);
4472822e66bSKuninori Morimoto 		snd_soc_component_update_bits(component, WM9090_POWER_MANAGEMENT_3,
44839b8eab7SMark Brown 				    WM9090_AGC_ENA, WM9090_AGC_ENA);
44939b8eab7SMark Brown 	} else {
4502822e66bSKuninori Morimoto 		snd_soc_component_update_bits(component, WM9090_POWER_MANAGEMENT_3,
45139b8eab7SMark Brown 				    WM9090_AGC_ENA, 0);
45239b8eab7SMark Brown 	}
45339b8eab7SMark Brown 
45439b8eab7SMark Brown 	return 0;
45539b8eab7SMark Brown 
45639b8eab7SMark Brown }
45739b8eab7SMark Brown 
45839b8eab7SMark Brown /*
45939b8eab7SMark Brown  * The machine driver should call this from their set_bias_level; if there
46039b8eab7SMark Brown  * isn't one then this can just be set as the set_bias_level function.
46139b8eab7SMark Brown  */
wm9090_set_bias_level(struct snd_soc_component * component,enum snd_soc_bias_level level)4622822e66bSKuninori Morimoto static int wm9090_set_bias_level(struct snd_soc_component *component,
46339b8eab7SMark Brown 				 enum snd_soc_bias_level level)
46439b8eab7SMark Brown {
4652822e66bSKuninori Morimoto 	struct wm9090_priv *wm9090 = snd_soc_component_get_drvdata(component);
46639b8eab7SMark Brown 
46739b8eab7SMark Brown 	switch (level) {
46839b8eab7SMark Brown 	case SND_SOC_BIAS_ON:
46939b8eab7SMark Brown 		break;
47039b8eab7SMark Brown 
47139b8eab7SMark Brown 	case SND_SOC_BIAS_PREPARE:
4722822e66bSKuninori Morimoto 		snd_soc_component_update_bits(component, WM9090_ANTIPOP2, WM9090_VMID_ENA,
47339b8eab7SMark Brown 				    WM9090_VMID_ENA);
4742822e66bSKuninori Morimoto 		snd_soc_component_update_bits(component, WM9090_POWER_MANAGEMENT_1,
47539b8eab7SMark Brown 				    WM9090_BIAS_ENA |
47639b8eab7SMark Brown 				    WM9090_VMID_RES_MASK,
47739b8eab7SMark Brown 				    WM9090_BIAS_ENA |
47839b8eab7SMark Brown 				    1 << WM9090_VMID_RES_SHIFT);
47939b8eab7SMark Brown 		msleep(1);  /* Probably an overestimate */
48039b8eab7SMark Brown 		break;
48139b8eab7SMark Brown 
48239b8eab7SMark Brown 	case SND_SOC_BIAS_STANDBY:
4832822e66bSKuninori Morimoto 		if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
48439b8eab7SMark Brown 			/* Restore the register cache */
485ec2c0fecSMark Brown 			regcache_sync(wm9090->regmap);
48639b8eab7SMark Brown 		}
48739b8eab7SMark Brown 
48839b8eab7SMark Brown 		/* We keep VMID off during standby since the combination of
48939b8eab7SMark Brown 		 * ground referenced outputs and class D speaker mean that
49039b8eab7SMark Brown 		 * latency is not an issue.
49139b8eab7SMark Brown 		 */
4922822e66bSKuninori Morimoto 		snd_soc_component_update_bits(component, WM9090_POWER_MANAGEMENT_1,
49339b8eab7SMark Brown 				    WM9090_BIAS_ENA | WM9090_VMID_RES_MASK, 0);
4942822e66bSKuninori Morimoto 		snd_soc_component_update_bits(component, WM9090_ANTIPOP2,
49539b8eab7SMark Brown 				    WM9090_VMID_ENA, 0);
49639b8eab7SMark Brown 		break;
49739b8eab7SMark Brown 
49839b8eab7SMark Brown 	case SND_SOC_BIAS_OFF:
49939b8eab7SMark Brown 		break;
50039b8eab7SMark Brown 	}
50139b8eab7SMark Brown 
50239b8eab7SMark Brown 	return 0;
50339b8eab7SMark Brown }
50439b8eab7SMark Brown 
wm9090_probe(struct snd_soc_component * component)5052822e66bSKuninori Morimoto static int wm9090_probe(struct snd_soc_component *component)
50639b8eab7SMark Brown {
50739b8eab7SMark Brown 	/* Configure some defaults; they will be written out when we
50839b8eab7SMark Brown 	 * bring the bias up.
50939b8eab7SMark Brown 	 */
5102822e66bSKuninori Morimoto 	snd_soc_component_update_bits(component, WM9090_IN1_LINE_INPUT_A_VOLUME,
511a1b3b5eeSMark Brown 			    WM9090_IN1_VU | WM9090_IN1A_ZC,
512a1b3b5eeSMark Brown 			    WM9090_IN1_VU | WM9090_IN1A_ZC);
5132822e66bSKuninori Morimoto 	snd_soc_component_update_bits(component, WM9090_IN1_LINE_INPUT_B_VOLUME,
514a1b3b5eeSMark Brown 			    WM9090_IN1_VU | WM9090_IN1B_ZC,
515a1b3b5eeSMark Brown 			    WM9090_IN1_VU | WM9090_IN1B_ZC);
5162822e66bSKuninori Morimoto 	snd_soc_component_update_bits(component, WM9090_IN2_LINE_INPUT_A_VOLUME,
517a1b3b5eeSMark Brown 			    WM9090_IN2_VU | WM9090_IN2A_ZC,
518a1b3b5eeSMark Brown 			    WM9090_IN2_VU | WM9090_IN2A_ZC);
5192822e66bSKuninori Morimoto 	snd_soc_component_update_bits(component, WM9090_IN2_LINE_INPUT_B_VOLUME,
520a1b3b5eeSMark Brown 			    WM9090_IN2_VU | WM9090_IN2B_ZC,
521a1b3b5eeSMark Brown 			    WM9090_IN2_VU | WM9090_IN2B_ZC);
5222822e66bSKuninori Morimoto 	snd_soc_component_update_bits(component, WM9090_SPEAKER_VOLUME_LEFT,
523a1b3b5eeSMark Brown 			    WM9090_SPKOUT_VU | WM9090_SPKOUTL_ZC,
524a1b3b5eeSMark Brown 			    WM9090_SPKOUT_VU | WM9090_SPKOUTL_ZC);
5252822e66bSKuninori Morimoto 	snd_soc_component_update_bits(component, WM9090_LEFT_OUTPUT_VOLUME,
526a1b3b5eeSMark Brown 			    WM9090_HPOUT1_VU | WM9090_HPOUT1L_ZC,
527a1b3b5eeSMark Brown 			    WM9090_HPOUT1_VU | WM9090_HPOUT1L_ZC);
5282822e66bSKuninori Morimoto 	snd_soc_component_update_bits(component, WM9090_RIGHT_OUTPUT_VOLUME,
529a1b3b5eeSMark Brown 			    WM9090_HPOUT1_VU | WM9090_HPOUT1R_ZC,
530a1b3b5eeSMark Brown 			    WM9090_HPOUT1_VU | WM9090_HPOUT1R_ZC);
53139b8eab7SMark Brown 
5322822e66bSKuninori Morimoto 	snd_soc_component_update_bits(component, WM9090_CLOCKING_1,
533a1b3b5eeSMark Brown 			    WM9090_TOCLK_ENA, WM9090_TOCLK_ENA);
53439b8eab7SMark Brown 
5352822e66bSKuninori Morimoto 	wm9090_add_controls(component);
53639b8eab7SMark Brown 
53739b8eab7SMark Brown 	return 0;
538f0fba2adSLiam Girdwood }
53939b8eab7SMark Brown 
5402822e66bSKuninori Morimoto static const struct snd_soc_component_driver soc_component_dev_wm9090 = {
541f0fba2adSLiam Girdwood 	.probe			= wm9090_probe,
542f0fba2adSLiam Girdwood 	.set_bias_level		= wm9090_set_bias_level,
5432822e66bSKuninori Morimoto 	.suspend_bias_off	= 1,
5442822e66bSKuninori Morimoto 	.idle_bias_on		= 1,
5452822e66bSKuninori Morimoto 	.use_pmdown_time	= 1,
546f0fba2adSLiam Girdwood };
547f0fba2adSLiam Girdwood 
548ec2c0fecSMark Brown static const struct regmap_config wm9090_regmap = {
549ec2c0fecSMark Brown 	.reg_bits = 8,
550ec2c0fecSMark Brown 	.val_bits = 16,
551ec2c0fecSMark Brown 
552ec2c0fecSMark Brown 	.max_register = WM9090_MAX_REGISTER,
553ec2c0fecSMark Brown 	.volatile_reg = wm9090_volatile,
554ec2c0fecSMark Brown 	.readable_reg = wm9090_readable,
555ec2c0fecSMark Brown 
556b028b1efSMark Brown 	.cache_type = REGCACHE_MAPLE,
557ec2c0fecSMark Brown 	.reg_defaults = wm9090_reg_defaults,
558ec2c0fecSMark Brown 	.num_reg_defaults = ARRAY_SIZE(wm9090_reg_defaults),
559ec2c0fecSMark Brown };
560ec2c0fecSMark Brown 
561ec2c0fecSMark Brown 
wm9090_i2c_probe(struct i2c_client * i2c)56297b0b6e3SStephen Kitt static int wm9090_i2c_probe(struct i2c_client *i2c)
563f0fba2adSLiam Girdwood {
564f0fba2adSLiam Girdwood 	struct wm9090_priv *wm9090;
565391d9e4eSMark Brown 	unsigned int reg;
566f0fba2adSLiam Girdwood 	int ret;
567f0fba2adSLiam Girdwood 
568455b91bfSMark Brown 	wm9090 = devm_kzalloc(&i2c->dev, sizeof(*wm9090), GFP_KERNEL);
5690463585cSSachin Kamat 	if (!wm9090)
570f0fba2adSLiam Girdwood 		return -ENOMEM;
571f0fba2adSLiam Girdwood 
572be7eaf53SSachin Kamat 	wm9090->regmap = devm_regmap_init_i2c(i2c, &wm9090_regmap);
573ec2c0fecSMark Brown 	if (IS_ERR(wm9090->regmap)) {
574ec2c0fecSMark Brown 		ret = PTR_ERR(wm9090->regmap);
575ec2c0fecSMark Brown 		dev_err(&i2c->dev, "Failed to allocate regmap: %d\n", ret);
576ec2c0fecSMark Brown 		return ret;
577ec2c0fecSMark Brown 	}
578ec2c0fecSMark Brown 
579391d9e4eSMark Brown 	ret = regmap_read(wm9090->regmap, WM9090_SOFTWARE_RESET, &reg);
580391d9e4eSMark Brown 	if (ret < 0)
581be7eaf53SSachin Kamat 		return ret;
582be7eaf53SSachin Kamat 
583391d9e4eSMark Brown 	if (reg != 0x9093) {
584905b4195SAxel Lin 		dev_err(&i2c->dev, "Device is not a WM9090, ID=%x\n", reg);
585be7eaf53SSachin Kamat 		return -ENODEV;
586391d9e4eSMark Brown 	}
587391d9e4eSMark Brown 
588391d9e4eSMark Brown 	ret = regmap_write(wm9090->regmap, WM9090_SOFTWARE_RESET, 0);
589391d9e4eSMark Brown 	if (ret < 0)
590be7eaf53SSachin Kamat 		return ret;
591391d9e4eSMark Brown 
592f0fba2adSLiam Girdwood 	if (i2c->dev.platform_data)
593f0fba2adSLiam Girdwood 		memcpy(&wm9090->pdata, i2c->dev.platform_data,
594f0fba2adSLiam Girdwood 		       sizeof(wm9090->pdata));
595f0fba2adSLiam Girdwood 
596f0fba2adSLiam Girdwood 	i2c_set_clientdata(i2c, wm9090);
597f0fba2adSLiam Girdwood 
5982822e66bSKuninori Morimoto 	ret =  devm_snd_soc_register_component(&i2c->dev,
5992822e66bSKuninori Morimoto 			&soc_component_dev_wm9090,  NULL, 0);
600ec2c0fecSMark Brown 	if (ret != 0) {
601ec2c0fecSMark Brown 		dev_err(&i2c->dev, "Failed to register CODEC: %d\n", ret);
602be7eaf53SSachin Kamat 		return ret;
603ec2c0fecSMark Brown 	}
604ec2c0fecSMark Brown 
605ec2c0fecSMark Brown 	return 0;
60639b8eab7SMark Brown }
60739b8eab7SMark Brown 
60839b8eab7SMark Brown static const struct i2c_device_id wm9090_id[] = {
609*ba2a2c37SUwe Kleine-König 	{ "wm9090" },
610*ba2a2c37SUwe Kleine-König 	{ "wm9093" },
61139b8eab7SMark Brown 	{ }
61239b8eab7SMark Brown };
61339b8eab7SMark Brown MODULE_DEVICE_TABLE(i2c, wm9090_id);
61439b8eab7SMark Brown 
61539b8eab7SMark Brown static struct i2c_driver wm9090_i2c_driver = {
61639b8eab7SMark Brown 	.driver = {
617091edccfSMark Brown 		.name = "wm9090",
61839b8eab7SMark Brown 	},
6199abcd240SUwe Kleine-König 	.probe = wm9090_i2c_probe,
62039b8eab7SMark Brown 	.id_table = wm9090_id,
62139b8eab7SMark Brown };
62239b8eab7SMark Brown 
62396124c29SSachin Kamat module_i2c_driver(wm9090_i2c_driver);
62439b8eab7SMark Brown 
62539b8eab7SMark Brown MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
62639b8eab7SMark Brown MODULE_DESCRIPTION("WM9090 ASoC driver");
62739b8eab7SMark Brown MODULE_LICENSE("GPL");
628