1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 274dc55edSMark Brown /* 374dc55edSMark Brown * wm8961.c -- WM8961 ALSA SoC Audio driver 474dc55edSMark Brown * 5656baaebSMark Brown * Copyright 2009-10 Wolfson Microelectronics, plc 6656baaebSMark Brown * 774dc55edSMark Brown * Author: Mark Brown 874dc55edSMark Brown * 974dc55edSMark Brown * Currently unimplemented features: 1074dc55edSMark Brown * - ALC 1174dc55edSMark Brown */ 1274dc55edSMark Brown 1374dc55edSMark Brown #include <linux/module.h> 1474dc55edSMark Brown #include <linux/moduleparam.h> 1574dc55edSMark Brown #include <linux/init.h> 1674dc55edSMark Brown #include <linux/delay.h> 1774dc55edSMark Brown #include <linux/pm.h> 1874dc55edSMark Brown #include <linux/i2c.h> 1935ecf7cdSMark Brown #include <linux/regmap.h> 205a0e3ad6STejun Heo #include <linux/slab.h> 2174dc55edSMark Brown #include <sound/core.h> 2274dc55edSMark Brown #include <sound/pcm.h> 2374dc55edSMark Brown #include <sound/pcm_params.h> 2474dc55edSMark Brown #include <sound/soc.h> 2574dc55edSMark Brown #include <sound/initval.h> 2674dc55edSMark Brown #include <sound/tlv.h> 2774dc55edSMark Brown 2874dc55edSMark Brown #include "wm8961.h" 2974dc55edSMark Brown 3074dc55edSMark Brown #define WM8961_MAX_REGISTER 0xFC 3174dc55edSMark Brown 3235ecf7cdSMark Brown static const struct reg_default wm8961_reg_defaults[] = { 3335ecf7cdSMark Brown { 0, 0x009F }, /* R0 - Left Input volume */ 3435ecf7cdSMark Brown { 1, 0x009F }, /* R1 - Right Input volume */ 3535ecf7cdSMark Brown { 2, 0x0000 }, /* R2 - LOUT1 volume */ 3635ecf7cdSMark Brown { 3, 0x0000 }, /* R3 - ROUT1 volume */ 3735ecf7cdSMark Brown { 4, 0x0020 }, /* R4 - Clocking1 */ 3835ecf7cdSMark Brown { 5, 0x0008 }, /* R5 - ADC & DAC Control 1 */ 3935ecf7cdSMark Brown { 6, 0x0000 }, /* R6 - ADC & DAC Control 2 */ 4035ecf7cdSMark Brown { 7, 0x000A }, /* R7 - Audio Interface 0 */ 4135ecf7cdSMark Brown { 8, 0x01F4 }, /* R8 - Clocking2 */ 4235ecf7cdSMark Brown { 9, 0x0000 }, /* R9 - Audio Interface 1 */ 4335ecf7cdSMark Brown { 10, 0x00FF }, /* R10 - Left DAC volume */ 4435ecf7cdSMark Brown { 11, 0x00FF }, /* R11 - Right DAC volume */ 4535ecf7cdSMark Brown 4635ecf7cdSMark Brown { 14, 0x0040 }, /* R14 - Audio Interface 2 */ 4735ecf7cdSMark Brown 4835ecf7cdSMark Brown { 17, 0x007B }, /* R17 - ALC1 */ 4935ecf7cdSMark Brown { 18, 0x0000 }, /* R18 - ALC2 */ 5035ecf7cdSMark Brown { 19, 0x0032 }, /* R19 - ALC3 */ 5135ecf7cdSMark Brown { 20, 0x0000 }, /* R20 - Noise Gate */ 5235ecf7cdSMark Brown { 21, 0x00C0 }, /* R21 - Left ADC volume */ 5335ecf7cdSMark Brown { 22, 0x00C0 }, /* R22 - Right ADC volume */ 5435ecf7cdSMark Brown { 23, 0x0120 }, /* R23 - Additional control(1) */ 5535ecf7cdSMark Brown { 24, 0x0000 }, /* R24 - Additional control(2) */ 5635ecf7cdSMark Brown { 25, 0x0000 }, /* R25 - Pwr Mgmt (1) */ 5735ecf7cdSMark Brown { 26, 0x0000 }, /* R26 - Pwr Mgmt (2) */ 5835ecf7cdSMark Brown { 27, 0x0000 }, /* R27 - Additional Control (3) */ 5935ecf7cdSMark Brown { 28, 0x0000 }, /* R28 - Anti-pop */ 6035ecf7cdSMark Brown 6135ecf7cdSMark Brown { 30, 0x005F }, /* R30 - Clocking 3 */ 6235ecf7cdSMark Brown 6335ecf7cdSMark Brown { 32, 0x0000 }, /* R32 - ADCL signal path */ 6435ecf7cdSMark Brown { 33, 0x0000 }, /* R33 - ADCR signal path */ 6535ecf7cdSMark Brown 6635ecf7cdSMark Brown { 40, 0x0000 }, /* R40 - LOUT2 volume */ 6735ecf7cdSMark Brown { 41, 0x0000 }, /* R41 - ROUT2 volume */ 6835ecf7cdSMark Brown 6935ecf7cdSMark Brown { 47, 0x0000 }, /* R47 - Pwr Mgmt (3) */ 7035ecf7cdSMark Brown { 48, 0x0023 }, /* R48 - Additional Control (4) */ 7135ecf7cdSMark Brown { 49, 0x0000 }, /* R49 - Class D Control 1 */ 7235ecf7cdSMark Brown 7335ecf7cdSMark Brown { 51, 0x0003 }, /* R51 - Class D Control 2 */ 7435ecf7cdSMark Brown 7535ecf7cdSMark Brown { 56, 0x0106 }, /* R56 - Clocking 4 */ 7635ecf7cdSMark Brown { 57, 0x0000 }, /* R57 - DSP Sidetone 0 */ 7735ecf7cdSMark Brown { 58, 0x0000 }, /* R58 - DSP Sidetone 1 */ 7835ecf7cdSMark Brown 7935ecf7cdSMark Brown { 60, 0x0000 }, /* R60 - DC Servo 0 */ 8035ecf7cdSMark Brown { 61, 0x0000 }, /* R61 - DC Servo 1 */ 8135ecf7cdSMark Brown 8235ecf7cdSMark Brown { 63, 0x015E }, /* R63 - DC Servo 3 */ 8335ecf7cdSMark Brown 8435ecf7cdSMark Brown { 65, 0x0010 }, /* R65 - DC Servo 5 */ 8535ecf7cdSMark Brown 8635ecf7cdSMark Brown { 68, 0x0003 }, /* R68 - Analogue PGA Bias */ 8735ecf7cdSMark Brown { 69, 0x0000 }, /* R69 - Analogue HP 0 */ 8835ecf7cdSMark Brown 8935ecf7cdSMark Brown { 71, 0x01FB }, /* R71 - Analogue HP 2 */ 9035ecf7cdSMark Brown { 72, 0x0000 }, /* R72 - Charge Pump 1 */ 9135ecf7cdSMark Brown 9235ecf7cdSMark Brown { 82, 0x0000 }, /* R82 - Charge Pump B */ 9335ecf7cdSMark Brown 9435ecf7cdSMark Brown { 87, 0x0000 }, /* R87 - Write Sequencer 1 */ 9535ecf7cdSMark Brown { 88, 0x0000 }, /* R88 - Write Sequencer 2 */ 9635ecf7cdSMark Brown { 89, 0x0000 }, /* R89 - Write Sequencer 3 */ 9735ecf7cdSMark Brown { 90, 0x0000 }, /* R90 - Write Sequencer 4 */ 9835ecf7cdSMark Brown { 91, 0x0000 }, /* R91 - Write Sequencer 5 */ 9935ecf7cdSMark Brown { 92, 0x0000 }, /* R92 - Write Sequencer 6 */ 10035ecf7cdSMark Brown { 93, 0x0000 }, /* R93 - Write Sequencer 7 */ 10135ecf7cdSMark Brown 10235ecf7cdSMark Brown { 252, 0x0001 }, /* R252 - General test 1 */ 10374dc55edSMark Brown }; 10474dc55edSMark Brown 10574dc55edSMark Brown struct wm8961_priv { 10635ecf7cdSMark Brown struct regmap *regmap; 10774dc55edSMark Brown int sysclk; 10874dc55edSMark Brown }; 10974dc55edSMark Brown 11035ecf7cdSMark Brown static bool wm8961_volatile(struct device *dev, unsigned int reg) 11174dc55edSMark Brown { 11274dc55edSMark Brown switch (reg) { 1138d50e447SMark Brown case WM8961_SOFTWARE_RESET: 11474dc55edSMark Brown case WM8961_WRITE_SEQUENCER_7: 11574dc55edSMark Brown case WM8961_DC_SERVO_1: 11635ecf7cdSMark Brown return true; 11774dc55edSMark Brown 11874dc55edSMark Brown default: 11935ecf7cdSMark Brown return false; 12035ecf7cdSMark Brown } 12135ecf7cdSMark Brown } 12235ecf7cdSMark Brown 12335ecf7cdSMark Brown static bool wm8961_readable(struct device *dev, unsigned int reg) 12435ecf7cdSMark Brown { 12535ecf7cdSMark Brown switch (reg) { 12635ecf7cdSMark Brown case WM8961_LEFT_INPUT_VOLUME: 12735ecf7cdSMark Brown case WM8961_RIGHT_INPUT_VOLUME: 12835ecf7cdSMark Brown case WM8961_LOUT1_VOLUME: 12935ecf7cdSMark Brown case WM8961_ROUT1_VOLUME: 13035ecf7cdSMark Brown case WM8961_CLOCKING1: 13135ecf7cdSMark Brown case WM8961_ADC_DAC_CONTROL_1: 13235ecf7cdSMark Brown case WM8961_ADC_DAC_CONTROL_2: 13335ecf7cdSMark Brown case WM8961_AUDIO_INTERFACE_0: 13435ecf7cdSMark Brown case WM8961_CLOCKING2: 13535ecf7cdSMark Brown case WM8961_AUDIO_INTERFACE_1: 13635ecf7cdSMark Brown case WM8961_LEFT_DAC_VOLUME: 13735ecf7cdSMark Brown case WM8961_RIGHT_DAC_VOLUME: 13835ecf7cdSMark Brown case WM8961_AUDIO_INTERFACE_2: 13935ecf7cdSMark Brown case WM8961_SOFTWARE_RESET: 14035ecf7cdSMark Brown case WM8961_ALC1: 14135ecf7cdSMark Brown case WM8961_ALC2: 14235ecf7cdSMark Brown case WM8961_ALC3: 14335ecf7cdSMark Brown case WM8961_NOISE_GATE: 14435ecf7cdSMark Brown case WM8961_LEFT_ADC_VOLUME: 14535ecf7cdSMark Brown case WM8961_RIGHT_ADC_VOLUME: 14635ecf7cdSMark Brown case WM8961_ADDITIONAL_CONTROL_1: 14735ecf7cdSMark Brown case WM8961_ADDITIONAL_CONTROL_2: 14835ecf7cdSMark Brown case WM8961_PWR_MGMT_1: 14935ecf7cdSMark Brown case WM8961_PWR_MGMT_2: 15035ecf7cdSMark Brown case WM8961_ADDITIONAL_CONTROL_3: 15135ecf7cdSMark Brown case WM8961_ANTI_POP: 15235ecf7cdSMark Brown case WM8961_CLOCKING_3: 15335ecf7cdSMark Brown case WM8961_ADCL_SIGNAL_PATH: 15435ecf7cdSMark Brown case WM8961_ADCR_SIGNAL_PATH: 15535ecf7cdSMark Brown case WM8961_LOUT2_VOLUME: 15635ecf7cdSMark Brown case WM8961_ROUT2_VOLUME: 15735ecf7cdSMark Brown case WM8961_PWR_MGMT_3: 15835ecf7cdSMark Brown case WM8961_ADDITIONAL_CONTROL_4: 15935ecf7cdSMark Brown case WM8961_CLASS_D_CONTROL_1: 16035ecf7cdSMark Brown case WM8961_CLASS_D_CONTROL_2: 16135ecf7cdSMark Brown case WM8961_CLOCKING_4: 16235ecf7cdSMark Brown case WM8961_DSP_SIDETONE_0: 16335ecf7cdSMark Brown case WM8961_DSP_SIDETONE_1: 16435ecf7cdSMark Brown case WM8961_DC_SERVO_0: 16535ecf7cdSMark Brown case WM8961_DC_SERVO_1: 16635ecf7cdSMark Brown case WM8961_DC_SERVO_3: 16735ecf7cdSMark Brown case WM8961_DC_SERVO_5: 16835ecf7cdSMark Brown case WM8961_ANALOGUE_PGA_BIAS: 16935ecf7cdSMark Brown case WM8961_ANALOGUE_HP_0: 17035ecf7cdSMark Brown case WM8961_ANALOGUE_HP_2: 17135ecf7cdSMark Brown case WM8961_CHARGE_PUMP_1: 17235ecf7cdSMark Brown case WM8961_CHARGE_PUMP_B: 17335ecf7cdSMark Brown case WM8961_WRITE_SEQUENCER_1: 17435ecf7cdSMark Brown case WM8961_WRITE_SEQUENCER_2: 17535ecf7cdSMark Brown case WM8961_WRITE_SEQUENCER_3: 17635ecf7cdSMark Brown case WM8961_WRITE_SEQUENCER_4: 17735ecf7cdSMark Brown case WM8961_WRITE_SEQUENCER_5: 17835ecf7cdSMark Brown case WM8961_WRITE_SEQUENCER_6: 17935ecf7cdSMark Brown case WM8961_WRITE_SEQUENCER_7: 18035ecf7cdSMark Brown case WM8961_GENERAL_TEST_1: 18135ecf7cdSMark Brown return true; 18235ecf7cdSMark Brown default: 18335ecf7cdSMark Brown return false; 18474dc55edSMark Brown } 18574dc55edSMark Brown } 18674dc55edSMark Brown 18774dc55edSMark Brown /* 18874dc55edSMark Brown * The headphone output supports special anti-pop sequences giving 18974dc55edSMark Brown * silent power up and power down. 19074dc55edSMark Brown */ 19174dc55edSMark Brown static int wm8961_hp_event(struct snd_soc_dapm_widget *w, 19274dc55edSMark Brown struct snd_kcontrol *kcontrol, int event) 19374dc55edSMark Brown { 194ce8d1015SKuninori Morimoto struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); 1956d75dfc3SKuninori Morimoto u16 hp_reg = snd_soc_component_read(component, WM8961_ANALOGUE_HP_0); 1966d75dfc3SKuninori Morimoto u16 cp_reg = snd_soc_component_read(component, WM8961_CHARGE_PUMP_1); 1976d75dfc3SKuninori Morimoto u16 pwr_reg = snd_soc_component_read(component, WM8961_PWR_MGMT_2); 1986d75dfc3SKuninori Morimoto u16 dcs_reg = snd_soc_component_read(component, WM8961_DC_SERVO_1); 19974dc55edSMark Brown int timeout = 500; 20074dc55edSMark Brown 20174dc55edSMark Brown if (event & SND_SOC_DAPM_POST_PMU) { 20274dc55edSMark Brown /* Make sure the output is shorted */ 20374dc55edSMark Brown hp_reg &= ~(WM8961_HPR_RMV_SHORT | WM8961_HPL_RMV_SHORT); 204ce8d1015SKuninori Morimoto snd_soc_component_write(component, WM8961_ANALOGUE_HP_0, hp_reg); 20574dc55edSMark Brown 20674dc55edSMark Brown /* Enable the charge pump */ 20774dc55edSMark Brown cp_reg |= WM8961_CP_ENA; 208ce8d1015SKuninori Morimoto snd_soc_component_write(component, WM8961_CHARGE_PUMP_1, cp_reg); 20974dc55edSMark Brown mdelay(5); 21074dc55edSMark Brown 21174dc55edSMark Brown /* Enable the PGA */ 21274dc55edSMark Brown pwr_reg |= WM8961_LOUT1_PGA | WM8961_ROUT1_PGA; 213ce8d1015SKuninori Morimoto snd_soc_component_write(component, WM8961_PWR_MGMT_2, pwr_reg); 21474dc55edSMark Brown 21574dc55edSMark Brown /* Enable the amplifier */ 21674dc55edSMark Brown hp_reg |= WM8961_HPR_ENA | WM8961_HPL_ENA; 217ce8d1015SKuninori Morimoto snd_soc_component_write(component, WM8961_ANALOGUE_HP_0, hp_reg); 21874dc55edSMark Brown 21974dc55edSMark Brown /* Second stage enable */ 22074dc55edSMark Brown hp_reg |= WM8961_HPR_ENA_DLY | WM8961_HPL_ENA_DLY; 221ce8d1015SKuninori Morimoto snd_soc_component_write(component, WM8961_ANALOGUE_HP_0, hp_reg); 22274dc55edSMark Brown 22374dc55edSMark Brown /* Enable the DC servo & trigger startup */ 22474dc55edSMark Brown dcs_reg |= 22574dc55edSMark Brown WM8961_DCS_ENA_CHAN_HPR | WM8961_DCS_TRIG_STARTUP_HPR | 22674dc55edSMark Brown WM8961_DCS_ENA_CHAN_HPL | WM8961_DCS_TRIG_STARTUP_HPL; 227ce8d1015SKuninori Morimoto dev_dbg(component->dev, "Enabling DC servo\n"); 22874dc55edSMark Brown 229ce8d1015SKuninori Morimoto snd_soc_component_write(component, WM8961_DC_SERVO_1, dcs_reg); 23074dc55edSMark Brown do { 23174dc55edSMark Brown msleep(1); 2326d75dfc3SKuninori Morimoto dcs_reg = snd_soc_component_read(component, WM8961_DC_SERVO_1); 23374dc55edSMark Brown } while (--timeout && 23474dc55edSMark Brown dcs_reg & (WM8961_DCS_TRIG_STARTUP_HPR | 23574dc55edSMark Brown WM8961_DCS_TRIG_STARTUP_HPL)); 23674dc55edSMark Brown if (dcs_reg & (WM8961_DCS_TRIG_STARTUP_HPR | 23774dc55edSMark Brown WM8961_DCS_TRIG_STARTUP_HPL)) 238ce8d1015SKuninori Morimoto dev_err(component->dev, "DC servo timed out\n"); 23974dc55edSMark Brown else 240ce8d1015SKuninori Morimoto dev_dbg(component->dev, "DC servo startup complete\n"); 24174dc55edSMark Brown 24274dc55edSMark Brown /* Enable the output stage */ 24374dc55edSMark Brown hp_reg |= WM8961_HPR_ENA_OUTP | WM8961_HPL_ENA_OUTP; 244ce8d1015SKuninori Morimoto snd_soc_component_write(component, WM8961_ANALOGUE_HP_0, hp_reg); 24574dc55edSMark Brown 24674dc55edSMark Brown /* Remove the short on the output stage */ 24774dc55edSMark Brown hp_reg |= WM8961_HPR_RMV_SHORT | WM8961_HPL_RMV_SHORT; 248ce8d1015SKuninori Morimoto snd_soc_component_write(component, WM8961_ANALOGUE_HP_0, hp_reg); 24974dc55edSMark Brown } 25074dc55edSMark Brown 25174dc55edSMark Brown if (event & SND_SOC_DAPM_PRE_PMD) { 25274dc55edSMark Brown /* Short the output */ 25374dc55edSMark Brown hp_reg &= ~(WM8961_HPR_RMV_SHORT | WM8961_HPL_RMV_SHORT); 254ce8d1015SKuninori Morimoto snd_soc_component_write(component, WM8961_ANALOGUE_HP_0, hp_reg); 25574dc55edSMark Brown 25674dc55edSMark Brown /* Disable the output stage */ 25774dc55edSMark Brown hp_reg &= ~(WM8961_HPR_ENA_OUTP | WM8961_HPL_ENA_OUTP); 258ce8d1015SKuninori Morimoto snd_soc_component_write(component, WM8961_ANALOGUE_HP_0, hp_reg); 25974dc55edSMark Brown 26074dc55edSMark Brown /* Disable DC offset cancellation */ 26174dc55edSMark Brown dcs_reg &= ~(WM8961_DCS_ENA_CHAN_HPR | 26274dc55edSMark Brown WM8961_DCS_ENA_CHAN_HPL); 263ce8d1015SKuninori Morimoto snd_soc_component_write(component, WM8961_DC_SERVO_1, dcs_reg); 26474dc55edSMark Brown 26574dc55edSMark Brown /* Finish up */ 26674dc55edSMark Brown hp_reg &= ~(WM8961_HPR_ENA_DLY | WM8961_HPR_ENA | 26774dc55edSMark Brown WM8961_HPL_ENA_DLY | WM8961_HPL_ENA); 268ce8d1015SKuninori Morimoto snd_soc_component_write(component, WM8961_ANALOGUE_HP_0, hp_reg); 26974dc55edSMark Brown 27074dc55edSMark Brown /* Disable the PGA */ 27174dc55edSMark Brown pwr_reg &= ~(WM8961_LOUT1_PGA | WM8961_ROUT1_PGA); 272ce8d1015SKuninori Morimoto snd_soc_component_write(component, WM8961_PWR_MGMT_2, pwr_reg); 27374dc55edSMark Brown 27474dc55edSMark Brown /* Disable the charge pump */ 275ce8d1015SKuninori Morimoto dev_dbg(component->dev, "Disabling charge pump\n"); 276ce8d1015SKuninori Morimoto snd_soc_component_write(component, WM8961_CHARGE_PUMP_1, 27774dc55edSMark Brown cp_reg & ~WM8961_CP_ENA); 27874dc55edSMark Brown } 27974dc55edSMark Brown 28074dc55edSMark Brown return 0; 28174dc55edSMark Brown } 28274dc55edSMark Brown 28374dc55edSMark Brown static int wm8961_spk_event(struct snd_soc_dapm_widget *w, 28474dc55edSMark Brown struct snd_kcontrol *kcontrol, int event) 28574dc55edSMark Brown { 286ce8d1015SKuninori Morimoto struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); 2876d75dfc3SKuninori Morimoto u16 pwr_reg = snd_soc_component_read(component, WM8961_PWR_MGMT_2); 2886d75dfc3SKuninori Morimoto u16 spk_reg = snd_soc_component_read(component, WM8961_CLASS_D_CONTROL_1); 28974dc55edSMark Brown 29074dc55edSMark Brown if (event & SND_SOC_DAPM_POST_PMU) { 29174dc55edSMark Brown /* Enable the PGA */ 29274dc55edSMark Brown pwr_reg |= WM8961_SPKL_PGA | WM8961_SPKR_PGA; 293ce8d1015SKuninori Morimoto snd_soc_component_write(component, WM8961_PWR_MGMT_2, pwr_reg); 29474dc55edSMark Brown 29574dc55edSMark Brown /* Enable the amplifier */ 29674dc55edSMark Brown spk_reg |= WM8961_SPKL_ENA | WM8961_SPKR_ENA; 297ce8d1015SKuninori Morimoto snd_soc_component_write(component, WM8961_CLASS_D_CONTROL_1, spk_reg); 29874dc55edSMark Brown } 29974dc55edSMark Brown 30074dc55edSMark Brown if (event & SND_SOC_DAPM_PRE_PMD) { 3017fcadfd1SAxel Lin /* Disable the amplifier */ 30274dc55edSMark Brown spk_reg &= ~(WM8961_SPKL_ENA | WM8961_SPKR_ENA); 303ce8d1015SKuninori Morimoto snd_soc_component_write(component, WM8961_CLASS_D_CONTROL_1, spk_reg); 30474dc55edSMark Brown 3057fcadfd1SAxel Lin /* Disable the PGA */ 30674dc55edSMark Brown pwr_reg &= ~(WM8961_SPKL_PGA | WM8961_SPKR_PGA); 307ce8d1015SKuninori Morimoto snd_soc_component_write(component, WM8961_PWR_MGMT_2, pwr_reg); 30874dc55edSMark Brown } 30974dc55edSMark Brown 31074dc55edSMark Brown return 0; 31174dc55edSMark Brown } 31274dc55edSMark Brown 31374dc55edSMark Brown static const char *adc_hpf_text[] = { 31474dc55edSMark Brown "Hi-fi", "Voice 1", "Voice 2", "Voice 3", 31574dc55edSMark Brown }; 31674dc55edSMark Brown 317a6616cdaSTakashi Iwai static SOC_ENUM_SINGLE_DECL(adc_hpf, 318a6616cdaSTakashi Iwai WM8961_ADC_DAC_CONTROL_2, 7, adc_hpf_text); 31974dc55edSMark Brown 32074dc55edSMark Brown static const char *dac_deemph_text[] = { 32174dc55edSMark Brown "None", "32kHz", "44.1kHz", "48kHz", 32274dc55edSMark Brown }; 32374dc55edSMark Brown 324a6616cdaSTakashi Iwai static SOC_ENUM_SINGLE_DECL(dac_deemph, 325a6616cdaSTakashi Iwai WM8961_ADC_DAC_CONTROL_1, 1, dac_deemph_text); 32674dc55edSMark Brown 32774dc55edSMark Brown static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1); 32874dc55edSMark Brown static const DECLARE_TLV_DB_SCALE(hp_sec_tlv, -700, 100, 0); 32974dc55edSMark Brown static const DECLARE_TLV_DB_SCALE(adc_tlv, -7200, 75, 1); 33074dc55edSMark Brown static const DECLARE_TLV_DB_SCALE(sidetone_tlv, -3600, 300, 0); 3315d272d89SLars-Peter Clausen static const DECLARE_TLV_DB_RANGE(boost_tlv, 33274dc55edSMark Brown 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), 33374dc55edSMark Brown 1, 1, TLV_DB_SCALE_ITEM(13, 0, 0), 33474dc55edSMark Brown 2, 2, TLV_DB_SCALE_ITEM(20, 0, 0), 3355d272d89SLars-Peter Clausen 3, 3, TLV_DB_SCALE_ITEM(29, 0, 0) 3365d272d89SLars-Peter Clausen ); 33774dc55edSMark Brown static const DECLARE_TLV_DB_SCALE(pga_tlv, -2325, 75, 0); 33874dc55edSMark Brown 33974dc55edSMark Brown static const struct snd_kcontrol_new wm8961_snd_controls[] = { 34074dc55edSMark Brown SOC_DOUBLE_R_TLV("Headphone Volume", WM8961_LOUT1_VOLUME, WM8961_ROUT1_VOLUME, 34174dc55edSMark Brown 0, 127, 0, out_tlv), 34274dc55edSMark Brown SOC_DOUBLE_TLV("Headphone Secondary Volume", WM8961_ANALOGUE_HP_2, 34374dc55edSMark Brown 6, 3, 7, 0, hp_sec_tlv), 34474dc55edSMark Brown SOC_DOUBLE_R("Headphone ZC Switch", WM8961_LOUT1_VOLUME, WM8961_ROUT1_VOLUME, 34574dc55edSMark Brown 7, 1, 0), 34674dc55edSMark Brown 34774dc55edSMark Brown SOC_DOUBLE_R_TLV("Speaker Volume", WM8961_LOUT2_VOLUME, WM8961_ROUT2_VOLUME, 34874dc55edSMark Brown 0, 127, 0, out_tlv), 34974dc55edSMark Brown SOC_DOUBLE_R("Speaker ZC Switch", WM8961_LOUT2_VOLUME, WM8961_ROUT2_VOLUME, 35074dc55edSMark Brown 7, 1, 0), 35174dc55edSMark Brown SOC_SINGLE("Speaker AC Gain", WM8961_CLASS_D_CONTROL_2, 0, 7, 0), 35274dc55edSMark Brown 35374dc55edSMark Brown SOC_SINGLE("DAC x128 OSR Switch", WM8961_ADC_DAC_CONTROL_2, 0, 1, 0), 35474dc55edSMark Brown SOC_ENUM("DAC Deemphasis", dac_deemph), 35574dc55edSMark Brown SOC_SINGLE("DAC Soft Mute Switch", WM8961_ADC_DAC_CONTROL_2, 3, 1, 0), 35674dc55edSMark Brown 35774dc55edSMark Brown SOC_DOUBLE_R_TLV("Sidetone Volume", WM8961_DSP_SIDETONE_0, 35874dc55edSMark Brown WM8961_DSP_SIDETONE_1, 4, 12, 0, sidetone_tlv), 35974dc55edSMark Brown 36074dc55edSMark Brown SOC_SINGLE("ADC High Pass Filter Switch", WM8961_ADC_DAC_CONTROL_1, 0, 1, 0), 36174dc55edSMark Brown SOC_ENUM("ADC High Pass Filter Mode", adc_hpf), 36274dc55edSMark Brown 36374dc55edSMark Brown SOC_DOUBLE_R_TLV("Capture Volume", 36474dc55edSMark Brown WM8961_LEFT_ADC_VOLUME, WM8961_RIGHT_ADC_VOLUME, 36574dc55edSMark Brown 1, 119, 0, adc_tlv), 36674dc55edSMark Brown SOC_DOUBLE_R_TLV("Capture Boost Volume", 36774dc55edSMark Brown WM8961_ADCL_SIGNAL_PATH, WM8961_ADCR_SIGNAL_PATH, 36874dc55edSMark Brown 4, 3, 0, boost_tlv), 36974dc55edSMark Brown SOC_DOUBLE_R_TLV("Capture PGA Volume", 37074dc55edSMark Brown WM8961_LEFT_INPUT_VOLUME, WM8961_RIGHT_INPUT_VOLUME, 37174dc55edSMark Brown 0, 62, 0, pga_tlv), 37274dc55edSMark Brown SOC_DOUBLE_R("Capture PGA ZC Switch", 37374dc55edSMark Brown WM8961_LEFT_INPUT_VOLUME, WM8961_RIGHT_INPUT_VOLUME, 37474dc55edSMark Brown 6, 1, 1), 37574dc55edSMark Brown SOC_DOUBLE_R("Capture PGA Switch", 37674dc55edSMark Brown WM8961_LEFT_INPUT_VOLUME, WM8961_RIGHT_INPUT_VOLUME, 37774dc55edSMark Brown 7, 1, 1), 37874dc55edSMark Brown }; 37974dc55edSMark Brown 38074dc55edSMark Brown static const char *sidetone_text[] = { 38174dc55edSMark Brown "None", "Left", "Right" 38274dc55edSMark Brown }; 38374dc55edSMark Brown 384a6616cdaSTakashi Iwai static SOC_ENUM_SINGLE_DECL(dacl_sidetone, 385a6616cdaSTakashi Iwai WM8961_DSP_SIDETONE_0, 2, sidetone_text); 38674dc55edSMark Brown 387a6616cdaSTakashi Iwai static SOC_ENUM_SINGLE_DECL(dacr_sidetone, 388a6616cdaSTakashi Iwai WM8961_DSP_SIDETONE_1, 2, sidetone_text); 38974dc55edSMark Brown 39074dc55edSMark Brown static const struct snd_kcontrol_new dacl_mux = 39174dc55edSMark Brown SOC_DAPM_ENUM("DACL Sidetone", dacl_sidetone); 39274dc55edSMark Brown 39374dc55edSMark Brown static const struct snd_kcontrol_new dacr_mux = 39474dc55edSMark Brown SOC_DAPM_ENUM("DACR Sidetone", dacr_sidetone); 39574dc55edSMark Brown 39674dc55edSMark Brown static const struct snd_soc_dapm_widget wm8961_dapm_widgets[] = { 39774dc55edSMark Brown SND_SOC_DAPM_INPUT("LINPUT"), 39874dc55edSMark Brown SND_SOC_DAPM_INPUT("RINPUT"), 39974dc55edSMark Brown 40074dc55edSMark Brown SND_SOC_DAPM_SUPPLY("CLK_DSP", WM8961_CLOCKING2, 4, 0, NULL, 0), 40174dc55edSMark Brown 40274dc55edSMark Brown SND_SOC_DAPM_PGA("Left Input", WM8961_PWR_MGMT_1, 5, 0, NULL, 0), 40374dc55edSMark Brown SND_SOC_DAPM_PGA("Right Input", WM8961_PWR_MGMT_1, 4, 0, NULL, 0), 40474dc55edSMark Brown 40574dc55edSMark Brown SND_SOC_DAPM_ADC("ADCL", "HiFi Capture", WM8961_PWR_MGMT_1, 3, 0), 40674dc55edSMark Brown SND_SOC_DAPM_ADC("ADCR", "HiFi Capture", WM8961_PWR_MGMT_1, 2, 0), 40774dc55edSMark Brown 40820abf088SMark Brown SND_SOC_DAPM_SUPPLY("MICBIAS", WM8961_PWR_MGMT_1, 1, 0, NULL, 0), 40974dc55edSMark Brown 41074dc55edSMark Brown SND_SOC_DAPM_MUX("DACL Sidetone", SND_SOC_NOPM, 0, 0, &dacl_mux), 41174dc55edSMark Brown SND_SOC_DAPM_MUX("DACR Sidetone", SND_SOC_NOPM, 0, 0, &dacr_mux), 41274dc55edSMark Brown 41374dc55edSMark Brown SND_SOC_DAPM_DAC("DACL", "HiFi Playback", WM8961_PWR_MGMT_2, 8, 0), 41474dc55edSMark Brown SND_SOC_DAPM_DAC("DACR", "HiFi Playback", WM8961_PWR_MGMT_2, 7, 0), 41574dc55edSMark Brown 41674dc55edSMark Brown /* Handle as a mono path for DCS */ 41774dc55edSMark Brown SND_SOC_DAPM_PGA_E("Headphone Output", SND_SOC_NOPM, 41874dc55edSMark Brown 4, 0, NULL, 0, wm8961_hp_event, 41974dc55edSMark Brown SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), 42074dc55edSMark Brown SND_SOC_DAPM_PGA_E("Speaker Output", SND_SOC_NOPM, 42174dc55edSMark Brown 4, 0, NULL, 0, wm8961_spk_event, 42274dc55edSMark Brown SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), 42374dc55edSMark Brown 42474dc55edSMark Brown SND_SOC_DAPM_OUTPUT("HP_L"), 42574dc55edSMark Brown SND_SOC_DAPM_OUTPUT("HP_R"), 42674dc55edSMark Brown SND_SOC_DAPM_OUTPUT("SPK_LN"), 42774dc55edSMark Brown SND_SOC_DAPM_OUTPUT("SPK_LP"), 42874dc55edSMark Brown SND_SOC_DAPM_OUTPUT("SPK_RN"), 42974dc55edSMark Brown SND_SOC_DAPM_OUTPUT("SPK_RP"), 43074dc55edSMark Brown }; 43174dc55edSMark Brown 43274dc55edSMark Brown 43374dc55edSMark Brown static const struct snd_soc_dapm_route audio_paths[] = { 43474dc55edSMark Brown { "DACL", NULL, "CLK_DSP" }, 43574dc55edSMark Brown { "DACL", NULL, "DACL Sidetone" }, 43674dc55edSMark Brown { "DACR", NULL, "CLK_DSP" }, 43774dc55edSMark Brown { "DACR", NULL, "DACR Sidetone" }, 43874dc55edSMark Brown 43974dc55edSMark Brown { "DACL Sidetone", "Left", "ADCL" }, 44074dc55edSMark Brown { "DACL Sidetone", "Right", "ADCR" }, 44174dc55edSMark Brown 44274dc55edSMark Brown { "DACR Sidetone", "Left", "ADCL" }, 44374dc55edSMark Brown { "DACR Sidetone", "Right", "ADCR" }, 44474dc55edSMark Brown 44574dc55edSMark Brown { "HP_L", NULL, "Headphone Output" }, 44674dc55edSMark Brown { "HP_R", NULL, "Headphone Output" }, 44774dc55edSMark Brown { "Headphone Output", NULL, "DACL" }, 44874dc55edSMark Brown { "Headphone Output", NULL, "DACR" }, 44974dc55edSMark Brown 45074dc55edSMark Brown { "SPK_LN", NULL, "Speaker Output" }, 45174dc55edSMark Brown { "SPK_LP", NULL, "Speaker Output" }, 45274dc55edSMark Brown { "SPK_RN", NULL, "Speaker Output" }, 45374dc55edSMark Brown { "SPK_RP", NULL, "Speaker Output" }, 45474dc55edSMark Brown 45574dc55edSMark Brown { "Speaker Output", NULL, "DACL" }, 45674dc55edSMark Brown { "Speaker Output", NULL, "DACR" }, 45774dc55edSMark Brown 45874dc55edSMark Brown { "ADCL", NULL, "Left Input" }, 45974dc55edSMark Brown { "ADCL", NULL, "CLK_DSP" }, 46074dc55edSMark Brown { "ADCR", NULL, "Right Input" }, 46174dc55edSMark Brown { "ADCR", NULL, "CLK_DSP" }, 46274dc55edSMark Brown 46374dc55edSMark Brown { "Left Input", NULL, "LINPUT" }, 46474dc55edSMark Brown { "Right Input", NULL, "RINPUT" }, 46574dc55edSMark Brown 46674dc55edSMark Brown }; 46774dc55edSMark Brown 46874dc55edSMark Brown /* Values for CLK_SYS_RATE */ 46974dc55edSMark Brown static struct { 47074dc55edSMark Brown int ratio; 47174dc55edSMark Brown u16 val; 47274dc55edSMark Brown } wm8961_clk_sys_ratio[] = { 47374dc55edSMark Brown { 64, 0 }, 47474dc55edSMark Brown { 128, 1 }, 47574dc55edSMark Brown { 192, 2 }, 47674dc55edSMark Brown { 256, 3 }, 47774dc55edSMark Brown { 384, 4 }, 47874dc55edSMark Brown { 512, 5 }, 47974dc55edSMark Brown { 768, 6 }, 48074dc55edSMark Brown { 1024, 7 }, 48174dc55edSMark Brown { 1408, 8 }, 48274dc55edSMark Brown { 1536, 9 }, 48374dc55edSMark Brown }; 48474dc55edSMark Brown 48574dc55edSMark Brown /* Values for SAMPLE_RATE */ 48674dc55edSMark Brown static struct { 48774dc55edSMark Brown int rate; 48874dc55edSMark Brown u16 val; 48974dc55edSMark Brown } wm8961_srate[] = { 49074dc55edSMark Brown { 48000, 0 }, 49174dc55edSMark Brown { 44100, 0 }, 49274dc55edSMark Brown { 32000, 1 }, 49374dc55edSMark Brown { 22050, 2 }, 49474dc55edSMark Brown { 24000, 2 }, 49574dc55edSMark Brown { 16000, 3 }, 49674dc55edSMark Brown { 11250, 4 }, 49774dc55edSMark Brown { 12000, 4 }, 49874dc55edSMark Brown { 8000, 5 }, 49974dc55edSMark Brown }; 50074dc55edSMark Brown 50174dc55edSMark Brown static int wm8961_hw_params(struct snd_pcm_substream *substream, 50274dc55edSMark Brown struct snd_pcm_hw_params *params, 50374dc55edSMark Brown struct snd_soc_dai *dai) 50474dc55edSMark Brown { 505ce8d1015SKuninori Morimoto struct snd_soc_component *component = dai->component; 506ce8d1015SKuninori Morimoto struct wm8961_priv *wm8961 = snd_soc_component_get_drvdata(component); 50774dc55edSMark Brown int i, best, target, fs; 50874dc55edSMark Brown u16 reg; 50974dc55edSMark Brown 51074dc55edSMark Brown fs = params_rate(params); 51174dc55edSMark Brown 51274dc55edSMark Brown if (!wm8961->sysclk) { 513ce8d1015SKuninori Morimoto dev_err(component->dev, "MCLK has not been specified\n"); 51474dc55edSMark Brown return -EINVAL; 51574dc55edSMark Brown } 51674dc55edSMark Brown 51774dc55edSMark Brown /* Find the closest sample rate for the filters */ 51874dc55edSMark Brown best = 0; 51974dc55edSMark Brown for (i = 0; i < ARRAY_SIZE(wm8961_srate); i++) { 52074dc55edSMark Brown if (abs(wm8961_srate[i].rate - fs) < 52174dc55edSMark Brown abs(wm8961_srate[best].rate - fs)) 52274dc55edSMark Brown best = i; 52374dc55edSMark Brown } 5246d75dfc3SKuninori Morimoto reg = snd_soc_component_read(component, WM8961_ADDITIONAL_CONTROL_3); 52574dc55edSMark Brown reg &= ~WM8961_SAMPLE_RATE_MASK; 52674dc55edSMark Brown reg |= wm8961_srate[best].val; 527ce8d1015SKuninori Morimoto snd_soc_component_write(component, WM8961_ADDITIONAL_CONTROL_3, reg); 528ce8d1015SKuninori Morimoto dev_dbg(component->dev, "Selected SRATE %dHz for %dHz\n", 52974dc55edSMark Brown wm8961_srate[best].rate, fs); 53074dc55edSMark Brown 53174dc55edSMark Brown /* Select a CLK_SYS/fs ratio equal to or higher than required */ 53274dc55edSMark Brown target = wm8961->sysclk / fs; 53374dc55edSMark Brown 53474dc55edSMark Brown if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && target < 64) { 535ce8d1015SKuninori Morimoto dev_err(component->dev, 53674dc55edSMark Brown "SYSCLK must be at least 64*fs for DAC\n"); 53774dc55edSMark Brown return -EINVAL; 53874dc55edSMark Brown } 53974dc55edSMark Brown if (substream->stream == SNDRV_PCM_STREAM_CAPTURE && target < 256) { 540ce8d1015SKuninori Morimoto dev_err(component->dev, 54174dc55edSMark Brown "SYSCLK must be at least 256*fs for ADC\n"); 54274dc55edSMark Brown return -EINVAL; 54374dc55edSMark Brown } 54474dc55edSMark Brown 54574dc55edSMark Brown for (i = 0; i < ARRAY_SIZE(wm8961_clk_sys_ratio); i++) { 54674dc55edSMark Brown if (wm8961_clk_sys_ratio[i].ratio >= target) 54774dc55edSMark Brown break; 54874dc55edSMark Brown } 54974dc55edSMark Brown if (i == ARRAY_SIZE(wm8961_clk_sys_ratio)) { 550ce8d1015SKuninori Morimoto dev_err(component->dev, "Unable to generate CLK_SYS_RATE\n"); 55174dc55edSMark Brown return -EINVAL; 55274dc55edSMark Brown } 553ce8d1015SKuninori Morimoto dev_dbg(component->dev, "Selected CLK_SYS_RATE of %d for %d/%d=%d\n", 55474dc55edSMark Brown wm8961_clk_sys_ratio[i].ratio, wm8961->sysclk, fs, 55574dc55edSMark Brown wm8961->sysclk / fs); 55674dc55edSMark Brown 5576d75dfc3SKuninori Morimoto reg = snd_soc_component_read(component, WM8961_CLOCKING_4); 55874dc55edSMark Brown reg &= ~WM8961_CLK_SYS_RATE_MASK; 55974dc55edSMark Brown reg |= wm8961_clk_sys_ratio[i].val << WM8961_CLK_SYS_RATE_SHIFT; 560ce8d1015SKuninori Morimoto snd_soc_component_write(component, WM8961_CLOCKING_4, reg); 56174dc55edSMark Brown 5626d75dfc3SKuninori Morimoto reg = snd_soc_component_read(component, WM8961_AUDIO_INTERFACE_0); 56374dc55edSMark Brown reg &= ~WM8961_WL_MASK; 5645d3aef91SMark Brown switch (params_width(params)) { 5655d3aef91SMark Brown case 16: 56674dc55edSMark Brown break; 5675d3aef91SMark Brown case 20: 56874dc55edSMark Brown reg |= 1 << WM8961_WL_SHIFT; 56974dc55edSMark Brown break; 5705d3aef91SMark Brown case 24: 57174dc55edSMark Brown reg |= 2 << WM8961_WL_SHIFT; 57274dc55edSMark Brown break; 5735d3aef91SMark Brown case 32: 57474dc55edSMark Brown reg |= 3 << WM8961_WL_SHIFT; 57574dc55edSMark Brown break; 57674dc55edSMark Brown default: 57774dc55edSMark Brown return -EINVAL; 57874dc55edSMark Brown } 579ce8d1015SKuninori Morimoto snd_soc_component_write(component, WM8961_AUDIO_INTERFACE_0, reg); 58074dc55edSMark Brown 58174dc55edSMark Brown /* Sloping stop-band filter is recommended for <= 24kHz */ 5826d75dfc3SKuninori Morimoto reg = snd_soc_component_read(component, WM8961_ADC_DAC_CONTROL_2); 58374dc55edSMark Brown if (fs <= 24000) 58474dc55edSMark Brown reg |= WM8961_DACSLOPE; 58574dc55edSMark Brown else 58608b1a384SAxel Lin reg &= ~WM8961_DACSLOPE; 587ce8d1015SKuninori Morimoto snd_soc_component_write(component, WM8961_ADC_DAC_CONTROL_2, reg); 58874dc55edSMark Brown 58974dc55edSMark Brown return 0; 59074dc55edSMark Brown } 59174dc55edSMark Brown 59274dc55edSMark Brown static int wm8961_set_sysclk(struct snd_soc_dai *dai, int clk_id, 59374dc55edSMark Brown unsigned int freq, 59474dc55edSMark Brown int dir) 59574dc55edSMark Brown { 596ce8d1015SKuninori Morimoto struct snd_soc_component *component = dai->component; 597ce8d1015SKuninori Morimoto struct wm8961_priv *wm8961 = snd_soc_component_get_drvdata(component); 5986d75dfc3SKuninori Morimoto u16 reg = snd_soc_component_read(component, WM8961_CLOCKING1); 59974dc55edSMark Brown 60074dc55edSMark Brown if (freq > 33000000) { 601ce8d1015SKuninori Morimoto dev_err(component->dev, "MCLK must be <33MHz\n"); 60274dc55edSMark Brown return -EINVAL; 60374dc55edSMark Brown } 60474dc55edSMark Brown 60574dc55edSMark Brown if (freq > 16500000) { 606ce8d1015SKuninori Morimoto dev_dbg(component->dev, "Using MCLK/2 for %dHz MCLK\n", freq); 60774dc55edSMark Brown reg |= WM8961_MCLKDIV; 60874dc55edSMark Brown freq /= 2; 60974dc55edSMark Brown } else { 610ce8d1015SKuninori Morimoto dev_dbg(component->dev, "Using MCLK/1 for %dHz MCLK\n", freq); 6112f7dceedSAxel Lin reg &= ~WM8961_MCLKDIV; 61274dc55edSMark Brown } 61374dc55edSMark Brown 614ce8d1015SKuninori Morimoto snd_soc_component_write(component, WM8961_CLOCKING1, reg); 61574dc55edSMark Brown 61674dc55edSMark Brown wm8961->sysclk = freq; 61774dc55edSMark Brown 61874dc55edSMark Brown return 0; 61974dc55edSMark Brown } 62074dc55edSMark Brown 62174dc55edSMark Brown static int wm8961_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) 62274dc55edSMark Brown { 623ce8d1015SKuninori Morimoto struct snd_soc_component *component = dai->component; 6246d75dfc3SKuninori Morimoto u16 aif = snd_soc_component_read(component, WM8961_AUDIO_INTERFACE_0); 62574dc55edSMark Brown 62674dc55edSMark Brown aif &= ~(WM8961_BCLKINV | WM8961_LRP | 62774dc55edSMark Brown WM8961_MS | WM8961_FORMAT_MASK); 62874dc55edSMark Brown 62974dc55edSMark Brown switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 63074dc55edSMark Brown case SND_SOC_DAIFMT_CBM_CFM: 63174dc55edSMark Brown aif |= WM8961_MS; 63274dc55edSMark Brown break; 63374dc55edSMark Brown case SND_SOC_DAIFMT_CBS_CFS: 63474dc55edSMark Brown break; 63574dc55edSMark Brown default: 63674dc55edSMark Brown return -EINVAL; 63774dc55edSMark Brown } 63874dc55edSMark Brown 63974dc55edSMark Brown switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 64074dc55edSMark Brown case SND_SOC_DAIFMT_RIGHT_J: 64174dc55edSMark Brown break; 64274dc55edSMark Brown 64374dc55edSMark Brown case SND_SOC_DAIFMT_LEFT_J: 64474dc55edSMark Brown aif |= 1; 64574dc55edSMark Brown break; 64674dc55edSMark Brown 64774dc55edSMark Brown case SND_SOC_DAIFMT_I2S: 64874dc55edSMark Brown aif |= 2; 64974dc55edSMark Brown break; 65074dc55edSMark Brown 65174dc55edSMark Brown case SND_SOC_DAIFMT_DSP_B: 65274dc55edSMark Brown aif |= WM8961_LRP; 6533e146b55SGustavo A. R. Silva fallthrough; 65474dc55edSMark Brown case SND_SOC_DAIFMT_DSP_A: 65574dc55edSMark Brown aif |= 3; 65674dc55edSMark Brown switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 65774dc55edSMark Brown case SND_SOC_DAIFMT_NB_NF: 65874dc55edSMark Brown case SND_SOC_DAIFMT_IB_NF: 65974dc55edSMark Brown break; 66074dc55edSMark Brown default: 66174dc55edSMark Brown return -EINVAL; 66274dc55edSMark Brown } 66374dc55edSMark Brown break; 66474dc55edSMark Brown 66574dc55edSMark Brown default: 66674dc55edSMark Brown return -EINVAL; 66774dc55edSMark Brown } 66874dc55edSMark Brown 66974dc55edSMark Brown switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 67074dc55edSMark Brown case SND_SOC_DAIFMT_NB_NF: 67174dc55edSMark Brown break; 67274dc55edSMark Brown case SND_SOC_DAIFMT_NB_IF: 67374dc55edSMark Brown aif |= WM8961_LRP; 67474dc55edSMark Brown break; 67574dc55edSMark Brown case SND_SOC_DAIFMT_IB_NF: 67674dc55edSMark Brown aif |= WM8961_BCLKINV; 67774dc55edSMark Brown break; 67874dc55edSMark Brown case SND_SOC_DAIFMT_IB_IF: 67974dc55edSMark Brown aif |= WM8961_BCLKINV | WM8961_LRP; 68074dc55edSMark Brown break; 68174dc55edSMark Brown default: 68274dc55edSMark Brown return -EINVAL; 68374dc55edSMark Brown } 68474dc55edSMark Brown 685ce8d1015SKuninori Morimoto return snd_soc_component_write(component, WM8961_AUDIO_INTERFACE_0, aif); 68674dc55edSMark Brown } 68774dc55edSMark Brown 68874dc55edSMark Brown static int wm8961_set_tristate(struct snd_soc_dai *dai, int tristate) 68974dc55edSMark Brown { 690ce8d1015SKuninori Morimoto struct snd_soc_component *component = dai->component; 6916d75dfc3SKuninori Morimoto u16 reg = snd_soc_component_read(component, WM8961_ADDITIONAL_CONTROL_2); 69274dc55edSMark Brown 69374dc55edSMark Brown if (tristate) 69474dc55edSMark Brown reg |= WM8961_TRIS; 69574dc55edSMark Brown else 69674dc55edSMark Brown reg &= ~WM8961_TRIS; 69774dc55edSMark Brown 698ce8d1015SKuninori Morimoto return snd_soc_component_write(component, WM8961_ADDITIONAL_CONTROL_2, reg); 69974dc55edSMark Brown } 70074dc55edSMark Brown 701*26d3c16eSKuninori Morimoto static int wm8961_mute(struct snd_soc_dai *dai, int mute, int direction) 70274dc55edSMark Brown { 703ce8d1015SKuninori Morimoto struct snd_soc_component *component = dai->component; 7046d75dfc3SKuninori Morimoto u16 reg = snd_soc_component_read(component, WM8961_ADC_DAC_CONTROL_1); 70574dc55edSMark Brown 70674dc55edSMark Brown if (mute) 70774dc55edSMark Brown reg |= WM8961_DACMU; 70874dc55edSMark Brown else 70974dc55edSMark Brown reg &= ~WM8961_DACMU; 71074dc55edSMark Brown 71174dc55edSMark Brown msleep(17); 71274dc55edSMark Brown 713ce8d1015SKuninori Morimoto return snd_soc_component_write(component, WM8961_ADC_DAC_CONTROL_1, reg); 71474dc55edSMark Brown } 71574dc55edSMark Brown 71674dc55edSMark Brown static int wm8961_set_clkdiv(struct snd_soc_dai *dai, int div_id, int div) 71774dc55edSMark Brown { 718ce8d1015SKuninori Morimoto struct snd_soc_component *component = dai->component; 71974dc55edSMark Brown u16 reg; 72074dc55edSMark Brown 72174dc55edSMark Brown switch (div_id) { 72274dc55edSMark Brown case WM8961_BCLK: 7236d75dfc3SKuninori Morimoto reg = snd_soc_component_read(component, WM8961_CLOCKING2); 72474dc55edSMark Brown reg &= ~WM8961_BCLKDIV_MASK; 72574dc55edSMark Brown reg |= div; 726ce8d1015SKuninori Morimoto snd_soc_component_write(component, WM8961_CLOCKING2, reg); 72774dc55edSMark Brown break; 72874dc55edSMark Brown 72974dc55edSMark Brown case WM8961_LRCLK: 7306d75dfc3SKuninori Morimoto reg = snd_soc_component_read(component, WM8961_AUDIO_INTERFACE_2); 73174dc55edSMark Brown reg &= ~WM8961_LRCLK_RATE_MASK; 73274dc55edSMark Brown reg |= div; 733ce8d1015SKuninori Morimoto snd_soc_component_write(component, WM8961_AUDIO_INTERFACE_2, reg); 73474dc55edSMark Brown break; 73574dc55edSMark Brown 73674dc55edSMark Brown default: 73774dc55edSMark Brown return -EINVAL; 73874dc55edSMark Brown } 73974dc55edSMark Brown 74074dc55edSMark Brown return 0; 74174dc55edSMark Brown } 74274dc55edSMark Brown 743ce8d1015SKuninori Morimoto static int wm8961_set_bias_level(struct snd_soc_component *component, 74474dc55edSMark Brown enum snd_soc_bias_level level) 74574dc55edSMark Brown { 74674dc55edSMark Brown u16 reg; 74774dc55edSMark Brown 74874dc55edSMark Brown /* This is all slightly unusual since we have no bypass paths 74974dc55edSMark Brown * and the output amplifier structure means we can just slam 75074dc55edSMark Brown * the biases straight up rather than having to ramp them 75174dc55edSMark Brown * slowly. 75274dc55edSMark Brown */ 75374dc55edSMark Brown switch (level) { 75474dc55edSMark Brown case SND_SOC_BIAS_ON: 75574dc55edSMark Brown break; 75674dc55edSMark Brown 75774dc55edSMark Brown case SND_SOC_BIAS_PREPARE: 758ce8d1015SKuninori Morimoto if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_STANDBY) { 75974dc55edSMark Brown /* Enable bias generation */ 7606d75dfc3SKuninori Morimoto reg = snd_soc_component_read(component, WM8961_ANTI_POP); 76174dc55edSMark Brown reg |= WM8961_BUFIOEN | WM8961_BUFDCOPEN; 762ce8d1015SKuninori Morimoto snd_soc_component_write(component, WM8961_ANTI_POP, reg); 76374dc55edSMark Brown 76474dc55edSMark Brown /* VMID=2*50k, VREF */ 7656d75dfc3SKuninori Morimoto reg = snd_soc_component_read(component, WM8961_PWR_MGMT_1); 76674dc55edSMark Brown reg &= ~WM8961_VMIDSEL_MASK; 76774dc55edSMark Brown reg |= (1 << WM8961_VMIDSEL_SHIFT) | WM8961_VREF; 768ce8d1015SKuninori Morimoto snd_soc_component_write(component, WM8961_PWR_MGMT_1, reg); 76974dc55edSMark Brown } 77074dc55edSMark Brown break; 77174dc55edSMark Brown 77274dc55edSMark Brown case SND_SOC_BIAS_STANDBY: 773ce8d1015SKuninori Morimoto if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_PREPARE) { 77474dc55edSMark Brown /* VREF off */ 7756d75dfc3SKuninori Morimoto reg = snd_soc_component_read(component, WM8961_PWR_MGMT_1); 77674dc55edSMark Brown reg &= ~WM8961_VREF; 777ce8d1015SKuninori Morimoto snd_soc_component_write(component, WM8961_PWR_MGMT_1, reg); 77874dc55edSMark Brown 77974dc55edSMark Brown /* Bias generation off */ 7806d75dfc3SKuninori Morimoto reg = snd_soc_component_read(component, WM8961_ANTI_POP); 78174dc55edSMark Brown reg &= ~(WM8961_BUFIOEN | WM8961_BUFDCOPEN); 782ce8d1015SKuninori Morimoto snd_soc_component_write(component, WM8961_ANTI_POP, reg); 78374dc55edSMark Brown 78474dc55edSMark Brown /* VMID off */ 7856d75dfc3SKuninori Morimoto reg = snd_soc_component_read(component, WM8961_PWR_MGMT_1); 78674dc55edSMark Brown reg &= ~WM8961_VMIDSEL_MASK; 787ce8d1015SKuninori Morimoto snd_soc_component_write(component, WM8961_PWR_MGMT_1, reg); 78874dc55edSMark Brown } 78974dc55edSMark Brown break; 79074dc55edSMark Brown 79174dc55edSMark Brown case SND_SOC_BIAS_OFF: 79274dc55edSMark Brown break; 79374dc55edSMark Brown } 79474dc55edSMark Brown 79574dc55edSMark Brown return 0; 79674dc55edSMark Brown } 79774dc55edSMark Brown 79874dc55edSMark Brown 79974dc55edSMark Brown #define WM8961_RATES SNDRV_PCM_RATE_8000_48000 80074dc55edSMark Brown 80174dc55edSMark Brown #define WM8961_FORMATS \ 80274dc55edSMark Brown (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ 80374dc55edSMark Brown SNDRV_PCM_FMTBIT_S24_LE) 80474dc55edSMark Brown 80585e7652dSLars-Peter Clausen static const struct snd_soc_dai_ops wm8961_dai_ops = { 80674dc55edSMark Brown .hw_params = wm8961_hw_params, 80774dc55edSMark Brown .set_sysclk = wm8961_set_sysclk, 80874dc55edSMark Brown .set_fmt = wm8961_set_fmt, 809*26d3c16eSKuninori Morimoto .mute_stream = wm8961_mute, 81074dc55edSMark Brown .set_tristate = wm8961_set_tristate, 81174dc55edSMark Brown .set_clkdiv = wm8961_set_clkdiv, 812*26d3c16eSKuninori Morimoto .no_capture_mute = 1, 81374dc55edSMark Brown }; 81474dc55edSMark Brown 815f0fba2adSLiam Girdwood static struct snd_soc_dai_driver wm8961_dai = { 816f0fba2adSLiam Girdwood .name = "wm8961-hifi", 81774dc55edSMark Brown .playback = { 81874dc55edSMark Brown .stream_name = "HiFi Playback", 81974dc55edSMark Brown .channels_min = 1, 82074dc55edSMark Brown .channels_max = 2, 82174dc55edSMark Brown .rates = WM8961_RATES, 82274dc55edSMark Brown .formats = WM8961_FORMATS,}, 82374dc55edSMark Brown .capture = { 82474dc55edSMark Brown .stream_name = "HiFi Capture", 82574dc55edSMark Brown .channels_min = 1, 82674dc55edSMark Brown .channels_max = 2, 82774dc55edSMark Brown .rates = WM8961_RATES, 82874dc55edSMark Brown .formats = WM8961_FORMATS,}, 82974dc55edSMark Brown .ops = &wm8961_dai_ops, 83074dc55edSMark Brown }; 83174dc55edSMark Brown 832ce8d1015SKuninori Morimoto static int wm8961_probe(struct snd_soc_component *component) 83374dc55edSMark Brown { 83474dc55edSMark Brown u16 reg; 83574dc55edSMark Brown 83674dc55edSMark Brown /* Enable class W */ 8376d75dfc3SKuninori Morimoto reg = snd_soc_component_read(component, WM8961_CHARGE_PUMP_B); 83874dc55edSMark Brown reg |= WM8961_CP_DYN_PWR_MASK; 839ce8d1015SKuninori Morimoto snd_soc_component_write(component, WM8961_CHARGE_PUMP_B, reg); 84074dc55edSMark Brown 84174dc55edSMark Brown /* Latch volume update bits (right channel only, we always 84274dc55edSMark Brown * write both out) and default ZC on. */ 8436d75dfc3SKuninori Morimoto reg = snd_soc_component_read(component, WM8961_ROUT1_VOLUME); 844ce8d1015SKuninori Morimoto snd_soc_component_write(component, WM8961_ROUT1_VOLUME, 84574dc55edSMark Brown reg | WM8961_LO1ZC | WM8961_OUT1VU); 846ce8d1015SKuninori Morimoto snd_soc_component_write(component, WM8961_LOUT1_VOLUME, reg | WM8961_LO1ZC); 8476d75dfc3SKuninori Morimoto reg = snd_soc_component_read(component, WM8961_ROUT2_VOLUME); 848ce8d1015SKuninori Morimoto snd_soc_component_write(component, WM8961_ROUT2_VOLUME, 84974dc55edSMark Brown reg | WM8961_SPKRZC | WM8961_SPKVU); 850ce8d1015SKuninori Morimoto snd_soc_component_write(component, WM8961_LOUT2_VOLUME, reg | WM8961_SPKLZC); 85174dc55edSMark Brown 8526d75dfc3SKuninori Morimoto reg = snd_soc_component_read(component, WM8961_RIGHT_ADC_VOLUME); 853ce8d1015SKuninori Morimoto snd_soc_component_write(component, WM8961_RIGHT_ADC_VOLUME, reg | WM8961_ADCVU); 8546d75dfc3SKuninori Morimoto reg = snd_soc_component_read(component, WM8961_RIGHT_INPUT_VOLUME); 855ce8d1015SKuninori Morimoto snd_soc_component_write(component, WM8961_RIGHT_INPUT_VOLUME, reg | WM8961_IPVU); 85674dc55edSMark Brown 85774dc55edSMark Brown /* Use soft mute by default */ 8586d75dfc3SKuninori Morimoto reg = snd_soc_component_read(component, WM8961_ADC_DAC_CONTROL_2); 85974dc55edSMark Brown reg |= WM8961_DACSMM; 860ce8d1015SKuninori Morimoto snd_soc_component_write(component, WM8961_ADC_DAC_CONTROL_2, reg); 86174dc55edSMark Brown 86274dc55edSMark Brown /* Use automatic clocking mode by default; for now this is all 86374dc55edSMark Brown * we support. 86474dc55edSMark Brown */ 8656d75dfc3SKuninori Morimoto reg = snd_soc_component_read(component, WM8961_CLOCKING_3); 86674dc55edSMark Brown reg &= ~WM8961_MANUAL_MODE; 867ce8d1015SKuninori Morimoto snd_soc_component_write(component, WM8961_CLOCKING_3, reg); 86874dc55edSMark Brown 869f0fba2adSLiam Girdwood return 0; 87074dc55edSMark Brown } 87174dc55edSMark Brown 872f0fba2adSLiam Girdwood #ifdef CONFIG_PM 873f0fba2adSLiam Girdwood 874ce8d1015SKuninori Morimoto static int wm8961_resume(struct snd_soc_component *component) 875f0fba2adSLiam Girdwood { 876ce8d1015SKuninori Morimoto snd_soc_component_cache_sync(component); 877f0fba2adSLiam Girdwood 878f0fba2adSLiam Girdwood return 0; 879f0fba2adSLiam Girdwood } 880f0fba2adSLiam Girdwood #else 881f0fba2adSLiam Girdwood #define wm8961_resume NULL 882f0fba2adSLiam Girdwood #endif 883f0fba2adSLiam Girdwood 884ce8d1015SKuninori Morimoto static const struct snd_soc_component_driver soc_component_dev_wm8961 = { 885f0fba2adSLiam Girdwood .probe = wm8961_probe, 886f0fba2adSLiam Girdwood .resume = wm8961_resume, 887f0fba2adSLiam Girdwood .set_bias_level = wm8961_set_bias_level, 888c4f50dbcSLars-Peter Clausen .controls = wm8961_snd_controls, 889c4f50dbcSLars-Peter Clausen .num_controls = ARRAY_SIZE(wm8961_snd_controls), 890c4f50dbcSLars-Peter Clausen .dapm_widgets = wm8961_dapm_widgets, 891c4f50dbcSLars-Peter Clausen .num_dapm_widgets = ARRAY_SIZE(wm8961_dapm_widgets), 892c4f50dbcSLars-Peter Clausen .dapm_routes = audio_paths, 893c4f50dbcSLars-Peter Clausen .num_dapm_routes = ARRAY_SIZE(audio_paths), 894ce8d1015SKuninori Morimoto .suspend_bias_off = 1, 895ce8d1015SKuninori Morimoto .idle_bias_on = 1, 896ce8d1015SKuninori Morimoto .use_pmdown_time = 1, 897ce8d1015SKuninori Morimoto .endianness = 1, 898ce8d1015SKuninori Morimoto .non_legacy_dai_naming = 1, 89935ecf7cdSMark Brown }; 90035ecf7cdSMark Brown 90135ecf7cdSMark Brown static const struct regmap_config wm8961_regmap = { 90235ecf7cdSMark Brown .reg_bits = 8, 90335ecf7cdSMark Brown .val_bits = 16, 90435ecf7cdSMark Brown .max_register = WM8961_MAX_REGISTER, 90535ecf7cdSMark Brown 90635ecf7cdSMark Brown .reg_defaults = wm8961_reg_defaults, 90735ecf7cdSMark Brown .num_reg_defaults = ARRAY_SIZE(wm8961_reg_defaults), 90835ecf7cdSMark Brown .cache_type = REGCACHE_RBTREE, 90935ecf7cdSMark Brown 91035ecf7cdSMark Brown .volatile_reg = wm8961_volatile, 91135ecf7cdSMark Brown .readable_reg = wm8961_readable, 912f0fba2adSLiam Girdwood }; 913f0fba2adSLiam Girdwood 9147a79e94eSBill Pemberton static int wm8961_i2c_probe(struct i2c_client *i2c, 91574dc55edSMark Brown const struct i2c_device_id *id) 91674dc55edSMark Brown { 91774dc55edSMark Brown struct wm8961_priv *wm8961; 918b306e84fSMark Brown unsigned int val; 919f0fba2adSLiam Girdwood int ret; 92074dc55edSMark Brown 9212ec2a906SMark Brown wm8961 = devm_kzalloc(&i2c->dev, sizeof(struct wm8961_priv), 9222ec2a906SMark Brown GFP_KERNEL); 92374dc55edSMark Brown if (wm8961 == NULL) 92474dc55edSMark Brown return -ENOMEM; 92574dc55edSMark Brown 92635ecf7cdSMark Brown wm8961->regmap = devm_regmap_init_i2c(i2c, &wm8961_regmap); 92735ecf7cdSMark Brown if (IS_ERR(wm8961->regmap)) 92835ecf7cdSMark Brown return PTR_ERR(wm8961->regmap); 92935ecf7cdSMark Brown 930b306e84fSMark Brown ret = regmap_read(wm8961->regmap, WM8961_SOFTWARE_RESET, &val); 931b306e84fSMark Brown if (ret != 0) { 932b306e84fSMark Brown dev_err(&i2c->dev, "Failed to read chip ID: %d\n", ret); 933b306e84fSMark Brown return ret; 934b306e84fSMark Brown } 935b306e84fSMark Brown 936b306e84fSMark Brown if (val != 0x1801) { 937b306e84fSMark Brown dev_err(&i2c->dev, "Device is not a WM8961: ID=0x%x\n", val); 938b306e84fSMark Brown return -EINVAL; 939b306e84fSMark Brown } 940b306e84fSMark Brown 941b306e84fSMark Brown /* This isn't volatile - readback doesn't correspond to write */ 942b306e84fSMark Brown regcache_cache_bypass(wm8961->regmap, true); 943b306e84fSMark Brown ret = regmap_read(wm8961->regmap, WM8961_RIGHT_INPUT_VOLUME, &val); 944b306e84fSMark Brown regcache_cache_bypass(wm8961->regmap, false); 945b306e84fSMark Brown 946b306e84fSMark Brown if (ret != 0) { 947b306e84fSMark Brown dev_err(&i2c->dev, "Failed to read chip revision: %d\n", ret); 948b306e84fSMark Brown return ret; 949b306e84fSMark Brown } 950b306e84fSMark Brown 951b306e84fSMark Brown dev_info(&i2c->dev, "WM8961 family %d revision %c\n", 952b306e84fSMark Brown (val & WM8961_DEVICE_ID_MASK) >> WM8961_DEVICE_ID_SHIFT, 953b306e84fSMark Brown ((val & WM8961_CHIP_REV_MASK) >> WM8961_CHIP_REV_SHIFT) 954b306e84fSMark Brown + 'A'); 955b306e84fSMark Brown 956b306e84fSMark Brown ret = regmap_write(wm8961->regmap, WM8961_SOFTWARE_RESET, 0x1801); 957b306e84fSMark Brown if (ret != 0) { 958b306e84fSMark Brown dev_err(&i2c->dev, "Failed to issue reset: %d\n", ret); 959b306e84fSMark Brown return ret; 960b306e84fSMark Brown } 961b306e84fSMark Brown 96274dc55edSMark Brown i2c_set_clientdata(i2c, wm8961); 96374dc55edSMark Brown 964ce8d1015SKuninori Morimoto ret = devm_snd_soc_register_component(&i2c->dev, 965ce8d1015SKuninori Morimoto &soc_component_dev_wm8961, &wm8961_dai, 1); 9662ec2a906SMark Brown 967f0fba2adSLiam Girdwood return ret; 96874dc55edSMark Brown } 96974dc55edSMark Brown 97074dc55edSMark Brown static const struct i2c_device_id wm8961_i2c_id[] = { 97174dc55edSMark Brown { "wm8961", 0 }, 97274dc55edSMark Brown { } 97374dc55edSMark Brown }; 97474dc55edSMark Brown MODULE_DEVICE_TABLE(i2c, wm8961_i2c_id); 97574dc55edSMark Brown 97674dc55edSMark Brown static struct i2c_driver wm8961_i2c_driver = { 97774dc55edSMark Brown .driver = { 978091edccfSMark Brown .name = "wm8961", 97974dc55edSMark Brown }, 98074dc55edSMark Brown .probe = wm8961_i2c_probe, 98174dc55edSMark Brown .id_table = wm8961_i2c_id, 98274dc55edSMark Brown }; 98374dc55edSMark Brown 9848b08eb28SSachin Kamat module_i2c_driver(wm8961_i2c_driver); 98574dc55edSMark Brown 98674dc55edSMark Brown MODULE_DESCRIPTION("ASoC WM8961 driver"); 98774dc55edSMark Brown MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); 98874dc55edSMark Brown MODULE_LICENSE("GPL"); 989