From d9bd3aea31aba94b5811776e0817a15ea4420e84 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Fri, 24 Jun 2022 12:47:05 +0200 Subject: ASoC: dt-bindings: max98396: add voltage supplies The device is supplied with 3 core voltages (DVVDIO, DVDD, AVDD), and PVDD and/or VBAT. Signed-off-by: Daniel Mack Link: https://lore.kernel.org/r/20220624104712.1934484-2-daniel@zonque.org Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/adi,max98396.yaml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/adi,max98396.yaml b/Documentation/devicetree/bindings/sound/adi,max98396.yaml index ec4c10c2598a..a23ac8036e1c 100644 --- a/Documentation/devicetree/bindings/sound/adi,max98396.yaml +++ b/Documentation/devicetree/bindings/sound/adi,max98396.yaml @@ -24,6 +24,21 @@ properties: maxItems: 1 description: I2C address of the device. + avdd-supply: + description: A 1.8V supply that powers up the AVDD pin. + + dvdd-supply: + description: A 1.2V supply that powers up the DVDD pin. + + dvddio-supply: + description: A 1.2V or 1.8V supply that powers up the VDDIO pin. + + pvdd-supply: + description: A 3.0V to 20V supply that powers up the PVDD pin. + + vbat-supply: + description: A 3.3V to 5.5V supply that powers up the VBAT pin. + adi,vmon-slot-no: description: slot number of the voltage sense monitor $ref: "/schemas/types.yaml#/definitions/uint32" @@ -72,6 +87,10 @@ examples: max98396: amplifier@39 { compatible = "adi,max98396"; reg = <0x39>; + dvdd-supply = <®ulator_1v2>; + dvddio-supply = <®ulator_1v8>; + avdd-supply = <®ulator_1v8>; + pvdd-supply = <®ulator_pvdd>; adi,vmon-slot-no = <0>; adi,imon-slot-no = <1>; reset-gpios = <&gpio 4 GPIO_ACTIVE_LOW>; -- cgit v1.2.3 From 0ce44afd29768e893ff9112ba208271e0d558780 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Fri, 24 Jun 2022 12:47:07 +0200 Subject: ASoC: dt-bindings: max98396: Document adi,bypass-slot-no This property allows to select the PCM data input channel that is routed to the speaker audio processing bypass path. The driver already implements this property. While at it, fix the default value for adi,imon-slot-no. Signed-off-by: Daniel Mack Link: https://lore.kernel.org/r/20220624104712.1934484-4-daniel@zonque.org Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/adi,max98396.yaml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/sound/adi,max98396.yaml b/Documentation/devicetree/bindings/sound/adi,max98396.yaml index a23ac8036e1c..8d2ef991db40 100644 --- a/Documentation/devicetree/bindings/sound/adi,max98396.yaml +++ b/Documentation/devicetree/bindings/sound/adi,max98396.yaml @@ -51,13 +51,22 @@ properties: $ref: "/schemas/types.yaml#/definitions/uint32" minimum: 0 maximum: 15 - default: 0 + default: 1 adi,spkfb-slot-no: description: slot number of speaker DSP monitor $ref: "/schemas/types.yaml#/definitions/uint32" minimum: 0 maximum: 15 + default: 2 + + adi,bypass-slot-no: + description: + Selects the PCM data input channel that is routed to the speaker + audio processing bypass path. + $ref: "/schemas/types.yaml#/definitions/uint32" + minimum: 0 + maximum: 15 default: 0 adi,interleave-mode: -- cgit v1.2.3 From 703ee0557f8921c96e8c42f832b3bd69b7bfb262 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Fri, 24 Jun 2022 12:47:08 +0200 Subject: ASoC: max98396: add voltage regulators The device has up to 5 potentially independent power supplies: AVDD, DVDD, DVVDIO, VBAT and PVDD. The former 3 are mandatory for the device to function. One of VBAT and PVDD should also be made available. Regulators are enabled during probe time and will stay active except when in suspend mode. Futher, the chip needs to be informed about the presence of VBAT through a bit in register 0x20a0. Signed-off-by: Daniel Mack Link: https://lore.kernel.org/r/20220624104712.1934484-5-daniel@zonque.org Signed-off-by: Mark Brown --- sound/soc/codecs/max98396.c | 117 +++++++++++++++++++++++++++++++++++++++++++- sound/soc/codecs/max98396.h | 7 +++ 2 files changed, 123 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/max98396.c b/sound/soc/codecs/max98396.c index 56eb62bb041f..06ac637f2696 100644 --- a/sound/soc/codecs/max98396.c +++ b/sound/soc/codecs/max98396.c @@ -5,11 +5,18 @@ #include #include #include +#include #include #include #include #include "max98396.h" +static const char * const max98396_core_supplies[MAX98396_NUM_CORE_SUPPLIES] = { + "avdd", + "dvdd", + "dvddio", +}; + static struct reg_default max98396_reg[] = { {MAX98396_R2000_SW_RESET, 0x00}, {MAX98396_R2001_INT_RAW1, 0x00}, @@ -1329,6 +1336,12 @@ static int max98396_probe(struct snd_soc_component *component) regmap_write(max98396->regmap, MAX98397_R2057_PCM_RX_SRC2, 0x10); } + /* Supply control */ + regmap_update_bits(max98396->regmap, + MAX98396_R20A0_AMP_SUPPLY_CTL, + MAX98396_AMP_SUPPLY_NOVBAT, + (max98396->vbat == NULL) ? + MAX98396_AMP_SUPPLY_NOVBAT : 0); /* Enable DC blocker */ regmap_update_bits(max98396->regmap, MAX98396_R2092_AMP_DSP_CFG, 1, 1); @@ -1424,12 +1437,38 @@ static int max98396_suspend(struct device *dev) regcache_cache_only(max98396->regmap, true); regcache_mark_dirty(max98396->regmap); + regulator_bulk_disable(MAX98396_NUM_CORE_SUPPLIES, + max98396->core_supplies); + if (max98396->pvdd) + regulator_disable(max98396->pvdd); + + if (max98396->vbat) + regulator_disable(max98396->vbat); + return 0; } static int max98396_resume(struct device *dev) { struct max98396_priv *max98396 = dev_get_drvdata(dev); + int ret; + + ret = regulator_bulk_enable(MAX98396_NUM_CORE_SUPPLIES, + max98396->core_supplies); + if (ret < 0) + return ret; + + if (max98396->pvdd) { + ret = regulator_enable(max98396->pvdd); + if (ret < 0) + return ret; + } + + if (max98396->vbat) { + ret = regulator_enable(max98396->vbat); + if (ret < 0) + return ret; + } regcache_cache_only(max98396->regmap, false); max98396_reset(max98396, dev); @@ -1513,11 +1552,24 @@ static void max98396_read_device_property(struct device *dev, max98396->bypass_slot = 0; } +static void max98396_core_supplies_disable(void *priv) +{ + struct max98396_priv *max98396 = priv; + + regulator_bulk_disable(MAX98396_NUM_CORE_SUPPLIES, + max98396->core_supplies); +} + +static void max98396_supply_disable(void *r) +{ + regulator_disable((struct regulator *) r); +} + static int max98396_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { struct max98396_priv *max98396 = NULL; - int ret, reg; + int i, ret, reg; max98396 = devm_kzalloc(&i2c->dev, sizeof(*max98396), GFP_KERNEL); @@ -1543,6 +1595,69 @@ static int max98396_i2c_probe(struct i2c_client *i2c, return ret; } + /* Obtain regulator supplies */ + for (i = 0; i < MAX98396_NUM_CORE_SUPPLIES; i++) + max98396->core_supplies[i].supply = max98396_core_supplies[i]; + + ret = devm_regulator_bulk_get(&i2c->dev, MAX98396_NUM_CORE_SUPPLIES, + max98396->core_supplies); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to request core supplies: %d\n", ret); + return ret; + } + + max98396->vbat = devm_regulator_get_optional(&i2c->dev, "vbat"); + if (IS_ERR(max98396->vbat)) { + if (PTR_ERR(max98396->vbat) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + max98396->vbat = NULL; + } + + max98396->pvdd = devm_regulator_get_optional(&i2c->dev, "pvdd"); + if (IS_ERR(max98396->pvdd)) { + if (PTR_ERR(max98396->pvdd) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + max98396->pvdd = NULL; + } + + ret = regulator_bulk_enable(MAX98396_NUM_CORE_SUPPLIES, + max98396->core_supplies); + if (ret < 0) { + dev_err(&i2c->dev, "Unable to enable core supplies: %d", ret); + return ret; + } + + ret = devm_add_action_or_reset(&i2c->dev, max98396_core_supplies_disable, + max98396); + if (ret < 0) + return ret; + + if (max98396->pvdd) { + ret = regulator_enable(max98396->pvdd); + if (ret < 0) + return ret; + + ret = devm_add_action_or_reset(&i2c->dev, + max98396_supply_disable, + max98396->pvdd); + if (ret < 0) + return ret; + } + + if (max98396->vbat) { + ret = regulator_enable(max98396->vbat); + if (ret < 0) + return ret; + + ret = devm_add_action_or_reset(&i2c->dev, + max98396_supply_disable, + max98396->vbat); + if (ret < 0) + return ret; + } + /* update interleave mode info */ if (device_property_read_bool(&i2c->dev, "adi,interleave_mode")) max98396->interleave_mode = true; diff --git a/sound/soc/codecs/max98396.h b/sound/soc/codecs/max98396.h index 694411038597..8fa081f5d2d3 100644 --- a/sound/soc/codecs/max98396.h +++ b/sound/soc/codecs/max98396.h @@ -274,6 +274,9 @@ #define MAX98396_DSP_SPK_SAFE_EN_SHIFT (5) #define MAX98396_DSP_SPK_WB_FLT_EN_SHIFT (6) +/* MAX98396_R20A0_AMP_SUPPLY_CTL */ +#define MAX98396_AMP_SUPPLY_NOVBAT (0x1 << 0) + /* MAX98396_R20E0_IV_SENSE_PATH_CFG */ #define MAX98396_IV_SENSE_DCBLK_EN_MASK (0x3 << 0) #define MAX98396_IV_SENSE_DCBLK_EN_SHIFT (0) @@ -291,9 +294,13 @@ enum { CODEC_TYPE_MAX98397, }; +#define MAX98396_NUM_CORE_SUPPLIES 3 + struct max98396_priv { struct regmap *regmap; struct gpio_desc *reset_gpio; + struct regulator_bulk_data core_supplies[MAX98396_NUM_CORE_SUPPLIES]; + struct regulator *pvdd, *vbat; unsigned int v_slot; unsigned int i_slot; unsigned int bypass_slot; -- cgit v1.2.3 From a8c1dc9e8f01811b0c3fee65b9bc4773b2d00d96 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Fri, 24 Jun 2022 12:47:09 +0200 Subject: ASoC: max98396: Improve some error prints Let's log what actually failed and log at some more places. Signed-off-by: Daniel Mack Link: https://lore.kernel.org/r/20220624104712.1934484-6-daniel@zonque.org Signed-off-by: Mark Brown --- sound/soc/codecs/max98396.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/max98396.c b/sound/soc/codecs/max98396.c index 06ac637f2696..faa81b4bb709 100644 --- a/sound/soc/codecs/max98396.c +++ b/sound/soc/codecs/max98396.c @@ -372,7 +372,8 @@ static int max98396_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) break; default: - dev_err(component->dev, "DAI invert mode unsupported\n"); + dev_err(component->dev, "DAI invert mode %d unsupported\n", + fmt & SND_SOC_DAIFMT_INV_MASK); return -EINVAL; } @@ -391,6 +392,8 @@ static int max98396_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) format |= MAX98396_PCM_FORMAT_TDM_MODE0; break; default: + dev_err(component->dev, "DAI format %d unsupported\n", + fmt & SND_SOC_DAIFMT_FORMAT_MASK); return -EINVAL; } @@ -461,8 +464,9 @@ static int max98396_set_clock(struct snd_soc_component *component, /* BCLK configuration */ value = max98396_get_bclk_sel(blr_clk_ratio); if (!value) { - dev_err(component->dev, "format unsupported %d\n", - params_format(params)); + dev_err(component->dev, + "blr_clk_ratio %d unsupported, format %d\n", + blr_clk_ratio, params_format(params)); return -EINVAL; } @@ -647,7 +651,7 @@ static int max98396_dai_tdm_slot(struct snd_soc_dai *dai, chan_sz = MAX98396_PCM_MODE_CFG_CHANSZ_32; break; default: - dev_err(component->dev, "format unsupported %d\n", + dev_err(component->dev, "slot width %d unsupported\n", slot_width); return -EINVAL; } -- cgit v1.2.3 From c529fd620b842c926ccc8cea913886d802c30f16 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Fri, 24 Jun 2022 12:47:10 +0200 Subject: ASoC: max98396: Fix register access for PCM format settings max98396_dai_set_fmt() modifes register 2041 and touches bits in the mask 0x3a. Make sure to use the right mask for that operation. Signed-off-by: Daniel Mack Link: https://lore.kernel.org/r/20220624104712.1934484-7-daniel@zonque.org Signed-off-by: Mark Brown --- sound/soc/codecs/max98396.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/max98396.c b/sound/soc/codecs/max98396.c index faa81b4bb709..0a1d98279a3e 100644 --- a/sound/soc/codecs/max98396.c +++ b/sound/soc/codecs/max98396.c @@ -349,12 +349,15 @@ static int max98396_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) { struct snd_soc_component *component = codec_dai->component; struct max98396_priv *max98396 = snd_soc_component_get_drvdata(component); - unsigned int format = 0; + unsigned int format_mask, format = 0; unsigned int bclk_pol = 0; int ret, status; int reg; bool update = false; + format_mask = MAX98396_PCM_MODE_CFG_FORMAT_MASK | + MAX98396_PCM_MODE_CFG_LRCLKEDGE; + dev_dbg(component->dev, "%s: fmt 0x%08X\n", __func__, fmt); switch (fmt & SND_SOC_DAIFMT_INV_MASK) { @@ -405,7 +408,7 @@ static int max98396_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) ret = regmap_read(max98396->regmap, MAX98396_R2041_PCM_MODE_CFG, ®); if (ret < 0) return -EINVAL; - if (format != (reg & MAX98396_PCM_BCLKEDGE_BSEL_MASK)) { + if (format != (reg & format_mask)) { update = true; } else { ret = regmap_read(max98396->regmap, @@ -422,8 +425,7 @@ static int max98396_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) regmap_update_bits(max98396->regmap, MAX98396_R2041_PCM_MODE_CFG, - MAX98396_PCM_BCLKEDGE_BSEL_MASK, - format); + format_mask, format); regmap_update_bits(max98396->regmap, MAX98396_R2042_PCM_CLK_SETUP, -- cgit v1.2.3 From f42924b49bf7935d21e2f2e98fdc9aa8dd176699 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Fri, 24 Jun 2022 12:47:11 +0200 Subject: ASoC: max98396: Implement DSP speaker monitor Allow the selection of the TDM slot that is used to send back speaker monitor data. The DT property adi,spkfb-slot-no can be used to configure this setting which defaults to 2. Signed-off-by: Daniel Mack Link: https://lore.kernel.org/r/20220624104712.1934484-8-daniel@zonque.org Signed-off-by: Mark Brown --- sound/soc/codecs/max98396.c | 8 ++++++++ sound/soc/codecs/max98396.h | 1 + 2 files changed, 9 insertions(+) diff --git a/sound/soc/codecs/max98396.c b/sound/soc/codecs/max98396.c index 0a1d98279a3e..f28831f4e74b 100644 --- a/sound/soc/codecs/max98396.c +++ b/sound/soc/codecs/max98396.c @@ -1377,6 +1377,9 @@ static int max98396_probe(struct snd_soc_component *component) regmap_write(max98396->regmap, MAX98396_R2045_PCM_TX_CTRL_2, max98396->i_slot); + regmap_write(max98396->regmap, + MAX98396_R204A_PCM_TX_CTRL_7, + max98396->spkfb_slot); if (max98396->v_slot < 8) if (max98396->device_id == CODEC_TYPE_MAX98396) @@ -1552,6 +1555,11 @@ static void max98396_read_device_property(struct device *dev, else max98396->i_slot = 1; + if (!device_property_read_u32(dev, "adi,spkfb-slot-no", &value)) + max98396->spkfb_slot = value & 0xF; + else + max98396->spkfb_slot = 2; + if (!device_property_read_u32(dev, "adi,bypass-slot-no", &value)) max98396->bypass_slot = value & 0xF; else diff --git a/sound/soc/codecs/max98396.h b/sound/soc/codecs/max98396.h index 8fa081f5d2d3..ff330ef61568 100644 --- a/sound/soc/codecs/max98396.h +++ b/sound/soc/codecs/max98396.h @@ -303,6 +303,7 @@ struct max98396_priv { struct regulator *pvdd, *vbat; unsigned int v_slot; unsigned int i_slot; + unsigned int spkfb_slot; unsigned int bypass_slot; bool interleave_mode; unsigned int ch_size; -- cgit v1.2.3