174dc55edSMark Brown /* 274dc55edSMark Brown * wm8961.c -- WM8961 ALSA SoC Audio driver 374dc55edSMark Brown * 4656baaebSMark Brown * Copyright 2009-10 Wolfson Microelectronics, plc 5656baaebSMark Brown * 674dc55edSMark Brown * Author: Mark Brown 774dc55edSMark Brown * 874dc55edSMark Brown * This program is free software; you can redistribute it and/or modify 974dc55edSMark Brown * it under the terms of the GNU General Public License version 2 as 1074dc55edSMark Brown * published by the Free Software Foundation. 1174dc55edSMark Brown * 1274dc55edSMark Brown * Currently unimplemented features: 1374dc55edSMark Brown * - ALC 1474dc55edSMark Brown */ 1574dc55edSMark Brown 1674dc55edSMark Brown #include <linux/module.h> 1774dc55edSMark Brown #include <linux/moduleparam.h> 1874dc55edSMark Brown #include <linux/init.h> 1974dc55edSMark Brown #include <linux/delay.h> 2074dc55edSMark Brown #include <linux/pm.h> 2174dc55edSMark Brown #include <linux/i2c.h> 2235ecf7cdSMark Brown #include <linux/regmap.h> 235a0e3ad6STejun Heo #include <linux/slab.h> 2474dc55edSMark Brown #include <sound/core.h> 2574dc55edSMark Brown #include <sound/pcm.h> 2674dc55edSMark Brown #include <sound/pcm_params.h> 2774dc55edSMark Brown #include <sound/soc.h> 2874dc55edSMark Brown #include <sound/initval.h> 2974dc55edSMark Brown #include <sound/tlv.h> 3074dc55edSMark Brown 3174dc55edSMark Brown #include "wm8961.h" 3274dc55edSMark Brown 3374dc55edSMark Brown #define WM8961_MAX_REGISTER 0xFC 3474dc55edSMark Brown 3535ecf7cdSMark Brown static const struct reg_default wm8961_reg_defaults[] = { 3635ecf7cdSMark Brown { 0, 0x009F }, /* R0 - Left Input volume */ 3735ecf7cdSMark Brown { 1, 0x009F }, /* R1 - Right Input volume */ 3835ecf7cdSMark Brown { 2, 0x0000 }, /* R2 - LOUT1 volume */ 3935ecf7cdSMark Brown { 3, 0x0000 }, /* R3 - ROUT1 volume */ 4035ecf7cdSMark Brown { 4, 0x0020 }, /* R4 - Clocking1 */ 4135ecf7cdSMark Brown { 5, 0x0008 }, /* R5 - ADC & DAC Control 1 */ 4235ecf7cdSMark Brown { 6, 0x0000 }, /* R6 - ADC & DAC Control 2 */ 4335ecf7cdSMark Brown { 7, 0x000A }, /* R7 - Audio Interface 0 */ 4435ecf7cdSMark Brown { 8, 0x01F4 }, /* R8 - Clocking2 */ 4535ecf7cdSMark Brown { 9, 0x0000 }, /* R9 - Audio Interface 1 */ 4635ecf7cdSMark Brown { 10, 0x00FF }, /* R10 - Left DAC volume */ 4735ecf7cdSMark Brown { 11, 0x00FF }, /* R11 - Right DAC volume */ 4835ecf7cdSMark Brown 4935ecf7cdSMark Brown { 14, 0x0040 }, /* R14 - Audio Interface 2 */ 5035ecf7cdSMark Brown 5135ecf7cdSMark Brown { 17, 0x007B }, /* R17 - ALC1 */ 5235ecf7cdSMark Brown { 18, 0x0000 }, /* R18 - ALC2 */ 5335ecf7cdSMark Brown { 19, 0x0032 }, /* R19 - ALC3 */ 5435ecf7cdSMark Brown { 20, 0x0000 }, /* R20 - Noise Gate */ 5535ecf7cdSMark Brown { 21, 0x00C0 }, /* R21 - Left ADC volume */ 5635ecf7cdSMark Brown { 22, 0x00C0 }, /* R22 - Right ADC volume */ 5735ecf7cdSMark Brown { 23, 0x0120 }, /* R23 - Additional control(1) */ 5835ecf7cdSMark Brown { 24, 0x0000 }, /* R24 - Additional control(2) */ 5935ecf7cdSMark Brown { 25, 0x0000 }, /* R25 - Pwr Mgmt (1) */ 6035ecf7cdSMark Brown { 26, 0x0000 }, /* R26 - Pwr Mgmt (2) */ 6135ecf7cdSMark Brown { 27, 0x0000 }, /* R27 - Additional Control (3) */ 6235ecf7cdSMark Brown { 28, 0x0000 }, /* R28 - Anti-pop */ 6335ecf7cdSMark Brown 6435ecf7cdSMark Brown { 30, 0x005F }, /* R30 - Clocking 3 */ 6535ecf7cdSMark Brown 6635ecf7cdSMark Brown { 32, 0x0000 }, /* R32 - ADCL signal path */ 6735ecf7cdSMark Brown { 33, 0x0000 }, /* R33 - ADCR signal path */ 6835ecf7cdSMark Brown 6935ecf7cdSMark Brown { 40, 0x0000 }, /* R40 - LOUT2 volume */ 7035ecf7cdSMark Brown { 41, 0x0000 }, /* R41 - ROUT2 volume */ 7135ecf7cdSMark Brown 7235ecf7cdSMark Brown { 47, 0x0000 }, /* R47 - Pwr Mgmt (3) */ 7335ecf7cdSMark Brown { 48, 0x0023 }, /* R48 - Additional Control (4) */ 7435ecf7cdSMark Brown { 49, 0x0000 }, /* R49 - Class D Control 1 */ 7535ecf7cdSMark Brown 7635ecf7cdSMark Brown { 51, 0x0003 }, /* R51 - Class D Control 2 */ 7735ecf7cdSMark Brown 7835ecf7cdSMark Brown { 56, 0x0106 }, /* R56 - Clocking 4 */ 7935ecf7cdSMark Brown { 57, 0x0000 }, /* R57 - DSP Sidetone 0 */ 8035ecf7cdSMark Brown { 58, 0x0000 }, /* R58 - DSP Sidetone 1 */ 8135ecf7cdSMark Brown 8235ecf7cdSMark Brown { 60, 0x0000 }, /* R60 - DC Servo 0 */ 8335ecf7cdSMark Brown { 61, 0x0000 }, /* R61 - DC Servo 1 */ 8435ecf7cdSMark Brown 8535ecf7cdSMark Brown { 63, 0x015E }, /* R63 - DC Servo 3 */ 8635ecf7cdSMark Brown 8735ecf7cdSMark Brown { 65, 0x0010 }, /* R65 - DC Servo 5 */ 8835ecf7cdSMark Brown 8935ecf7cdSMark Brown { 68, 0x0003 }, /* R68 - Analogue PGA Bias */ 9035ecf7cdSMark Brown { 69, 0x0000 }, /* R69 - Analogue HP 0 */ 9135ecf7cdSMark Brown 9235ecf7cdSMark Brown { 71, 0x01FB }, /* R71 - Analogue HP 2 */ 9335ecf7cdSMark Brown { 72, 0x0000 }, /* R72 - Charge Pump 1 */ 9435ecf7cdSMark Brown 9535ecf7cdSMark Brown { 82, 0x0000 }, /* R82 - Charge Pump B */ 9635ecf7cdSMark Brown 9735ecf7cdSMark Brown { 87, 0x0000 }, /* R87 - Write Sequencer 1 */ 9835ecf7cdSMark Brown { 88, 0x0000 }, /* R88 - Write Sequencer 2 */ 9935ecf7cdSMark Brown { 89, 0x0000 }, /* R89 - Write Sequencer 3 */ 10035ecf7cdSMark Brown { 90, 0x0000 }, /* R90 - Write Sequencer 4 */ 10135ecf7cdSMark Brown { 91, 0x0000 }, /* R91 - Write Sequencer 5 */ 10235ecf7cdSMark Brown { 92, 0x0000 }, /* R92 - Write Sequencer 6 */ 10335ecf7cdSMark Brown { 93, 0x0000 }, /* R93 - Write Sequencer 7 */ 10435ecf7cdSMark Brown 10535ecf7cdSMark Brown { 252, 0x0001 }, /* R252 - General test 1 */ 10674dc55edSMark Brown }; 10774dc55edSMark Brown 10874dc55edSMark Brown struct wm8961_priv { 10935ecf7cdSMark Brown struct regmap *regmap; 11074dc55edSMark Brown int sysclk; 11174dc55edSMark Brown }; 11274dc55edSMark Brown 11335ecf7cdSMark Brown static bool wm8961_volatile(struct device *dev, unsigned int reg) 11474dc55edSMark Brown { 11574dc55edSMark Brown switch (reg) { 1168d50e447SMark Brown case WM8961_SOFTWARE_RESET: 11774dc55edSMark Brown case WM8961_WRITE_SEQUENCER_7: 11874dc55edSMark Brown case WM8961_DC_SERVO_1: 11935ecf7cdSMark Brown return true; 12074dc55edSMark Brown 12174dc55edSMark Brown default: 12235ecf7cdSMark Brown return false; 12335ecf7cdSMark Brown } 12435ecf7cdSMark Brown } 12535ecf7cdSMark Brown 12635ecf7cdSMark Brown static bool wm8961_readable(struct device *dev, unsigned int reg) 12735ecf7cdSMark Brown { 12835ecf7cdSMark Brown switch (reg) { 12935ecf7cdSMark Brown case WM8961_LEFT_INPUT_VOLUME: 13035ecf7cdSMark Brown case WM8961_RIGHT_INPUT_VOLUME: 13135ecf7cdSMark Brown case WM8961_LOUT1_VOLUME: 13235ecf7cdSMark Brown case WM8961_ROUT1_VOLUME: 13335ecf7cdSMark Brown case WM8961_CLOCKING1: 13435ecf7cdSMark Brown case WM8961_ADC_DAC_CONTROL_1: 13535ecf7cdSMark Brown case WM8961_ADC_DAC_CONTROL_2: 13635ecf7cdSMark Brown case WM8961_AUDIO_INTERFACE_0: 13735ecf7cdSMark Brown case WM8961_CLOCKING2: 13835ecf7cdSMark Brown case WM8961_AUDIO_INTERFACE_1: 13935ecf7cdSMark Brown case WM8961_LEFT_DAC_VOLUME: 14035ecf7cdSMark Brown case WM8961_RIGHT_DAC_VOLUME: 14135ecf7cdSMark Brown case WM8961_AUDIO_INTERFACE_2: 14235ecf7cdSMark Brown case WM8961_SOFTWARE_RESET: 14335ecf7cdSMark Brown case WM8961_ALC1: 14435ecf7cdSMark Brown case WM8961_ALC2: 14535ecf7cdSMark Brown case WM8961_ALC3: 14635ecf7cdSMark Brown case WM8961_NOISE_GATE: 14735ecf7cdSMark Brown case WM8961_LEFT_ADC_VOLUME: 14835ecf7cdSMark Brown case WM8961_RIGHT_ADC_VOLUME: 14935ecf7cdSMark Brown case WM8961_ADDITIONAL_CONTROL_1: 15035ecf7cdSMark Brown case WM8961_ADDITIONAL_CONTROL_2: 15135ecf7cdSMark Brown case WM8961_PWR_MGMT_1: 15235ecf7cdSMark Brown case WM8961_PWR_MGMT_2: 15335ecf7cdSMark Brown case WM8961_ADDITIONAL_CONTROL_3: 15435ecf7cdSMark Brown case WM8961_ANTI_POP: 15535ecf7cdSMark Brown case WM8961_CLOCKING_3: 15635ecf7cdSMark Brown case WM8961_ADCL_SIGNAL_PATH: 15735ecf7cdSMark Brown case WM8961_ADCR_SIGNAL_PATH: 15835ecf7cdSMark Brown case WM8961_LOUT2_VOLUME: 15935ecf7cdSMark Brown case WM8961_ROUT2_VOLUME: 16035ecf7cdSMark Brown case WM8961_PWR_MGMT_3: 16135ecf7cdSMark Brown case WM8961_ADDITIONAL_CONTROL_4: 16235ecf7cdSMark Brown case WM8961_CLASS_D_CONTROL_1: 16335ecf7cdSMark Brown case WM8961_CLASS_D_CONTROL_2: 16435ecf7cdSMark Brown case WM8961_CLOCKING_4: 16535ecf7cdSMark Brown case WM8961_DSP_SIDETONE_0: 16635ecf7cdSMark Brown case WM8961_DSP_SIDETONE_1: 16735ecf7cdSMark Brown case WM8961_DC_SERVO_0: 16835ecf7cdSMark Brown case WM8961_DC_SERVO_1: 16935ecf7cdSMark Brown case WM8961_DC_SERVO_3: 17035ecf7cdSMark Brown case WM8961_DC_SERVO_5: 17135ecf7cdSMark Brown case WM8961_ANALOGUE_PGA_BIAS: 17235ecf7cdSMark Brown case WM8961_ANALOGUE_HP_0: 17335ecf7cdSMark Brown case WM8961_ANALOGUE_HP_2: 17435ecf7cdSMark Brown case WM8961_CHARGE_PUMP_1: 17535ecf7cdSMark Brown case WM8961_CHARGE_PUMP_B: 17635ecf7cdSMark Brown case WM8961_WRITE_SEQUENCER_1: 17735ecf7cdSMark Brown case WM8961_WRITE_SEQUENCER_2: 17835ecf7cdSMark Brown case WM8961_WRITE_SEQUENCER_3: 17935ecf7cdSMark Brown case WM8961_WRITE_SEQUENCER_4: 18035ecf7cdSMark Brown case WM8961_WRITE_SEQUENCER_5: 18135ecf7cdSMark Brown case WM8961_WRITE_SEQUENCER_6: 18235ecf7cdSMark Brown case WM8961_WRITE_SEQUENCER_7: 18335ecf7cdSMark Brown case WM8961_GENERAL_TEST_1: 18435ecf7cdSMark Brown return true; 18535ecf7cdSMark Brown default: 18635ecf7cdSMark Brown return false; 18774dc55edSMark Brown } 18874dc55edSMark Brown } 18974dc55edSMark Brown 19074dc55edSMark Brown /* 19174dc55edSMark Brown * The headphone output supports special anti-pop sequences giving 19274dc55edSMark Brown * silent power up and power down. 19374dc55edSMark Brown */ 19474dc55edSMark Brown static int wm8961_hp_event(struct snd_soc_dapm_widget *w, 19574dc55edSMark Brown struct snd_kcontrol *kcontrol, int event) 19674dc55edSMark Brown { 1970e13064dSLars-Peter Clausen struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); 1988d50e447SMark Brown u16 hp_reg = snd_soc_read(codec, WM8961_ANALOGUE_HP_0); 1998d50e447SMark Brown u16 cp_reg = snd_soc_read(codec, WM8961_CHARGE_PUMP_1); 2008d50e447SMark Brown u16 pwr_reg = snd_soc_read(codec, WM8961_PWR_MGMT_2); 2018d50e447SMark Brown u16 dcs_reg = snd_soc_read(codec, WM8961_DC_SERVO_1); 20274dc55edSMark Brown int timeout = 500; 20374dc55edSMark Brown 20474dc55edSMark Brown if (event & SND_SOC_DAPM_POST_PMU) { 20574dc55edSMark Brown /* Make sure the output is shorted */ 20674dc55edSMark Brown hp_reg &= ~(WM8961_HPR_RMV_SHORT | WM8961_HPL_RMV_SHORT); 2078d50e447SMark Brown snd_soc_write(codec, WM8961_ANALOGUE_HP_0, hp_reg); 20874dc55edSMark Brown 20974dc55edSMark Brown /* Enable the charge pump */ 21074dc55edSMark Brown cp_reg |= WM8961_CP_ENA; 2118d50e447SMark Brown snd_soc_write(codec, WM8961_CHARGE_PUMP_1, cp_reg); 21274dc55edSMark Brown mdelay(5); 21374dc55edSMark Brown 21474dc55edSMark Brown /* Enable the PGA */ 21574dc55edSMark Brown pwr_reg |= WM8961_LOUT1_PGA | WM8961_ROUT1_PGA; 2168d50e447SMark Brown snd_soc_write(codec, WM8961_PWR_MGMT_2, pwr_reg); 21774dc55edSMark Brown 21874dc55edSMark Brown /* Enable the amplifier */ 21974dc55edSMark Brown hp_reg |= WM8961_HPR_ENA | WM8961_HPL_ENA; 2208d50e447SMark Brown snd_soc_write(codec, WM8961_ANALOGUE_HP_0, hp_reg); 22174dc55edSMark Brown 22274dc55edSMark Brown /* Second stage enable */ 22374dc55edSMark Brown hp_reg |= WM8961_HPR_ENA_DLY | WM8961_HPL_ENA_DLY; 2248d50e447SMark Brown snd_soc_write(codec, WM8961_ANALOGUE_HP_0, hp_reg); 22574dc55edSMark Brown 22674dc55edSMark Brown /* Enable the DC servo & trigger startup */ 22774dc55edSMark Brown dcs_reg |= 22874dc55edSMark Brown WM8961_DCS_ENA_CHAN_HPR | WM8961_DCS_TRIG_STARTUP_HPR | 22974dc55edSMark Brown WM8961_DCS_ENA_CHAN_HPL | WM8961_DCS_TRIG_STARTUP_HPL; 23074dc55edSMark Brown dev_dbg(codec->dev, "Enabling DC servo\n"); 23174dc55edSMark Brown 2328d50e447SMark Brown snd_soc_write(codec, WM8961_DC_SERVO_1, dcs_reg); 23374dc55edSMark Brown do { 23474dc55edSMark Brown msleep(1); 2358d50e447SMark Brown dcs_reg = snd_soc_read(codec, WM8961_DC_SERVO_1); 23674dc55edSMark Brown } while (--timeout && 23774dc55edSMark Brown dcs_reg & (WM8961_DCS_TRIG_STARTUP_HPR | 23874dc55edSMark Brown WM8961_DCS_TRIG_STARTUP_HPL)); 23974dc55edSMark Brown if (dcs_reg & (WM8961_DCS_TRIG_STARTUP_HPR | 24074dc55edSMark Brown WM8961_DCS_TRIG_STARTUP_HPL)) 24174dc55edSMark Brown dev_err(codec->dev, "DC servo timed out\n"); 24274dc55edSMark Brown else 24374dc55edSMark Brown dev_dbg(codec->dev, "DC servo startup complete\n"); 24474dc55edSMark Brown 24574dc55edSMark Brown /* Enable the output stage */ 24674dc55edSMark Brown hp_reg |= WM8961_HPR_ENA_OUTP | WM8961_HPL_ENA_OUTP; 2478d50e447SMark Brown snd_soc_write(codec, WM8961_ANALOGUE_HP_0, hp_reg); 24874dc55edSMark Brown 24974dc55edSMark Brown /* Remove the short on the output stage */ 25074dc55edSMark Brown hp_reg |= WM8961_HPR_RMV_SHORT | WM8961_HPL_RMV_SHORT; 2518d50e447SMark Brown snd_soc_write(codec, WM8961_ANALOGUE_HP_0, hp_reg); 25274dc55edSMark Brown } 25374dc55edSMark Brown 25474dc55edSMark Brown if (event & SND_SOC_DAPM_PRE_PMD) { 25574dc55edSMark Brown /* Short the output */ 25674dc55edSMark Brown hp_reg &= ~(WM8961_HPR_RMV_SHORT | WM8961_HPL_RMV_SHORT); 2578d50e447SMark Brown snd_soc_write(codec, WM8961_ANALOGUE_HP_0, hp_reg); 25874dc55edSMark Brown 25974dc55edSMark Brown /* Disable the output stage */ 26074dc55edSMark Brown hp_reg &= ~(WM8961_HPR_ENA_OUTP | WM8961_HPL_ENA_OUTP); 2618d50e447SMark Brown snd_soc_write(codec, WM8961_ANALOGUE_HP_0, hp_reg); 26274dc55edSMark Brown 26374dc55edSMark Brown /* Disable DC offset cancellation */ 26474dc55edSMark Brown dcs_reg &= ~(WM8961_DCS_ENA_CHAN_HPR | 26574dc55edSMark Brown WM8961_DCS_ENA_CHAN_HPL); 2668d50e447SMark Brown snd_soc_write(codec, WM8961_DC_SERVO_1, dcs_reg); 26774dc55edSMark Brown 26874dc55edSMark Brown /* Finish up */ 26974dc55edSMark Brown hp_reg &= ~(WM8961_HPR_ENA_DLY | WM8961_HPR_ENA | 27074dc55edSMark Brown WM8961_HPL_ENA_DLY | WM8961_HPL_ENA); 2718d50e447SMark Brown snd_soc_write(codec, WM8961_ANALOGUE_HP_0, hp_reg); 27274dc55edSMark Brown 27374dc55edSMark Brown /* Disable the PGA */ 27474dc55edSMark Brown pwr_reg &= ~(WM8961_LOUT1_PGA | WM8961_ROUT1_PGA); 2758d50e447SMark Brown snd_soc_write(codec, WM8961_PWR_MGMT_2, pwr_reg); 27674dc55edSMark Brown 27774dc55edSMark Brown /* Disable the charge pump */ 27874dc55edSMark Brown dev_dbg(codec->dev, "Disabling charge pump\n"); 2798d50e447SMark Brown snd_soc_write(codec, WM8961_CHARGE_PUMP_1, 28074dc55edSMark Brown cp_reg & ~WM8961_CP_ENA); 28174dc55edSMark Brown } 28274dc55edSMark Brown 28374dc55edSMark Brown return 0; 28474dc55edSMark Brown } 28574dc55edSMark Brown 28674dc55edSMark Brown static int wm8961_spk_event(struct snd_soc_dapm_widget *w, 28774dc55edSMark Brown struct snd_kcontrol *kcontrol, int event) 28874dc55edSMark Brown { 2890e13064dSLars-Peter Clausen struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); 2908d50e447SMark Brown u16 pwr_reg = snd_soc_read(codec, WM8961_PWR_MGMT_2); 2918d50e447SMark Brown u16 spk_reg = snd_soc_read(codec, WM8961_CLASS_D_CONTROL_1); 29274dc55edSMark Brown 29374dc55edSMark Brown if (event & SND_SOC_DAPM_POST_PMU) { 29474dc55edSMark Brown /* Enable the PGA */ 29574dc55edSMark Brown pwr_reg |= WM8961_SPKL_PGA | WM8961_SPKR_PGA; 2968d50e447SMark Brown snd_soc_write(codec, WM8961_PWR_MGMT_2, pwr_reg); 29774dc55edSMark Brown 29874dc55edSMark Brown /* Enable the amplifier */ 29974dc55edSMark Brown spk_reg |= WM8961_SPKL_ENA | WM8961_SPKR_ENA; 3008d50e447SMark Brown snd_soc_write(codec, WM8961_CLASS_D_CONTROL_1, spk_reg); 30174dc55edSMark Brown } 30274dc55edSMark Brown 30374dc55edSMark Brown if (event & SND_SOC_DAPM_PRE_PMD) { 3047fcadfd1SAxel Lin /* Disable the amplifier */ 30574dc55edSMark Brown spk_reg &= ~(WM8961_SPKL_ENA | WM8961_SPKR_ENA); 3068d50e447SMark Brown snd_soc_write(codec, WM8961_CLASS_D_CONTROL_1, spk_reg); 30774dc55edSMark Brown 3087fcadfd1SAxel Lin /* Disable the PGA */ 30974dc55edSMark Brown pwr_reg &= ~(WM8961_SPKL_PGA | WM8961_SPKR_PGA); 3108d50e447SMark Brown snd_soc_write(codec, WM8961_PWR_MGMT_2, pwr_reg); 31174dc55edSMark Brown } 31274dc55edSMark Brown 31374dc55edSMark Brown return 0; 31474dc55edSMark Brown } 31574dc55edSMark Brown 31674dc55edSMark Brown static const char *adc_hpf_text[] = { 31774dc55edSMark Brown "Hi-fi", "Voice 1", "Voice 2", "Voice 3", 31874dc55edSMark Brown }; 31974dc55edSMark Brown 320a6616cdaSTakashi Iwai static SOC_ENUM_SINGLE_DECL(adc_hpf, 321a6616cdaSTakashi Iwai WM8961_ADC_DAC_CONTROL_2, 7, adc_hpf_text); 32274dc55edSMark Brown 32374dc55edSMark Brown static const char *dac_deemph_text[] = { 32474dc55edSMark Brown "None", "32kHz", "44.1kHz", "48kHz", 32574dc55edSMark Brown }; 32674dc55edSMark Brown 327a6616cdaSTakashi Iwai static SOC_ENUM_SINGLE_DECL(dac_deemph, 328a6616cdaSTakashi Iwai WM8961_ADC_DAC_CONTROL_1, 1, dac_deemph_text); 32974dc55edSMark Brown 33074dc55edSMark Brown static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1); 33174dc55edSMark Brown static const DECLARE_TLV_DB_SCALE(hp_sec_tlv, -700, 100, 0); 33274dc55edSMark Brown static const DECLARE_TLV_DB_SCALE(adc_tlv, -7200, 75, 1); 33374dc55edSMark Brown static const DECLARE_TLV_DB_SCALE(sidetone_tlv, -3600, 300, 0); 334*5d272d89SLars-Peter Clausen static const DECLARE_TLV_DB_RANGE(boost_tlv, 33574dc55edSMark Brown 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), 33674dc55edSMark Brown 1, 1, TLV_DB_SCALE_ITEM(13, 0, 0), 33774dc55edSMark Brown 2, 2, TLV_DB_SCALE_ITEM(20, 0, 0), 338*5d272d89SLars-Peter Clausen 3, 3, TLV_DB_SCALE_ITEM(29, 0, 0) 339*5d272d89SLars-Peter Clausen ); 34074dc55edSMark Brown static const DECLARE_TLV_DB_SCALE(pga_tlv, -2325, 75, 0); 34174dc55edSMark Brown 34274dc55edSMark Brown static const struct snd_kcontrol_new wm8961_snd_controls[] = { 34374dc55edSMark Brown SOC_DOUBLE_R_TLV("Headphone Volume", WM8961_LOUT1_VOLUME, WM8961_ROUT1_VOLUME, 34474dc55edSMark Brown 0, 127, 0, out_tlv), 34574dc55edSMark Brown SOC_DOUBLE_TLV("Headphone Secondary Volume", WM8961_ANALOGUE_HP_2, 34674dc55edSMark Brown 6, 3, 7, 0, hp_sec_tlv), 34774dc55edSMark Brown SOC_DOUBLE_R("Headphone ZC Switch", WM8961_LOUT1_VOLUME, WM8961_ROUT1_VOLUME, 34874dc55edSMark Brown 7, 1, 0), 34974dc55edSMark Brown 35074dc55edSMark Brown SOC_DOUBLE_R_TLV("Speaker Volume", WM8961_LOUT2_VOLUME, WM8961_ROUT2_VOLUME, 35174dc55edSMark Brown 0, 127, 0, out_tlv), 35274dc55edSMark Brown SOC_DOUBLE_R("Speaker ZC Switch", WM8961_LOUT2_VOLUME, WM8961_ROUT2_VOLUME, 35374dc55edSMark Brown 7, 1, 0), 35474dc55edSMark Brown SOC_SINGLE("Speaker AC Gain", WM8961_CLASS_D_CONTROL_2, 0, 7, 0), 35574dc55edSMark Brown 35674dc55edSMark Brown SOC_SINGLE("DAC x128 OSR Switch", WM8961_ADC_DAC_CONTROL_2, 0, 1, 0), 35774dc55edSMark Brown SOC_ENUM("DAC Deemphasis", dac_deemph), 35874dc55edSMark Brown SOC_SINGLE("DAC Soft Mute Switch", WM8961_ADC_DAC_CONTROL_2, 3, 1, 0), 35974dc55edSMark Brown 36074dc55edSMark Brown SOC_DOUBLE_R_TLV("Sidetone Volume", WM8961_DSP_SIDETONE_0, 36174dc55edSMark Brown WM8961_DSP_SIDETONE_1, 4, 12, 0, sidetone_tlv), 36274dc55edSMark Brown 36374dc55edSMark Brown SOC_SINGLE("ADC High Pass Filter Switch", WM8961_ADC_DAC_CONTROL_1, 0, 1, 0), 36474dc55edSMark Brown SOC_ENUM("ADC High Pass Filter Mode", adc_hpf), 36574dc55edSMark Brown 36674dc55edSMark Brown SOC_DOUBLE_R_TLV("Capture Volume", 36774dc55edSMark Brown WM8961_LEFT_ADC_VOLUME, WM8961_RIGHT_ADC_VOLUME, 36874dc55edSMark Brown 1, 119, 0, adc_tlv), 36974dc55edSMark Brown SOC_DOUBLE_R_TLV("Capture Boost Volume", 37074dc55edSMark Brown WM8961_ADCL_SIGNAL_PATH, WM8961_ADCR_SIGNAL_PATH, 37174dc55edSMark Brown 4, 3, 0, boost_tlv), 37274dc55edSMark Brown SOC_DOUBLE_R_TLV("Capture PGA Volume", 37374dc55edSMark Brown WM8961_LEFT_INPUT_VOLUME, WM8961_RIGHT_INPUT_VOLUME, 37474dc55edSMark Brown 0, 62, 0, pga_tlv), 37574dc55edSMark Brown SOC_DOUBLE_R("Capture PGA ZC Switch", 37674dc55edSMark Brown WM8961_LEFT_INPUT_VOLUME, WM8961_RIGHT_INPUT_VOLUME, 37774dc55edSMark Brown 6, 1, 1), 37874dc55edSMark Brown SOC_DOUBLE_R("Capture PGA Switch", 37974dc55edSMark Brown WM8961_LEFT_INPUT_VOLUME, WM8961_RIGHT_INPUT_VOLUME, 38074dc55edSMark Brown 7, 1, 1), 38174dc55edSMark Brown }; 38274dc55edSMark Brown 38374dc55edSMark Brown static const char *sidetone_text[] = { 38474dc55edSMark Brown "None", "Left", "Right" 38574dc55edSMark Brown }; 38674dc55edSMark Brown 387a6616cdaSTakashi Iwai static SOC_ENUM_SINGLE_DECL(dacl_sidetone, 388a6616cdaSTakashi Iwai WM8961_DSP_SIDETONE_0, 2, sidetone_text); 38974dc55edSMark Brown 390a6616cdaSTakashi Iwai static SOC_ENUM_SINGLE_DECL(dacr_sidetone, 391a6616cdaSTakashi Iwai WM8961_DSP_SIDETONE_1, 2, sidetone_text); 39274dc55edSMark Brown 39374dc55edSMark Brown static const struct snd_kcontrol_new dacl_mux = 39474dc55edSMark Brown SOC_DAPM_ENUM("DACL Sidetone", dacl_sidetone); 39574dc55edSMark Brown 39674dc55edSMark Brown static const struct snd_kcontrol_new dacr_mux = 39774dc55edSMark Brown SOC_DAPM_ENUM("DACR Sidetone", dacr_sidetone); 39874dc55edSMark Brown 39974dc55edSMark Brown static const struct snd_soc_dapm_widget wm8961_dapm_widgets[] = { 40074dc55edSMark Brown SND_SOC_DAPM_INPUT("LINPUT"), 40174dc55edSMark Brown SND_SOC_DAPM_INPUT("RINPUT"), 40274dc55edSMark Brown 40374dc55edSMark Brown SND_SOC_DAPM_SUPPLY("CLK_DSP", WM8961_CLOCKING2, 4, 0, NULL, 0), 40474dc55edSMark Brown 40574dc55edSMark Brown SND_SOC_DAPM_PGA("Left Input", WM8961_PWR_MGMT_1, 5, 0, NULL, 0), 40674dc55edSMark Brown SND_SOC_DAPM_PGA("Right Input", WM8961_PWR_MGMT_1, 4, 0, NULL, 0), 40774dc55edSMark Brown 40874dc55edSMark Brown SND_SOC_DAPM_ADC("ADCL", "HiFi Capture", WM8961_PWR_MGMT_1, 3, 0), 40974dc55edSMark Brown SND_SOC_DAPM_ADC("ADCR", "HiFi Capture", WM8961_PWR_MGMT_1, 2, 0), 41074dc55edSMark Brown 41120abf088SMark Brown SND_SOC_DAPM_SUPPLY("MICBIAS", WM8961_PWR_MGMT_1, 1, 0, NULL, 0), 41274dc55edSMark Brown 41374dc55edSMark Brown SND_SOC_DAPM_MUX("DACL Sidetone", SND_SOC_NOPM, 0, 0, &dacl_mux), 41474dc55edSMark Brown SND_SOC_DAPM_MUX("DACR Sidetone", SND_SOC_NOPM, 0, 0, &dacr_mux), 41574dc55edSMark Brown 41674dc55edSMark Brown SND_SOC_DAPM_DAC("DACL", "HiFi Playback", WM8961_PWR_MGMT_2, 8, 0), 41774dc55edSMark Brown SND_SOC_DAPM_DAC("DACR", "HiFi Playback", WM8961_PWR_MGMT_2, 7, 0), 41874dc55edSMark Brown 41974dc55edSMark Brown /* Handle as a mono path for DCS */ 42074dc55edSMark Brown SND_SOC_DAPM_PGA_E("Headphone Output", SND_SOC_NOPM, 42174dc55edSMark Brown 4, 0, NULL, 0, wm8961_hp_event, 42274dc55edSMark Brown SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), 42374dc55edSMark Brown SND_SOC_DAPM_PGA_E("Speaker Output", SND_SOC_NOPM, 42474dc55edSMark Brown 4, 0, NULL, 0, wm8961_spk_event, 42574dc55edSMark Brown SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), 42674dc55edSMark Brown 42774dc55edSMark Brown SND_SOC_DAPM_OUTPUT("HP_L"), 42874dc55edSMark Brown SND_SOC_DAPM_OUTPUT("HP_R"), 42974dc55edSMark Brown SND_SOC_DAPM_OUTPUT("SPK_LN"), 43074dc55edSMark Brown SND_SOC_DAPM_OUTPUT("SPK_LP"), 43174dc55edSMark Brown SND_SOC_DAPM_OUTPUT("SPK_RN"), 43274dc55edSMark Brown SND_SOC_DAPM_OUTPUT("SPK_RP"), 43374dc55edSMark Brown }; 43474dc55edSMark Brown 43574dc55edSMark Brown 43674dc55edSMark Brown static const struct snd_soc_dapm_route audio_paths[] = { 43774dc55edSMark Brown { "DACL", NULL, "CLK_DSP" }, 43874dc55edSMark Brown { "DACL", NULL, "DACL Sidetone" }, 43974dc55edSMark Brown { "DACR", NULL, "CLK_DSP" }, 44074dc55edSMark Brown { "DACR", NULL, "DACR Sidetone" }, 44174dc55edSMark Brown 44274dc55edSMark Brown { "DACL Sidetone", "Left", "ADCL" }, 44374dc55edSMark Brown { "DACL Sidetone", "Right", "ADCR" }, 44474dc55edSMark Brown 44574dc55edSMark Brown { "DACR Sidetone", "Left", "ADCL" }, 44674dc55edSMark Brown { "DACR Sidetone", "Right", "ADCR" }, 44774dc55edSMark Brown 44874dc55edSMark Brown { "HP_L", NULL, "Headphone Output" }, 44974dc55edSMark Brown { "HP_R", NULL, "Headphone Output" }, 45074dc55edSMark Brown { "Headphone Output", NULL, "DACL" }, 45174dc55edSMark Brown { "Headphone Output", NULL, "DACR" }, 45274dc55edSMark Brown 45374dc55edSMark Brown { "SPK_LN", NULL, "Speaker Output" }, 45474dc55edSMark Brown { "SPK_LP", NULL, "Speaker Output" }, 45574dc55edSMark Brown { "SPK_RN", NULL, "Speaker Output" }, 45674dc55edSMark Brown { "SPK_RP", NULL, "Speaker Output" }, 45774dc55edSMark Brown 45874dc55edSMark Brown { "Speaker Output", NULL, "DACL" }, 45974dc55edSMark Brown { "Speaker Output", NULL, "DACR" }, 46074dc55edSMark Brown 46174dc55edSMark Brown { "ADCL", NULL, "Left Input" }, 46274dc55edSMark Brown { "ADCL", NULL, "CLK_DSP" }, 46374dc55edSMark Brown { "ADCR", NULL, "Right Input" }, 46474dc55edSMark Brown { "ADCR", NULL, "CLK_DSP" }, 46574dc55edSMark Brown 46674dc55edSMark Brown { "Left Input", NULL, "LINPUT" }, 46774dc55edSMark Brown { "Right Input", NULL, "RINPUT" }, 46874dc55edSMark Brown 46974dc55edSMark Brown }; 47074dc55edSMark Brown 47174dc55edSMark Brown /* Values for CLK_SYS_RATE */ 47274dc55edSMark Brown static struct { 47374dc55edSMark Brown int ratio; 47474dc55edSMark Brown u16 val; 47574dc55edSMark Brown } wm8961_clk_sys_ratio[] = { 47674dc55edSMark Brown { 64, 0 }, 47774dc55edSMark Brown { 128, 1 }, 47874dc55edSMark Brown { 192, 2 }, 47974dc55edSMark Brown { 256, 3 }, 48074dc55edSMark Brown { 384, 4 }, 48174dc55edSMark Brown { 512, 5 }, 48274dc55edSMark Brown { 768, 6 }, 48374dc55edSMark Brown { 1024, 7 }, 48474dc55edSMark Brown { 1408, 8 }, 48574dc55edSMark Brown { 1536, 9 }, 48674dc55edSMark Brown }; 48774dc55edSMark Brown 48874dc55edSMark Brown /* Values for SAMPLE_RATE */ 48974dc55edSMark Brown static struct { 49074dc55edSMark Brown int rate; 49174dc55edSMark Brown u16 val; 49274dc55edSMark Brown } wm8961_srate[] = { 49374dc55edSMark Brown { 48000, 0 }, 49474dc55edSMark Brown { 44100, 0 }, 49574dc55edSMark Brown { 32000, 1 }, 49674dc55edSMark Brown { 22050, 2 }, 49774dc55edSMark Brown { 24000, 2 }, 49874dc55edSMark Brown { 16000, 3 }, 49974dc55edSMark Brown { 11250, 4 }, 50074dc55edSMark Brown { 12000, 4 }, 50174dc55edSMark Brown { 8000, 5 }, 50274dc55edSMark Brown }; 50374dc55edSMark Brown 50474dc55edSMark Brown static int wm8961_hw_params(struct snd_pcm_substream *substream, 50574dc55edSMark Brown struct snd_pcm_hw_params *params, 50674dc55edSMark Brown struct snd_soc_dai *dai) 50774dc55edSMark Brown { 50874dc55edSMark Brown struct snd_soc_codec *codec = dai->codec; 509b2c812e2SMark Brown struct wm8961_priv *wm8961 = snd_soc_codec_get_drvdata(codec); 51074dc55edSMark Brown int i, best, target, fs; 51174dc55edSMark Brown u16 reg; 51274dc55edSMark Brown 51374dc55edSMark Brown fs = params_rate(params); 51474dc55edSMark Brown 51574dc55edSMark Brown if (!wm8961->sysclk) { 51674dc55edSMark Brown dev_err(codec->dev, "MCLK has not been specified\n"); 51774dc55edSMark Brown return -EINVAL; 51874dc55edSMark Brown } 51974dc55edSMark Brown 52074dc55edSMark Brown /* Find the closest sample rate for the filters */ 52174dc55edSMark Brown best = 0; 52274dc55edSMark Brown for (i = 0; i < ARRAY_SIZE(wm8961_srate); i++) { 52374dc55edSMark Brown if (abs(wm8961_srate[i].rate - fs) < 52474dc55edSMark Brown abs(wm8961_srate[best].rate - fs)) 52574dc55edSMark Brown best = i; 52674dc55edSMark Brown } 5278d50e447SMark Brown reg = snd_soc_read(codec, WM8961_ADDITIONAL_CONTROL_3); 52874dc55edSMark Brown reg &= ~WM8961_SAMPLE_RATE_MASK; 52974dc55edSMark Brown reg |= wm8961_srate[best].val; 5308d50e447SMark Brown snd_soc_write(codec, WM8961_ADDITIONAL_CONTROL_3, reg); 53174dc55edSMark Brown dev_dbg(codec->dev, "Selected SRATE %dHz for %dHz\n", 53274dc55edSMark Brown wm8961_srate[best].rate, fs); 53374dc55edSMark Brown 53474dc55edSMark Brown /* Select a CLK_SYS/fs ratio equal to or higher than required */ 53574dc55edSMark Brown target = wm8961->sysclk / fs; 53674dc55edSMark Brown 53774dc55edSMark Brown if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && target < 64) { 53874dc55edSMark Brown dev_err(codec->dev, 53974dc55edSMark Brown "SYSCLK must be at least 64*fs for DAC\n"); 54074dc55edSMark Brown return -EINVAL; 54174dc55edSMark Brown } 54274dc55edSMark Brown if (substream->stream == SNDRV_PCM_STREAM_CAPTURE && target < 256) { 54374dc55edSMark Brown dev_err(codec->dev, 54474dc55edSMark Brown "SYSCLK must be at least 256*fs for ADC\n"); 54574dc55edSMark Brown return -EINVAL; 54674dc55edSMark Brown } 54774dc55edSMark Brown 54874dc55edSMark Brown for (i = 0; i < ARRAY_SIZE(wm8961_clk_sys_ratio); i++) { 54974dc55edSMark Brown if (wm8961_clk_sys_ratio[i].ratio >= target) 55074dc55edSMark Brown break; 55174dc55edSMark Brown } 55274dc55edSMark Brown if (i == ARRAY_SIZE(wm8961_clk_sys_ratio)) { 55374dc55edSMark Brown dev_err(codec->dev, "Unable to generate CLK_SYS_RATE\n"); 55474dc55edSMark Brown return -EINVAL; 55574dc55edSMark Brown } 55674dc55edSMark Brown dev_dbg(codec->dev, "Selected CLK_SYS_RATE of %d for %d/%d=%d\n", 55774dc55edSMark Brown wm8961_clk_sys_ratio[i].ratio, wm8961->sysclk, fs, 55874dc55edSMark Brown wm8961->sysclk / fs); 55974dc55edSMark Brown 5608d50e447SMark Brown reg = snd_soc_read(codec, WM8961_CLOCKING_4); 56174dc55edSMark Brown reg &= ~WM8961_CLK_SYS_RATE_MASK; 56274dc55edSMark Brown reg |= wm8961_clk_sys_ratio[i].val << WM8961_CLK_SYS_RATE_SHIFT; 5638d50e447SMark Brown snd_soc_write(codec, WM8961_CLOCKING_4, reg); 56474dc55edSMark Brown 5658d50e447SMark Brown reg = snd_soc_read(codec, WM8961_AUDIO_INTERFACE_0); 56674dc55edSMark Brown reg &= ~WM8961_WL_MASK; 5675d3aef91SMark Brown switch (params_width(params)) { 5685d3aef91SMark Brown case 16: 56974dc55edSMark Brown break; 5705d3aef91SMark Brown case 20: 57174dc55edSMark Brown reg |= 1 << WM8961_WL_SHIFT; 57274dc55edSMark Brown break; 5735d3aef91SMark Brown case 24: 57474dc55edSMark Brown reg |= 2 << WM8961_WL_SHIFT; 57574dc55edSMark Brown break; 5765d3aef91SMark Brown case 32: 57774dc55edSMark Brown reg |= 3 << WM8961_WL_SHIFT; 57874dc55edSMark Brown break; 57974dc55edSMark Brown default: 58074dc55edSMark Brown return -EINVAL; 58174dc55edSMark Brown } 5828d50e447SMark Brown snd_soc_write(codec, WM8961_AUDIO_INTERFACE_0, reg); 58374dc55edSMark Brown 58474dc55edSMark Brown /* Sloping stop-band filter is recommended for <= 24kHz */ 5858d50e447SMark Brown reg = snd_soc_read(codec, WM8961_ADC_DAC_CONTROL_2); 58674dc55edSMark Brown if (fs <= 24000) 58774dc55edSMark Brown reg |= WM8961_DACSLOPE; 58874dc55edSMark Brown else 58908b1a384SAxel Lin reg &= ~WM8961_DACSLOPE; 5908d50e447SMark Brown snd_soc_write(codec, WM8961_ADC_DAC_CONTROL_2, reg); 59174dc55edSMark Brown 59274dc55edSMark Brown return 0; 59374dc55edSMark Brown } 59474dc55edSMark Brown 59574dc55edSMark Brown static int wm8961_set_sysclk(struct snd_soc_dai *dai, int clk_id, 59674dc55edSMark Brown unsigned int freq, 59774dc55edSMark Brown int dir) 59874dc55edSMark Brown { 59974dc55edSMark Brown struct snd_soc_codec *codec = dai->codec; 600b2c812e2SMark Brown struct wm8961_priv *wm8961 = snd_soc_codec_get_drvdata(codec); 6018d50e447SMark Brown u16 reg = snd_soc_read(codec, WM8961_CLOCKING1); 60274dc55edSMark Brown 60374dc55edSMark Brown if (freq > 33000000) { 60474dc55edSMark Brown dev_err(codec->dev, "MCLK must be <33MHz\n"); 60574dc55edSMark Brown return -EINVAL; 60674dc55edSMark Brown } 60774dc55edSMark Brown 60874dc55edSMark Brown if (freq > 16500000) { 60974dc55edSMark Brown dev_dbg(codec->dev, "Using MCLK/2 for %dHz MCLK\n", freq); 61074dc55edSMark Brown reg |= WM8961_MCLKDIV; 61174dc55edSMark Brown freq /= 2; 61274dc55edSMark Brown } else { 61374dc55edSMark Brown dev_dbg(codec->dev, "Using MCLK/1 for %dHz MCLK\n", freq); 6142f7dceedSAxel Lin reg &= ~WM8961_MCLKDIV; 61574dc55edSMark Brown } 61674dc55edSMark Brown 6178d50e447SMark Brown snd_soc_write(codec, WM8961_CLOCKING1, reg); 61874dc55edSMark Brown 61974dc55edSMark Brown wm8961->sysclk = freq; 62074dc55edSMark Brown 62174dc55edSMark Brown return 0; 62274dc55edSMark Brown } 62374dc55edSMark Brown 62474dc55edSMark Brown static int wm8961_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) 62574dc55edSMark Brown { 62674dc55edSMark Brown struct snd_soc_codec *codec = dai->codec; 6278d50e447SMark Brown u16 aif = snd_soc_read(codec, WM8961_AUDIO_INTERFACE_0); 62874dc55edSMark Brown 62974dc55edSMark Brown aif &= ~(WM8961_BCLKINV | WM8961_LRP | 63074dc55edSMark Brown WM8961_MS | WM8961_FORMAT_MASK); 63174dc55edSMark Brown 63274dc55edSMark Brown switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 63374dc55edSMark Brown case SND_SOC_DAIFMT_CBM_CFM: 63474dc55edSMark Brown aif |= WM8961_MS; 63574dc55edSMark Brown break; 63674dc55edSMark Brown case SND_SOC_DAIFMT_CBS_CFS: 63774dc55edSMark Brown break; 63874dc55edSMark Brown default: 63974dc55edSMark Brown return -EINVAL; 64074dc55edSMark Brown } 64174dc55edSMark Brown 64274dc55edSMark Brown switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 64374dc55edSMark Brown case SND_SOC_DAIFMT_RIGHT_J: 64474dc55edSMark Brown break; 64574dc55edSMark Brown 64674dc55edSMark Brown case SND_SOC_DAIFMT_LEFT_J: 64774dc55edSMark Brown aif |= 1; 64874dc55edSMark Brown break; 64974dc55edSMark Brown 65074dc55edSMark Brown case SND_SOC_DAIFMT_I2S: 65174dc55edSMark Brown aif |= 2; 65274dc55edSMark Brown break; 65374dc55edSMark Brown 65474dc55edSMark Brown case SND_SOC_DAIFMT_DSP_B: 65574dc55edSMark Brown aif |= WM8961_LRP; 65674dc55edSMark Brown case SND_SOC_DAIFMT_DSP_A: 65774dc55edSMark Brown aif |= 3; 65874dc55edSMark Brown switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 65974dc55edSMark Brown case SND_SOC_DAIFMT_NB_NF: 66074dc55edSMark Brown case SND_SOC_DAIFMT_IB_NF: 66174dc55edSMark Brown break; 66274dc55edSMark Brown default: 66374dc55edSMark Brown return -EINVAL; 66474dc55edSMark Brown } 66574dc55edSMark Brown break; 66674dc55edSMark Brown 66774dc55edSMark Brown default: 66874dc55edSMark Brown return -EINVAL; 66974dc55edSMark Brown } 67074dc55edSMark Brown 67174dc55edSMark Brown switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 67274dc55edSMark Brown case SND_SOC_DAIFMT_NB_NF: 67374dc55edSMark Brown break; 67474dc55edSMark Brown case SND_SOC_DAIFMT_NB_IF: 67574dc55edSMark Brown aif |= WM8961_LRP; 67674dc55edSMark Brown break; 67774dc55edSMark Brown case SND_SOC_DAIFMT_IB_NF: 67874dc55edSMark Brown aif |= WM8961_BCLKINV; 67974dc55edSMark Brown break; 68074dc55edSMark Brown case SND_SOC_DAIFMT_IB_IF: 68174dc55edSMark Brown aif |= WM8961_BCLKINV | WM8961_LRP; 68274dc55edSMark Brown break; 68374dc55edSMark Brown default: 68474dc55edSMark Brown return -EINVAL; 68574dc55edSMark Brown } 68674dc55edSMark Brown 6878d50e447SMark Brown return snd_soc_write(codec, WM8961_AUDIO_INTERFACE_0, aif); 68874dc55edSMark Brown } 68974dc55edSMark Brown 69074dc55edSMark Brown static int wm8961_set_tristate(struct snd_soc_dai *dai, int tristate) 69174dc55edSMark Brown { 69274dc55edSMark Brown struct snd_soc_codec *codec = dai->codec; 6938d50e447SMark Brown u16 reg = snd_soc_read(codec, WM8961_ADDITIONAL_CONTROL_2); 69474dc55edSMark Brown 69574dc55edSMark Brown if (tristate) 69674dc55edSMark Brown reg |= WM8961_TRIS; 69774dc55edSMark Brown else 69874dc55edSMark Brown reg &= ~WM8961_TRIS; 69974dc55edSMark Brown 7008d50e447SMark Brown return snd_soc_write(codec, WM8961_ADDITIONAL_CONTROL_2, reg); 70174dc55edSMark Brown } 70274dc55edSMark Brown 70374dc55edSMark Brown static int wm8961_digital_mute(struct snd_soc_dai *dai, int mute) 70474dc55edSMark Brown { 70574dc55edSMark Brown struct snd_soc_codec *codec = dai->codec; 7068d50e447SMark Brown u16 reg = snd_soc_read(codec, WM8961_ADC_DAC_CONTROL_1); 70774dc55edSMark Brown 70874dc55edSMark Brown if (mute) 70974dc55edSMark Brown reg |= WM8961_DACMU; 71074dc55edSMark Brown else 71174dc55edSMark Brown reg &= ~WM8961_DACMU; 71274dc55edSMark Brown 71374dc55edSMark Brown msleep(17); 71474dc55edSMark Brown 7158d50e447SMark Brown return snd_soc_write(codec, WM8961_ADC_DAC_CONTROL_1, reg); 71674dc55edSMark Brown } 71774dc55edSMark Brown 71874dc55edSMark Brown static int wm8961_set_clkdiv(struct snd_soc_dai *dai, int div_id, int div) 71974dc55edSMark Brown { 72074dc55edSMark Brown struct snd_soc_codec *codec = dai->codec; 72174dc55edSMark Brown u16 reg; 72274dc55edSMark Brown 72374dc55edSMark Brown switch (div_id) { 72474dc55edSMark Brown case WM8961_BCLK: 7258d50e447SMark Brown reg = snd_soc_read(codec, WM8961_CLOCKING2); 72674dc55edSMark Brown reg &= ~WM8961_BCLKDIV_MASK; 72774dc55edSMark Brown reg |= div; 7288d50e447SMark Brown snd_soc_write(codec, WM8961_CLOCKING2, reg); 72974dc55edSMark Brown break; 73074dc55edSMark Brown 73174dc55edSMark Brown case WM8961_LRCLK: 7328d50e447SMark Brown reg = snd_soc_read(codec, WM8961_AUDIO_INTERFACE_2); 73374dc55edSMark Brown reg &= ~WM8961_LRCLK_RATE_MASK; 73474dc55edSMark Brown reg |= div; 7358d50e447SMark Brown snd_soc_write(codec, WM8961_AUDIO_INTERFACE_2, reg); 73674dc55edSMark Brown break; 73774dc55edSMark Brown 73874dc55edSMark Brown default: 73974dc55edSMark Brown return -EINVAL; 74074dc55edSMark Brown } 74174dc55edSMark Brown 74274dc55edSMark Brown return 0; 74374dc55edSMark Brown } 74474dc55edSMark Brown 74574dc55edSMark Brown static int wm8961_set_bias_level(struct snd_soc_codec *codec, 74674dc55edSMark Brown enum snd_soc_bias_level level) 74774dc55edSMark Brown { 74874dc55edSMark Brown u16 reg; 74974dc55edSMark Brown 75074dc55edSMark Brown /* This is all slightly unusual since we have no bypass paths 75174dc55edSMark Brown * and the output amplifier structure means we can just slam 75274dc55edSMark Brown * the biases straight up rather than having to ramp them 75374dc55edSMark Brown * slowly. 75474dc55edSMark Brown */ 75574dc55edSMark Brown switch (level) { 75674dc55edSMark Brown case SND_SOC_BIAS_ON: 75774dc55edSMark Brown break; 75874dc55edSMark Brown 75974dc55edSMark Brown case SND_SOC_BIAS_PREPARE: 760049e17d7SLars-Peter Clausen if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_STANDBY) { 76174dc55edSMark Brown /* Enable bias generation */ 7628d50e447SMark Brown reg = snd_soc_read(codec, WM8961_ANTI_POP); 76374dc55edSMark Brown reg |= WM8961_BUFIOEN | WM8961_BUFDCOPEN; 7648d50e447SMark Brown snd_soc_write(codec, WM8961_ANTI_POP, reg); 76574dc55edSMark Brown 76674dc55edSMark Brown /* VMID=2*50k, VREF */ 7678d50e447SMark Brown reg = snd_soc_read(codec, WM8961_PWR_MGMT_1); 76874dc55edSMark Brown reg &= ~WM8961_VMIDSEL_MASK; 76974dc55edSMark Brown reg |= (1 << WM8961_VMIDSEL_SHIFT) | WM8961_VREF; 7708d50e447SMark Brown snd_soc_write(codec, WM8961_PWR_MGMT_1, reg); 77174dc55edSMark Brown } 77274dc55edSMark Brown break; 77374dc55edSMark Brown 77474dc55edSMark Brown case SND_SOC_BIAS_STANDBY: 775049e17d7SLars-Peter Clausen if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_PREPARE) { 77674dc55edSMark Brown /* VREF off */ 7778d50e447SMark Brown reg = snd_soc_read(codec, WM8961_PWR_MGMT_1); 77874dc55edSMark Brown reg &= ~WM8961_VREF; 7798d50e447SMark Brown snd_soc_write(codec, WM8961_PWR_MGMT_1, reg); 78074dc55edSMark Brown 78174dc55edSMark Brown /* Bias generation off */ 7828d50e447SMark Brown reg = snd_soc_read(codec, WM8961_ANTI_POP); 78374dc55edSMark Brown reg &= ~(WM8961_BUFIOEN | WM8961_BUFDCOPEN); 7848d50e447SMark Brown snd_soc_write(codec, WM8961_ANTI_POP, reg); 78574dc55edSMark Brown 78674dc55edSMark Brown /* VMID off */ 7878d50e447SMark Brown reg = snd_soc_read(codec, WM8961_PWR_MGMT_1); 78874dc55edSMark Brown reg &= ~WM8961_VMIDSEL_MASK; 7898d50e447SMark Brown snd_soc_write(codec, WM8961_PWR_MGMT_1, reg); 79074dc55edSMark Brown } 79174dc55edSMark Brown break; 79274dc55edSMark Brown 79374dc55edSMark Brown case SND_SOC_BIAS_OFF: 79474dc55edSMark Brown break; 79574dc55edSMark Brown } 79674dc55edSMark Brown 79774dc55edSMark Brown return 0; 79874dc55edSMark Brown } 79974dc55edSMark Brown 80074dc55edSMark Brown 80174dc55edSMark Brown #define WM8961_RATES SNDRV_PCM_RATE_8000_48000 80274dc55edSMark Brown 80374dc55edSMark Brown #define WM8961_FORMATS \ 80474dc55edSMark Brown (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ 80574dc55edSMark Brown SNDRV_PCM_FMTBIT_S24_LE) 80674dc55edSMark Brown 80785e7652dSLars-Peter Clausen static const struct snd_soc_dai_ops wm8961_dai_ops = { 80874dc55edSMark Brown .hw_params = wm8961_hw_params, 80974dc55edSMark Brown .set_sysclk = wm8961_set_sysclk, 81074dc55edSMark Brown .set_fmt = wm8961_set_fmt, 81174dc55edSMark Brown .digital_mute = wm8961_digital_mute, 81274dc55edSMark Brown .set_tristate = wm8961_set_tristate, 81374dc55edSMark Brown .set_clkdiv = wm8961_set_clkdiv, 81474dc55edSMark Brown }; 81574dc55edSMark Brown 816f0fba2adSLiam Girdwood static struct snd_soc_dai_driver wm8961_dai = { 817f0fba2adSLiam Girdwood .name = "wm8961-hifi", 81874dc55edSMark Brown .playback = { 81974dc55edSMark Brown .stream_name = "HiFi Playback", 82074dc55edSMark Brown .channels_min = 1, 82174dc55edSMark Brown .channels_max = 2, 82274dc55edSMark Brown .rates = WM8961_RATES, 82374dc55edSMark Brown .formats = WM8961_FORMATS,}, 82474dc55edSMark Brown .capture = { 82574dc55edSMark Brown .stream_name = "HiFi Capture", 82674dc55edSMark Brown .channels_min = 1, 82774dc55edSMark Brown .channels_max = 2, 82874dc55edSMark Brown .rates = WM8961_RATES, 82974dc55edSMark Brown .formats = WM8961_FORMATS,}, 83074dc55edSMark Brown .ops = &wm8961_dai_ops, 83174dc55edSMark Brown }; 83274dc55edSMark Brown 833f0fba2adSLiam Girdwood static int wm8961_probe(struct snd_soc_codec *codec) 83474dc55edSMark Brown { 83574dc55edSMark Brown u16 reg; 83674dc55edSMark Brown 83774dc55edSMark Brown /* Enable class W */ 8388d50e447SMark Brown reg = snd_soc_read(codec, WM8961_CHARGE_PUMP_B); 83974dc55edSMark Brown reg |= WM8961_CP_DYN_PWR_MASK; 8408d50e447SMark Brown snd_soc_write(codec, WM8961_CHARGE_PUMP_B, reg); 84174dc55edSMark Brown 84274dc55edSMark Brown /* Latch volume update bits (right channel only, we always 84374dc55edSMark Brown * write both out) and default ZC on. */ 8448d50e447SMark Brown reg = snd_soc_read(codec, WM8961_ROUT1_VOLUME); 8458d50e447SMark Brown snd_soc_write(codec, WM8961_ROUT1_VOLUME, 84674dc55edSMark Brown reg | WM8961_LO1ZC | WM8961_OUT1VU); 8478d50e447SMark Brown snd_soc_write(codec, WM8961_LOUT1_VOLUME, reg | WM8961_LO1ZC); 8488d50e447SMark Brown reg = snd_soc_read(codec, WM8961_ROUT2_VOLUME); 8498d50e447SMark Brown snd_soc_write(codec, WM8961_ROUT2_VOLUME, 85074dc55edSMark Brown reg | WM8961_SPKRZC | WM8961_SPKVU); 8518d50e447SMark Brown snd_soc_write(codec, WM8961_LOUT2_VOLUME, reg | WM8961_SPKLZC); 85274dc55edSMark Brown 8538d50e447SMark Brown reg = snd_soc_read(codec, WM8961_RIGHT_ADC_VOLUME); 8548d50e447SMark Brown snd_soc_write(codec, WM8961_RIGHT_ADC_VOLUME, reg | WM8961_ADCVU); 8558d50e447SMark Brown reg = snd_soc_read(codec, WM8961_RIGHT_INPUT_VOLUME); 8568d50e447SMark Brown snd_soc_write(codec, WM8961_RIGHT_INPUT_VOLUME, reg | WM8961_IPVU); 85774dc55edSMark Brown 85874dc55edSMark Brown /* Use soft mute by default */ 8598d50e447SMark Brown reg = snd_soc_read(codec, WM8961_ADC_DAC_CONTROL_2); 86074dc55edSMark Brown reg |= WM8961_DACSMM; 8618d50e447SMark Brown snd_soc_write(codec, WM8961_ADC_DAC_CONTROL_2, reg); 86274dc55edSMark Brown 86374dc55edSMark Brown /* Use automatic clocking mode by default; for now this is all 86474dc55edSMark Brown * we support. 86574dc55edSMark Brown */ 8668d50e447SMark Brown reg = snd_soc_read(codec, WM8961_CLOCKING_3); 86774dc55edSMark Brown reg &= ~WM8961_MANUAL_MODE; 8688d50e447SMark Brown snd_soc_write(codec, WM8961_CLOCKING_3, reg); 86974dc55edSMark Brown 870f0fba2adSLiam Girdwood return 0; 87174dc55edSMark Brown } 87274dc55edSMark Brown 873f0fba2adSLiam Girdwood #ifdef CONFIG_PM 874f0fba2adSLiam Girdwood 875f0fba2adSLiam Girdwood static int wm8961_resume(struct snd_soc_codec *codec) 876f0fba2adSLiam Girdwood { 877202a51a8SMark Brown snd_soc_cache_sync(codec); 878f0fba2adSLiam Girdwood 879f0fba2adSLiam Girdwood return 0; 880f0fba2adSLiam Girdwood } 881f0fba2adSLiam Girdwood #else 882f0fba2adSLiam Girdwood #define wm8961_resume NULL 883f0fba2adSLiam Girdwood #endif 884f0fba2adSLiam Girdwood 885f0fba2adSLiam Girdwood static struct snd_soc_codec_driver soc_codec_dev_wm8961 = { 886f0fba2adSLiam Girdwood .probe = wm8961_probe, 887f0fba2adSLiam Girdwood .resume = wm8961_resume, 888f0fba2adSLiam Girdwood .set_bias_level = wm8961_set_bias_level, 8897bea32c5SLars-Peter Clausen .suspend_bias_off = true, 890c4f50dbcSLars-Peter Clausen 891c4f50dbcSLars-Peter Clausen .controls = wm8961_snd_controls, 892c4f50dbcSLars-Peter Clausen .num_controls = ARRAY_SIZE(wm8961_snd_controls), 893c4f50dbcSLars-Peter Clausen .dapm_widgets = wm8961_dapm_widgets, 894c4f50dbcSLars-Peter Clausen .num_dapm_widgets = ARRAY_SIZE(wm8961_dapm_widgets), 895c4f50dbcSLars-Peter Clausen .dapm_routes = audio_paths, 896c4f50dbcSLars-Peter Clausen .num_dapm_routes = ARRAY_SIZE(audio_paths), 89735ecf7cdSMark Brown }; 89835ecf7cdSMark Brown 89935ecf7cdSMark Brown static const struct regmap_config wm8961_regmap = { 90035ecf7cdSMark Brown .reg_bits = 8, 90135ecf7cdSMark Brown .val_bits = 16, 90235ecf7cdSMark Brown .max_register = WM8961_MAX_REGISTER, 90335ecf7cdSMark Brown 90435ecf7cdSMark Brown .reg_defaults = wm8961_reg_defaults, 90535ecf7cdSMark Brown .num_reg_defaults = ARRAY_SIZE(wm8961_reg_defaults), 90635ecf7cdSMark Brown .cache_type = REGCACHE_RBTREE, 90735ecf7cdSMark Brown 90835ecf7cdSMark Brown .volatile_reg = wm8961_volatile, 90935ecf7cdSMark Brown .readable_reg = wm8961_readable, 910f0fba2adSLiam Girdwood }; 911f0fba2adSLiam Girdwood 9127a79e94eSBill Pemberton static int wm8961_i2c_probe(struct i2c_client *i2c, 91374dc55edSMark Brown const struct i2c_device_id *id) 91474dc55edSMark Brown { 91574dc55edSMark Brown struct wm8961_priv *wm8961; 916b306e84fSMark Brown unsigned int val; 917f0fba2adSLiam Girdwood int ret; 91874dc55edSMark Brown 9192ec2a906SMark Brown wm8961 = devm_kzalloc(&i2c->dev, sizeof(struct wm8961_priv), 9202ec2a906SMark Brown GFP_KERNEL); 92174dc55edSMark Brown if (wm8961 == NULL) 92274dc55edSMark Brown return -ENOMEM; 92374dc55edSMark Brown 92435ecf7cdSMark Brown wm8961->regmap = devm_regmap_init_i2c(i2c, &wm8961_regmap); 92535ecf7cdSMark Brown if (IS_ERR(wm8961->regmap)) 92635ecf7cdSMark Brown return PTR_ERR(wm8961->regmap); 92735ecf7cdSMark Brown 928b306e84fSMark Brown ret = regmap_read(wm8961->regmap, WM8961_SOFTWARE_RESET, &val); 929b306e84fSMark Brown if (ret != 0) { 930b306e84fSMark Brown dev_err(&i2c->dev, "Failed to read chip ID: %d\n", ret); 931b306e84fSMark Brown return ret; 932b306e84fSMark Brown } 933b306e84fSMark Brown 934b306e84fSMark Brown if (val != 0x1801) { 935b306e84fSMark Brown dev_err(&i2c->dev, "Device is not a WM8961: ID=0x%x\n", val); 936b306e84fSMark Brown return -EINVAL; 937b306e84fSMark Brown } 938b306e84fSMark Brown 939b306e84fSMark Brown /* This isn't volatile - readback doesn't correspond to write */ 940b306e84fSMark Brown regcache_cache_bypass(wm8961->regmap, true); 941b306e84fSMark Brown ret = regmap_read(wm8961->regmap, WM8961_RIGHT_INPUT_VOLUME, &val); 942b306e84fSMark Brown regcache_cache_bypass(wm8961->regmap, false); 943b306e84fSMark Brown 944b306e84fSMark Brown if (ret != 0) { 945b306e84fSMark Brown dev_err(&i2c->dev, "Failed to read chip revision: %d\n", ret); 946b306e84fSMark Brown return ret; 947b306e84fSMark Brown } 948b306e84fSMark Brown 949b306e84fSMark Brown dev_info(&i2c->dev, "WM8961 family %d revision %c\n", 950b306e84fSMark Brown (val & WM8961_DEVICE_ID_MASK) >> WM8961_DEVICE_ID_SHIFT, 951b306e84fSMark Brown ((val & WM8961_CHIP_REV_MASK) >> WM8961_CHIP_REV_SHIFT) 952b306e84fSMark Brown + 'A'); 953b306e84fSMark Brown 954b306e84fSMark Brown ret = regmap_write(wm8961->regmap, WM8961_SOFTWARE_RESET, 0x1801); 955b306e84fSMark Brown if (ret != 0) { 956b306e84fSMark Brown dev_err(&i2c->dev, "Failed to issue reset: %d\n", ret); 957b306e84fSMark Brown return ret; 958b306e84fSMark Brown } 959b306e84fSMark Brown 96074dc55edSMark Brown i2c_set_clientdata(i2c, wm8961); 96174dc55edSMark Brown 962f0fba2adSLiam Girdwood ret = snd_soc_register_codec(&i2c->dev, 963f0fba2adSLiam Girdwood &soc_codec_dev_wm8961, &wm8961_dai, 1); 9642ec2a906SMark Brown 965f0fba2adSLiam Girdwood return ret; 96674dc55edSMark Brown } 96774dc55edSMark Brown 9687a79e94eSBill Pemberton static int wm8961_i2c_remove(struct i2c_client *client) 96974dc55edSMark Brown { 970f0fba2adSLiam Girdwood snd_soc_unregister_codec(&client->dev); 9712ec2a906SMark Brown 97274dc55edSMark Brown return 0; 97374dc55edSMark Brown } 97474dc55edSMark Brown 97574dc55edSMark Brown static const struct i2c_device_id wm8961_i2c_id[] = { 97674dc55edSMark Brown { "wm8961", 0 }, 97774dc55edSMark Brown { } 97874dc55edSMark Brown }; 97974dc55edSMark Brown MODULE_DEVICE_TABLE(i2c, wm8961_i2c_id); 98074dc55edSMark Brown 98174dc55edSMark Brown static struct i2c_driver wm8961_i2c_driver = { 98274dc55edSMark Brown .driver = { 983091edccfSMark Brown .name = "wm8961", 98474dc55edSMark Brown .owner = THIS_MODULE, 98574dc55edSMark Brown }, 98674dc55edSMark Brown .probe = wm8961_i2c_probe, 9877a79e94eSBill Pemberton .remove = wm8961_i2c_remove, 98874dc55edSMark Brown .id_table = wm8961_i2c_id, 98974dc55edSMark Brown }; 99074dc55edSMark Brown 9918b08eb28SSachin Kamat module_i2c_driver(wm8961_i2c_driver); 99274dc55edSMark Brown 99374dc55edSMark Brown MODULE_DESCRIPTION("ASoC WM8961 driver"); 99474dc55edSMark Brown MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); 99574dc55edSMark Brown MODULE_LICENSE("GPL"); 996