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, ®);
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