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 { 19774dc55edSMark Brown struct snd_soc_codec *codec = w->codec; 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 { 28974dc55edSMark Brown struct snd_soc_codec *codec = w->codec; 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); 33474dc55edSMark Brown static unsigned int boost_tlv[] = { 33574dc55edSMark Brown TLV_DB_RANGE_HEAD(4), 33674dc55edSMark Brown 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), 33774dc55edSMark Brown 1, 1, TLV_DB_SCALE_ITEM(13, 0, 0), 33874dc55edSMark Brown 2, 2, TLV_DB_SCALE_ITEM(20, 0, 0), 33974dc55edSMark Brown 3, 3, TLV_DB_SCALE_ITEM(29, 0, 0), 34074dc55edSMark Brown }; 34174dc55edSMark Brown static const DECLARE_TLV_DB_SCALE(pga_tlv, -2325, 75, 0); 34274dc55edSMark Brown 34374dc55edSMark Brown static const struct snd_kcontrol_new wm8961_snd_controls[] = { 34474dc55edSMark Brown SOC_DOUBLE_R_TLV("Headphone Volume", WM8961_LOUT1_VOLUME, WM8961_ROUT1_VOLUME, 34574dc55edSMark Brown 0, 127, 0, out_tlv), 34674dc55edSMark Brown SOC_DOUBLE_TLV("Headphone Secondary Volume", WM8961_ANALOGUE_HP_2, 34774dc55edSMark Brown 6, 3, 7, 0, hp_sec_tlv), 34874dc55edSMark Brown SOC_DOUBLE_R("Headphone ZC Switch", WM8961_LOUT1_VOLUME, WM8961_ROUT1_VOLUME, 34974dc55edSMark Brown 7, 1, 0), 35074dc55edSMark Brown 35174dc55edSMark Brown SOC_DOUBLE_R_TLV("Speaker Volume", WM8961_LOUT2_VOLUME, WM8961_ROUT2_VOLUME, 35274dc55edSMark Brown 0, 127, 0, out_tlv), 35374dc55edSMark Brown SOC_DOUBLE_R("Speaker ZC Switch", WM8961_LOUT2_VOLUME, WM8961_ROUT2_VOLUME, 35474dc55edSMark Brown 7, 1, 0), 35574dc55edSMark Brown SOC_SINGLE("Speaker AC Gain", WM8961_CLASS_D_CONTROL_2, 0, 7, 0), 35674dc55edSMark Brown 35774dc55edSMark Brown SOC_SINGLE("DAC x128 OSR Switch", WM8961_ADC_DAC_CONTROL_2, 0, 1, 0), 35874dc55edSMark Brown SOC_ENUM("DAC Deemphasis", dac_deemph), 35974dc55edSMark Brown SOC_SINGLE("DAC Soft Mute Switch", WM8961_ADC_DAC_CONTROL_2, 3, 1, 0), 36074dc55edSMark Brown 36174dc55edSMark Brown SOC_DOUBLE_R_TLV("Sidetone Volume", WM8961_DSP_SIDETONE_0, 36274dc55edSMark Brown WM8961_DSP_SIDETONE_1, 4, 12, 0, sidetone_tlv), 36374dc55edSMark Brown 36474dc55edSMark Brown SOC_SINGLE("ADC High Pass Filter Switch", WM8961_ADC_DAC_CONTROL_1, 0, 1, 0), 36574dc55edSMark Brown SOC_ENUM("ADC High Pass Filter Mode", adc_hpf), 36674dc55edSMark Brown 36774dc55edSMark Brown SOC_DOUBLE_R_TLV("Capture Volume", 36874dc55edSMark Brown WM8961_LEFT_ADC_VOLUME, WM8961_RIGHT_ADC_VOLUME, 36974dc55edSMark Brown 1, 119, 0, adc_tlv), 37074dc55edSMark Brown SOC_DOUBLE_R_TLV("Capture Boost Volume", 37174dc55edSMark Brown WM8961_ADCL_SIGNAL_PATH, WM8961_ADCR_SIGNAL_PATH, 37274dc55edSMark Brown 4, 3, 0, boost_tlv), 37374dc55edSMark Brown SOC_DOUBLE_R_TLV("Capture PGA Volume", 37474dc55edSMark Brown WM8961_LEFT_INPUT_VOLUME, WM8961_RIGHT_INPUT_VOLUME, 37574dc55edSMark Brown 0, 62, 0, pga_tlv), 37674dc55edSMark Brown SOC_DOUBLE_R("Capture PGA ZC Switch", 37774dc55edSMark Brown WM8961_LEFT_INPUT_VOLUME, WM8961_RIGHT_INPUT_VOLUME, 37874dc55edSMark Brown 6, 1, 1), 37974dc55edSMark Brown SOC_DOUBLE_R("Capture PGA Switch", 38074dc55edSMark Brown WM8961_LEFT_INPUT_VOLUME, WM8961_RIGHT_INPUT_VOLUME, 38174dc55edSMark Brown 7, 1, 1), 38274dc55edSMark Brown }; 38374dc55edSMark Brown 38474dc55edSMark Brown static const char *sidetone_text[] = { 38574dc55edSMark Brown "None", "Left", "Right" 38674dc55edSMark Brown }; 38774dc55edSMark Brown 388a6616cdaSTakashi Iwai static SOC_ENUM_SINGLE_DECL(dacl_sidetone, 389a6616cdaSTakashi Iwai WM8961_DSP_SIDETONE_0, 2, sidetone_text); 39074dc55edSMark Brown 391a6616cdaSTakashi Iwai static SOC_ENUM_SINGLE_DECL(dacr_sidetone, 392a6616cdaSTakashi Iwai WM8961_DSP_SIDETONE_1, 2, sidetone_text); 39374dc55edSMark Brown 39474dc55edSMark Brown static const struct snd_kcontrol_new dacl_mux = 39574dc55edSMark Brown SOC_DAPM_ENUM("DACL Sidetone", dacl_sidetone); 39674dc55edSMark Brown 39774dc55edSMark Brown static const struct snd_kcontrol_new dacr_mux = 39874dc55edSMark Brown SOC_DAPM_ENUM("DACR Sidetone", dacr_sidetone); 39974dc55edSMark Brown 40074dc55edSMark Brown static const struct snd_soc_dapm_widget wm8961_dapm_widgets[] = { 40174dc55edSMark Brown SND_SOC_DAPM_INPUT("LINPUT"), 40274dc55edSMark Brown SND_SOC_DAPM_INPUT("RINPUT"), 40374dc55edSMark Brown 40474dc55edSMark Brown SND_SOC_DAPM_SUPPLY("CLK_DSP", WM8961_CLOCKING2, 4, 0, NULL, 0), 40574dc55edSMark Brown 40674dc55edSMark Brown SND_SOC_DAPM_PGA("Left Input", WM8961_PWR_MGMT_1, 5, 0, NULL, 0), 40774dc55edSMark Brown SND_SOC_DAPM_PGA("Right Input", WM8961_PWR_MGMT_1, 4, 0, NULL, 0), 40874dc55edSMark Brown 40974dc55edSMark Brown SND_SOC_DAPM_ADC("ADCL", "HiFi Capture", WM8961_PWR_MGMT_1, 3, 0), 41074dc55edSMark Brown SND_SOC_DAPM_ADC("ADCR", "HiFi Capture", WM8961_PWR_MGMT_1, 2, 0), 41174dc55edSMark Brown 41220abf088SMark Brown SND_SOC_DAPM_SUPPLY("MICBIAS", WM8961_PWR_MGMT_1, 1, 0, NULL, 0), 41374dc55edSMark Brown 41474dc55edSMark Brown SND_SOC_DAPM_MUX("DACL Sidetone", SND_SOC_NOPM, 0, 0, &dacl_mux), 41574dc55edSMark Brown SND_SOC_DAPM_MUX("DACR Sidetone", SND_SOC_NOPM, 0, 0, &dacr_mux), 41674dc55edSMark Brown 41774dc55edSMark Brown SND_SOC_DAPM_DAC("DACL", "HiFi Playback", WM8961_PWR_MGMT_2, 8, 0), 41874dc55edSMark Brown SND_SOC_DAPM_DAC("DACR", "HiFi Playback", WM8961_PWR_MGMT_2, 7, 0), 41974dc55edSMark Brown 42074dc55edSMark Brown /* Handle as a mono path for DCS */ 42174dc55edSMark Brown SND_SOC_DAPM_PGA_E("Headphone Output", SND_SOC_NOPM, 42274dc55edSMark Brown 4, 0, NULL, 0, wm8961_hp_event, 42374dc55edSMark Brown SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), 42474dc55edSMark Brown SND_SOC_DAPM_PGA_E("Speaker Output", SND_SOC_NOPM, 42574dc55edSMark Brown 4, 0, NULL, 0, wm8961_spk_event, 42674dc55edSMark Brown SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), 42774dc55edSMark Brown 42874dc55edSMark Brown SND_SOC_DAPM_OUTPUT("HP_L"), 42974dc55edSMark Brown SND_SOC_DAPM_OUTPUT("HP_R"), 43074dc55edSMark Brown SND_SOC_DAPM_OUTPUT("SPK_LN"), 43174dc55edSMark Brown SND_SOC_DAPM_OUTPUT("SPK_LP"), 43274dc55edSMark Brown SND_SOC_DAPM_OUTPUT("SPK_RN"), 43374dc55edSMark Brown SND_SOC_DAPM_OUTPUT("SPK_RP"), 43474dc55edSMark Brown }; 43574dc55edSMark Brown 43674dc55edSMark Brown 43774dc55edSMark Brown static const struct snd_soc_dapm_route audio_paths[] = { 43874dc55edSMark Brown { "DACL", NULL, "CLK_DSP" }, 43974dc55edSMark Brown { "DACL", NULL, "DACL Sidetone" }, 44074dc55edSMark Brown { "DACR", NULL, "CLK_DSP" }, 44174dc55edSMark Brown { "DACR", NULL, "DACR Sidetone" }, 44274dc55edSMark Brown 44374dc55edSMark Brown { "DACL Sidetone", "Left", "ADCL" }, 44474dc55edSMark Brown { "DACL Sidetone", "Right", "ADCR" }, 44574dc55edSMark Brown 44674dc55edSMark Brown { "DACR Sidetone", "Left", "ADCL" }, 44774dc55edSMark Brown { "DACR Sidetone", "Right", "ADCR" }, 44874dc55edSMark Brown 44974dc55edSMark Brown { "HP_L", NULL, "Headphone Output" }, 45074dc55edSMark Brown { "HP_R", NULL, "Headphone Output" }, 45174dc55edSMark Brown { "Headphone Output", NULL, "DACL" }, 45274dc55edSMark Brown { "Headphone Output", NULL, "DACR" }, 45374dc55edSMark Brown 45474dc55edSMark Brown { "SPK_LN", NULL, "Speaker Output" }, 45574dc55edSMark Brown { "SPK_LP", NULL, "Speaker Output" }, 45674dc55edSMark Brown { "SPK_RN", NULL, "Speaker Output" }, 45774dc55edSMark Brown { "SPK_RP", NULL, "Speaker Output" }, 45874dc55edSMark Brown 45974dc55edSMark Brown { "Speaker Output", NULL, "DACL" }, 46074dc55edSMark Brown { "Speaker Output", NULL, "DACR" }, 46174dc55edSMark Brown 46274dc55edSMark Brown { "ADCL", NULL, "Left Input" }, 46374dc55edSMark Brown { "ADCL", NULL, "CLK_DSP" }, 46474dc55edSMark Brown { "ADCR", NULL, "Right Input" }, 46574dc55edSMark Brown { "ADCR", NULL, "CLK_DSP" }, 46674dc55edSMark Brown 46774dc55edSMark Brown { "Left Input", NULL, "LINPUT" }, 46874dc55edSMark Brown { "Right Input", NULL, "RINPUT" }, 46974dc55edSMark Brown 47074dc55edSMark Brown }; 47174dc55edSMark Brown 47274dc55edSMark Brown /* Values for CLK_SYS_RATE */ 47374dc55edSMark Brown static struct { 47474dc55edSMark Brown int ratio; 47574dc55edSMark Brown u16 val; 47674dc55edSMark Brown } wm8961_clk_sys_ratio[] = { 47774dc55edSMark Brown { 64, 0 }, 47874dc55edSMark Brown { 128, 1 }, 47974dc55edSMark Brown { 192, 2 }, 48074dc55edSMark Brown { 256, 3 }, 48174dc55edSMark Brown { 384, 4 }, 48274dc55edSMark Brown { 512, 5 }, 48374dc55edSMark Brown { 768, 6 }, 48474dc55edSMark Brown { 1024, 7 }, 48574dc55edSMark Brown { 1408, 8 }, 48674dc55edSMark Brown { 1536, 9 }, 48774dc55edSMark Brown }; 48874dc55edSMark Brown 48974dc55edSMark Brown /* Values for SAMPLE_RATE */ 49074dc55edSMark Brown static struct { 49174dc55edSMark Brown int rate; 49274dc55edSMark Brown u16 val; 49374dc55edSMark Brown } wm8961_srate[] = { 49474dc55edSMark Brown { 48000, 0 }, 49574dc55edSMark Brown { 44100, 0 }, 49674dc55edSMark Brown { 32000, 1 }, 49774dc55edSMark Brown { 22050, 2 }, 49874dc55edSMark Brown { 24000, 2 }, 49974dc55edSMark Brown { 16000, 3 }, 50074dc55edSMark Brown { 11250, 4 }, 50174dc55edSMark Brown { 12000, 4 }, 50274dc55edSMark Brown { 8000, 5 }, 50374dc55edSMark Brown }; 50474dc55edSMark Brown 50574dc55edSMark Brown static int wm8961_hw_params(struct snd_pcm_substream *substream, 50674dc55edSMark Brown struct snd_pcm_hw_params *params, 50774dc55edSMark Brown struct snd_soc_dai *dai) 50874dc55edSMark Brown { 50974dc55edSMark Brown struct snd_soc_codec *codec = dai->codec; 510b2c812e2SMark Brown struct wm8961_priv *wm8961 = snd_soc_codec_get_drvdata(codec); 51174dc55edSMark Brown int i, best, target, fs; 51274dc55edSMark Brown u16 reg; 51374dc55edSMark Brown 51474dc55edSMark Brown fs = params_rate(params); 51574dc55edSMark Brown 51674dc55edSMark Brown if (!wm8961->sysclk) { 51774dc55edSMark Brown dev_err(codec->dev, "MCLK has not been specified\n"); 51874dc55edSMark Brown return -EINVAL; 51974dc55edSMark Brown } 52074dc55edSMark Brown 52174dc55edSMark Brown /* Find the closest sample rate for the filters */ 52274dc55edSMark Brown best = 0; 52374dc55edSMark Brown for (i = 0; i < ARRAY_SIZE(wm8961_srate); i++) { 52474dc55edSMark Brown if (abs(wm8961_srate[i].rate - fs) < 52574dc55edSMark Brown abs(wm8961_srate[best].rate - fs)) 52674dc55edSMark Brown best = i; 52774dc55edSMark Brown } 5288d50e447SMark Brown reg = snd_soc_read(codec, WM8961_ADDITIONAL_CONTROL_3); 52974dc55edSMark Brown reg &= ~WM8961_SAMPLE_RATE_MASK; 53074dc55edSMark Brown reg |= wm8961_srate[best].val; 5318d50e447SMark Brown snd_soc_write(codec, WM8961_ADDITIONAL_CONTROL_3, reg); 53274dc55edSMark Brown dev_dbg(codec->dev, "Selected SRATE %dHz for %dHz\n", 53374dc55edSMark Brown wm8961_srate[best].rate, fs); 53474dc55edSMark Brown 53574dc55edSMark Brown /* Select a CLK_SYS/fs ratio equal to or higher than required */ 53674dc55edSMark Brown target = wm8961->sysclk / fs; 53774dc55edSMark Brown 53874dc55edSMark Brown if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && target < 64) { 53974dc55edSMark Brown dev_err(codec->dev, 54074dc55edSMark Brown "SYSCLK must be at least 64*fs for DAC\n"); 54174dc55edSMark Brown return -EINVAL; 54274dc55edSMark Brown } 54374dc55edSMark Brown if (substream->stream == SNDRV_PCM_STREAM_CAPTURE && target < 256) { 54474dc55edSMark Brown dev_err(codec->dev, 54574dc55edSMark Brown "SYSCLK must be at least 256*fs for ADC\n"); 54674dc55edSMark Brown return -EINVAL; 54774dc55edSMark Brown } 54874dc55edSMark Brown 54974dc55edSMark Brown for (i = 0; i < ARRAY_SIZE(wm8961_clk_sys_ratio); i++) { 55074dc55edSMark Brown if (wm8961_clk_sys_ratio[i].ratio >= target) 55174dc55edSMark Brown break; 55274dc55edSMark Brown } 55374dc55edSMark Brown if (i == ARRAY_SIZE(wm8961_clk_sys_ratio)) { 55474dc55edSMark Brown dev_err(codec->dev, "Unable to generate CLK_SYS_RATE\n"); 55574dc55edSMark Brown return -EINVAL; 55674dc55edSMark Brown } 55774dc55edSMark Brown dev_dbg(codec->dev, "Selected CLK_SYS_RATE of %d for %d/%d=%d\n", 55874dc55edSMark Brown wm8961_clk_sys_ratio[i].ratio, wm8961->sysclk, fs, 55974dc55edSMark Brown wm8961->sysclk / fs); 56074dc55edSMark Brown 5618d50e447SMark Brown reg = snd_soc_read(codec, WM8961_CLOCKING_4); 56274dc55edSMark Brown reg &= ~WM8961_CLK_SYS_RATE_MASK; 56374dc55edSMark Brown reg |= wm8961_clk_sys_ratio[i].val << WM8961_CLK_SYS_RATE_SHIFT; 5648d50e447SMark Brown snd_soc_write(codec, WM8961_CLOCKING_4, reg); 56574dc55edSMark Brown 5668d50e447SMark Brown reg = snd_soc_read(codec, WM8961_AUDIO_INTERFACE_0); 56774dc55edSMark Brown reg &= ~WM8961_WL_MASK; 568*5d3aef91SMark Brown switch (params_width(params)) { 569*5d3aef91SMark Brown case 16: 57074dc55edSMark Brown break; 571*5d3aef91SMark Brown case 20: 57274dc55edSMark Brown reg |= 1 << WM8961_WL_SHIFT; 57374dc55edSMark Brown break; 574*5d3aef91SMark Brown case 24: 57574dc55edSMark Brown reg |= 2 << WM8961_WL_SHIFT; 57674dc55edSMark Brown break; 577*5d3aef91SMark Brown case 32: 57874dc55edSMark Brown reg |= 3 << WM8961_WL_SHIFT; 57974dc55edSMark Brown break; 58074dc55edSMark Brown default: 58174dc55edSMark Brown return -EINVAL; 58274dc55edSMark Brown } 5838d50e447SMark Brown snd_soc_write(codec, WM8961_AUDIO_INTERFACE_0, reg); 58474dc55edSMark Brown 58574dc55edSMark Brown /* Sloping stop-band filter is recommended for <= 24kHz */ 5868d50e447SMark Brown reg = snd_soc_read(codec, WM8961_ADC_DAC_CONTROL_2); 58774dc55edSMark Brown if (fs <= 24000) 58874dc55edSMark Brown reg |= WM8961_DACSLOPE; 58974dc55edSMark Brown else 59008b1a384SAxel Lin reg &= ~WM8961_DACSLOPE; 5918d50e447SMark Brown snd_soc_write(codec, WM8961_ADC_DAC_CONTROL_2, reg); 59274dc55edSMark Brown 59374dc55edSMark Brown return 0; 59474dc55edSMark Brown } 59574dc55edSMark Brown 59674dc55edSMark Brown static int wm8961_set_sysclk(struct snd_soc_dai *dai, int clk_id, 59774dc55edSMark Brown unsigned int freq, 59874dc55edSMark Brown int dir) 59974dc55edSMark Brown { 60074dc55edSMark Brown struct snd_soc_codec *codec = dai->codec; 601b2c812e2SMark Brown struct wm8961_priv *wm8961 = snd_soc_codec_get_drvdata(codec); 6028d50e447SMark Brown u16 reg = snd_soc_read(codec, WM8961_CLOCKING1); 60374dc55edSMark Brown 60474dc55edSMark Brown if (freq > 33000000) { 60574dc55edSMark Brown dev_err(codec->dev, "MCLK must be <33MHz\n"); 60674dc55edSMark Brown return -EINVAL; 60774dc55edSMark Brown } 60874dc55edSMark Brown 60974dc55edSMark Brown if (freq > 16500000) { 61074dc55edSMark Brown dev_dbg(codec->dev, "Using MCLK/2 for %dHz MCLK\n", freq); 61174dc55edSMark Brown reg |= WM8961_MCLKDIV; 61274dc55edSMark Brown freq /= 2; 61374dc55edSMark Brown } else { 61474dc55edSMark Brown dev_dbg(codec->dev, "Using MCLK/1 for %dHz MCLK\n", freq); 6152f7dceedSAxel Lin reg &= ~WM8961_MCLKDIV; 61674dc55edSMark Brown } 61774dc55edSMark Brown 6188d50e447SMark Brown snd_soc_write(codec, WM8961_CLOCKING1, reg); 61974dc55edSMark Brown 62074dc55edSMark Brown wm8961->sysclk = freq; 62174dc55edSMark Brown 62274dc55edSMark Brown return 0; 62374dc55edSMark Brown } 62474dc55edSMark Brown 62574dc55edSMark Brown static int wm8961_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) 62674dc55edSMark Brown { 62774dc55edSMark Brown struct snd_soc_codec *codec = dai->codec; 6288d50e447SMark Brown u16 aif = snd_soc_read(codec, WM8961_AUDIO_INTERFACE_0); 62974dc55edSMark Brown 63074dc55edSMark Brown aif &= ~(WM8961_BCLKINV | WM8961_LRP | 63174dc55edSMark Brown WM8961_MS | WM8961_FORMAT_MASK); 63274dc55edSMark Brown 63374dc55edSMark Brown switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 63474dc55edSMark Brown case SND_SOC_DAIFMT_CBM_CFM: 63574dc55edSMark Brown aif |= WM8961_MS; 63674dc55edSMark Brown break; 63774dc55edSMark Brown case SND_SOC_DAIFMT_CBS_CFS: 63874dc55edSMark Brown break; 63974dc55edSMark Brown default: 64074dc55edSMark Brown return -EINVAL; 64174dc55edSMark Brown } 64274dc55edSMark Brown 64374dc55edSMark Brown switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 64474dc55edSMark Brown case SND_SOC_DAIFMT_RIGHT_J: 64574dc55edSMark Brown break; 64674dc55edSMark Brown 64774dc55edSMark Brown case SND_SOC_DAIFMT_LEFT_J: 64874dc55edSMark Brown aif |= 1; 64974dc55edSMark Brown break; 65074dc55edSMark Brown 65174dc55edSMark Brown case SND_SOC_DAIFMT_I2S: 65274dc55edSMark Brown aif |= 2; 65374dc55edSMark Brown break; 65474dc55edSMark Brown 65574dc55edSMark Brown case SND_SOC_DAIFMT_DSP_B: 65674dc55edSMark Brown aif |= WM8961_LRP; 65774dc55edSMark Brown case SND_SOC_DAIFMT_DSP_A: 65874dc55edSMark Brown aif |= 3; 65974dc55edSMark Brown switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 66074dc55edSMark Brown case SND_SOC_DAIFMT_NB_NF: 66174dc55edSMark Brown case SND_SOC_DAIFMT_IB_NF: 66274dc55edSMark Brown break; 66374dc55edSMark Brown default: 66474dc55edSMark Brown return -EINVAL; 66574dc55edSMark Brown } 66674dc55edSMark Brown break; 66774dc55edSMark Brown 66874dc55edSMark Brown default: 66974dc55edSMark Brown return -EINVAL; 67074dc55edSMark Brown } 67174dc55edSMark Brown 67274dc55edSMark Brown switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 67374dc55edSMark Brown case SND_SOC_DAIFMT_NB_NF: 67474dc55edSMark Brown break; 67574dc55edSMark Brown case SND_SOC_DAIFMT_NB_IF: 67674dc55edSMark Brown aif |= WM8961_LRP; 67774dc55edSMark Brown break; 67874dc55edSMark Brown case SND_SOC_DAIFMT_IB_NF: 67974dc55edSMark Brown aif |= WM8961_BCLKINV; 68074dc55edSMark Brown break; 68174dc55edSMark Brown case SND_SOC_DAIFMT_IB_IF: 68274dc55edSMark Brown aif |= WM8961_BCLKINV | WM8961_LRP; 68374dc55edSMark Brown break; 68474dc55edSMark Brown default: 68574dc55edSMark Brown return -EINVAL; 68674dc55edSMark Brown } 68774dc55edSMark Brown 6888d50e447SMark Brown return snd_soc_write(codec, WM8961_AUDIO_INTERFACE_0, aif); 68974dc55edSMark Brown } 69074dc55edSMark Brown 69174dc55edSMark Brown static int wm8961_set_tristate(struct snd_soc_dai *dai, int tristate) 69274dc55edSMark Brown { 69374dc55edSMark Brown struct snd_soc_codec *codec = dai->codec; 6948d50e447SMark Brown u16 reg = snd_soc_read(codec, WM8961_ADDITIONAL_CONTROL_2); 69574dc55edSMark Brown 69674dc55edSMark Brown if (tristate) 69774dc55edSMark Brown reg |= WM8961_TRIS; 69874dc55edSMark Brown else 69974dc55edSMark Brown reg &= ~WM8961_TRIS; 70074dc55edSMark Brown 7018d50e447SMark Brown return snd_soc_write(codec, WM8961_ADDITIONAL_CONTROL_2, reg); 70274dc55edSMark Brown } 70374dc55edSMark Brown 70474dc55edSMark Brown static int wm8961_digital_mute(struct snd_soc_dai *dai, int mute) 70574dc55edSMark Brown { 70674dc55edSMark Brown struct snd_soc_codec *codec = dai->codec; 7078d50e447SMark Brown u16 reg = snd_soc_read(codec, WM8961_ADC_DAC_CONTROL_1); 70874dc55edSMark Brown 70974dc55edSMark Brown if (mute) 71074dc55edSMark Brown reg |= WM8961_DACMU; 71174dc55edSMark Brown else 71274dc55edSMark Brown reg &= ~WM8961_DACMU; 71374dc55edSMark Brown 71474dc55edSMark Brown msleep(17); 71574dc55edSMark Brown 7168d50e447SMark Brown return snd_soc_write(codec, WM8961_ADC_DAC_CONTROL_1, reg); 71774dc55edSMark Brown } 71874dc55edSMark Brown 71974dc55edSMark Brown static int wm8961_set_clkdiv(struct snd_soc_dai *dai, int div_id, int div) 72074dc55edSMark Brown { 72174dc55edSMark Brown struct snd_soc_codec *codec = dai->codec; 72274dc55edSMark Brown u16 reg; 72374dc55edSMark Brown 72474dc55edSMark Brown switch (div_id) { 72574dc55edSMark Brown case WM8961_BCLK: 7268d50e447SMark Brown reg = snd_soc_read(codec, WM8961_CLOCKING2); 72774dc55edSMark Brown reg &= ~WM8961_BCLKDIV_MASK; 72874dc55edSMark Brown reg |= div; 7298d50e447SMark Brown snd_soc_write(codec, WM8961_CLOCKING2, reg); 73074dc55edSMark Brown break; 73174dc55edSMark Brown 73274dc55edSMark Brown case WM8961_LRCLK: 7338d50e447SMark Brown reg = snd_soc_read(codec, WM8961_AUDIO_INTERFACE_2); 73474dc55edSMark Brown reg &= ~WM8961_LRCLK_RATE_MASK; 73574dc55edSMark Brown reg |= div; 7368d50e447SMark Brown snd_soc_write(codec, WM8961_AUDIO_INTERFACE_2, reg); 73774dc55edSMark Brown break; 73874dc55edSMark Brown 73974dc55edSMark Brown default: 74074dc55edSMark Brown return -EINVAL; 74174dc55edSMark Brown } 74274dc55edSMark Brown 74374dc55edSMark Brown return 0; 74474dc55edSMark Brown } 74574dc55edSMark Brown 74674dc55edSMark Brown static int wm8961_set_bias_level(struct snd_soc_codec *codec, 74774dc55edSMark Brown enum snd_soc_bias_level level) 74874dc55edSMark Brown { 74974dc55edSMark Brown u16 reg; 75074dc55edSMark Brown 75174dc55edSMark Brown /* This is all slightly unusual since we have no bypass paths 75274dc55edSMark Brown * and the output amplifier structure means we can just slam 75374dc55edSMark Brown * the biases straight up rather than having to ramp them 75474dc55edSMark Brown * slowly. 75574dc55edSMark Brown */ 75674dc55edSMark Brown switch (level) { 75774dc55edSMark Brown case SND_SOC_BIAS_ON: 75874dc55edSMark Brown break; 75974dc55edSMark Brown 76074dc55edSMark Brown case SND_SOC_BIAS_PREPARE: 761ce6120ccSLiam Girdwood if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY) { 76274dc55edSMark Brown /* Enable bias generation */ 7638d50e447SMark Brown reg = snd_soc_read(codec, WM8961_ANTI_POP); 76474dc55edSMark Brown reg |= WM8961_BUFIOEN | WM8961_BUFDCOPEN; 7658d50e447SMark Brown snd_soc_write(codec, WM8961_ANTI_POP, reg); 76674dc55edSMark Brown 76774dc55edSMark Brown /* VMID=2*50k, VREF */ 7688d50e447SMark Brown reg = snd_soc_read(codec, WM8961_PWR_MGMT_1); 76974dc55edSMark Brown reg &= ~WM8961_VMIDSEL_MASK; 77074dc55edSMark Brown reg |= (1 << WM8961_VMIDSEL_SHIFT) | WM8961_VREF; 7718d50e447SMark Brown snd_soc_write(codec, WM8961_PWR_MGMT_1, reg); 77274dc55edSMark Brown } 77374dc55edSMark Brown break; 77474dc55edSMark Brown 77574dc55edSMark Brown case SND_SOC_BIAS_STANDBY: 776ce6120ccSLiam Girdwood if (codec->dapm.bias_level == SND_SOC_BIAS_PREPARE) { 77774dc55edSMark Brown /* VREF off */ 7788d50e447SMark Brown reg = snd_soc_read(codec, WM8961_PWR_MGMT_1); 77974dc55edSMark Brown reg &= ~WM8961_VREF; 7808d50e447SMark Brown snd_soc_write(codec, WM8961_PWR_MGMT_1, reg); 78174dc55edSMark Brown 78274dc55edSMark Brown /* Bias generation off */ 7838d50e447SMark Brown reg = snd_soc_read(codec, WM8961_ANTI_POP); 78474dc55edSMark Brown reg &= ~(WM8961_BUFIOEN | WM8961_BUFDCOPEN); 7858d50e447SMark Brown snd_soc_write(codec, WM8961_ANTI_POP, reg); 78674dc55edSMark Brown 78774dc55edSMark Brown /* VMID off */ 7888d50e447SMark Brown reg = snd_soc_read(codec, WM8961_PWR_MGMT_1); 78974dc55edSMark Brown reg &= ~WM8961_VMIDSEL_MASK; 7908d50e447SMark Brown snd_soc_write(codec, WM8961_PWR_MGMT_1, reg); 79174dc55edSMark Brown } 79274dc55edSMark Brown break; 79374dc55edSMark Brown 79474dc55edSMark Brown case SND_SOC_BIAS_OFF: 79574dc55edSMark Brown break; 79674dc55edSMark Brown } 79774dc55edSMark Brown 798ce6120ccSLiam Girdwood codec->dapm.bias_level = level; 79974dc55edSMark Brown 80074dc55edSMark Brown return 0; 80174dc55edSMark Brown } 80274dc55edSMark Brown 80374dc55edSMark Brown 80474dc55edSMark Brown #define WM8961_RATES SNDRV_PCM_RATE_8000_48000 80574dc55edSMark Brown 80674dc55edSMark Brown #define WM8961_FORMATS \ 80774dc55edSMark Brown (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ 80874dc55edSMark Brown SNDRV_PCM_FMTBIT_S24_LE) 80974dc55edSMark Brown 81085e7652dSLars-Peter Clausen static const struct snd_soc_dai_ops wm8961_dai_ops = { 81174dc55edSMark Brown .hw_params = wm8961_hw_params, 81274dc55edSMark Brown .set_sysclk = wm8961_set_sysclk, 81374dc55edSMark Brown .set_fmt = wm8961_set_fmt, 81474dc55edSMark Brown .digital_mute = wm8961_digital_mute, 81574dc55edSMark Brown .set_tristate = wm8961_set_tristate, 81674dc55edSMark Brown .set_clkdiv = wm8961_set_clkdiv, 81774dc55edSMark Brown }; 81874dc55edSMark Brown 819f0fba2adSLiam Girdwood static struct snd_soc_dai_driver wm8961_dai = { 820f0fba2adSLiam Girdwood .name = "wm8961-hifi", 82174dc55edSMark Brown .playback = { 82274dc55edSMark Brown .stream_name = "HiFi Playback", 82374dc55edSMark Brown .channels_min = 1, 82474dc55edSMark Brown .channels_max = 2, 82574dc55edSMark Brown .rates = WM8961_RATES, 82674dc55edSMark Brown .formats = WM8961_FORMATS,}, 82774dc55edSMark Brown .capture = { 82874dc55edSMark Brown .stream_name = "HiFi Capture", 82974dc55edSMark Brown .channels_min = 1, 83074dc55edSMark Brown .channels_max = 2, 83174dc55edSMark Brown .rates = WM8961_RATES, 83274dc55edSMark Brown .formats = WM8961_FORMATS,}, 83374dc55edSMark Brown .ops = &wm8961_dai_ops, 83474dc55edSMark Brown }; 83574dc55edSMark Brown 836f0fba2adSLiam Girdwood static int wm8961_probe(struct snd_soc_codec *codec) 83774dc55edSMark Brown { 838ce6120ccSLiam Girdwood struct snd_soc_dapm_context *dapm = &codec->dapm; 83974dc55edSMark Brown u16 reg; 84074dc55edSMark Brown 84174dc55edSMark Brown /* Enable class W */ 8428d50e447SMark Brown reg = snd_soc_read(codec, WM8961_CHARGE_PUMP_B); 84374dc55edSMark Brown reg |= WM8961_CP_DYN_PWR_MASK; 8448d50e447SMark Brown snd_soc_write(codec, WM8961_CHARGE_PUMP_B, reg); 84574dc55edSMark Brown 84674dc55edSMark Brown /* Latch volume update bits (right channel only, we always 84774dc55edSMark Brown * write both out) and default ZC on. */ 8488d50e447SMark Brown reg = snd_soc_read(codec, WM8961_ROUT1_VOLUME); 8498d50e447SMark Brown snd_soc_write(codec, WM8961_ROUT1_VOLUME, 85074dc55edSMark Brown reg | WM8961_LO1ZC | WM8961_OUT1VU); 8518d50e447SMark Brown snd_soc_write(codec, WM8961_LOUT1_VOLUME, reg | WM8961_LO1ZC); 8528d50e447SMark Brown reg = snd_soc_read(codec, WM8961_ROUT2_VOLUME); 8538d50e447SMark Brown snd_soc_write(codec, WM8961_ROUT2_VOLUME, 85474dc55edSMark Brown reg | WM8961_SPKRZC | WM8961_SPKVU); 8558d50e447SMark Brown snd_soc_write(codec, WM8961_LOUT2_VOLUME, reg | WM8961_SPKLZC); 85674dc55edSMark Brown 8578d50e447SMark Brown reg = snd_soc_read(codec, WM8961_RIGHT_ADC_VOLUME); 8588d50e447SMark Brown snd_soc_write(codec, WM8961_RIGHT_ADC_VOLUME, reg | WM8961_ADCVU); 8598d50e447SMark Brown reg = snd_soc_read(codec, WM8961_RIGHT_INPUT_VOLUME); 8608d50e447SMark Brown snd_soc_write(codec, WM8961_RIGHT_INPUT_VOLUME, reg | WM8961_IPVU); 86174dc55edSMark Brown 86274dc55edSMark Brown /* Use soft mute by default */ 8638d50e447SMark Brown reg = snd_soc_read(codec, WM8961_ADC_DAC_CONTROL_2); 86474dc55edSMark Brown reg |= WM8961_DACSMM; 8658d50e447SMark Brown snd_soc_write(codec, WM8961_ADC_DAC_CONTROL_2, reg); 86674dc55edSMark Brown 86774dc55edSMark Brown /* Use automatic clocking mode by default; for now this is all 86874dc55edSMark Brown * we support. 86974dc55edSMark Brown */ 8708d50e447SMark Brown reg = snd_soc_read(codec, WM8961_CLOCKING_3); 87174dc55edSMark Brown reg &= ~WM8961_MANUAL_MODE; 8728d50e447SMark Brown snd_soc_write(codec, WM8961_CLOCKING_3, reg); 87374dc55edSMark Brown 87474dc55edSMark Brown wm8961_set_bias_level(codec, SND_SOC_BIAS_STANDBY); 87574dc55edSMark Brown 876022658beSLiam Girdwood snd_soc_add_codec_controls(codec, wm8961_snd_controls, 877f0fba2adSLiam Girdwood ARRAY_SIZE(wm8961_snd_controls)); 878ce6120ccSLiam Girdwood snd_soc_dapm_new_controls(dapm, wm8961_dapm_widgets, 879f0fba2adSLiam Girdwood ARRAY_SIZE(wm8961_dapm_widgets)); 880ce6120ccSLiam Girdwood snd_soc_dapm_add_routes(dapm, audio_paths, ARRAY_SIZE(audio_paths)); 88174dc55edSMark Brown 88274dc55edSMark Brown return 0; 88374dc55edSMark Brown } 88474dc55edSMark Brown 885f0fba2adSLiam Girdwood static int wm8961_remove(struct snd_soc_codec *codec) 88674dc55edSMark Brown { 887f0fba2adSLiam Girdwood wm8961_set_bias_level(codec, SND_SOC_BIAS_OFF); 888f0fba2adSLiam Girdwood return 0; 88974dc55edSMark Brown } 89074dc55edSMark Brown 891f0fba2adSLiam Girdwood #ifdef CONFIG_PM 89284b315eeSLars-Peter Clausen static int wm8961_suspend(struct snd_soc_codec *codec) 893f0fba2adSLiam Girdwood { 894f0fba2adSLiam Girdwood wm8961_set_bias_level(codec, SND_SOC_BIAS_OFF); 895f0fba2adSLiam Girdwood 896f0fba2adSLiam Girdwood return 0; 897f0fba2adSLiam Girdwood } 898f0fba2adSLiam Girdwood 899f0fba2adSLiam Girdwood static int wm8961_resume(struct snd_soc_codec *codec) 900f0fba2adSLiam Girdwood { 901202a51a8SMark Brown snd_soc_cache_sync(codec); 902f0fba2adSLiam Girdwood 903f0fba2adSLiam Girdwood wm8961_set_bias_level(codec, SND_SOC_BIAS_STANDBY); 904f0fba2adSLiam Girdwood 905f0fba2adSLiam Girdwood return 0; 906f0fba2adSLiam Girdwood } 907f0fba2adSLiam Girdwood #else 908f0fba2adSLiam Girdwood #define wm8961_suspend NULL 909f0fba2adSLiam Girdwood #define wm8961_resume NULL 910f0fba2adSLiam Girdwood #endif 911f0fba2adSLiam Girdwood 912f0fba2adSLiam Girdwood static struct snd_soc_codec_driver soc_codec_dev_wm8961 = { 913f0fba2adSLiam Girdwood .probe = wm8961_probe, 914f0fba2adSLiam Girdwood .remove = wm8961_remove, 915f0fba2adSLiam Girdwood .suspend = wm8961_suspend, 916f0fba2adSLiam Girdwood .resume = wm8961_resume, 917f0fba2adSLiam Girdwood .set_bias_level = wm8961_set_bias_level, 91835ecf7cdSMark Brown }; 91935ecf7cdSMark Brown 92035ecf7cdSMark Brown static const struct regmap_config wm8961_regmap = { 92135ecf7cdSMark Brown .reg_bits = 8, 92235ecf7cdSMark Brown .val_bits = 16, 92335ecf7cdSMark Brown .max_register = WM8961_MAX_REGISTER, 92435ecf7cdSMark Brown 92535ecf7cdSMark Brown .reg_defaults = wm8961_reg_defaults, 92635ecf7cdSMark Brown .num_reg_defaults = ARRAY_SIZE(wm8961_reg_defaults), 92735ecf7cdSMark Brown .cache_type = REGCACHE_RBTREE, 92835ecf7cdSMark Brown 92935ecf7cdSMark Brown .volatile_reg = wm8961_volatile, 93035ecf7cdSMark Brown .readable_reg = wm8961_readable, 931f0fba2adSLiam Girdwood }; 932f0fba2adSLiam Girdwood 9337a79e94eSBill Pemberton static int wm8961_i2c_probe(struct i2c_client *i2c, 93474dc55edSMark Brown const struct i2c_device_id *id) 93574dc55edSMark Brown { 93674dc55edSMark Brown struct wm8961_priv *wm8961; 937b306e84fSMark Brown unsigned int val; 938f0fba2adSLiam Girdwood int ret; 93974dc55edSMark Brown 9402ec2a906SMark Brown wm8961 = devm_kzalloc(&i2c->dev, sizeof(struct wm8961_priv), 9412ec2a906SMark Brown GFP_KERNEL); 94274dc55edSMark Brown if (wm8961 == NULL) 94374dc55edSMark Brown return -ENOMEM; 94474dc55edSMark Brown 94535ecf7cdSMark Brown wm8961->regmap = devm_regmap_init_i2c(i2c, &wm8961_regmap); 94635ecf7cdSMark Brown if (IS_ERR(wm8961->regmap)) 94735ecf7cdSMark Brown return PTR_ERR(wm8961->regmap); 94835ecf7cdSMark Brown 949b306e84fSMark Brown ret = regmap_read(wm8961->regmap, WM8961_SOFTWARE_RESET, &val); 950b306e84fSMark Brown if (ret != 0) { 951b306e84fSMark Brown dev_err(&i2c->dev, "Failed to read chip ID: %d\n", ret); 952b306e84fSMark Brown return ret; 953b306e84fSMark Brown } 954b306e84fSMark Brown 955b306e84fSMark Brown if (val != 0x1801) { 956b306e84fSMark Brown dev_err(&i2c->dev, "Device is not a WM8961: ID=0x%x\n", val); 957b306e84fSMark Brown return -EINVAL; 958b306e84fSMark Brown } 959b306e84fSMark Brown 960b306e84fSMark Brown /* This isn't volatile - readback doesn't correspond to write */ 961b306e84fSMark Brown regcache_cache_bypass(wm8961->regmap, true); 962b306e84fSMark Brown ret = regmap_read(wm8961->regmap, WM8961_RIGHT_INPUT_VOLUME, &val); 963b306e84fSMark Brown regcache_cache_bypass(wm8961->regmap, false); 964b306e84fSMark Brown 965b306e84fSMark Brown if (ret != 0) { 966b306e84fSMark Brown dev_err(&i2c->dev, "Failed to read chip revision: %d\n", ret); 967b306e84fSMark Brown return ret; 968b306e84fSMark Brown } 969b306e84fSMark Brown 970b306e84fSMark Brown dev_info(&i2c->dev, "WM8961 family %d revision %c\n", 971b306e84fSMark Brown (val & WM8961_DEVICE_ID_MASK) >> WM8961_DEVICE_ID_SHIFT, 972b306e84fSMark Brown ((val & WM8961_CHIP_REV_MASK) >> WM8961_CHIP_REV_SHIFT) 973b306e84fSMark Brown + 'A'); 974b306e84fSMark Brown 975b306e84fSMark Brown ret = regmap_write(wm8961->regmap, WM8961_SOFTWARE_RESET, 0x1801); 976b306e84fSMark Brown if (ret != 0) { 977b306e84fSMark Brown dev_err(&i2c->dev, "Failed to issue reset: %d\n", ret); 978b306e84fSMark Brown return ret; 979b306e84fSMark Brown } 980b306e84fSMark Brown 98174dc55edSMark Brown i2c_set_clientdata(i2c, wm8961); 98274dc55edSMark Brown 983f0fba2adSLiam Girdwood ret = snd_soc_register_codec(&i2c->dev, 984f0fba2adSLiam Girdwood &soc_codec_dev_wm8961, &wm8961_dai, 1); 9852ec2a906SMark Brown 986f0fba2adSLiam Girdwood return ret; 98774dc55edSMark Brown } 98874dc55edSMark Brown 9897a79e94eSBill Pemberton static int wm8961_i2c_remove(struct i2c_client *client) 99074dc55edSMark Brown { 991f0fba2adSLiam Girdwood snd_soc_unregister_codec(&client->dev); 9922ec2a906SMark Brown 99374dc55edSMark Brown return 0; 99474dc55edSMark Brown } 99574dc55edSMark Brown 99674dc55edSMark Brown static const struct i2c_device_id wm8961_i2c_id[] = { 99774dc55edSMark Brown { "wm8961", 0 }, 99874dc55edSMark Brown { } 99974dc55edSMark Brown }; 100074dc55edSMark Brown MODULE_DEVICE_TABLE(i2c, wm8961_i2c_id); 100174dc55edSMark Brown 100274dc55edSMark Brown static struct i2c_driver wm8961_i2c_driver = { 100374dc55edSMark Brown .driver = { 1004091edccfSMark Brown .name = "wm8961", 100574dc55edSMark Brown .owner = THIS_MODULE, 100674dc55edSMark Brown }, 100774dc55edSMark Brown .probe = wm8961_i2c_probe, 10087a79e94eSBill Pemberton .remove = wm8961_i2c_remove, 100974dc55edSMark Brown .id_table = wm8961_i2c_id, 101074dc55edSMark Brown }; 101174dc55edSMark Brown 10128b08eb28SSachin Kamat module_i2c_driver(wm8961_i2c_driver); 101374dc55edSMark Brown 101474dc55edSMark Brown MODULE_DESCRIPTION("ASoC WM8961 driver"); 101574dc55edSMark Brown MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); 101674dc55edSMark Brown MODULE_LICENSE("GPL"); 1017