@@ -93,8 +93,30 @@ static void lpass_cpu_daiops_shutdown(struct snd_pcm_substream *substream,
93
93
struct snd_soc_dai * dai )
94
94
{
95
95
struct lpass_data * drvdata = snd_soc_dai_get_drvdata (dai );
96
+ struct lpaif_i2sctl * i2sctl = drvdata -> i2sctl ;
97
+ unsigned int id = dai -> driver -> id ;
96
98
97
99
clk_disable_unprepare (drvdata -> mi2s_osr_clk [dai -> driver -> id ]);
100
+ /*
101
+ * Ensure LRCLK is disabled even in device node validation.
102
+ * Will not impact if disabled in lpass_cpu_daiops_trigger()
103
+ * suspend.
104
+ */
105
+ if (substream -> stream == SNDRV_PCM_STREAM_PLAYBACK )
106
+ regmap_fields_write (i2sctl -> spken , id , LPAIF_I2SCTL_SPKEN_DISABLE );
107
+ else
108
+ regmap_fields_write (i2sctl -> micen , id , LPAIF_I2SCTL_MICEN_DISABLE );
109
+
110
+ /*
111
+ * BCLK may not be enabled if lpass_cpu_daiops_prepare is called before
112
+ * lpass_cpu_daiops_shutdown. It's paired with the clk_enable in
113
+ * lpass_cpu_daiops_prepare.
114
+ */
115
+ if (drvdata -> mi2s_was_prepared [dai -> driver -> id ]) {
116
+ drvdata -> mi2s_was_prepared [dai -> driver -> id ] = false;
117
+ clk_disable (drvdata -> mi2s_bit_clk [dai -> driver -> id ]);
118
+ }
119
+
98
120
clk_unprepare (drvdata -> mi2s_bit_clk [dai -> driver -> id ]);
99
121
}
100
122
@@ -275,6 +297,18 @@ static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream,
275
297
case SNDRV_PCM_TRIGGER_START :
276
298
case SNDRV_PCM_TRIGGER_RESUME :
277
299
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE :
300
+ /*
301
+ * Ensure lpass BCLK/LRCLK is enabled during
302
+ * device resume as lpass_cpu_daiops_prepare() is not called
303
+ * after the device resumes. We don't check mi2s_was_prepared before
304
+ * enable/disable BCLK in trigger events because:
305
+ * 1. These trigger events are paired, so the BCLK
306
+ * enable_count is balanced.
307
+ * 2. the BCLK can be shared (ex: headset and headset mic),
308
+ * we need to increase the enable_count so that we don't
309
+ * turn off the shared BCLK while other devices are using
310
+ * it.
311
+ */
278
312
if (substream -> stream == SNDRV_PCM_STREAM_PLAYBACK ) {
279
313
ret = regmap_fields_write (i2sctl -> spken , id ,
280
314
LPAIF_I2SCTL_SPKEN_ENABLE );
@@ -296,6 +330,10 @@ static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream,
296
330
case SNDRV_PCM_TRIGGER_STOP :
297
331
case SNDRV_PCM_TRIGGER_SUSPEND :
298
332
case SNDRV_PCM_TRIGGER_PAUSE_PUSH :
333
+ /*
334
+ * To ensure lpass BCLK/LRCLK is disabled during
335
+ * device suspend.
336
+ */
299
337
if (substream -> stream == SNDRV_PCM_STREAM_PLAYBACK ) {
300
338
ret = regmap_fields_write (i2sctl -> spken , id ,
301
339
LPAIF_I2SCTL_SPKEN_DISABLE );
@@ -315,12 +353,53 @@ static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream,
315
353
return ret ;
316
354
}
317
355
356
+ static int lpass_cpu_daiops_prepare (struct snd_pcm_substream * substream ,
357
+ struct snd_soc_dai * dai )
358
+ {
359
+ struct lpass_data * drvdata = snd_soc_dai_get_drvdata (dai );
360
+ struct lpaif_i2sctl * i2sctl = drvdata -> i2sctl ;
361
+ unsigned int id = dai -> driver -> id ;
362
+ int ret ;
363
+
364
+ /*
365
+ * Ensure lpass BCLK/LRCLK is enabled bit before playback/capture
366
+ * data flow starts. This allows other codec to have some delay before
367
+ * the data flow.
368
+ * (ex: to drop start up pop noise before capture starts).
369
+ */
370
+ if (substream -> stream == SNDRV_PCM_STREAM_PLAYBACK )
371
+ ret = regmap_fields_write (i2sctl -> spken , id , LPAIF_I2SCTL_SPKEN_ENABLE );
372
+ else
373
+ ret = regmap_fields_write (i2sctl -> micen , id , LPAIF_I2SCTL_MICEN_ENABLE );
374
+
375
+ if (ret ) {
376
+ dev_err (dai -> dev , "error writing to i2sctl reg: %d\n" , ret );
377
+ return ret ;
378
+ }
379
+
380
+ /*
381
+ * Check mi2s_was_prepared before enabling BCLK as lpass_cpu_daiops_prepare can
382
+ * be called multiple times. It's paired with the clk_disable in
383
+ * lpass_cpu_daiops_shutdown.
384
+ */
385
+ if (!drvdata -> mi2s_was_prepared [dai -> driver -> id ]) {
386
+ ret = clk_enable (drvdata -> mi2s_bit_clk [id ]);
387
+ if (ret ) {
388
+ dev_err (dai -> dev , "error in enabling mi2s bit clk: %d\n" , ret );
389
+ return ret ;
390
+ }
391
+ drvdata -> mi2s_was_prepared [dai -> driver -> id ] = true;
392
+ }
393
+ return 0 ;
394
+ }
395
+
318
396
const struct snd_soc_dai_ops asoc_qcom_lpass_cpu_dai_ops = {
319
397
.set_sysclk = lpass_cpu_daiops_set_sysclk ,
320
398
.startup = lpass_cpu_daiops_startup ,
321
399
.shutdown = lpass_cpu_daiops_shutdown ,
322
400
.hw_params = lpass_cpu_daiops_hw_params ,
323
401
.trigger = lpass_cpu_daiops_trigger ,
402
+ .prepare = lpass_cpu_daiops_prepare ,
324
403
};
325
404
EXPORT_SYMBOL_GPL (asoc_qcom_lpass_cpu_dai_ops );
326
405
0 commit comments