Skip to content

Commit 1473237

Browse files
dlbeerbroonie
authored andcommitted
ASoC: tas5805m: rework to avoid scheduling while atomic.
There's some setup we need to do in order to get the DSP initialized, and this can't be done until a bit-clock is ready. In an earlier version of this driver, this work was done in a DAPM callback. The DAPM callback doesn't guarantee that the bit-clock is running, so the work was moved instead to the trigger callback. Unfortunately this callback runs in atomic context, and the setup code needs to do I2C transactions. Here we use a work_struct to kick off the setup in a thread instead. Fixes: ec45268 ("ASoC: add support for TAS5805M digital amplifier") Signed-off-by: Daniel Beer <[email protected]> Link: https://lore.kernel.org/r/85d8ba405cb009a7a3249b556dc8f3bdb1754fdf.1675497326.git.daniel.beer@igorinstitute.com Signed-off-by: Mark Brown <[email protected]>
1 parent 2e7c665 commit 1473237

File tree

1 file changed

+87
-41
lines changed

1 file changed

+87
-41
lines changed

sound/soc/codecs/tas5805m.c

Lines changed: 87 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ static const uint32_t tas5805m_volume[] = {
154154
#define TAS5805M_VOLUME_MIN 0
155155

156156
struct tas5805m_priv {
157+
struct i2c_client *i2c;
157158
struct regulator *pvdd;
158159
struct gpio_desc *gpio_pdn_n;
159160

@@ -165,6 +166,9 @@ struct tas5805m_priv {
165166
int vol[2];
166167
bool is_powered;
167168
bool is_muted;
169+
170+
struct work_struct work;
171+
struct mutex lock;
168172
};
169173

170174
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)
181185
regmap_bulk_write(rm, offset, v, ARRAY_SIZE(v));
182186
}
183187

184-
static void tas5805m_refresh(struct snd_soc_component *component)
188+
static void tas5805m_refresh(struct tas5805m_priv *tas5805m)
185189
{
186-
struct tas5805m_priv *tas5805m =
187-
snd_soc_component_get_drvdata(component);
188190
struct regmap *rm = tas5805m->regmap;
189191

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

193195
regmap_write(rm, REG_PAGE, 0x00);
@@ -226,8 +228,11 @@ static int tas5805m_vol_get(struct snd_kcontrol *kcontrol,
226228
struct tas5805m_priv *tas5805m =
227229
snd_soc_component_get_drvdata(component);
228230

231+
mutex_lock(&tas5805m->lock);
229232
ucontrol->value.integer.value[0] = tas5805m->vol[0];
230233
ucontrol->value.integer.value[1] = tas5805m->vol[1];
234+
mutex_unlock(&tas5805m->lock);
235+
231236
return 0;
232237
}
233238

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

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

257+
mutex_lock(&tas5805m->lock);
251258
if (tas5805m->vol[0] != ucontrol->value.integer.value[0] ||
252259
tas5805m->vol[1] != ucontrol->value.integer.value[1]) {
253260
tas5805m->vol[0] = ucontrol->value.integer.value[0];
@@ -256,11 +263,12 @@ static int tas5805m_vol_put(struct snd_kcontrol *kcontrol,
256263
tas5805m->vol[0], tas5805m->vol[1],
257264
tas5805m->is_powered);
258265
if (tas5805m->is_powered)
259-
tas5805m_refresh(component);
260-
return 1;
266+
tas5805m_refresh(tas5805m);
267+
ret = 1;
261268
}
269+
mutex_unlock(&tas5805m->lock);
262270

263-
return 0;
271+
return ret;
264272
}
265273

