Commit 77c77f03 authored by Mark Brown's avatar Mark Brown

ASoC: max98396: Some assorted fixes and additions

Merge series from Daniel Mack <daniel@zonque.org>:

This is a series of some patches that I collected while using the
max98396 driver is a TDM mode setup.

They correct BSEL and PCM mode configs, add support for power supplies
and add some bits to the documentation.

The code is tested in TDM-16 and TDM-8 mode with 32 channel width.
parents 7b0cd83c f42924b4
...@@ -24,6 +24,21 @@ properties: ...@@ -24,6 +24,21 @@ properties:
maxItems: 1 maxItems: 1
description: I2C address of the device. 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: adi,vmon-slot-no:
description: slot number of the voltage sense monitor description: slot number of the voltage sense monitor
$ref: "/schemas/types.yaml#/definitions/uint32" $ref: "/schemas/types.yaml#/definitions/uint32"
...@@ -36,13 +51,22 @@ properties: ...@@ -36,13 +51,22 @@ properties:
$ref: "/schemas/types.yaml#/definitions/uint32" $ref: "/schemas/types.yaml#/definitions/uint32"
minimum: 0 minimum: 0
maximum: 15 maximum: 15
default: 0 default: 1
adi,spkfb-slot-no: adi,spkfb-slot-no:
description: slot number of speaker DSP monitor description: slot number of speaker DSP monitor
$ref: "/schemas/types.yaml#/definitions/uint32" $ref: "/schemas/types.yaml#/definitions/uint32"
minimum: 0 minimum: 0
maximum: 15 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 default: 0
adi,interleave-mode: adi,interleave-mode:
...@@ -72,6 +96,10 @@ examples: ...@@ -72,6 +96,10 @@ examples:
max98396: amplifier@39 { max98396: amplifier@39 {
compatible = "adi,max98396"; compatible = "adi,max98396";
reg = <0x39>; reg = <0x39>;
dvdd-supply = <&regulator_1v2>;
dvddio-supply = <&regulator_1v8>;
avdd-supply = <&regulator_1v8>;
pvdd-supply = <&regulator_pvdd>;
adi,vmon-slot-no = <0>; adi,vmon-slot-no = <0>;
adi,imon-slot-no = <1>; adi,imon-slot-no = <1>;
reset-gpios = <&gpio 4 GPIO_ACTIVE_LOW>; reset-gpios = <&gpio 4 GPIO_ACTIVE_LOW>;
......
...@@ -5,11 +5,18 @@ ...@@ -5,11 +5,18 @@
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/module.h> #include <linux/module.h>
#include <sound/pcm_params.h> #include <sound/pcm_params.h>
#include <linux/regulator/consumer.h>
#include <sound/soc.h> #include <sound/soc.h>
#include <linux/gpio.h> #include <linux/gpio.h>
#include <sound/tlv.h> #include <sound/tlv.h>
#include "max98396.h" #include "max98396.h"
static const char * const max98396_core_supplies[MAX98396_NUM_CORE_SUPPLIES] = {
"avdd",
"dvdd",
"dvddio",
};
static struct reg_default max98396_reg[] = { static struct reg_default max98396_reg[] = {
{MAX98396_R2000_SW_RESET, 0x00}, {MAX98396_R2000_SW_RESET, 0x00},
{MAX98396_R2001_INT_RAW1, 0x00}, {MAX98396_R2001_INT_RAW1, 0x00},
...@@ -342,12 +349,15 @@ static int max98396_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) ...@@ -342,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 snd_soc_component *component = codec_dai->component;
struct max98396_priv *max98396 = snd_soc_component_get_drvdata(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; unsigned int bclk_pol = 0;
int ret, status; int ret, status;
int reg; int reg;
bool update = false; 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); dev_dbg(component->dev, "%s: fmt 0x%08X\n", __func__, fmt);
switch (fmt & SND_SOC_DAIFMT_INV_MASK) { switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
...@@ -365,7 +375,8 @@ static int max98396_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) ...@@ -365,7 +375,8 @@ static int max98396_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
break; break;
default: 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; return -EINVAL;
} }
...@@ -384,6 +395,8 @@ static int max98396_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) ...@@ -384,6 +395,8 @@ static int max98396_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
format |= MAX98396_PCM_FORMAT_TDM_MODE0; format |= MAX98396_PCM_FORMAT_TDM_MODE0;
break; break;
default: default:
dev_err(component->dev, "DAI format %d unsupported\n",
fmt & SND_SOC_DAIFMT_FORMAT_MASK);
return -EINVAL; return -EINVAL;
} }
...@@ -395,7 +408,7 @@ static int max98396_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) ...@@ -395,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, &reg); ret = regmap_read(max98396->regmap, MAX98396_R2041_PCM_MODE_CFG, &reg);
if (ret < 0) if (ret < 0)
return -EINVAL; return -EINVAL;
if (format != (reg & MAX98396_PCM_BCLKEDGE_BSEL_MASK)) { if (format != (reg & format_mask)) {
update = true; update = true;
} else { } else {
ret = regmap_read(max98396->regmap, ret = regmap_read(max98396->regmap,
...@@ -412,8 +425,7 @@ static int max98396_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) ...@@ -412,8 +425,7 @@ static int max98396_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
regmap_update_bits(max98396->regmap, regmap_update_bits(max98396->regmap,
MAX98396_R2041_PCM_MODE_CFG, MAX98396_R2041_PCM_MODE_CFG,
MAX98396_PCM_BCLKEDGE_BSEL_MASK, format_mask, format);
format);
regmap_update_bits(max98396->regmap, regmap_update_bits(max98396->regmap,
MAX98396_R2042_PCM_CLK_SETUP, MAX98396_R2042_PCM_CLK_SETUP,
...@@ -454,8 +466,9 @@ static int max98396_set_clock(struct snd_soc_component *component, ...@@ -454,8 +466,9 @@ static int max98396_set_clock(struct snd_soc_component *component,
/* BCLK configuration */ /* BCLK configuration */
value = max98396_get_bclk_sel(blr_clk_ratio); value = max98396_get_bclk_sel(blr_clk_ratio);
if (!value) { if (!value) {
dev_err(component->dev, "format unsupported %d\n", dev_err(component->dev,
params_format(params)); "blr_clk_ratio %d unsupported, format %d\n",
blr_clk_ratio, params_format(params));
return -EINVAL; return -EINVAL;
} }
...@@ -640,7 +653,7 @@ static int max98396_dai_tdm_slot(struct snd_soc_dai *dai, ...@@ -640,7 +653,7 @@ static int max98396_dai_tdm_slot(struct snd_soc_dai *dai,
chan_sz = MAX98396_PCM_MODE_CFG_CHANSZ_32; chan_sz = MAX98396_PCM_MODE_CFG_CHANSZ_32;
break; break;
default: default:
dev_err(component->dev, "format unsupported %d\n", dev_err(component->dev, "slot width %d unsupported\n",
slot_width); slot_width);
return -EINVAL; return -EINVAL;
} }
...@@ -1329,6 +1342,12 @@ static int max98396_probe(struct snd_soc_component *component) ...@@ -1329,6 +1342,12 @@ static int max98396_probe(struct snd_soc_component *component)
regmap_write(max98396->regmap, regmap_write(max98396->regmap,
MAX98397_R2057_PCM_RX_SRC2, 0x10); 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 */ /* Enable DC blocker */
regmap_update_bits(max98396->regmap, regmap_update_bits(max98396->regmap,
MAX98396_R2092_AMP_DSP_CFG, 1, 1); MAX98396_R2092_AMP_DSP_CFG, 1, 1);
...@@ -1358,6 +1377,9 @@ static int max98396_probe(struct snd_soc_component *component) ...@@ -1358,6 +1377,9 @@ static int max98396_probe(struct snd_soc_component *component)
regmap_write(max98396->regmap, regmap_write(max98396->regmap,
MAX98396_R2045_PCM_TX_CTRL_2, MAX98396_R2045_PCM_TX_CTRL_2,
max98396->i_slot); max98396->i_slot);
regmap_write(max98396->regmap,
MAX98396_R204A_PCM_TX_CTRL_7,
max98396->spkfb_slot);
if (max98396->v_slot < 8) if (max98396->v_slot < 8)
if (max98396->device_id == CODEC_TYPE_MAX98396) if (max98396->device_id == CODEC_TYPE_MAX98396)
...@@ -1424,12 +1446,38 @@ static int max98396_suspend(struct device *dev) ...@@ -1424,12 +1446,38 @@ static int max98396_suspend(struct device *dev)
regcache_cache_only(max98396->regmap, true); regcache_cache_only(max98396->regmap, true);
regcache_mark_dirty(max98396->regmap); 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; return 0;
} }
static int max98396_resume(struct device *dev) static int max98396_resume(struct device *dev)
{ {
struct max98396_priv *max98396 = dev_get_drvdata(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); regcache_cache_only(max98396->regmap, false);
max98396_reset(max98396, dev); max98396_reset(max98396, dev);
...@@ -1507,17 +1555,35 @@ static void max98396_read_device_property(struct device *dev, ...@@ -1507,17 +1555,35 @@ static void max98396_read_device_property(struct device *dev,
else else
max98396->i_slot = 1; 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)) if (!device_property_read_u32(dev, "adi,bypass-slot-no", &value))
max98396->bypass_slot = value & 0xF; max98396->bypass_slot = value & 0xF;
else else
max98396->bypass_slot = 0; 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, static int max98396_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id) const struct i2c_device_id *id)
{ {
struct max98396_priv *max98396 = NULL; struct max98396_priv *max98396 = NULL;
int ret, reg; int i, ret, reg;
max98396 = devm_kzalloc(&i2c->dev, sizeof(*max98396), GFP_KERNEL); max98396 = devm_kzalloc(&i2c->dev, sizeof(*max98396), GFP_KERNEL);
...@@ -1543,6 +1609,69 @@ static int max98396_i2c_probe(struct i2c_client *i2c, ...@@ -1543,6 +1609,69 @@ static int max98396_i2c_probe(struct i2c_client *i2c,
return ret; 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 */ /* update interleave mode info */
if (device_property_read_bool(&i2c->dev, "adi,interleave_mode")) if (device_property_read_bool(&i2c->dev, "adi,interleave_mode"))
max98396->interleave_mode = true; max98396->interleave_mode = true;
......
...@@ -274,6 +274,9 @@ ...@@ -274,6 +274,9 @@
#define MAX98396_DSP_SPK_SAFE_EN_SHIFT (5) #define MAX98396_DSP_SPK_SAFE_EN_SHIFT (5)
#define MAX98396_DSP_SPK_WB_FLT_EN_SHIFT (6) #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 */ /* MAX98396_R20E0_IV_SENSE_PATH_CFG */
#define MAX98396_IV_SENSE_DCBLK_EN_MASK (0x3 << 0) #define MAX98396_IV_SENSE_DCBLK_EN_MASK (0x3 << 0)
#define MAX98396_IV_SENSE_DCBLK_EN_SHIFT (0) #define MAX98396_IV_SENSE_DCBLK_EN_SHIFT (0)
...@@ -291,11 +294,16 @@ enum { ...@@ -291,11 +294,16 @@ enum {
CODEC_TYPE_MAX98397, CODEC_TYPE_MAX98397,
}; };
#define MAX98396_NUM_CORE_SUPPLIES 3
struct max98396_priv { struct max98396_priv {
struct regmap *regmap; struct regmap *regmap;
struct gpio_desc *reset_gpio; 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 v_slot;
unsigned int i_slot; unsigned int i_slot;
unsigned int spkfb_slot;
unsigned int bypass_slot; unsigned int bypass_slot;
bool interleave_mode; bool interleave_mode;
unsigned int ch_size; unsigned int ch_size;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment