aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKuninori Morimoto <kuninori.morimoto.gx@renesas.com>2018-01-24 00:11:42 -0500
committerMark Brown <broonie@kernel.org>2018-01-24 06:31:25 -0500
commit03bbf9f5e4eb9944511cd218d3c1b18809d12eb2 (patch)
tree43841ce05e23cfe51d10a1172b6037fcbdfbe171
parent4fbd8d194f06c8a3fd2af1ce560ddb31f7ec8323 (diff)
ASoC: ak4613: call dummy write for PW_MGMT1/3 when Playback
Power Down Release Command (PMVR, PMDAC, RSTN, PMDA1-PMDA6) which are located on PW_MGMT1 / PW_MGMT3 register must be write again after at least 5 LRCK cycle or later on each command. Otherwise, Playback volume will be 0dB. Basically, it should be 1. PowerDownRelease by Power Management1 <= call 1.x after 5LRCK 1.x Dummy write to Power Management1 2. PowerDownRelease by Power Management3 <= call 2.x after 5LRCK 2.x Dummy write to Power Management3 To avoid too many dummy write, this patch is merging these. 1. PowerDownRelease by Power Management1 2. PowerDownRelease by Power Management3 <= call after 5LRCK 2.x Dummy write to Power Management1/3 <= merge dummy write This patch adds dummy write when Playback Start timing. Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> Signed-off-by: Mark Brown <broonie@kernel.org>
-rw-r--r--sound/soc/codecs/ak4613.c78
1 files changed, 78 insertions, 0 deletions
diff --git a/sound/soc/codecs/ak4613.c b/sound/soc/codecs/ak4613.c
index b95bb8b52e51..3d1cf4784e87 100644
--- a/sound/soc/codecs/ak4613.c
+++ b/sound/soc/codecs/ak4613.c
@@ -15,6 +15,7 @@
15 */ 15 */
16 16
17#include <linux/clk.h> 17#include <linux/clk.h>
18#include <linux/delay.h>
18#include <linux/i2c.h> 19#include <linux/i2c.h>
19#include <linux/slab.h> 20#include <linux/slab.h>
20#include <linux/of_device.h> 21#include <linux/of_device.h>
@@ -95,6 +96,9 @@ struct ak4613_priv {
95 struct mutex lock; 96 struct mutex lock;
96 const struct ak4613_interface *iface; 97 const struct ak4613_interface *iface;
97 struct snd_pcm_hw_constraint_list constraint; 98 struct snd_pcm_hw_constraint_list constraint;
99 struct work_struct dummy_write_work;
100 struct snd_soc_component *component;
101 unsigned int rate;
98 unsigned int sysclk; 102 unsigned int sysclk;
99 103
100 unsigned int fmt; 104 unsigned int fmt;
@@ -392,6 +396,7 @@ static int ak4613_dai_hw_params(struct snd_pcm_substream *substream,
392 default: 396 default:
393 return -EINVAL; 397 return -EINVAL;
394 } 398 }
399 priv->rate = rate;
395 400
396 /* 401 /*
397 * FIXME 402 * FIXME
@@ -467,11 +472,83 @@ static int ak4613_set_bias_level(struct snd_soc_codec *codec,
467 return 0; 472 return 0;
468} 473}
469 474
475static void ak4613_dummy_write(struct work_struct *work)
476{
477 struct ak4613_priv *priv = container_of(work,
478 struct ak4613_priv,
479 dummy_write_work);
480 struct snd_soc_component *component = priv->component;
481 unsigned int mgmt1;
482 unsigned int mgmt3;
483
484 /*
485 * PW_MGMT1 / PW_MGMT3 needs dummy write at least after 5 LR clocks
486 *
487 * Note
488 *
489 * To avoid extra delay, we want to avoid preemption here,
490 * but we can't. Because it uses I2C access which is using IRQ
491 * and sleep. Thus, delay might be more than 5 LR clocks
492 * see also
493 * ak4613_dai_trigger()
494 */
495 udelay(5000000 / priv->rate);
496
497 snd_soc_component_read(component, PW_MGMT1, &mgmt1);
498 snd_soc_component_read(component, PW_MGMT3, &mgmt3);
499
500 snd_soc_component_write(component, PW_MGMT1, mgmt1);
501 snd_soc_component_write(component, PW_MGMT3, mgmt3);
502}
503
504static int ak4613_dai_trigger(struct snd_pcm_substream *substream, int cmd,
505 struct snd_soc_dai *dai)
506{
507 struct snd_soc_codec *codec = dai->codec;
508 struct ak4613_priv *priv = snd_soc_codec_get_drvdata(codec);
509
510 /*
511 * FIXME
512 *
513 * PW_MGMT1 / PW_MGMT3 needs dummy write at least after 5 LR clocks
514 * from Power Down Release. Otherwise, Playback volume will be 0dB.
515 * To avoid complex multiple delay/dummy_write method from
516 * ak4613_set_bias_level() / SND_SOC_DAPM_DAC_E("DACx", ...),
517 * call it once here.
518 *
519 * But, unfortunately, we can't "write" here because here is atomic
520 * context (It uses I2C access for writing).
521 * Thus, use schedule_work() to switching to normal context
522 * immediately.
523 *
524 * Note
525 *
526 * Calling ak4613_dummy_write() function might be delayed.
527 * In such case, ak4613 volume might be temporarily 0dB when
528 * beggining of playback.
529 * see also
530 * ak4613_dummy_write()
531 */
532
533 if ((cmd != SNDRV_PCM_TRIGGER_START) &&
534 (cmd != SNDRV_PCM_TRIGGER_RESUME))
535 return 0;
536
537 if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
538 return 0;
539
540 priv->component = &codec->component;
541 schedule_work(&priv->dummy_write_work);
542
543 return 0;
544}
545
470static const struct snd_soc_dai_ops ak4613_dai_ops = { 546static const struct snd_soc_dai_ops ak4613_dai_ops = {
471 .startup = ak4613_dai_startup, 547 .startup = ak4613_dai_startup,
472 .shutdown = ak4613_dai_shutdown, 548 .shutdown = ak4613_dai_shutdown,
473 .set_sysclk = ak4613_dai_set_sysclk, 549 .set_sysclk = ak4613_dai_set_sysclk,
474 .set_fmt = ak4613_dai_set_fmt, 550 .set_fmt = ak4613_dai_set_fmt,
551 .trigger = ak4613_dai_trigger,
475 .hw_params = ak4613_dai_hw_params, 552 .hw_params = ak4613_dai_hw_params,
476}; 553};
477 554
@@ -590,6 +667,7 @@ static int ak4613_i2c_probe(struct i2c_client *i2c,
590 priv->iface = NULL; 667 priv->iface = NULL;
591 priv->cnt = 0; 668 priv->cnt = 0;
592 priv->sysclk = 0; 669 priv->sysclk = 0;
670 INIT_WORK(&priv->dummy_write_work, ak4613_dummy_write);
593 671
594 mutex_init(&priv->lock); 672 mutex_init(&priv->lock);
595 673