266274
static const struct snd_kcontrol_new tas5805m_snd_controls[] = {
@@ -294,54 +302,83 @@ static int tas5805m_trigger(struct snd_pcm_substream *substream, int cmd,
294302
struct snd_soc_component *component = dai->component;
295303
struct tas5805m_priv *tas5805m =
296304
snd_soc_component_get_drvdata(component);
297-
struct regmap *rm = tas5805m->regmap;
298-
unsigned int chan, global1, global2;
299305

300306
switch (cmd) {
301307
case SNDRV_PCM_TRIGGER_START:
302308
case SNDRV_PCM_TRIGGER_RESUME:
303309
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
304-
dev_dbg(component->dev, "DSP startup\n");
305-
306-
/* We mustn't issue any I2C transactions until the I2S
307-
* clock is stable. Furthermore, we must allow a 5ms
308-
* delay after the first set of register writes to
309-
* allow the DSP to boot before configuring it.
310-
*/
311-
usleep_range(5000, 10000);
312-
send_cfg(rm, dsp_cfg_preboot,
313-
ARRAY_SIZE(dsp_cfg_preboot));
314-
usleep_range(5000, 15000);
315-
send_cfg(rm, tas5805m->dsp_cfg_data,
316-
tas5805m->dsp_cfg_len);
317-
318-
tas5805m->is_powered = true;
319-
tas5805m_refresh(component);
310+
dev_dbg(component->dev, "clock start\n");
311+
schedule_work(&tas5805m->work);
320312
break;
321313

322314
case SNDRV_PCM_TRIGGER_STOP:
323315
case SNDRV_PCM_TRIGGER_SUSPEND:
324316
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
325-
dev_dbg(component->dev, "DSP shutdown\n");
317+
break;
326318

327-
tas5805m->is_powered = false;
319+
default:
320+
return -EINVAL;
321+
}
328322

329-
regmap_write(rm, REG_PAGE, 0x00);
330-
regmap_write(rm, REG_BOOK, 0x00);
323+
return 0;
324+
}
331325

332-
regmap_read(rm, REG_CHAN_FAULT, &chan);
333-
regmap_read(rm, REG_GLOBAL_FAULT1, &global1);
334-
regmap_read(rm, REG_GLOBAL_FAULT2, &global2);
326+
static void do_work(struct work_struct *work)
327+
{
328+
struct tas5805m_priv *tas5805m =
329+
container_of(work, struct tas5805m_priv, work);
330+
struct regmap *rm = tas5805m->regmap;
335331

336-
dev_dbg(component->dev,
337-
"fault regs: CHAN=%02x, GLOBAL1=%02x, GLOBAL2=%02x\n",
338-
chan, global1, global2);
332+
dev_dbg(&tas5805m->i2c->dev, "DSP startup\n");
339333

340-
regmap_write(rm, REG_DEVICE_CTRL_2, DCTRL2_MODE_HIZ);
341-
break;
334+
mutex_lock(&tas5805m->lock);
335+
/* We mustn't issue any I2C transactions until the I2S
336+
* clock is stable. Furthermore, we must allow a 5ms
337+
* delay after the first set of register writes to
338+
* allow the DSP to boot before configuring it.
339+
*/
340+
usleep_range(5000, 10000);
341+
send_cfg(rm, dsp_cfg_preboot, ARRAY_SIZE(dsp_cfg_preboot));
342+
usleep_range(5000, 15000);
343+
send_cfg(rm, tas5805m->dsp_cfg_data, tas5805m->dsp_cfg_len);
344+
345+
tas5805m->is_powered = true;
346+
tas5805m_refresh(tas5805m);
347+
mutex_unlock(&tas5805m->lock);
348+
}
342349

343-
default:
344-
return -EINVAL;
350+
static int tas5805m_dac_event(struct snd_soc_dapm_widget *w,
351+
struct snd_kcontrol *kcontrol, int event)
352+
{
353+
struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
354+
struct tas5805m_priv *tas5805m =
355+
snd_soc_component_get_drvdata(component);
356+
struct regmap *rm = tas5805m->regmap;
357+
358+
if (event & SND_SOC_DAPM_PRE_PMD) {
359+
unsigned int chan, global1, global2;
360+
361+
dev_dbg(component->dev, "DSP shutdown\n");
362+
cancel_work_sync(&tas5805m->work);
363+
364+
mutex_lock(&tas5805m->lock);
365+
if (tas5805m->is_powered) {
366+
tas5805m->is_powered = false;
367+
368+
regmap_write(rm, REG_PAGE, 0x00);
369+
regmap_write(rm, REG_BOOK, 0x00);
370+
371+
regmap_read(rm, REG_CHAN_FAULT, &chan);
372+
regmap_read(rm, REG_GLOBAL_FAULT1, &global1);
373+
regmap_read(rm, REG_GLOBAL_FAULT2, &global2);
374+
375+
dev_dbg(component->dev, "fault regs: CHAN=%02x, "
376+
"GLOBAL1=%02x, GLOBAL2=%02x\n",
377+
chan, global1, global2);
378+
379+
regmap_write(rm, REG_DEVICE_CTRL_2, DCTRL2_MODE_HIZ);
380+
}
381+
mutex_unlock(&tas5805m->lock);
345382
}
346383

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

355392
static const struct snd_soc_dapm_widget tas5805m_dapm_widgets[] = {
356393
SND_SOC_DAPM_AIF_IN("DAC IN", "Playback", 0, SND_SOC_NOPM, 0, 0),
357-
SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0),
394+
SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0,
395+
tas5805m_dac_event, SND_SOC_DAPM_PRE_PMD),
358396
SND_SOC_DAPM_OUTPUT("OUT")
359397
};
360398

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

416+
mutex_lock(&tas5805m->lock);
378417
dev_dbg(component->dev, "set mute=%d (is_powered=%d)\n",
379418
mute, tas5805m->is_powered);
419+
380420
tas5805m->is_muted = mute;
381421
if (tas5805m->is_powered)
382-
tas5805m_refresh(component);
422+
tas5805m_refresh(tas5805m);
423+
mutex_unlock(&tas5805m->lock);
383424

384425
return 0;
385426
}
@@ -434,6 +475,7 @@ static int tas5805m_i2c_probe(struct i2c_client *i2c)
434475
if (!tas5805m)
435476
return -ENOMEM;
436477

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

552+
INIT_WORK(&tas5805m->work, do_work);
553+
mutex_init(&tas5805m->lock);
554+
510555
/* Don't register through devm. We need to be able to unregister
511556
* the component prior to deasserting PDN#
512557
*/
@@ -527,6 +572,7 @@ static void tas5805m_i2c_remove(struct i2c_client *i2c)
527572
struct device *dev = &i2c->dev;
528573
struct tas5805m_priv *tas5805m = dev_get_drvdata(dev);
529574

575+
cancel_work_sync(&tas5805m->work);
530576
snd_soc_unregister_component(dev);
531577
gpiod_set_value(tas5805m->gpio_pdn_n, 0);
532578
usleep_range(10000, 15000);

0 commit comments

Comments
 (0)