From 93d5fc8bd143d94105279796451dcfd3d657453a Mon Sep 17 00:00:00 2001 From: Antonio Borneo Date: Tue, 23 Jun 2015 22:52:12 +0800 Subject: [PATCH 01/11] ASoC: wm0010: Remove redundant spi driver bus initialization In ancient times it was necessary to manually initialize the bus field of an spi_driver to spi_bus_type. These days this is done in spi_register_driver(), so we can drop the manual assignment. Signed-off-by: Antonio Borneo Acked-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/wm0010.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/soc/codecs/wm0010.c b/sound/soc/codecs/wm0010.c index 6560a66b3f35..e1da49f39334 100644 --- a/sound/soc/codecs/wm0010.c +++ b/sound/soc/codecs/wm0010.c @@ -1003,7 +1003,6 @@ static int wm0010_spi_remove(struct spi_device *spi) static struct spi_driver wm0010_spi_driver = { .driver = { .name = "wm0010", - .bus = &spi_bus_type, .owner = THIS_MODULE, }, .probe = wm0010_spi_probe, From 6702dfcc571d962df499f7466f54e07d044e6cd1 Mon Sep 17 00:00:00 2001 From: Sergey Kiselev Date: Fri, 5 Jun 2015 11:55:27 -0700 Subject: [PATCH 02/11] ASoC: wm8731: initialize the hardware when loading the codec driver This patch moves the requesting supplies, hardware reset and initialization from wm8731_probe to wm8731_i2c_probe and wm8731_spi_probe. So that the codec hardware is initialized when loading the codec driver, and not when loading the machine driver. This avoids unnecesary hardware resets and re-initializations when re-loading the machine driver. Signed-off-by: Sergey Kiselev Signed-off-by: Mark Brown --- sound/soc/codecs/wm8731.c | 74 ++++++++++++++++++++++----------------- 1 file changed, 42 insertions(+), 32 deletions(-) diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c index 915ea11ad4b6..f22935a36a0a 100644 --- a/sound/soc/codecs/wm8731.c +++ b/sound/soc/codecs/wm8731.c @@ -84,7 +84,7 @@ static bool wm8731_writeable(struct device *dev, unsigned int reg) return reg <= WM8731_RESET; } -#define wm8731_reset(c) snd_soc_write(c, WM8731_RESET, 0) +#define wm8731_reset(m) regmap_write(m, WM8731_RESET, 0) static const char *wm8731_input_select[] = {"Line In", "Mic"}; @@ -571,69 +571,63 @@ static struct snd_soc_dai_driver wm8731_dai = { .symmetric_rates = 1, }; -static int wm8731_probe(struct snd_soc_codec *codec) +static int wm8731_request_supplies(struct device *dev, + struct wm8731_priv *wm8731) { - struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec); int ret = 0, i; for (i = 0; i < ARRAY_SIZE(wm8731->supplies); i++) wm8731->supplies[i].supply = wm8731_supply_names[i]; - ret = devm_regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8731->supplies), + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(wm8731->supplies), wm8731->supplies); if (ret != 0) { - dev_err(codec->dev, "Failed to request supplies: %d\n", ret); + dev_err(dev, "Failed to request supplies: %d\n", ret); return ret; } ret = regulator_bulk_enable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies); if (ret != 0) { - dev_err(codec->dev, "Failed to enable supplies: %d\n", ret); + dev_err(dev, "Failed to enable supplies: %d\n", ret); return ret; } - ret = wm8731_reset(codec); + return 0; +} + +static int wm8731_hw_init(struct device *dev, struct wm8731_priv *wm8731) +{ + int ret = 0; + + ret = wm8731_reset(wm8731->regmap); if (ret < 0) { - dev_err(codec->dev, "Failed to issue reset: %d\n", ret); + dev_err(dev, "Failed to issue reset: %d\n", ret); goto err_regulator_enable; } - snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY); + /* Clear POWEROFF, keep everything else disabled */ + regmap_write(wm8731->regmap, WM8731_PWR, 0x7f); /* Latch the update bits */ - snd_soc_update_bits(codec, WM8731_LOUT1V, 0x100, 0); - snd_soc_update_bits(codec, WM8731_ROUT1V, 0x100, 0); - snd_soc_update_bits(codec, WM8731_LINVOL, 0x100, 0); - snd_soc_update_bits(codec, WM8731_RINVOL, 0x100, 0); + regmap_update_bits(wm8731->regmap, WM8731_LOUT1V, 0x100, 0); + regmap_update_bits(wm8731->regmap, WM8731_ROUT1V, 0x100, 0); + regmap_update_bits(wm8731->regmap, WM8731_LINVOL, 0x100, 0); + regmap_update_bits(wm8731->regmap, WM8731_RINVOL, 0x100, 0); /* Disable bypass path by default */ - snd_soc_update_bits(codec, WM8731_APANA, 0x8, 0); + regmap_update_bits(wm8731->regmap, WM8731_APANA, 0x8, 0); - /* Regulators will have been enabled by bias management */ - regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies); - - return 0; + regcache_mark_dirty(wm8731->regmap); err_regulator_enable: + /* Regulators will be enabled by bias management */ regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies); return ret; } -/* power down chip */ -static int wm8731_remove(struct snd_soc_codec *codec) -{ - struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec); - - regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies); - - return 0; -} - static struct snd_soc_codec_driver soc_codec_dev_wm8731 = { - .probe = wm8731_probe, - .remove = wm8731_remove, .set_bias_level = wm8731_set_bias_level, .suspend_bias_off = true, @@ -690,6 +684,12 @@ static int wm8731_spi_probe(struct spi_device *spi) mutex_init(&wm8731->lock); + spi_set_drvdata(spi, wm8731); + + ret = wm8731_request_supplies(&spi->dev, wm8731); + if (ret != 0) + return ret; + wm8731->regmap = devm_regmap_init_spi(spi, &wm8731_regmap); if (IS_ERR(wm8731->regmap)) { ret = PTR_ERR(wm8731->regmap); @@ -698,7 +698,9 @@ static int wm8731_spi_probe(struct spi_device *spi) return ret; } - spi_set_drvdata(spi, wm8731); + ret = wm8731_hw_init(&spi->dev, wm8731); + if (ret != 0) + return ret; ret = snd_soc_register_codec(&spi->dev, &soc_codec_dev_wm8731, &wm8731_dai, 1); @@ -754,6 +756,12 @@ static int wm8731_i2c_probe(struct i2c_client *i2c, mutex_init(&wm8731->lock); + i2c_set_clientdata(i2c, wm8731); + + ret = wm8731_request_supplies(&i2c->dev, wm8731); + if (ret != 0) + return ret; + wm8731->regmap = devm_regmap_init_i2c(i2c, &wm8731_regmap); if (IS_ERR(wm8731->regmap)) { ret = PTR_ERR(wm8731->regmap); @@ -762,7 +770,9 @@ static int wm8731_i2c_probe(struct i2c_client *i2c, return ret; } - i2c_set_clientdata(i2c, wm8731); + ret = wm8731_hw_init(&i2c->dev, wm8731); + if (ret != 0) + return ret; ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_wm8731, &wm8731_dai, 1); From cef6daa919c6912c4da629c3cee9c789f90583b1 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Sat, 20 Jun 2015 15:55:52 -0300 Subject: [PATCH 03/11] ASoC: wm8731: Check for clk_prepare_enable() error clk_prepare_enable() may fail, so we should better check its return value and propagate it in the case of error. Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown --- sound/soc/codecs/wm8731.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c index f22935a36a0a..628d50c40cc4 100644 --- a/sound/soc/codecs/wm8731.c +++ b/sound/soc/codecs/wm8731.c @@ -496,8 +496,11 @@ static int wm8731_set_bias_level(struct snd_soc_codec *codec, switch (level) { case SND_SOC_BIAS_ON: - if (wm8731->mclk) - clk_prepare_enable(wm8731->mclk); + if (wm8731->mclk) { + ret = clk_prepare_enable(wm8731->mclk); + if (ret) + return ret; + } break; case SND_SOC_BIAS_PREPARE: break; From 030e6ee241f0586308622bcdd273a317976b3169 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Thu, 2 Jul 2015 21:26:44 +0800 Subject: [PATCH 04/11] ASoC: wm0010: Remove spurious missing IRQF_ONESHOT This reverts 58d468328646 ("ASoC: wm0010: Add missing IRQF_ONESHOT"). The coccinelle warnings is false positive because the original code does set IRQF_ONESHOT by "trigger |= IRQF_ONESHOT;". Signed-off-by: Axel Lin Acked-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/wm0010.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/wm0010.c b/sound/soc/codecs/wm0010.c index e1da49f39334..f2c6ad4b8fde 100644 --- a/sound/soc/codecs/wm0010.c +++ b/sound/soc/codecs/wm0010.c @@ -953,7 +953,7 @@ static int wm0010_spi_probe(struct spi_device *spi) trigger = IRQF_TRIGGER_FALLING; trigger |= IRQF_ONESHOT; - ret = request_threaded_irq(irq, NULL, wm0010_irq, trigger | IRQF_ONESHOT, + ret = request_threaded_irq(irq, NULL, wm0010_irq, trigger, "wm0010", wm0010); if (ret) { dev_err(wm0010->dev, "Failed to request IRQ %d: %d\n", From efc04ca22303dee1f34d038af3addc13621bed58 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Thu, 2 Jul 2015 21:27:48 +0800 Subject: [PATCH 05/11] ASoC: wm5100: Remove spurious IRQF_ONESHOT flag This reverts 3d907cc30d07 ("ASoC: wm5100: Pass the IRQF_ONESHOT flag"). The coccinelle warnings is false positive because the original code does set IRQF_ONESHOT by "trigger |= IRQF_ONESHOT;". Signed-off-by: Axel Lin Acked-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/wm5100.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c index 4c10cd88c1af..98495dd61239 100644 --- a/sound/soc/codecs/wm5100.c +++ b/sound/soc/codecs/wm5100.c @@ -2570,13 +2570,11 @@ static int wm5100_i2c_probe(struct i2c_client *i2c, if (irq_flags & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) ret = request_threaded_irq(i2c->irq, NULL, - wm5100_edge_irq, - irq_flags | IRQF_ONESHOT, + wm5100_edge_irq, irq_flags, "wm5100", wm5100); else ret = request_threaded_irq(i2c->irq, NULL, wm5100_irq, - irq_flags | IRQF_ONESHOT, - "wm5100", + irq_flags, "wm5100", wm5100); if (ret != 0) { From f089d4d20fcbcf16a62ef3b4b57f41ecf59a5d83 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 7 Jul 2015 15:28:12 +0100 Subject: [PATCH 06/11] mfd: wm5110: Add registers for custom write sequence triggers This register will be needed as part of some additional support for the headphone path on wm5110, so this patch adds the register and sets up its regmap config. Signed-off-by: Charles Keepax Acked-by: Lee Jones Signed-off-by: Mark Brown --- drivers/mfd/wm5110-tables.c | 2 ++ include/linux/mfd/arizona/registers.h | 37 +++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/drivers/mfd/wm5110-tables.c b/drivers/mfd/wm5110-tables.c index 12cad94b4035..62a4aa13cb98 100644 --- a/drivers/mfd/wm5110-tables.c +++ b/drivers/mfd/wm5110-tables.c @@ -676,6 +676,7 @@ static const struct reg_default wm5110_reg_default[] = { { 0x00000032, 0x0100 }, /* R50 - PWM Drive 3 */ { 0x00000040, 0x0000 }, /* R64 - Wake control */ { 0x00000041, 0x0000 }, /* R65 - Sequence control */ + { 0x00000042, 0x0000 }, /* R66 - Spare Triggers */ { 0x00000061, 0x01FF }, /* R97 - Sample Rate Sequence Select 1 */ { 0x00000062, 0x01FF }, /* R98 - Sample Rate Sequence Select 2 */ { 0x00000063, 0x01FF }, /* R99 - Sample Rate Sequence Select 3 */ @@ -1716,6 +1717,7 @@ static bool wm5110_readable_register(struct device *dev, unsigned int reg) case ARIZONA_PWM_DRIVE_3: case ARIZONA_WAKE_CONTROL: case ARIZONA_SEQUENCE_CONTROL: + case ARIZONA_SPARE_TRIGGERS: case ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_1: case ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_2: case ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_3: diff --git a/include/linux/mfd/arizona/registers.h b/include/linux/mfd/arizona/registers.h index 3499d36e6067..11affb3c2768 100644 --- a/include/linux/mfd/arizona/registers.h +++ b/include/linux/mfd/arizona/registers.h @@ -39,6 +39,7 @@ #define ARIZONA_PWM_DRIVE_3 0x32 #define ARIZONA_WAKE_CONTROL 0x40 #define ARIZONA_SEQUENCE_CONTROL 0x41 +#define ARIZONA_SPARE_TRIGGERS 0x42 #define ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_1 0x61 #define ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_2 0x62 #define ARIZONA_SAMPLE_RATE_SEQUENCE_SELECT_3 0x63 @@ -1430,6 +1431,42 @@ #define ARIZONA_WSEQ_ENA_JD2_RISE_SHIFT 0 /* WSEQ_ENA_JD2_RISE */ #define ARIZONA_WSEQ_ENA_JD2_RISE_WIDTH 1 /* WSEQ_ENA_JD2_RISE */ +/* + * R66 (0x42) - Spare Triggers + */ +#define ARIZONA_WS_TRG8 0x0080 /* WS_TRG8 */ +#define ARIZONA_WS_TRG8_MASK 0x0080 /* WS_TRG8 */ +#define ARIZONA_WS_TRG8_SHIFT 7 /* WS_TRG8 */ +#define ARIZONA_WS_TRG8_WIDTH 1 /* WS_TRG8 */ +#define ARIZONA_WS_TRG7 0x0040 /* WS_TRG7 */ +#define ARIZONA_WS_TRG7_MASK 0x0040 /* WS_TRG7 */ +#define ARIZONA_WS_TRG7_SHIFT 6 /* WS_TRG7 */ +#define ARIZONA_WS_TRG7_WIDTH 1 /* WS_TRG7 */ +#define ARIZONA_WS_TRG6 0x0020 /* WS_TRG6 */ +#define ARIZONA_WS_TRG6_MASK 0x0020 /* WS_TRG6 */ +#define ARIZONA_WS_TRG6_SHIFT 5 /* WS_TRG6 */ +#define ARIZONA_WS_TRG6_WIDTH 1 /* WS_TRG6 */ +#define ARIZONA_WS_TRG5 0x0010 /* WS_TRG5 */ +#define ARIZONA_WS_TRG5_MASK 0x0010 /* WS_TRG5 */ +#define ARIZONA_WS_TRG5_SHIFT 4 /* WS_TRG5 */ +#define ARIZONA_WS_TRG5_WIDTH 1 /* WS_TRG5 */ +#define ARIZONA_WS_TRG4 0x0008 /* WS_TRG4 */ +#define ARIZONA_WS_TRG4_MASK 0x0008 /* WS_TRG4 */ +#define ARIZONA_WS_TRG4_SHIFT 3 /* WS_TRG4 */ +#define ARIZONA_WS_TRG4_WIDTH 1 /* WS_TRG4 */ +#define ARIZONA_WS_TRG3 0x0004 /* WS_TRG3 */ +#define ARIZONA_WS_TRG3_MASK 0x0004 /* WS_TRG3 */ +#define ARIZONA_WS_TRG3_SHIFT 2 /* WS_TRG3 */ +#define ARIZONA_WS_TRG3_WIDTH 1 /* WS_TRG3 */ +#define ARIZONA_WS_TRG2 0x0002 /* WS_TRG2 */ +#define ARIZONA_WS_TRG2_MASK 0x0002 /* WS_TRG2 */ +#define ARIZONA_WS_TRG2_SHIFT 1 /* WS_TRG2 */ +#define ARIZONA_WS_TRG2_WIDTH 1 /* WS_TRG2 */ +#define ARIZONA_WS_TRG1 0x0001 /* WS_TRG1 */ +#define ARIZONA_WS_TRG1_MASK 0x0001 /* WS_TRG1 */ +#define ARIZONA_WS_TRG1_SHIFT 0 /* WS_TRG1 */ +#define ARIZONA_WS_TRG1_WIDTH 1 /* WS_TRG1 */ + /* * R97 (0x61) - Sample Rate Sequence Select 1 */ From 81207880cef207cd89db863f9aa1d65f22b4f2a2 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 7 Jul 2015 15:28:13 +0100 Subject: [PATCH 07/11] mfd: wm5110: Add register patch for rev E and above Add a register patch for rev E and above that configures the location of some write sequences to assist with the headphone enables. Signed-off-by: Charles Keepax Acked-by: Lee Jones Signed-off-by: Mark Brown --- drivers/mfd/wm5110-tables.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/mfd/wm5110-tables.c b/drivers/mfd/wm5110-tables.c index 62a4aa13cb98..e08924261fef 100644 --- a/drivers/mfd/wm5110-tables.c +++ b/drivers/mfd/wm5110-tables.c @@ -249,6 +249,16 @@ static const struct reg_default wm5110_revd_patch[] = { { 0x80, 0x0 }, }; +/* Add extra headphone write sequence locations */ +static const struct reg_default wm5110_reve_patch[] = { + { 0x80, 0x3 }, + { 0x80, 0x3 }, + { 0x4b, 0x138 }, + { 0x4c, 0x13d }, + { 0x80, 0x0 }, + { 0x80, 0x0 }, +}; + /* We use a function so we can use ARRAY_SIZE() */ int wm5110_patch(struct arizona *arizona) { @@ -266,7 +276,9 @@ int wm5110_patch(struct arizona *arizona) wm5110_revd_patch, ARRAY_SIZE(wm5110_revd_patch)); default: - return 0; + return regmap_register_patch(arizona->regmap, + wm5110_reve_patch, + ARRAY_SIZE(wm5110_reve_patch)); } } EXPORT_SYMBOL_GPL(wm5110_patch); From d1acd31883d78f905a930493ff145ca4a25ad680 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 7 Jul 2015 15:28:14 +0100 Subject: [PATCH 08/11] ASoC: wm5110: Add special DRE on/off handling for the headphone path For the best performance the headphone path enable/disable must be handled specially for the situations of DRE on and DRE off. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/wm5110.c | 285 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 276 insertions(+), 9 deletions(-) diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c index 709fcc6169d8..b5c201b1e454 100644 --- a/sound/soc/codecs/wm5110.c +++ b/sound/soc/codecs/wm5110.c @@ -131,6 +131,25 @@ static const struct reg_default wm5110_sysclk_revd_patch[] = { { 0x33fb, 0xfe00 }, }; +static const struct reg_default wm5110_sysclk_reve_patch[] = { + { 0x3270, 0xE410 }, + { 0x3271, 0x3078 }, + { 0x3272, 0xE410 }, + { 0x3273, 0x3070 }, + { 0x3274, 0xE410 }, + { 0x3275, 0x3066 }, + { 0x3276, 0xE410 }, + { 0x3277, 0x3056 }, + { 0x327A, 0xE414 }, + { 0x327B, 0x3078 }, + { 0x327C, 0xE414 }, + { 0x327D, 0x3070 }, + { 0x327E, 0xE414 }, + { 0x327F, 0x3066 }, + { 0x3280, 0xE414 }, + { 0x3281, 0x3056 }, +}; + static int wm5110_sysclk_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -146,7 +165,9 @@ static int wm5110_sysclk_ev(struct snd_soc_dapm_widget *w, patch_size = ARRAY_SIZE(wm5110_sysclk_revd_patch); break; default: - return 0; + patch = wm5110_sysclk_reve_patch; + patch_size = ARRAY_SIZE(wm5110_sysclk_reve_patch); + break; } switch (event) { @@ -164,6 +185,249 @@ static int wm5110_sysclk_ev(struct snd_soc_dapm_widget *w, return 0; } +static const struct reg_default wm5110_no_dre_left_enable[] = { + { 0x3024, 0xE410 }, + { 0x3025, 0x0056 }, + { 0x301B, 0x0224 }, + { 0x301F, 0x4263 }, + { 0x3021, 0x5291 }, + { 0x3030, 0xE410 }, + { 0x3031, 0x3066 }, + { 0x3032, 0xE410 }, + { 0x3033, 0x3070 }, + { 0x3034, 0xE410 }, + { 0x3035, 0x3078 }, + { 0x3036, 0xE410 }, + { 0x3037, 0x3080 }, + { 0x3038, 0xE410 }, + { 0x3039, 0x3080 }, +}; + +static const struct reg_default wm5110_dre_left_enable[] = { + { 0x3024, 0x0231 }, + { 0x3025, 0x0B00 }, + { 0x301B, 0x0227 }, + { 0x301F, 0x4266 }, + { 0x3021, 0x5294 }, + { 0x3030, 0xE231 }, + { 0x3031, 0x0266 }, + { 0x3032, 0x8231 }, + { 0x3033, 0x4B15 }, + { 0x3034, 0x8231 }, + { 0x3035, 0x0B15 }, + { 0x3036, 0xE231 }, + { 0x3037, 0x5294 }, + { 0x3038, 0x0231 }, + { 0x3039, 0x0B00 }, +}; + +static const struct reg_default wm5110_no_dre_right_enable[] = { + { 0x3074, 0xE414 }, + { 0x3075, 0x0056 }, + { 0x306B, 0x0224 }, + { 0x306F, 0x4263 }, + { 0x3071, 0x5291 }, + { 0x3080, 0xE414 }, + { 0x3081, 0x3066 }, + { 0x3082, 0xE414 }, + { 0x3083, 0x3070 }, + { 0x3084, 0xE414 }, + { 0x3085, 0x3078 }, + { 0x3086, 0xE414 }, + { 0x3087, 0x3080 }, + { 0x3088, 0xE414 }, + { 0x3089, 0x3080 }, +}; + +static const struct reg_default wm5110_dre_right_enable[] = { + { 0x3074, 0x0231 }, + { 0x3075, 0x0B00 }, + { 0x306B, 0x0227 }, + { 0x306F, 0x4266 }, + { 0x3071, 0x5294 }, + { 0x3080, 0xE231 }, + { 0x3081, 0x0266 }, + { 0x3082, 0x8231 }, + { 0x3083, 0x4B17 }, + { 0x3084, 0x8231 }, + { 0x3085, 0x0B17 }, + { 0x3086, 0xE231 }, + { 0x3087, 0x5294 }, + { 0x3088, 0x0231 }, + { 0x3089, 0x0B00 }, +}; + +static int wm5110_hp_pre_enable(struct snd_soc_dapm_widget *w) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); + struct arizona *arizona = priv->arizona; + unsigned int val = snd_soc_read(codec, ARIZONA_DRE_ENABLE); + const struct reg_default *wseq; + int nregs; + + switch (w->shift) { + case ARIZONA_OUT1L_ENA_SHIFT: + if (val & ARIZONA_DRE1L_ENA_MASK) { + wseq = wm5110_dre_left_enable; + nregs = ARRAY_SIZE(wm5110_dre_left_enable); + } else { + wseq = wm5110_no_dre_left_enable; + nregs = ARRAY_SIZE(wm5110_no_dre_left_enable); + priv->out_up_delay += 10; + } + break; + case ARIZONA_OUT1R_ENA_SHIFT: + if (val & ARIZONA_DRE1R_ENA_MASK) { + wseq = wm5110_dre_right_enable; + nregs = ARRAY_SIZE(wm5110_dre_right_enable); + } else { + wseq = wm5110_no_dre_right_enable; + nregs = ARRAY_SIZE(wm5110_no_dre_right_enable); + priv->out_up_delay += 10; + } + break; + default: + return 0; + } + + return regmap_multi_reg_write(arizona->regmap, wseq, nregs); +} + +static int wm5110_hp_pre_disable(struct snd_soc_dapm_widget *w) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); + unsigned int val = snd_soc_read(codec, ARIZONA_DRE_ENABLE); + + switch (w->shift) { + case ARIZONA_OUT1L_ENA_SHIFT: + if (!(val & ARIZONA_DRE1L_ENA_MASK)) { + snd_soc_update_bits(codec, ARIZONA_SPARE_TRIGGERS, + ARIZONA_WS_TRG1, ARIZONA_WS_TRG1); + snd_soc_update_bits(codec, ARIZONA_SPARE_TRIGGERS, + ARIZONA_WS_TRG1, 0); + priv->out_down_delay += 27; + } + break; + case ARIZONA_OUT1R_ENA_SHIFT: + if (!(val & ARIZONA_DRE1R_ENA_MASK)) { + snd_soc_update_bits(codec, ARIZONA_SPARE_TRIGGERS, + ARIZONA_WS_TRG2, ARIZONA_WS_TRG2); + snd_soc_update_bits(codec, ARIZONA_SPARE_TRIGGERS, + ARIZONA_WS_TRG2, 0); + priv->out_down_delay += 27; + } + break; + default: + break; + } + + return 0; +} + +static int wm5110_hp_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); + + switch (priv->arizona->rev) { + case 0 ... 3: + break; + default: + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wm5110_hp_pre_enable(w); + break; + case SND_SOC_DAPM_PRE_PMD: + wm5110_hp_pre_disable(w); + break; + default: + break; + } + break; + } + + return arizona_hp_ev(w, kcontrol, event); +} + +static int wm5110_clear_pga_volume(struct arizona *arizona, int output) +{ + struct reg_default clear_pga = { + ARIZONA_OUTPUT_PATH_CONFIG_1L + output * 4, 0x80 + }; + int ret; + + ret = regmap_multi_reg_write_bypassed(arizona->regmap, &clear_pga, 1); + if (ret) + dev_err(arizona->dev, "Failed to clear PGA (0x%x): %d\n", + clear_pga.reg, ret); + + return ret; +} + +static int wm5110_put_dre(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + struct arizona *arizona = dev_get_drvdata(codec->dev->parent); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int ena, dre; + unsigned int mask = (0x1 << mc->shift) | (0x1 << mc->rshift); + unsigned int lnew = (!!ucontrol->value.integer.value[0]) << mc->shift; + unsigned int rnew = (!!ucontrol->value.integer.value[1]) << mc->rshift; + unsigned int lold, rold; + unsigned int lena, rena; + int ret; + + snd_soc_dapm_mutex_lock(dapm); + + ret = regmap_read(arizona->regmap, ARIZONA_OUTPUT_ENABLES_1, &ena); + if (ret) { + dev_err(arizona->dev, "Failed to read output state: %d\n", ret); + goto err; + } + ret = regmap_read(arizona->regmap, ARIZONA_DRE_ENABLE, &dre); + if (ret) { + dev_err(arizona->dev, "Failed to read DRE state: %d\n", ret); + goto err; + } + + lold = dre & (1 << mc->shift); + rold = dre & (1 << mc->rshift); + /* Enables are channel wise swapped from the DRE enables */ + lena = ena & (1 << mc->rshift); + rena = ena & (1 << mc->shift); + + if ((lena && lnew != lold) || (rena && rnew != rold)) { + dev_err(arizona->dev, "Can't change DRE on active outputs\n"); + ret = -EBUSY; + goto err; + } + + ret = regmap_update_bits(arizona->regmap, ARIZONA_DRE_ENABLE, + mask, lnew | rnew); + if (ret) { + dev_err(arizona->dev, "Failed to set DRE: %d\n", ret); + goto err; + } + + /* Force reset of PGA volumes, if turning DRE off */ + if (!lnew && lold) + wm5110_clear_pga_volume(arizona, mc->shift); + + if (!rnew && rold) + wm5110_clear_pga_volume(arizona, mc->rshift); + +err: + snd_soc_dapm_mutex_unlock(dapm); + + return ret; +} + static DECLARE_TLV_DB_SCALE(ana_tlv, 0, 100, 0); static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0); @@ -409,12 +673,15 @@ SOC_DOUBLE("SPKDAT1 Switch", ARIZONA_PDM_SPK1_CTRL_1, ARIZONA_SPK1L_MUTE_SHIFT, SOC_DOUBLE("SPKDAT2 Switch", ARIZONA_PDM_SPK2_CTRL_1, ARIZONA_SPK2L_MUTE_SHIFT, ARIZONA_SPK2R_MUTE_SHIFT, 1, 1), -SOC_DOUBLE("HPOUT1 DRE Switch", ARIZONA_DRE_ENABLE, - ARIZONA_DRE1L_ENA_SHIFT, ARIZONA_DRE1R_ENA_SHIFT, 1, 0), -SOC_DOUBLE("HPOUT2 DRE Switch", ARIZONA_DRE_ENABLE, - ARIZONA_DRE2L_ENA_SHIFT, ARIZONA_DRE2R_ENA_SHIFT, 1, 0), -SOC_DOUBLE("HPOUT3 DRE Switch", ARIZONA_DRE_ENABLE, - ARIZONA_DRE3L_ENA_SHIFT, ARIZONA_DRE3R_ENA_SHIFT, 1, 0), +SOC_DOUBLE_EXT("HPOUT1 DRE Switch", ARIZONA_DRE_ENABLE, + ARIZONA_DRE1L_ENA_SHIFT, ARIZONA_DRE1R_ENA_SHIFT, 1, 0, + snd_soc_get_volsw, wm5110_put_dre), +SOC_DOUBLE_EXT("HPOUT2 DRE Switch", ARIZONA_DRE_ENABLE, + ARIZONA_DRE2L_ENA_SHIFT, ARIZONA_DRE2R_ENA_SHIFT, 1, 0, + snd_soc_get_volsw, wm5110_put_dre), +SOC_DOUBLE_EXT("HPOUT3 DRE Switch", ARIZONA_DRE_ENABLE, + ARIZONA_DRE3L_ENA_SHIFT, ARIZONA_DRE3R_ENA_SHIFT, 1, 0, + snd_soc_get_volsw, wm5110_put_dre), SOC_ENUM("Output Ramp Up", arizona_out_vi_ramp), SOC_ENUM("Output Ramp Down", arizona_out_vd_ramp), @@ -904,11 +1171,11 @@ SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 0, ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX2_ENA_SHIFT, 0), SND_SOC_DAPM_PGA_E("OUT1L", SND_SOC_NOPM, - ARIZONA_OUT1L_ENA_SHIFT, 0, NULL, 0, arizona_hp_ev, + ARIZONA_OUT1L_ENA_SHIFT, 0, NULL, 0, wm5110_hp_ev, SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), SND_SOC_DAPM_PGA_E("OUT1R", SND_SOC_NOPM, - ARIZONA_OUT1R_ENA_SHIFT, 0, NULL, 0, arizona_hp_ev, + ARIZONA_OUT1R_ENA_SHIFT, 0, NULL, 0, wm5110_hp_ev, SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), SND_SOC_DAPM_PGA_E("OUT2L", ARIZONA_OUTPUT_ENABLES_1, From dc606e05468fc5e3b125bffdd54253f853bfbb8e Mon Sep 17 00:00:00 2001 From: Nariman Poushin Date: Fri, 17 Jul 2015 15:11:10 +0100 Subject: [PATCH 09/11] ASoC: wm5110: Use reg_sequence for multi_reg_write/register_patch Introduced by: commit 8019ff6cfc04 ("regmap: Use reg_sequence for multi_reg_write / register_patch") Interacting with: commit d1acd31883d7 ("ASoC: wm5110: Add special DRE on/off handling for the headphone path") Signed-off-by: Nariman Poushin Signed-off-by: Mark Brown --- sound/soc/codecs/wm5110.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c index b5c201b1e454..1f29991c1f71 100644 --- a/sound/soc/codecs/wm5110.c +++ b/sound/soc/codecs/wm5110.c @@ -185,7 +185,7 @@ static int wm5110_sysclk_ev(struct snd_soc_dapm_widget *w, return 0; } -static const struct reg_default wm5110_no_dre_left_enable[] = { +static const struct reg_sequence wm5110_no_dre_left_enable[] = { { 0x3024, 0xE410 }, { 0x3025, 0x0056 }, { 0x301B, 0x0224 }, @@ -203,7 +203,7 @@ static const struct reg_default wm5110_no_dre_left_enable[] = { { 0x3039, 0x3080 }, }; -static const struct reg_default wm5110_dre_left_enable[] = { +static const struct reg_sequence wm5110_dre_left_enable[] = { { 0x3024, 0x0231 }, { 0x3025, 0x0B00 }, { 0x301B, 0x0227 }, @@ -221,7 +221,7 @@ static const struct reg_default wm5110_dre_left_enable[] = { { 0x3039, 0x0B00 }, }; -static const struct reg_default wm5110_no_dre_right_enable[] = { +static const struct reg_sequence wm5110_no_dre_right_enable[] = { { 0x3074, 0xE414 }, { 0x3075, 0x0056 }, { 0x306B, 0x0224 }, @@ -239,7 +239,7 @@ static const struct reg_default wm5110_no_dre_right_enable[] = { { 0x3089, 0x3080 }, }; -static const struct reg_default wm5110_dre_right_enable[] = { +static const struct reg_sequence wm5110_dre_right_enable[] = { { 0x3074, 0x0231 }, { 0x3075, 0x0B00 }, { 0x306B, 0x0227 }, @@ -263,7 +263,7 @@ static int wm5110_hp_pre_enable(struct snd_soc_dapm_widget *w) struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); struct arizona *arizona = priv->arizona; unsigned int val = snd_soc_read(codec, ARIZONA_DRE_ENABLE); - const struct reg_default *wseq; + const struct reg_sequence *wseq; int nregs; switch (w->shift) { @@ -354,7 +354,7 @@ static int wm5110_hp_ev(struct snd_soc_dapm_widget *w, static int wm5110_clear_pga_volume(struct arizona *arizona, int output) { - struct reg_default clear_pga = { + struct reg_sequence clear_pga = { ARIZONA_OUTPUT_PATH_CONFIG_1L + output * 4, 0x80 }; int ret; From c8a6b92be8516c92cf46bc127fa0adf53a05d31c Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 27 Jul 2015 10:56:23 +0200 Subject: [PATCH 10/11] ASoC: wm8804: Drop duplicate const SOC_ENUM_SINGLE_DECL() already includes a const, drop the extra const. Fixes the following sparse warning: sound/soc/codecs/wm8804.c:101:14: warning: duplicate const Signed-off-by: Lars-Peter Clausen Acked-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/wm8804.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/wm8804.c b/sound/soc/codecs/wm8804.c index c195c2e8af07..8d914702cae4 100644 --- a/sound/soc/codecs/wm8804.c +++ b/sound/soc/codecs/wm8804.c @@ -98,7 +98,7 @@ WM8804_REGULATOR_EVENT(0) WM8804_REGULATOR_EVENT(1) static const char *txsrc_text[] = { "S/PDIF RX", "AIF" }; -static const SOC_ENUM_SINGLE_DECL(txsrc, WM8804_SPDTX4, 6, txsrc_text); +static SOC_ENUM_SINGLE_DECL(txsrc, WM8804_SPDTX4, 6, txsrc_text); static const struct snd_kcontrol_new wm8804_tx_source_mux[] = { SOC_DAPM_ENUM_EXT("Input Source", txsrc, From f611cdd8eb33ac6ca1196319b27b7f7f24ef98a9 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sat, 1 Aug 2015 10:03:42 +0800 Subject: [PATCH 11/11] ASoC: wm8731: Drop wm8731_writeable function When .max_register is set and .writeable_reg is not implement, registers between 0 and .max_register are writeable. This is the same as current implementation of wm8731_writeable(), so just drop implementation for .writeable_reg callback. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- sound/soc/codecs/wm8731.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c index 628d50c40cc4..6c73d379d93f 100644 --- a/sound/soc/codecs/wm8731.c +++ b/sound/soc/codecs/wm8731.c @@ -79,11 +79,6 @@ static bool wm8731_volatile(struct device *dev, unsigned int reg) return reg == WM8731_RESET; } -static bool wm8731_writeable(struct device *dev, unsigned int reg) -{ - return reg <= WM8731_RESET; -} - #define wm8731_reset(m) regmap_write(m, WM8731_RESET, 0) static const char *wm8731_input_select[] = {"Line In", "Mic"}; @@ -655,7 +650,6 @@ static const struct regmap_config wm8731_regmap = { .max_register = WM8731_RESET, .volatile_reg = wm8731_volatile, - .writeable_reg = wm8731_writeable, .cache_type = REGCACHE_RBTREE, .reg_defaults = wm8731_reg_defaults,