diff options
Diffstat (limited to 'sound/soc/codecs/wm8903.c')
-rw-r--r-- | sound/soc/codecs/wm8903.c | 664 |
1 files changed, 388 insertions, 276 deletions
diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c index 4ad8ebd290e..c91fb2f99c1 100644 --- a/sound/soc/codecs/wm8903.c +++ b/sound/soc/codecs/wm8903.c @@ -23,8 +23,9 @@ #include <linux/gpio.h> #include <linux/pm.h> #include <linux/i2c.h> -#include <linux/platform_device.h> +#include <linux/regmap.h> #include <linux/slab.h> +#include <linux/irq.h> #include <sound/core.h> #include <sound/jack.h> #include <sound/pcm.h> @@ -38,184 +39,85 @@ #include "wm8903.h" /* Register defaults at reset */ -static u16 wm8903_reg_defaults[] = { - 0x8903, /* R0 - SW Reset and ID */ - 0x0000, /* R1 - Revision Number */ - 0x0000, /* R2 */ - 0x0000, /* R3 */ - 0x0018, /* R4 - Bias Control 0 */ - 0x0000, /* R5 - VMID Control 0 */ - 0x0000, /* R6 - Mic Bias Control 0 */ - 0x0000, /* R7 */ - 0x0001, /* R8 - Analogue DAC 0 */ - 0x0000, /* R9 */ - 0x0001, /* R10 - Analogue ADC 0 */ - 0x0000, /* R11 */ - 0x0000, /* R12 - Power Management 0 */ - 0x0000, /* R13 - Power Management 1 */ - 0x0000, /* R14 - Power Management 2 */ - 0x0000, /* R15 - Power Management 3 */ - 0x0000, /* R16 - Power Management 4 */ - 0x0000, /* R17 - Power Management 5 */ - 0x0000, /* R18 - Power Management 6 */ - 0x0000, /* R19 */ - 0x0400, /* R20 - Clock Rates 0 */ - 0x0D07, /* R21 - Clock Rates 1 */ - 0x0000, /* R22 - Clock Rates 2 */ - 0x0000, /* R23 */ - 0x0050, /* R24 - Audio Interface 0 */ - 0x0242, /* R25 - Audio Interface 1 */ - 0x0008, /* R26 - Audio Interface 2 */ - 0x0022, /* R27 - Audio Interface 3 */ - 0x0000, /* R28 */ - 0x0000, /* R29 */ - 0x00C0, /* R30 - DAC Digital Volume Left */ - 0x00C0, /* R31 - DAC Digital Volume Right */ - 0x0000, /* R32 - DAC Digital 0 */ - 0x0000, /* R33 - DAC Digital 1 */ - 0x0000, /* R34 */ - 0x0000, /* R35 */ - 0x00C0, /* R36 - ADC Digital Volume Left */ - 0x00C0, /* R37 - ADC Digital Volume Right */ - 0x0000, /* R38 - ADC Digital 0 */ - 0x0073, /* R39 - Digital Microphone 0 */ - 0x09BF, /* R40 - DRC 0 */ - 0x3241, /* R41 - DRC 1 */ - 0x0020, /* R42 - DRC 2 */ - 0x0000, /* R43 - DRC 3 */ - 0x0085, /* R44 - Analogue Left Input 0 */ - 0x0085, /* R45 - Analogue Right Input 0 */ - 0x0044, /* R46 - Analogue Left Input 1 */ - 0x0044, /* R47 - Analogue Right Input 1 */ - 0x0000, /* R48 */ - 0x0000, /* R49 */ - 0x0008, /* R50 - Analogue Left Mix 0 */ - 0x0004, /* R51 - Analogue Right Mix 0 */ - 0x0000, /* R52 - Analogue Spk Mix Left 0 */ - 0x0000, /* R53 - Analogue Spk Mix Left 1 */ - 0x0000, /* R54 - Analogue Spk Mix Right 0 */ - 0x0000, /* R55 - Analogue Spk Mix Right 1 */ - 0x0000, /* R56 */ - 0x002D, /* R57 - Analogue OUT1 Left */ - 0x002D, /* R58 - Analogue OUT1 Right */ - 0x0039, /* R59 - Analogue OUT2 Left */ - 0x0039, /* R60 - Analogue OUT2 Right */ - 0x0100, /* R61 */ - 0x0139, /* R62 - Analogue OUT3 Left */ - 0x0139, /* R63 - Analogue OUT3 Right */ - 0x0000, /* R64 */ - 0x0000, /* R65 - Analogue SPK Output Control 0 */ - 0x0000, /* R66 */ - 0x0010, /* R67 - DC Servo 0 */ - 0x0100, /* R68 */ - 0x00A4, /* R69 - DC Servo 2 */ - 0x0807, /* R70 */ - 0x0000, /* R71 */ - 0x0000, /* R72 */ - 0x0000, /* R73 */ - 0x0000, /* R74 */ - 0x0000, /* R75 */ - 0x0000, /* R76 */ - 0x0000, /* R77 */ - 0x0000, /* R78 */ - 0x000E, /* R79 */ - 0x0000, /* R80 */ - 0x0000, /* R81 */ - 0x0000, /* R82 */ - 0x0000, /* R83 */ - 0x0000, /* R84 */ - 0x0000, /* R85 */ - 0x0000, /* R86 */ - 0x0006, /* R87 */ - 0x0000, /* R88 */ - 0x0000, /* R89 */ - 0x0000, /* R90 - Analogue HP 0 */ - 0x0060, /* R91 */ - 0x0000, /* R92 */ - 0x0000, /* R93 */ - 0x0000, /* R94 - Analogue Lineout 0 */ - 0x0060, /* R95 */ - 0x0000, /* R96 */ - 0x0000, /* R97 */ - 0x0000, /* R98 - Charge Pump 0 */ - 0x1F25, /* R99 */ - 0x2B19, /* R100 */ - 0x01C0, /* R101 */ - 0x01EF, /* R102 */ - 0x2B00, /* R103 */ - 0x0000, /* R104 - Class W 0 */ - 0x01C0, /* R105 */ - 0x1C10, /* R106 */ - 0x0000, /* R107 */ - 0x0000, /* R108 - Write Sequencer 0 */ - 0x0000, /* R109 - Write Sequencer 1 */ - 0x0000, /* R110 - Write Sequencer 2 */ - 0x0000, /* R111 - Write Sequencer 3 */ - 0x0000, /* R112 - Write Sequencer 4 */ - 0x0000, /* R113 */ - 0x0000, /* R114 - Control Interface */ - 0x0000, /* R115 */ - 0x00A8, /* R116 - GPIO Control 1 */ - 0x00A8, /* R117 - GPIO Control 2 */ - 0x00A8, /* R118 - GPIO Control 3 */ - 0x0220, /* R119 - GPIO Control 4 */ - 0x01A0, /* R120 - GPIO Control 5 */ - 0x0000, /* R121 - Interrupt Status 1 */ - 0xFFFF, /* R122 - Interrupt Status 1 Mask */ - 0x0000, /* R123 - Interrupt Polarity 1 */ - 0x0000, /* R124 */ - 0x0003, /* R125 */ - 0x0000, /* R126 - Interrupt Control */ - 0x0000, /* R127 */ - 0x0005, /* R128 */ - 0x0000, /* R129 - Control Interface Test 1 */ - 0x0000, /* R130 */ - 0x0000, /* R131 */ - 0x0000, /* R132 */ - 0x0000, /* R133 */ - 0x0000, /* R134 */ - 0x03FF, /* R135 */ - 0x0007, /* R136 */ - 0x0040, /* R137 */ - 0x0000, /* R138 */ - 0x0000, /* R139 */ - 0x0000, /* R140 */ - 0x0000, /* R141 */ - 0x0000, /* R142 */ - 0x0000, /* R143 */ - 0x0000, /* R144 */ - 0x0000, /* R145 */ - 0x0000, /* R146 */ - 0x0000, /* R147 */ - 0x4000, /* R148 */ - 0x6810, /* R149 - Charge Pump Test 1 */ - 0x0004, /* R150 */ - 0x0000, /* R151 */ - 0x0000, /* R152 */ - 0x0000, /* R153 */ - 0x0000, /* R154 */ - 0x0000, /* R155 */ - 0x0000, /* R156 */ - 0x0000, /* R157 */ - 0x0000, /* R158 */ - 0x0000, /* R159 */ - 0x0000, /* R160 */ - 0x0000, /* R161 */ - 0x0000, /* R162 */ - 0x0000, /* R163 */ - 0x0028, /* R164 - Clock Rate Test 4 */ - 0x0004, /* R165 */ - 0x0000, /* R166 */ - 0x0060, /* R167 */ - 0x0000, /* R168 */ - 0x0000, /* R169 */ - 0x0000, /* R170 */ - 0x0000, /* R171 */ - 0x0000, /* R172 - Analogue Output Bias 0 */ +static const struct reg_default wm8903_reg_defaults[] = { + { 4, 0x0018 }, /* R4 - Bias Control 0 */ + { 5, 0x0000 }, /* R5 - VMID Control 0 */ + { 6, 0x0000 }, /* R6 - Mic Bias Control 0 */ + { 8, 0x0001 }, /* R8 - Analogue DAC 0 */ + { 10, 0x0001 }, /* R10 - Analogue ADC 0 */ + { 12, 0x0000 }, /* R12 - Power Management 0 */ + { 13, 0x0000 }, /* R13 - Power Management 1 */ + { 14, 0x0000 }, /* R14 - Power Management 2 */ + { 15, 0x0000 }, /* R15 - Power Management 3 */ + { 16, 0x0000 }, /* R16 - Power Management 4 */ + { 17, 0x0000 }, /* R17 - Power Management 5 */ + { 18, 0x0000 }, /* R18 - Power Management 6 */ + { 20, 0x0400 }, /* R20 - Clock Rates 0 */ + { 21, 0x0D07 }, /* R21 - Clock Rates 1 */ + { 22, 0x0000 }, /* R22 - Clock Rates 2 */ + { 24, 0x0050 }, /* R24 - Audio Interface 0 */ + { 25, 0x0242 }, /* R25 - Audio Interface 1 */ + { 26, 0x0008 }, /* R26 - Audio Interface 2 */ + { 27, 0x0022 }, /* R27 - Audio Interface 3 */ + { 30, 0x00C0 }, /* R30 - DAC Digital Volume Left */ + { 31, 0x00C0 }, /* R31 - DAC Digital Volume Right */ + { 32, 0x0000 }, /* R32 - DAC Digital 0 */ + { 33, 0x0000 }, /* R33 - DAC Digital 1 */ + { 36, 0x00C0 }, /* R36 - ADC Digital Volume Left */ + { 37, 0x00C0 }, /* R37 - ADC Digital Volume Right */ + { 38, 0x0000 }, /* R38 - ADC Digital 0 */ + { 39, 0x0073 }, /* R39 - Digital Microphone 0 */ + { 40, 0x09BF }, /* R40 - DRC 0 */ + { 41, 0x3241 }, /* R41 - DRC 1 */ + { 42, 0x0020 }, /* R42 - DRC 2 */ + { 43, 0x0000 }, /* R43 - DRC 3 */ + { 44, 0x0085 }, /* R44 - Analogue Left Input 0 */ + { 45, 0x0085 }, /* R45 - Analogue Right Input 0 */ + { 46, 0x0044 }, /* R46 - Analogue Left Input 1 */ + { 47, 0x0044 }, /* R47 - Analogue Right Input 1 */ + { 50, 0x0008 }, /* R50 - Analogue Left Mix 0 */ + { 51, 0x0004 }, /* R51 - Analogue Right Mix 0 */ + { 52, 0x0000 }, /* R52 - Analogue Spk Mix Left 0 */ + { 53, 0x0000 }, /* R53 - Analogue Spk Mix Left 1 */ + { 54, 0x0000 }, /* R54 - Analogue Spk Mix Right 0 */ + { 55, 0x0000 }, /* R55 - Analogue Spk Mix Right 1 */ + { 57, 0x002D }, /* R57 - Analogue OUT1 Left */ + { 58, 0x002D }, /* R58 - Analogue OUT1 Right */ + { 59, 0x0039 }, /* R59 - Analogue OUT2 Left */ + { 60, 0x0039 }, /* R60 - Analogue OUT2 Right */ + { 62, 0x0139 }, /* R62 - Analogue OUT3 Left */ + { 63, 0x0139 }, /* R63 - Analogue OUT3 Right */ + { 64, 0x0000 }, /* R65 - Analogue SPK Output Control 0 */ + { 67, 0x0010 }, /* R67 - DC Servo 0 */ + { 69, 0x00A4 }, /* R69 - DC Servo 2 */ + { 90, 0x0000 }, /* R90 - Analogue HP 0 */ + { 94, 0x0000 }, /* R94 - Analogue Lineout 0 */ + { 98, 0x0000 }, /* R98 - Charge Pump 0 */ + { 104, 0x0000 }, /* R104 - Class W 0 */ + { 108, 0x0000 }, /* R108 - Write Sequencer 0 */ + { 109, 0x0000 }, /* R109 - Write Sequencer 1 */ + { 110, 0x0000 }, /* R110 - Write Sequencer 2 */ + { 111, 0x0000 }, /* R111 - Write Sequencer 3 */ + { 112, 0x0000 }, /* R112 - Write Sequencer 4 */ + { 114, 0x0000 }, /* R114 - Control Interface */ + { 116, 0x00A8 }, /* R116 - GPIO Control 1 */ + { 117, 0x00A8 }, /* R117 - GPIO Control 2 */ + { 118, 0x00A8 }, /* R118 - GPIO Control 3 */ + { 119, 0x0220 }, /* R119 - GPIO Control 4 */ + { 120, 0x01A0 }, /* R120 - GPIO Control 5 */ + { 122, 0xFFFF }, /* R122 - Interrupt Status 1 Mask */ + { 123, 0x0000 }, /* R123 - Interrupt Polarity 1 */ + { 126, 0x0000 }, /* R126 - Interrupt Control */ + { 129, 0x0000 }, /* R129 - Control Interface Test 1 */ + { 149, 0x6810 }, /* R149 - Charge Pump Test 1 */ + { 164, 0x0028 }, /* R164 - Clock Rate Test 4 */ + { 172, 0x0000 }, /* R172 - Analogue Output Bias 0 */ }; struct wm8903_priv { + struct wm8903_platform_data *pdata; struct snd_soc_codec *codec; + struct regmap *regmap; int sysclk; int irq; @@ -240,7 +142,93 @@ struct wm8903_priv { #endif }; -static int wm8903_volatile_register(struct snd_soc_codec *codec, unsigned int reg) +static bool wm8903_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WM8903_SW_RESET_AND_ID: + case WM8903_REVISION_NUMBER: + case WM8903_BIAS_CONTROL_0: + case WM8903_VMID_CONTROL_0: + case WM8903_MIC_BIAS_CONTROL_0: + case WM8903_ANALOGUE_DAC_0: + case WM8903_ANALOGUE_ADC_0: + case WM8903_POWER_MANAGEMENT_0: + case WM8903_POWER_MANAGEMENT_1: + case WM8903_POWER_MANAGEMENT_2: + case WM8903_POWER_MANAGEMENT_3: + case WM8903_POWER_MANAGEMENT_4: + case WM8903_POWER_MANAGEMENT_5: + case WM8903_POWER_MANAGEMENT_6: + case WM8903_CLOCK_RATES_0: + case WM8903_CLOCK_RATES_1: + case WM8903_CLOCK_RATES_2: + case WM8903_AUDIO_INTERFACE_0: + case WM8903_AUDIO_INTERFACE_1: + case WM8903_AUDIO_INTERFACE_2: + case WM8903_AUDIO_INTERFACE_3: + case WM8903_DAC_DIGITAL_VOLUME_LEFT: + case WM8903_DAC_DIGITAL_VOLUME_RIGHT: + case WM8903_DAC_DIGITAL_0: + case WM8903_DAC_DIGITAL_1: + case WM8903_ADC_DIGITAL_VOLUME_LEFT: + case WM8903_ADC_DIGITAL_VOLUME_RIGHT: + case WM8903_ADC_DIGITAL_0: + case WM8903_DIGITAL_MICROPHONE_0: + case WM8903_DRC_0: + case WM8903_DRC_1: + case WM8903_DRC_2: + case WM8903_DRC_3: + case WM8903_ANALOGUE_LEFT_INPUT_0: + case WM8903_ANALOGUE_RIGHT_INPUT_0: + case WM8903_ANALOGUE_LEFT_INPUT_1: + case WM8903_ANALOGUE_RIGHT_INPUT_1: + case WM8903_ANALOGUE_LEFT_MIX_0: + case WM8903_ANALOGUE_RIGHT_MIX_0: + case WM8903_ANALOGUE_SPK_MIX_LEFT_0: + case WM8903_ANALOGUE_SPK_MIX_LEFT_1: + case WM8903_ANALOGUE_SPK_MIX_RIGHT_0: + case WM8903_ANALOGUE_SPK_MIX_RIGHT_1: + case WM8903_ANALOGUE_OUT1_LEFT: + case WM8903_ANALOGUE_OUT1_RIGHT: + case WM8903_ANALOGUE_OUT2_LEFT: + case WM8903_ANALOGUE_OUT2_RIGHT: + case WM8903_ANALOGUE_OUT3_LEFT: + case WM8903_ANALOGUE_OUT3_RIGHT: + case WM8903_ANALOGUE_SPK_OUTPUT_CONTROL_0: + case WM8903_DC_SERVO_0: + case WM8903_DC_SERVO_2: + case WM8903_DC_SERVO_READBACK_1: + case WM8903_DC_SERVO_READBACK_2: + case WM8903_DC_SERVO_READBACK_3: + case WM8903_DC_SERVO_READBACK_4: + case WM8903_ANALOGUE_HP_0: + case WM8903_ANALOGUE_LINEOUT_0: + case WM8903_CHARGE_PUMP_0: + case WM8903_CLASS_W_0: + case WM8903_WRITE_SEQUENCER_0: + case WM8903_WRITE_SEQUENCER_1: + case WM8903_WRITE_SEQUENCER_2: + case WM8903_WRITE_SEQUENCER_3: + case WM8903_WRITE_SEQUENCER_4: + case WM8903_CONTROL_INTERFACE: + case WM8903_GPIO_CONTROL_1: + case WM8903_GPIO_CONTROL_2: + case WM8903_GPIO_CONTROL_3: + case WM8903_GPIO_CONTROL_4: + case WM8903_GPIO_CONTROL_5: + case WM8903_INTERRUPT_STATUS_1: + case WM8903_INTERRUPT_STATUS_1_MASK: + case WM8903_INTERRUPT_POLARITY_1: + case WM8903_INTERRUPT_CONTROL: + case WM8903_CLOCK_RATE_TEST_4: + case WM8903_ANALOGUE_OUTPUT_BIAS_0: + return true; + default: + return false; + } +} + +static bool wm8903_volatile_register(struct device *dev, unsigned int reg) { switch (reg) { case WM8903_SW_RESET_AND_ID: @@ -258,13 +246,6 @@ static int wm8903_volatile_register(struct snd_soc_codec *codec, unsigned int re } } -static void wm8903_reset(struct snd_soc_codec *codec) -{ - snd_soc_write(codec, WM8903_SW_RESET_AND_ID, 0); - memcpy(codec->reg_cache, wm8903_reg_defaults, - sizeof(wm8903_reg_defaults)); -} - static int wm8903_cp_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -839,7 +820,7 @@ SND_SOC_DAPM_OUTPUT("LON"), SND_SOC_DAPM_OUTPUT("ROP"), SND_SOC_DAPM_OUTPUT("RON"), -SND_SOC_DAPM_MICBIAS("Mic Bias", WM8903_MIC_BIAS_CONTROL_0, 0, 0), +SND_SOC_DAPM_SUPPLY("MICBIAS", WM8903_MIC_BIAS_CONTROL_0, 0, 0, NULL, 0), SND_SOC_DAPM_MUX("Left Input Mux", SND_SOC_NOPM, 0, 0, &linput_mux), SND_SOC_DAPM_MUX("Left Input Inverting Mux", SND_SOC_NOPM, 0, 0, @@ -948,7 +929,7 @@ SND_SOC_DAPM_SUPPLY("CLK_SYS", WM8903_CLOCK_RATES_2, 2, 0, NULL, 0), static const struct snd_soc_dapm_route wm8903_intercon[] = { { "CLK_DSP", NULL, "CLK_SYS" }, - { "Mic Bias", NULL, "CLK_SYS" }, + { "MICBIAS", NULL, "CLK_SYS" }, { "HPL_DCS", NULL, "CLK_SYS" }, { "HPR_DCS", NULL, "CLK_SYS" }, { "LINEOUTL_DCS", NULL, "CLK_SYS" }, @@ -1732,7 +1713,7 @@ static irqreturn_t wm8903_irq(int irq, void *data) SNDRV_PCM_FMTBIT_S20_3LE |\ SNDRV_PCM_FMTBIT_S24_LE) -static struct snd_soc_dai_ops wm8903_dai_ops = { +static const struct snd_soc_dai_ops wm8903_dai_ops = { .hw_params = wm8903_hw_params, .digital_mute = wm8903_digital_mute, .set_fmt = wm8903_set_dai_fmt, @@ -1759,7 +1740,7 @@ static struct snd_soc_dai_driver wm8903_dai = { .symmetric_rates = 1, }; -static int wm8903_suspend(struct snd_soc_codec *codec, pm_message_t state) +static int wm8903_suspend(struct snd_soc_codec *codec) { wm8903_set_bias_level(codec, SND_SOC_BIAS_OFF); @@ -1768,23 +1749,11 @@ static int wm8903_suspend(struct snd_soc_codec *codec, pm_message_t state) static int wm8903_resume(struct snd_soc_codec *codec) { - int i; - u16 *reg_cache = codec->reg_cache; - u16 *tmp_cache = kmemdup(reg_cache, sizeof(wm8903_reg_defaults), - GFP_KERNEL); + struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec); - /* Bring the codec back up to standby first to minimise pop/clicks */ - wm8903_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + regcache_sync(wm8903->regmap); - /* Sync back everything else */ - if (tmp_cache) { - for (i = 2; i < ARRAY_SIZE(wm8903_reg_defaults); i++) - if (tmp_cache[i] != reg_cache[i]) - snd_soc_write(codec, i, tmp_cache[i]); - kfree(tmp_cache); - } else { - dev_err(codec->dev, "Failed to allocate temporary cache\n"); - } + wm8903_set_bias_level(codec, SND_SOC_BIAS_STANDBY); return 0; } @@ -1808,13 +1777,18 @@ static int wm8903_gpio_direction_in(struct gpio_chip *chip, unsigned offset) struct wm8903_priv *wm8903 = gpio_to_wm8903(chip); struct snd_soc_codec *codec = wm8903->codec; unsigned int mask, val; + int ret; mask = WM8903_GP1_FN_MASK | WM8903_GP1_DIR_MASK; val = (WM8903_GPn_FN_GPIO_INPUT << WM8903_GP1_FN_SHIFT) | WM8903_GP1_DIR; - return snd_soc_update_bits(codec, WM8903_GPIO_CONTROL_1 + offset, - mask, val); + ret = snd_soc_update_bits(codec, WM8903_GPIO_CONTROL_1 + offset, + mask, val); + if (ret < 0) + return ret; + + return 0; } static int wm8903_gpio_get(struct gpio_chip *chip, unsigned offset) @@ -1834,13 +1808,18 @@ static int wm8903_gpio_direction_out(struct gpio_chip *chip, struct wm8903_priv *wm8903 = gpio_to_wm8903(chip); struct snd_soc_codec *codec = wm8903->codec; unsigned int mask, val; + int ret; mask = WM8903_GP1_FN_MASK | WM8903_GP1_DIR_MASK | WM8903_GP1_LVL_MASK; val = (WM8903_GPn_FN_GPIO_OUTPUT << WM8903_GP1_FN_SHIFT) | (value << WM8903_GP2_LVL_SHIFT); - return snd_soc_update_bits(codec, WM8903_GPIO_CONTROL_1 + offset, - mask, val); + ret = snd_soc_update_bits(codec, WM8903_GPIO_CONTROL_1 + offset, + mask, val); + if (ret < 0) + return ret; + + return 0; } static void wm8903_gpio_set(struct gpio_chip *chip, unsigned offset, int value) @@ -1867,14 +1846,14 @@ static struct gpio_chip wm8903_template_chip = { static void wm8903_init_gpio(struct snd_soc_codec *codec) { struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec); - struct wm8903_platform_data *pdata = dev_get_platdata(codec->dev); + struct wm8903_platform_data *pdata = wm8903->pdata; int ret; wm8903->gpio_chip = wm8903_template_chip; wm8903->gpio_chip.ngpio = WM8903_NUM_GPIO; wm8903->gpio_chip.dev = codec->dev; - if (pdata && pdata->gpio_base) + if (pdata->gpio_base) wm8903->gpio_chip.base = pdata->gpio_base; else wm8903->gpio_chip.base = -1; @@ -1905,78 +1884,65 @@ static void wm8903_free_gpio(struct snd_soc_codec *codec) static int wm8903_probe(struct snd_soc_codec *codec) { - struct wm8903_platform_data *pdata = dev_get_platdata(codec->dev); struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec); + struct wm8903_platform_data *pdata = wm8903->pdata; int ret, i; int trigger, irq_pol; u16 val; + bool mic_gpio = false; wm8903->codec = codec; + codec->control_data = wm8903->regmap; - ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_I2C); + ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_REGMAP); if (ret != 0) { dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); return ret; } - val = snd_soc_read(codec, WM8903_SW_RESET_AND_ID); - if (val != wm8903_reg_defaults[WM8903_SW_RESET_AND_ID]) { - dev_err(codec->dev, - "Device with ID register %x is not a WM8903\n", val); - return -ENODEV; - } - - val = snd_soc_read(codec, WM8903_REVISION_NUMBER); - dev_info(codec->dev, "WM8903 revision %c\n", - (val & WM8903_CHIP_REV_MASK) + 'A'); + /* Set up GPIOs, detect if any are MIC detect outputs */ + for (i = 0; i < ARRAY_SIZE(pdata->gpio_cfg); i++) { + if ((!pdata->gpio_cfg[i]) || + (pdata->gpio_cfg[i] > WM8903_GPIO_CONFIG_ZERO)) + continue; - wm8903_reset(codec); + snd_soc_write(codec, WM8903_GPIO_CONTROL_1 + i, + pdata->gpio_cfg[i] & 0x7fff); - /* Set up GPIOs and microphone detection */ - if (pdata) { - bool mic_gpio = false; + val = (pdata->gpio_cfg[i] & WM8903_GP1_FN_MASK) + >> WM8903_GP1_FN_SHIFT; - for (i = 0; i < ARRAY_SIZE(pdata->gpio_cfg); i++) { - if (pdata->gpio_cfg[i] == WM8903_GPIO_NO_CONFIG) - continue; - - snd_soc_write(codec, WM8903_GPIO_CONTROL_1 + i, - pdata->gpio_cfg[i] & 0xffff); - - val = (pdata->gpio_cfg[i] & WM8903_GP1_FN_MASK) - >> WM8903_GP1_FN_SHIFT; - - switch (val) { - case WM8903_GPn_FN_MICBIAS_CURRENT_DETECT: - case WM8903_GPn_FN_MICBIAS_SHORT_DETECT: - mic_gpio = true; - break; - default: - break; - } + switch (val) { + case WM8903_GPn_FN_MICBIAS_CURRENT_DETECT: + case WM8903_GPn_FN_MICBIAS_SHORT_DETECT: + mic_gpio = true; + break; + default: + break; } + } + + /* Set up microphone detection */ + snd_soc_write(codec, WM8903_MIC_BIAS_CONTROL_0, + pdata->micdet_cfg); - snd_soc_write(codec, WM8903_MIC_BIAS_CONTROL_0, - pdata->micdet_cfg); + /* Microphone detection needs the WSEQ clock */ + if (pdata->micdet_cfg) + snd_soc_update_bits(codec, WM8903_WRITE_SEQUENCER_0, + WM8903_WSEQ_ENA, WM8903_WSEQ_ENA); - /* Microphone detection needs the WSEQ clock */ - if (pdata->micdet_cfg) - snd_soc_update_bits(codec, WM8903_WRITE_SEQUENCER_0, - WM8903_WSEQ_ENA, WM8903_WSEQ_ENA); + /* If microphone detection is enabled by pdata but + * detected via IRQ then interrupts can be lost before + * the machine driver has set up microphone detection + * IRQs as the IRQs are clear on read. The detection + * will be enabled when the machine driver configures. + */ + WARN_ON(!mic_gpio && (pdata->micdet_cfg & WM8903_MICDET_ENA)); - /* If microphone detection is enabled by pdata but - * detected via IRQ then interrupts can be lost before - * the machine driver has set up microphone detection - * IRQs as the IRQs are clear on read. The detection - * will be enabled when the machine driver configures. - */ - WARN_ON(!mic_gpio && (pdata->micdet_cfg & WM8903_MICDET_ENA)); + wm8903->mic_delay = pdata->micdet_delay; - wm8903->mic_delay = pdata->micdet_delay; - } - if (wm8903->irq) { - if (pdata && pdata->irq_active_low) { + if (pdata->irq_active_low) { trigger = IRQF_TRIGGER_LOW; irq_pol = WM8903_IRQ_POL; } else { @@ -2035,9 +2001,6 @@ static int wm8903_probe(struct snd_soc_codec *codec) WM8903_DAC_MUTEMODE | WM8903_DAC_MUTE, WM8903_DAC_MUTEMODE | WM8903_DAC_MUTE); - snd_soc_add_controls(codec, wm8903_snd_controls, - ARRAY_SIZE(wm8903_snd_controls)); - wm8903_init_gpio(codec); return ret; @@ -2062,45 +2025,198 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8903 = { .suspend = wm8903_suspend, .resume = wm8903_resume, .set_bias_level = wm8903_set_bias_level, - .reg_cache_size = ARRAY_SIZE(wm8903_reg_defaults), - .reg_word_size = sizeof(u16), - .reg_cache_default = wm8903_reg_defaults, - .volatile_register = wm8903_volatile_register, .seq_notifier = wm8903_seq_notifier, + .controls = wm8903_snd_controls, + .num_controls = ARRAY_SIZE(wm8903_snd_controls), .dapm_widgets = wm8903_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(wm8903_dapm_widgets), .dapm_routes = wm8903_intercon, .num_dapm_routes = ARRAY_SIZE(wm8903_intercon), }; -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +static const struct regmap_config wm8903_regmap = { + .reg_bits = 8, + .val_bits = 16, + + .max_register = WM8903_MAX_REGISTER, + .volatile_reg = wm8903_volatile_register, + .readable_reg = wm8903_readable_register, + + .cache_type = REGCACHE_RBTREE, + .reg_defaults = wm8903_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(wm8903_reg_defaults), +}; + +static int wm8903_set_pdata_irq_trigger(struct i2c_client *i2c, + struct wm8903_platform_data *pdata) +{ + struct irq_data *irq_data = irq_get_irq_data(i2c->irq); + if (!irq_data) { + dev_err(&i2c->dev, "Invalid IRQ: %d\n", + i2c->irq); + return -EINVAL; + } + + switch (irqd_get_trigger_type(irq_data)) { + case IRQ_TYPE_NONE: + default: + /* + * We assume the controller imposes no restrictions, + * so we are able to select active-high + */ + /* Fall-through */ + case IRQ_TYPE_LEVEL_HIGH: + pdata->irq_active_low = false; + break; + case IRQ_TYPE_LEVEL_LOW: + pdata->irq_active_low = true; + break; + } + + return 0; +} + +static int wm8903_set_pdata_from_of(struct i2c_client *i2c, + struct wm8903_platform_data *pdata) +{ + const struct device_node *np = i2c->dev.of_node; + u32 val32; + int i; + + if (of_property_read_u32(np, "micdet-cfg", &val32) >= 0) + pdata->micdet_cfg = val32; + + if (of_property_read_u32(np, "micdet-delay", &val32) >= 0) + pdata->micdet_delay = val32; + + if (of_property_read_u32_array(np, "gpio-cfg", pdata->gpio_cfg, + ARRAY_SIZE(pdata->gpio_cfg)) >= 0) { + /* + * In device tree: 0 means "write 0", + * 0xffffffff means "don't touch". + * + * In platform data: 0 means "don't touch", + * 0x8000 means "write 0". + * + * Note: WM8903_GPIO_CONFIG_ZERO == 0x8000. + * + * Convert from DT to pdata representation here, + * so no other code needs to change. + */ + for (i = 0; i < ARRAY_SIZE(pdata->gpio_cfg); i++) { + if (pdata->gpio_cfg[i] == 0) { + pdata->gpio_cfg[i] = WM8903_GPIO_CONFIG_ZERO; + } else if (pdata->gpio_cfg[i] == 0xffffffff) { + pdata->gpio_cfg[i] = 0; + } else if (pdata->gpio_cfg[i] > 0x7fff) { + dev_err(&i2c->dev, "Invalid gpio-cfg[%d] %x\n", + i, pdata->gpio_cfg[i]); + return -EINVAL; + } + } + } + + return 0; +} + static __devinit int wm8903_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { + struct wm8903_platform_data *pdata = dev_get_platdata(&i2c->dev); struct wm8903_priv *wm8903; + unsigned int val; int ret; - wm8903 = kzalloc(sizeof(struct wm8903_priv), GFP_KERNEL); + wm8903 = devm_kzalloc(&i2c->dev, sizeof(struct wm8903_priv), + GFP_KERNEL); if (wm8903 == NULL) return -ENOMEM; + wm8903->regmap = regmap_init_i2c(i2c, &wm8903_regmap); + if (IS_ERR(wm8903->regmap)) { + ret = PTR_ERR(wm8903->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + i2c_set_clientdata(i2c, wm8903); wm8903->irq = i2c->irq; + /* If no platform data was supplied, create storage for defaults */ + if (pdata) { + wm8903->pdata = pdata; + } else { + wm8903->pdata = devm_kzalloc(&i2c->dev, + sizeof(struct wm8903_platform_data), + GFP_KERNEL); + if (wm8903->pdata == NULL) { + dev_err(&i2c->dev, "Failed to allocate pdata\n"); + return -ENOMEM; + } + + if (i2c->irq) { + ret = wm8903_set_pdata_irq_trigger(i2c, wm8903->pdata); + if (ret != 0) + return ret; + } + + if (i2c->dev.of_node) { + ret = wm8903_set_pdata_from_of(i2c, wm8903->pdata); + if (ret != 0) + return ret; + } + } + + ret = regmap_read(wm8903->regmap, WM8903_SW_RESET_AND_ID, &val); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to read chip ID: %d\n", ret); + goto err; + } + if (val != 0x8903) { + dev_err(&i2c->dev, "Device with ID %x is not a WM8903\n", val); + ret = -ENODEV; + goto err; + } + + ret = regmap_read(wm8903->regmap, WM8903_REVISION_NUMBER, &val); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to read chip revision: %d\n", ret); + goto err; + } + dev_info(&i2c->dev, "WM8903 revision %c\n", + (val & WM8903_CHIP_REV_MASK) + 'A'); + + /* Reset the device */ + regmap_write(wm8903->regmap, WM8903_SW_RESET_AND_ID, 0x8903); + ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_wm8903, &wm8903_dai, 1); - if (ret < 0) - kfree(wm8903); + if (ret != 0) + goto err; + + return 0; +err: + regmap_exit(wm8903->regmap); return ret; } static __devexit int wm8903_i2c_remove(struct i2c_client *client) { + struct wm8903_priv *wm8903 = i2c_get_clientdata(client); + + regmap_exit(wm8903->regmap); snd_soc_unregister_codec(&client->dev); - kfree(i2c_get_clientdata(client)); + return 0; } +static const struct of_device_id wm8903_of_match[] = { + { .compatible = "wlf,wm8903", }, + {}, +}; +MODULE_DEVICE_TABLE(of, wm8903_of_match); + static const struct i2c_device_id wm8903_i2c_id[] = { { "wm8903", 0 }, { } @@ -2111,32 +2227,28 @@ static struct i2c_driver wm8903_i2c_driver = { .driver = { .name = "wm8903", .owner = THIS_MODULE, + .of_match_table = wm8903_of_match, }, .probe = wm8903_i2c_probe, .remove = __devexit_p(wm8903_i2c_remove), .id_table = wm8903_i2c_id, }; -#endif static int __init wm8903_modinit(void) { int ret = 0; -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) ret = i2c_add_driver(&wm8903_i2c_driver); if (ret != 0) { printk(KERN_ERR "Failed to register wm8903 I2C driver: %d\n", ret); } -#endif return ret; } module_init(wm8903_modinit); static void __exit wm8903_exit(void) { -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) i2c_del_driver(&wm8903_i2c_driver); -#endif } module_exit(wm8903_exit); |