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:
commit
77c77f03fe
|
@ -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:
|
||||||
$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:
|
||||||
max98396: amplifier@39 {
|
max98396: amplifier@39 {
|
||||||
compatible = "adi,max98396";
|
compatible = "adi,max98396";
|
||||||
reg = <0x39>;
|
reg = <0x39>;
|
||||||
|
dvdd-supply = <®ulator_1v2>;
|
||||||
|
dvddio-supply = <®ulator_1v8>;
|
||||||
|
avdd-supply = <®ulator_1v8>;
|
||||||
|
pvdd-supply = <®ulator_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 @@
|
||||||
#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)
|
||||||
{
|
{
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
ret = regmap_read(max98396->regmap, MAX98396_R2041_PCM_MODE_CFG, ®);
|
ret = regmap_read(max98396->regmap, MAX98396_R2041_PCM_MODE_CFG, ®);
|
||||||
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)
|
||||||
|
|
||||||
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,
|
||||||
/* 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,
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
|
|
||||||
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,
|
||||||
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,
|
||||||
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 @@
|
||||||
#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 {
|
||||||
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;
|
||||||
|
|
Loading…
Reference in New Issue