Unverified Commit 95ff4aad authored by Mark Brown's avatar Mark Brown
Browse files

Two bug fixes for tas5805m codec driver

Merge series from Daniel Beer <daniel.beer@igorinstitute.com>:

This pair of patches fixes two issues which crept in while revising the
original submission, at a time when I no longer had access to test
hardware.

The fixes here have been tested and verified on hardware.
parents fe1e7e8c e0576cd6
Loading
Loading
Loading
Loading
+90 −41
Original line number Diff line number Diff line
@@ -154,6 +154,7 @@ static const uint32_t tas5805m_volume[] = {
#define TAS5805M_VOLUME_MIN	0

struct tas5805m_priv {
	struct i2c_client		*i2c;
	struct regulator		*pvdd;
	struct gpio_desc		*gpio_pdn_n;

@@ -165,6 +166,9 @@ struct tas5805m_priv {
	int				vol[2];
	bool				is_powered;
	bool				is_muted;

	struct work_struct		work;
	struct mutex			lock;
};

static void set_dsp_scale(struct regmap *rm, int offset, int vol)
@@ -181,13 +185,11 @@ static void set_dsp_scale(struct regmap *rm, int offset, int vol)
	regmap_bulk_write(rm, offset, v, ARRAY_SIZE(v));
}

static void tas5805m_refresh(struct snd_soc_component *component)
static void tas5805m_refresh(struct tas5805m_priv *tas5805m)
{
	struct tas5805m_priv *tas5805m =
		snd_soc_component_get_drvdata(component);
	struct regmap *rm = tas5805m->regmap;

	dev_dbg(component->dev, "refresh: is_muted=%d, vol=%d/%d\n",
	dev_dbg(&tas5805m->i2c->dev, "refresh: is_muted=%d, vol=%d/%d\n",
		tas5805m->is_muted, tas5805m->vol[0], tas5805m->vol[1]);

	regmap_write(rm, REG_PAGE, 0x00);
@@ -201,6 +203,9 @@ static void tas5805m_refresh(struct snd_soc_component *component)
	set_dsp_scale(rm, 0x24, tas5805m->vol[0]);
	set_dsp_scale(rm, 0x28, tas5805m->vol[1]);

	regmap_write(rm, REG_PAGE, 0x00);
	regmap_write(rm, REG_BOOK, 0x00);

	/* Set/clear digital soft-mute */
	regmap_write(rm, REG_DEVICE_CTRL_2,
		(tas5805m->is_muted ? DCTRL2_MUTE : 0) |
@@ -226,8 +231,11 @@ static int tas5805m_vol_get(struct snd_kcontrol *kcontrol,
	struct tas5805m_priv *tas5805m =
		snd_soc_component_get_drvdata(component);

	mutex_lock(&tas5805m->lock);
	ucontrol->value.integer.value[0] = tas5805m->vol[0];
	ucontrol->value.integer.value[1] = tas5805m->vol[1];
	mutex_unlock(&tas5805m->lock);

	return 0;
}

@@ -243,11 +251,13 @@ static int tas5805m_vol_put(struct snd_kcontrol *kcontrol,
		snd_soc_kcontrol_component(kcontrol);
	struct tas5805m_priv *tas5805m =
		snd_soc_component_get_drvdata(component);
	int ret = 0;

	if (!(volume_is_valid(ucontrol->value.integer.value[0]) &&
	      volume_is_valid(ucontrol->value.integer.value[1])))
		return -EINVAL;

	mutex_lock(&tas5805m->lock);
	if (tas5805m->vol[0] != ucontrol->value.integer.value[0] ||
	    tas5805m->vol[1] != ucontrol->value.integer.value[1]) {
		tas5805m->vol[0] = ucontrol->value.integer.value[0];
@@ -256,11 +266,12 @@ static int tas5805m_vol_put(struct snd_kcontrol *kcontrol,
			tas5805m->vol[0], tas5805m->vol[1],
			tas5805m->is_powered);
		if (tas5805m->is_powered)
			tas5805m_refresh(component);
		return 1;
			tas5805m_refresh(tas5805m);
		ret = 1;
	}
	mutex_unlock(&tas5805m->lock);

	return 0;
	return ret;
}

static const struct snd_kcontrol_new tas5805m_snd_controls[] = {
@@ -294,36 +305,67 @@ static int tas5805m_trigger(struct snd_pcm_substream *substream, int cmd,
	struct snd_soc_component *component = dai->component;
	struct tas5805m_priv *tas5805m =
		snd_soc_component_get_drvdata(component);
	struct regmap *rm = tas5805m->regmap;
	unsigned int chan, global1, global2;

	switch (cmd) {
	case SNDRV_PCM_TRIGGER_START:
	case SNDRV_PCM_TRIGGER_RESUME:
	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
		dev_dbg(component->dev, "DSP startup\n");
		dev_dbg(component->dev, "clock start\n");
		schedule_work(&tas5805m->work);
		break;

	case SNDRV_PCM_TRIGGER_STOP:
	case SNDRV_PCM_TRIGGER_SUSPEND:
	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
		break;

	default:
		return -EINVAL;
	}

	return 0;
}

static void do_work(struct work_struct *work)
{
	struct tas5805m_priv *tas5805m =
	       container_of(work, struct tas5805m_priv, work);
	struct regmap *rm = tas5805m->regmap;

	dev_dbg(&tas5805m->i2c->dev, "DSP startup\n");

	mutex_lock(&tas5805m->lock);
	/* We mustn't issue any I2C transactions until the I2S
	 * clock is stable. Furthermore, we must allow a 5ms
	 * delay after the first set of register writes to
	 * allow the DSP to boot before configuring it.
	 */
	usleep_range(5000, 10000);
		send_cfg(rm, dsp_cfg_preboot,
			ARRAY_SIZE(dsp_cfg_preboot));
	send_cfg(rm, dsp_cfg_preboot, ARRAY_SIZE(dsp_cfg_preboot));
	usleep_range(5000, 15000);
		send_cfg(rm, tas5805m->dsp_cfg_data,
			tas5805m->dsp_cfg_len);
	send_cfg(rm, tas5805m->dsp_cfg_data, tas5805m->dsp_cfg_len);

	tas5805m->is_powered = true;
		tas5805m_refresh(component);
		break;
	tas5805m_refresh(tas5805m);
	mutex_unlock(&tas5805m->lock);
}

static int tas5805m_dac_event(struct snd_soc_dapm_widget *w,
			      struct snd_kcontrol *kcontrol, int event)
{
	struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
	struct tas5805m_priv *tas5805m =
		snd_soc_component_get_drvdata(component);
	struct regmap *rm = tas5805m->regmap;

	if (event & SND_SOC_DAPM_PRE_PMD) {
		unsigned int chan, global1, global2;

	case SNDRV_PCM_TRIGGER_STOP:
	case SNDRV_PCM_TRIGGER_SUSPEND:
	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
		dev_dbg(component->dev, "DSP shutdown\n");
		cancel_work_sync(&tas5805m->work);

		mutex_lock(&tas5805m->lock);
		if (tas5805m->is_powered) {
			tas5805m->is_powered = false;

			regmap_write(rm, REG_PAGE, 0x00);
@@ -333,15 +375,13 @@ static int tas5805m_trigger(struct snd_pcm_substream *substream, int cmd,
			regmap_read(rm, REG_GLOBAL_FAULT1, &global1);
			regmap_read(rm, REG_GLOBAL_FAULT2, &global2);

		dev_dbg(component->dev,
			"fault regs: CHAN=%02x, GLOBAL1=%02x, GLOBAL2=%02x\n",
			dev_dbg(component->dev, "fault regs: CHAN=%02x, "
				"GLOBAL1=%02x, GLOBAL2=%02x\n",
				chan, global1, global2);

			regmap_write(rm, REG_DEVICE_CTRL_2, DCTRL2_MODE_HIZ);
		break;

	default:
		return -EINVAL;
		}
		mutex_unlock(&tas5805m->lock);
	}

	return 0;
@@ -354,7 +394,8 @@ static const struct snd_soc_dapm_route tas5805m_audio_map[] = {

static const struct snd_soc_dapm_widget tas5805m_dapm_widgets[] = {
	SND_SOC_DAPM_AIF_IN("DAC IN", "Playback", 0, SND_SOC_NOPM, 0, 0),
	SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0),
	SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0,
		tas5805m_dac_event, SND_SOC_DAPM_PRE_PMD),
	SND_SOC_DAPM_OUTPUT("OUT")
};

@@ -375,11 +416,14 @@ static int tas5805m_mute(struct snd_soc_dai *dai, int mute, int direction)
	struct tas5805m_priv *tas5805m =
		snd_soc_component_get_drvdata(component);

	mutex_lock(&tas5805m->lock);
	dev_dbg(component->dev, "set mute=%d (is_powered=%d)\n",
		mute, tas5805m->is_powered);

	tas5805m->is_muted = mute;
	if (tas5805m->is_powered)
		tas5805m_refresh(component);
		tas5805m_refresh(tas5805m);
	mutex_unlock(&tas5805m->lock);

	return 0;
}
@@ -434,6 +478,7 @@ static int tas5805m_i2c_probe(struct i2c_client *i2c)
	if (!tas5805m)
		return -ENOMEM;

	tas5805m->i2c = i2c;
	tas5805m->pvdd = devm_regulator_get(dev, "pvdd");
	if (IS_ERR(tas5805m->pvdd)) {
		dev_err(dev, "failed to get pvdd supply: %ld\n",
@@ -507,6 +552,9 @@ static int tas5805m_i2c_probe(struct i2c_client *i2c)
	gpiod_set_value(tas5805m->gpio_pdn_n, 1);
	usleep_range(10000, 15000);

	INIT_WORK(&tas5805m->work, do_work);
	mutex_init(&tas5805m->lock);

	/* Don't register through devm. We need to be able to unregister
	 * the component prior to deasserting PDN#
	 */
@@ -527,6 +575,7 @@ static void tas5805m_i2c_remove(struct i2c_client *i2c)
	struct device *dev = &i2c->dev;
	struct tas5805m_priv *tas5805m = dev_get_drvdata(dev);

	cancel_work_sync(&tas5805m->work);
	snd_soc_unregister_component(dev);
	gpiod_set_value(tas5805m->gpio_pdn_n, 0);
	usleep_range(10000, 15000);