From 0b170f7acd2ab1ca0771b933493b9241706117b4 Mon Sep 17 00:00:00 2001 From: Jiada Wang Date: Fri, 23 Oct 2015 14:18:48 +0900 Subject: [PATCH 1/5] ASoC: wm8962: set ALC2 as non-volatile register Previously ALC2 register is set as a volatile register, declare it as one of ALC Coefficients register together with other non-volatile registers will cause issue, in case wm8962 has enter suspend mode, and cache_only flag is set, any attempt to read from ALC2 will fail. Because the 5 status bits in ALC2 aren't used anywhere nor are useful to end user, so this patch removes ALC2 register from volatile register list to make ALC2 be possible to be accessed when cache_only flag is set. Signed-off-by: Jiada Wang Acked-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/wm8962.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index b4eb975da981..2976200abb3a 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -131,7 +131,7 @@ static const struct reg_default wm8962_reg[] = { { 15, 0x6243 }, /* R15 - Software Reset */ { 17, 0x007B }, /* R17 - ALC1 */ - + { 18, 0x0000 }, /* R18 - ALC2 */ { 19, 0x1C32 }, /* R19 - ALC3 */ { 20, 0x3200 }, /* R20 - Noise Gate */ { 21, 0x00C0 }, /* R21 - Left ADC volume */ @@ -794,7 +794,6 @@ static bool wm8962_volatile_register(struct device *dev, unsigned int reg) case WM8962_CLOCKING1: case WM8962_CLOCKING2: case WM8962_SOFTWARE_RESET: - case WM8962_ALC2: case WM8962_THERMAL_SHUTDOWN_STATUS: case WM8962_ADDITIONAL_CONTROL_4: case WM8962_DC_SERVO_6: From 700dadfefc3df1f63dfbae7cb42fda147f4c074c Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Sun, 1 Nov 2015 20:23:39 +0100 Subject: [PATCH 2/5] ASoC: wm9713: convert to regmap Convert the Wolfson WM9713 to regmap API. This will leverage all the regmap functions (debug, registers update, etc ...). As a bonus, this will pave the path to gpio chip introduction, and devicetree support. This was tested on the mioa701 board, pxa27x based, in PCM playback, and through suspend/resume. Signed-off-by: Robert Jarzmik Reviewed-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 1 + sound/soc/codecs/wm9713.c | 176 +++++++++++++++++++++++--------------- 2 files changed, 109 insertions(+), 68 deletions(-) diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index cfdafc4c11ea..a62b91989ac7 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -891,6 +891,7 @@ config SND_SOC_WM9712 config SND_SOC_WM9713 tristate + select REGMAP_AC97 # Amp config SND_SOC_LM4857 diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c index 4083a5130cbd..22985c42764c 100644 --- a/sound/soc/codecs/wm9713.c +++ b/sound/soc/codecs/wm9713.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -39,33 +40,15 @@ struct wm9713_priv { struct mutex lock; }; -static unsigned int ac97_read(struct snd_soc_codec *codec, - unsigned int reg); -static int ac97_write(struct snd_soc_codec *codec, - unsigned int reg, unsigned int val); - -/* - * WM9713 register cache - * Reg 0x3c bit 15 is used by touch driver. - */ -static const u16 wm9713_reg[] = { - 0x6174, 0x8080, 0x8080, 0x8080, - 0xc880, 0xe808, 0xe808, 0x0808, - 0x00da, 0x8000, 0xd600, 0xaaa0, - 0xaaa0, 0xaaa0, 0x0000, 0x0000, - 0x0f0f, 0x0040, 0x0000, 0x7f00, - 0x0405, 0x0410, 0xbb80, 0xbb80, - 0x0000, 0xbb80, 0x0000, 0x4523, - 0x0000, 0x2000, 0x7eff, 0xffff, - 0x0000, 0x0000, 0x0080, 0x0000, - 0x0000, 0x0000, 0xfffe, 0xffff, - 0x0000, 0x0000, 0x0000, 0xfffe, - 0x4000, 0x0000, 0x0000, 0x0000, - 0xb032, 0x3e00, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0006, - 0x0001, 0x0000, 0x574d, 0x4c13, -}; +static unsigned int ac97_read(struct snd_soc_codec *codec, unsigned int reg) +{ + return snd_soc_read(codec, reg); +} +static int ac97_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int val) +{ + return snd_soc_write(codec, reg, val); +} #define HPL_MIXER 0 #define HPR_MIXER 1 @@ -674,40 +657,98 @@ static const struct snd_soc_dapm_route wm9713_audio_map[] = { {"Capture Mono Mux", "Right", "Right Capture Source"}, }; -static unsigned int ac97_read(struct snd_soc_codec *codec, - unsigned int reg) +static bool wm9713_readable_reg(struct device *dev, unsigned int reg) { - struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec); - u16 *cache = codec->reg_cache; - - if (reg == AC97_RESET || reg == AC97_GPIO_STATUS || - reg == AC97_VENDOR_ID1 || reg == AC97_VENDOR_ID2 || - reg == AC97_CD) - return soc_ac97_ops->read(wm9713->ac97, reg); - else { - reg = reg >> 1; - - if (reg >= (ARRAY_SIZE(wm9713_reg))) - return -EIO; - - return cache[reg]; + switch (reg) { + case AC97_RESET ... AC97_PCM_SURR_DAC_RATE: + case AC97_PCM_LR_ADC_RATE: + case AC97_CENTER_LFE_MASTER: + case AC97_SPDIF ... AC97_LINE1_LEVEL: + case AC97_GPIO_CFG ... 0x5c: + case AC97_CODEC_CLASS_REV ... AC97_PCI_SID: + case 0x74 ... AC97_VENDOR_ID2: + return true; + default: + return false; } } -static int ac97_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int val) +static bool wm9713_writeable_reg(struct device *dev, unsigned int reg) { - struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec); - - u16 *cache = codec->reg_cache; - soc_ac97_ops->write(wm9713->ac97, reg, val); - reg = reg >> 1; - if (reg < (ARRAY_SIZE(wm9713_reg))) - cache[reg] = val; - - return 0; + switch (reg) { + case AC97_VENDOR_ID1: + case AC97_VENDOR_ID2: + return false; + default: + return wm9713_readable_reg(dev, reg); + } } +static const struct reg_default wm9713_reg_defaults[] = { + { 0x02, 0x8080 }, /* Speaker Output Volume */ + { 0x04, 0x8080 }, /* Headphone Output Volume */ + { 0x06, 0x8080 }, /* Out3/OUT4 Volume */ + { 0x08, 0xc880 }, /* Mono Volume */ + { 0x0a, 0xe808 }, /* LINEIN Volume */ + { 0x0c, 0xe808 }, /* DAC PGA Volume */ + { 0x0e, 0x0808 }, /* MIC PGA Volume */ + { 0x10, 0x00da }, /* MIC Routing Control */ + { 0x12, 0x8000 }, /* Record PGA Volume */ + { 0x14, 0xd600 }, /* Record Routing */ + { 0x16, 0xaaa0 }, /* PCBEEP Volume */ + { 0x18, 0xaaa0 }, /* VxDAC Volume */ + { 0x1a, 0xaaa0 }, /* AUXDAC Volume */ + { 0x1c, 0x0000 }, /* Output PGA Mux */ + { 0x1e, 0x0000 }, /* DAC 3D control */ + { 0x20, 0x0f0f }, /* DAC Tone Control*/ + { 0x22, 0x0040 }, /* MIC Input Select & Bias */ + { 0x24, 0x0000 }, /* Output Volume Mapping & Jack */ + { 0x26, 0x7f00 }, /* Powerdown Ctrl/Stat*/ + { 0x28, 0x0405 }, /* Extended Audio ID */ + { 0x2a, 0x0410 }, /* Extended Audio Start/Ctrl */ + { 0x2c, 0xbb80 }, /* Audio DACs Sample Rate */ + { 0x2e, 0xbb80 }, /* AUXDAC Sample Rate */ + { 0x32, 0xbb80 }, /* Audio ADCs Sample Rate */ + { 0x36, 0x4523 }, /* PCM codec control */ + { 0x3a, 0x2000 }, /* SPDIF control */ + { 0x3c, 0xfdff }, /* Powerdown 1 */ + { 0x3e, 0xffff }, /* Powerdown 2 */ + { 0x40, 0x0000 }, /* General Purpose */ + { 0x42, 0x0000 }, /* Fast Power-Up Control */ + { 0x44, 0x0080 }, /* MCLK/PLL Control */ + { 0x46, 0x0000 }, /* MCLK/PLL Control */ + { 0x4c, 0xfffe }, /* GPIO Pin Configuration */ + { 0x4e, 0xffff }, /* GPIO Pin Polarity / Type */ + { 0x50, 0x0000 }, /* GPIO Pin Sticky */ + { 0x52, 0x0000 }, /* GPIO Pin Wake-Up */ + /* GPIO Pin Status */ + { 0x56, 0xfffe }, /* GPIO Pin Sharing */ + { 0x58, 0x4000 }, /* GPIO PullUp/PullDown */ + { 0x5a, 0x0000 }, /* Additional Functions 1 */ + { 0x5c, 0x0000 }, /* Additional Functions 2 */ + { 0x60, 0xb032 }, /* ALC Control */ + { 0x62, 0x3e00 }, /* ALC / Noise Gate Control */ + { 0x64, 0x0000 }, /* AUXDAC input control */ + { 0x74, 0x0000 }, /* Digitiser Reg 1 */ + { 0x76, 0x0006 }, /* Digitiser Reg 2 */ + { 0x78, 0x0001 }, /* Digitiser Reg 3 */ + { 0x7a, 0x0000 }, /* Digitiser Read Back */ +}; + +static const struct regmap_config wm9713_regmap_config = { + .reg_bits = 16, + .reg_stride = 2, + .val_bits = 16, + .max_register = 0x7e, + .cache_type = REGCACHE_RBTREE, + + .reg_defaults = wm9713_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(wm9713_reg_defaults), + .volatile_reg = regmap_ac97_default_volatile, + .readable_reg = wm9713_readable_reg, + .writeable_reg = wm9713_writeable_reg, +}; + /* PLL divisors */ struct _pll_div { u32 divsel:1; @@ -1173,8 +1214,7 @@ static int wm9713_soc_suspend(struct snd_soc_codec *codec) static int wm9713_soc_resume(struct snd_soc_codec *codec) { struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec); - int i, ret; - u16 *cache = codec->reg_cache; + int ret; ret = snd_ac97_reset(wm9713->ac97, true, WM9713_VENDOR_ID, WM9713_VENDOR_ID_MASK); @@ -1189,12 +1229,8 @@ static int wm9713_soc_resume(struct snd_soc_codec *codec) /* only synchronise the codec if warm reset failed */ if (ret == 0) { - for (i = 2; i < ARRAY_SIZE(wm9713_reg) << 1; i += 2) { - if (i == AC97_POWERDOWN || i == AC97_EXTENDED_MID || - i == AC97_EXTENDED_MSTATUS || i > 0x66) - continue; - soc_ac97_ops->write(wm9713->ac97, i, cache[i>>1]); - } + regcache_mark_dirty(codec->component.regmap); + snd_soc_cache_sync(codec); } return ret; @@ -1203,6 +1239,7 @@ static int wm9713_soc_resume(struct snd_soc_codec *codec) static int wm9713_soc_probe(struct snd_soc_codec *codec) { struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec); + struct regmap *regmap; int reg; wm9713->ac97 = snd_soc_new_ac97_codec(codec, WM9713_VENDOR_ID, @@ -1210,6 +1247,14 @@ static int wm9713_soc_probe(struct snd_soc_codec *codec) if (IS_ERR(wm9713->ac97)) return PTR_ERR(wm9713->ac97); + regmap = devm_regmap_init_ac97(wm9713->ac97, &wm9713_regmap_config); + if (IS_ERR(regmap)) { + snd_soc_free_ac97_codec(wm9713->ac97); + return PTR_ERR(regmap); + } + + snd_soc_codec_init_regmap(codec, regmap); + /* unmute the adc - move to kcontrol */ reg = ac97_read(codec, AC97_CD) & 0x7fff; ac97_write(codec, AC97_CD, reg); @@ -1221,6 +1266,7 @@ static int wm9713_soc_remove(struct snd_soc_codec *codec) { struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec); + snd_soc_codec_exit_regmap(codec); snd_soc_free_ac97_codec(wm9713->ac97); return 0; } @@ -1230,13 +1276,7 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9713 = { .remove = wm9713_soc_remove, .suspend = wm9713_soc_suspend, .resume = wm9713_soc_resume, - .read = ac97_read, - .write = ac97_write, .set_bias_level = wm9713_set_bias_level, - .reg_cache_size = ARRAY_SIZE(wm9713_reg), - .reg_word_size = sizeof(u16), - .reg_cache_step = 2, - .reg_cache_default = wm9713_reg, .controls = wm9713_snd_ac97_controls, .num_controls = ARRAY_SIZE(wm9713_snd_ac97_controls), From fa1a51f3cd9849fcfb811c7cd62d6e2028ad4ea9 Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Sun, 1 Nov 2015 20:23:40 +0100 Subject: [PATCH 3/5] ASoC: wm9713: use snd_soc_*() calls to update ac97 registers Convert wm9713 to use the more modern registers manipulation functions, such as snd_soc_read(), snd_soc_write() and snd_soc_update_bits(). Signed-off-by: Robert Jarzmik Reviewed-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/wm9713.c | 140 ++++++++++++++------------------------ 1 file changed, 52 insertions(+), 88 deletions(-) diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c index 22985c42764c..79e143625ac3 100644 --- a/sound/soc/codecs/wm9713.c +++ b/sound/soc/codecs/wm9713.c @@ -40,16 +40,6 @@ struct wm9713_priv { struct mutex lock; }; -static unsigned int ac97_read(struct snd_soc_codec *codec, unsigned int reg) -{ - return snd_soc_read(codec, reg); -} -static int ac97_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int val) -{ - return snd_soc_write(codec, reg, val); -} - #define HPL_MIXER 0 #define HPR_MIXER 1 @@ -203,18 +193,15 @@ static int wm9713_voice_shutdown(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); - u16 status, rate; if (WARN_ON(event != SND_SOC_DAPM_PRE_PMD)) return -EINVAL; /* Gracefully shut down the voice interface. */ - status = ac97_read(codec, AC97_EXTENDED_MID) | 0x1000; - rate = ac97_read(codec, AC97_HANDSET_RATE) & 0xF0FF; - ac97_write(codec, AC97_HANDSET_RATE, rate | 0x0200); + snd_soc_update_bits(codec, AC97_HANDSET_RATE, 0x0f00, 0x0200); schedule_timeout_interruptible(msecs_to_jiffies(1)); - ac97_write(codec, AC97_HANDSET_RATE, rate | 0x0F00); - ac97_write(codec, AC97_EXTENDED_MID, status); + snd_soc_update_bits(codec, AC97_HANDSET_RATE, 0x0f00, 0x0f00); + snd_soc_update_bits(codec, AC97_EXTENDED_MID, 0x1000, 0x1000); return 0; } @@ -834,10 +821,8 @@ static int wm9713_set_pll(struct snd_soc_codec *codec, /* turn PLL off ? */ if (freq_in == 0) { /* disable PLL power and select ext source */ - reg = ac97_read(codec, AC97_HANDSET_RATE); - ac97_write(codec, AC97_HANDSET_RATE, reg | 0x0080); - reg = ac97_read(codec, AC97_EXTENDED_MID); - ac97_write(codec, AC97_EXTENDED_MID, reg | 0x0200); + snd_soc_update_bits(codec, AC97_HANDSET_RATE, 0x0080, 0x0080); + snd_soc_update_bits(codec, AC97_EXTENDED_MID, 0x0200, 0x0200); wm9713->pll_in = 0; return 0; } @@ -847,7 +832,7 @@ static int wm9713_set_pll(struct snd_soc_codec *codec, if (pll_div.k == 0) { reg = (pll_div.n << 12) | (pll_div.lf << 11) | (pll_div.divsel << 9) | (pll_div.divctl << 8); - ac97_write(codec, AC97_LINE1_LEVEL, reg); + snd_soc_write(codec, AC97_LINE1_LEVEL, reg); } else { /* write the fractional k to the reg 0x46 pages */ reg2 = (pll_div.n << 12) | (pll_div.lf << 11) | (1 << 10) | @@ -855,33 +840,31 @@ static int wm9713_set_pll(struct snd_soc_codec *codec, /* K [21:20] */ reg = reg2 | (0x5 << 4) | (pll_div.k >> 20); - ac97_write(codec, AC97_LINE1_LEVEL, reg); + snd_soc_write(codec, AC97_LINE1_LEVEL, reg); /* K [19:16] */ reg = reg2 | (0x4 << 4) | ((pll_div.k >> 16) & 0xf); - ac97_write(codec, AC97_LINE1_LEVEL, reg); + snd_soc_write(codec, AC97_LINE1_LEVEL, reg); /* K [15:12] */ reg = reg2 | (0x3 << 4) | ((pll_div.k >> 12) & 0xf); - ac97_write(codec, AC97_LINE1_LEVEL, reg); + snd_soc_write(codec, AC97_LINE1_LEVEL, reg); /* K [11:8] */ reg = reg2 | (0x2 << 4) | ((pll_div.k >> 8) & 0xf); - ac97_write(codec, AC97_LINE1_LEVEL, reg); + snd_soc_write(codec, AC97_LINE1_LEVEL, reg); /* K [7:4] */ reg = reg2 | (0x1 << 4) | ((pll_div.k >> 4) & 0xf); - ac97_write(codec, AC97_LINE1_LEVEL, reg); + snd_soc_write(codec, AC97_LINE1_LEVEL, reg); reg = reg2 | (0x0 << 4) | (pll_div.k & 0xf); /* K [3:0] */ - ac97_write(codec, AC97_LINE1_LEVEL, reg); + snd_soc_write(codec, AC97_LINE1_LEVEL, reg); } /* turn PLL on and select as source */ - reg = ac97_read(codec, AC97_EXTENDED_MID); - ac97_write(codec, AC97_EXTENDED_MID, reg & 0xfdff); - reg = ac97_read(codec, AC97_HANDSET_RATE); - ac97_write(codec, AC97_HANDSET_RATE, reg & 0xff7f); + snd_soc_update_bits(codec, AC97_EXTENDED_MID, 0x0200, 0x0000); + snd_soc_update_bits(codec, AC97_HANDSET_RATE, 0x0080, 0x0000); wm9713->pll_in = freq_in; /* wait 10ms AC97 link frames for the link to stabilise */ @@ -904,10 +887,10 @@ static int wm9713_set_dai_tristate(struct snd_soc_dai *codec_dai, int tristate) { struct snd_soc_codec *codec = codec_dai->codec; - u16 reg = ac97_read(codec, AC97_CENTER_LFE_MASTER) & 0x9fff; if (tristate) - ac97_write(codec, AC97_CENTER_LFE_MASTER, reg); + snd_soc_update_bits(codec, AC97_CENTER_LFE_MASTER, + 0x6000, 0x0000); return 0; } @@ -920,36 +903,30 @@ static int wm9713_set_dai_clkdiv(struct snd_soc_dai *codec_dai, int div_id, int div) { struct snd_soc_codec *codec = codec_dai->codec; - u16 reg; switch (div_id) { case WM9713_PCMCLK_DIV: - reg = ac97_read(codec, AC97_HANDSET_RATE) & 0xf0ff; - ac97_write(codec, AC97_HANDSET_RATE, reg | div); + snd_soc_update_bits(codec, AC97_HANDSET_RATE, 0x0f00, div); break; case WM9713_CLKA_MULT: - reg = ac97_read(codec, AC97_HANDSET_RATE) & 0xfffd; - ac97_write(codec, AC97_HANDSET_RATE, reg | div); + snd_soc_update_bits(codec, AC97_HANDSET_RATE, 0x0002, div); break; case WM9713_CLKB_MULT: - reg = ac97_read(codec, AC97_HANDSET_RATE) & 0xfffb; - ac97_write(codec, AC97_HANDSET_RATE, reg | div); + snd_soc_update_bits(codec, AC97_HANDSET_RATE, 0x0004, div); break; case WM9713_HIFI_DIV: - reg = ac97_read(codec, AC97_HANDSET_RATE) & 0x8fff; - ac97_write(codec, AC97_HANDSET_RATE, reg | div); + snd_soc_update_bits(codec, AC97_HANDSET_RATE, 0x7000, div); break; case WM9713_PCMBCLK_DIV: - reg = ac97_read(codec, AC97_CENTER_LFE_MASTER) & 0xf1ff; - ac97_write(codec, AC97_CENTER_LFE_MASTER, reg | div); + snd_soc_update_bits(codec, AC97_CENTER_LFE_MASTER, 0x0e00, div); break; case WM9713_PCMCLK_PLL_DIV: - reg = ac97_read(codec, AC97_LINE1_LEVEL) & 0xff80; - ac97_write(codec, AC97_LINE1_LEVEL, reg | 0x60 | div); + snd_soc_update_bits(codec, AC97_LINE1_LEVEL, + 0x007f, div | 0x60); break; case WM9713_HIFI_PLL_DIV: - reg = ac97_read(codec, AC97_LINE1_LEVEL) & 0xff80; - ac97_write(codec, AC97_LINE1_LEVEL, reg | 0x70 | div); + snd_soc_update_bits(codec, AC97_LINE1_LEVEL, + 0x007f, div | 0x70); break; default: return -EINVAL; @@ -962,7 +939,7 @@ static int wm9713_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) { struct snd_soc_codec *codec = codec_dai->codec; - u16 gpio = ac97_read(codec, AC97_GPIO_CFG) & 0xffc5; + u16 gpio = snd_soc_read(codec, AC97_GPIO_CFG) & 0xffc5; u16 reg = 0x8000; /* clock masters */ @@ -1015,8 +992,8 @@ static int wm9713_set_dai_fmt(struct snd_soc_dai *codec_dai, break; } - ac97_write(codec, AC97_GPIO_CFG, gpio); - ac97_write(codec, AC97_CENTER_LFE_MASTER, reg); + snd_soc_write(codec, AC97_GPIO_CFG, gpio); + snd_soc_write(codec, AC97_CENTER_LFE_MASTER, reg); return 0; } @@ -1025,24 +1002,24 @@ static int wm9713_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_codec *codec = dai->codec; - u16 reg = ac97_read(codec, AC97_CENTER_LFE_MASTER) & 0xfff3; + /* enable PCM interface in master mode */ switch (params_width(params)) { case 16: break; case 20: - reg |= 0x0004; + snd_soc_update_bits(codec, AC97_CENTER_LFE_MASTER, + 0x000c, 0x0004); break; case 24: - reg |= 0x0008; + snd_soc_update_bits(codec, AC97_CENTER_LFE_MASTER, + 0x000c, 0x0008); break; case 32: - reg |= 0x000c; + snd_soc_update_bits(codec, AC97_CENTER_LFE_MASTER, + 0x000c, 0x000c); break; } - - /* enable PCM interface in master mode */ - ac97_write(codec, AC97_CENTER_LFE_MASTER, reg); return 0; } @@ -1052,17 +1029,15 @@ static int ac97_hifi_prepare(struct snd_pcm_substream *substream, struct snd_soc_codec *codec = dai->codec; struct snd_pcm_runtime *runtime = substream->runtime; int reg; - u16 vra; - vra = ac97_read(codec, AC97_EXTENDED_STATUS); - ac97_write(codec, AC97_EXTENDED_STATUS, vra | 0x1); + snd_soc_update_bits(codec, AC97_EXTENDED_STATUS, 0x0001, 0x0001); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) reg = AC97_PCM_FRONT_DAC_RATE; else reg = AC97_PCM_LR_ADC_RATE; - return ac97_write(codec, reg, runtime->rate); + return snd_soc_write(codec, reg, runtime->rate); } static int ac97_aux_prepare(struct snd_pcm_substream *substream, @@ -1070,17 +1045,14 @@ static int ac97_aux_prepare(struct snd_pcm_substream *substream, { struct snd_soc_codec *codec = dai->codec; struct snd_pcm_runtime *runtime = substream->runtime; - u16 vra, xsle; - vra = ac97_read(codec, AC97_EXTENDED_STATUS); - ac97_write(codec, AC97_EXTENDED_STATUS, vra | 0x1); - xsle = ac97_read(codec, AC97_PCI_SID); - ac97_write(codec, AC97_PCI_SID, xsle | 0x8000); + snd_soc_update_bits(codec, AC97_EXTENDED_STATUS, 0x0001, 0x0001); + snd_soc_update_bits(codec, AC97_PCI_SID, 0x8000, 0x8000); if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) return -ENODEV; - return ac97_write(codec, AC97_PCM_SURR_DAC_RATE, runtime->rate); + return snd_soc_write(codec, AC97_PCM_SURR_DAC_RATE, runtime->rate); } #define WM9713_RATES (SNDRV_PCM_RATE_8000 | \ @@ -1169,27 +1141,23 @@ static struct snd_soc_dai_driver wm9713_dai[] = { static int wm9713_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { - u16 reg; - switch (level) { case SND_SOC_BIAS_ON: /* enable thermal shutdown */ - reg = ac97_read(codec, AC97_EXTENDED_MID) & 0x1bff; - ac97_write(codec, AC97_EXTENDED_MID, reg); + snd_soc_update_bits(codec, AC97_EXTENDED_MID, 0xe400, 0x0000); break; case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: /* enable master bias and vmid */ - reg = ac97_read(codec, AC97_EXTENDED_MID) & 0x3bff; - ac97_write(codec, AC97_EXTENDED_MID, reg); - ac97_write(codec, AC97_POWERDOWN, 0x0000); + snd_soc_update_bits(codec, AC97_EXTENDED_MID, 0xc400, 0x0000); + snd_soc_write(codec, AC97_POWERDOWN, 0x0000); break; case SND_SOC_BIAS_OFF: /* disable everything including AC link */ - ac97_write(codec, AC97_EXTENDED_MID, 0xffff); - ac97_write(codec, AC97_EXTENDED_MSTATUS, 0xffff); - ac97_write(codec, AC97_POWERDOWN, 0xffff); + snd_soc_write(codec, AC97_EXTENDED_MID, 0xffff); + snd_soc_write(codec, AC97_EXTENDED_MSTATUS, 0xffff); + snd_soc_write(codec, AC97_POWERDOWN, 0xffff); break; } return 0; @@ -1197,16 +1165,14 @@ static int wm9713_set_bias_level(struct snd_soc_codec *codec, static int wm9713_soc_suspend(struct snd_soc_codec *codec) { - u16 reg; - /* Disable everything except touchpanel - that will be handled * by the touch driver and left disabled if touch is not in * use. */ - reg = ac97_read(codec, AC97_EXTENDED_MID); - ac97_write(codec, AC97_EXTENDED_MID, reg | 0x7fff); - ac97_write(codec, AC97_EXTENDED_MSTATUS, 0xffff); - ac97_write(codec, AC97_POWERDOWN, 0x6f00); - ac97_write(codec, AC97_POWERDOWN, 0xffff); + snd_soc_update_bits(codec, AC97_EXTENDED_MID, 0x7fff, + 0x7fff); + snd_soc_write(codec, AC97_EXTENDED_MSTATUS, 0xffff); + snd_soc_write(codec, AC97_POWERDOWN, 0x6f00); + snd_soc_write(codec, AC97_POWERDOWN, 0xffff); return 0; } @@ -1240,7 +1206,6 @@ static int wm9713_soc_probe(struct snd_soc_codec *codec) { struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec); struct regmap *regmap; - int reg; wm9713->ac97 = snd_soc_new_ac97_codec(codec, WM9713_VENDOR_ID, WM9713_VENDOR_ID_MASK); @@ -1256,8 +1221,7 @@ static int wm9713_soc_probe(struct snd_soc_codec *codec) snd_soc_codec_init_regmap(codec, regmap); /* unmute the adc - move to kcontrol */ - reg = ac97_read(codec, AC97_CD) & 0x7fff; - ac97_write(codec, AC97_CD, reg); + snd_soc_update_bits(codec, AC97_CD, 0x7fff, 0x0000); return 0; } From 2005bd881d273456e26a0b2027976f75fc47701f Mon Sep 17 00:00:00 2001 From: Mans Rullgard Date: Wed, 16 Dec 2015 13:02:55 +0000 Subject: [PATCH 4/5] ASoC: wm8974: add devicetree support This adds devicetree support to the wm8974 codec driver. With a DT-based kernel, there is no board-specific setting to select the driver so allow it to be manually chosen. Signed-off-by: Mans Rullgard Acked-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 3 ++- sound/soc/codecs/wm8974.c | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index cfdafc4c11ea..e36b14c5cb57 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -838,7 +838,8 @@ config SND_SOC_WM8971 tristate config SND_SOC_WM8974 - tristate + tristate "Wolfson Microelectronics WM8974 codec" + depends on I2C config SND_SOC_WM8978 tristate "Wolfson Microelectronics WM8978 codec" diff --git a/sound/soc/codecs/wm8974.c b/sound/soc/codecs/wm8974.c index 0a60677397b3..45ba828b19de 100644 --- a/sound/soc/codecs/wm8974.c +++ b/sound/soc/codecs/wm8974.c @@ -631,9 +631,16 @@ static const struct i2c_device_id wm8974_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, wm8974_i2c_id); +static const struct of_device_id wm8974_of_match[] = { + { .compatible = "wlf,wm8974", }, + { } +}; +MODULE_DEVICE_TABLE(of, wm8974_of_match); + static struct i2c_driver wm8974_i2c_driver = { .driver = { .name = "wm8974", + .of_match_table = wm8974_of_match, }, .probe = wm8974_i2c_probe, .remove = wm8974_i2c_remove, From 47d358bbf22d504a41007e7fbf71b5b4b1b45b79 Mon Sep 17 00:00:00 2001 From: Mans Rullgard Date: Wed, 16 Dec 2015 13:55:13 +0000 Subject: [PATCH 5/5] ASoC: wm8974: add binding for WM8974 codec This adds a binding for the Wolfson WM8974 mono audio codec. Signed-off-by: Mans Rullgard Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/wlf,wm8974.txt | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/wlf,wm8974.txt diff --git a/Documentation/devicetree/bindings/sound/wlf,wm8974.txt b/Documentation/devicetree/bindings/sound/wlf,wm8974.txt new file mode 100644 index 000000000000..01d3a7c83419 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/wlf,wm8974.txt @@ -0,0 +1,15 @@ +WM8974 audio CODEC + +This device supports both I2C and SPI (configured with pin strapping +on the board). + +Required properties: + - compatible: "wlf,wm8974" + - reg: the I2C address or SPI chip select number of the device + +Examples: + +codec: wm8974@1a { + compatible = "wlf,wm8974"; + reg = <0x1a>; +};