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.
This commit is contained in:
Mark Brown 2022-06-27 21:45:57 +01:00
commit 77c77f03fe
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0
3 changed files with 175 additions and 10 deletions

View File

@ -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"
@ -36,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:
@ -72,6 +96,10 @@ examples:
max98396: amplifier@39 {
compatible = "adi,max98396";
reg = <0x39>;
dvdd-supply = <&regulator_1v2>;
dvddio-supply = <&regulator_1v8>;
avdd-supply = <&regulator_1v8>;
pvdd-supply = <&regulator_pvdd>;
adi,vmon-slot-no = <0>;
adi,imon-slot-no = <1>;
reset-gpios = <&gpio 4 GPIO_ACTIVE_LOW>;

View File

@ -5,11 +5,18 @@
#include <linux/i2c.h>
#include <linux/module.h>
#include <sound/pcm_params.h>
#include <linux/regulator/consumer.h>
#include <sound/soc.h>
#include <linux/gpio.h>
#include <sound/tlv.h>
#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},
@ -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 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) {
@ -365,7 +375,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;
}
@ -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;
break;
default:
dev_err(component->dev, "DAI format %d unsupported\n",
fmt & SND_SOC_DAIFMT_FORMAT_MASK);
return -EINVAL;
}
@ -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);
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,
@ -412,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,
@ -454,8 +466,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;
}
@ -640,7 +653,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;
}
@ -1329,6 +1342,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);
@ -1358,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)
@ -1424,12 +1446,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);
@ -1507,17 +1555,35 @@ 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
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 +1609,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;

View File

@ -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,11 +294,16 @@ 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 spkfb_slot;
unsigned int bypass_slot;
bool interleave_mode;
unsigned int ch_size;