Unverified Commit 77c77f03 authored by Mark Brown's avatar Mark Brown
Browse files

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
Loading
Loading
Loading
Loading
+29 −1
Original line number Diff line number Diff line
@@ -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>;
+138 −9
Original line number Diff line number Diff line
@@ -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;
+8 −0
Original line number Diff line number Diff line
@@ -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;