[alsa-devel] [PATCH v3] ASoC: ak4613: call dummy write for PW_MGMT1/3 when Playback
From: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
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.
Tested-by: Hiroyuki Yokoyama hiroyuki.yokoyama.vx@renesas.com Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- v2 -> v3
- use delayed_work with 0 delay
Sakamoto-san
This patch uses delayed_work with 0 delay, thus, call-back function will be called imminently. There was 0 jiffies delay between trigger <-> callback in my test.
sound/soc/codecs/ak4613.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+)
diff --git a/sound/soc/codecs/ak4613.c b/sound/soc/codecs/ak4613.c index ee9e822..a7856d5 100644 --- a/sound/soc/codecs/ak4613.c +++ b/sound/soc/codecs/ak4613.c @@ -15,6 +15,7 @@ */
#include <linux/clk.h> +#include <linux/delay.h> #include <linux/i2c.h> #include <linux/slab.h> #include <linux/of_device.h> @@ -95,6 +96,9 @@ struct ak4613_priv { struct mutex lock; const struct ak4613_interface *iface; struct snd_pcm_hw_constraint_list constraint; + struct delayed_work dummy_write_work; + struct snd_soc_component *component; + unsigned int rate; unsigned int sysclk;
unsigned int fmt; @@ -392,6 +396,7 @@ static int ak4613_dai_hw_params(struct snd_pcm_substream *substream, default: return -EINVAL; } + priv->rate = rate;
/* * FIXME @@ -467,11 +472,65 @@ static int ak4613_set_bias_level(struct snd_soc_component *component, return 0; }
+static void ak4613_dummy_write(struct work_struct *work) +{ + struct ak4613_priv *priv = container_of(work, + struct ak4613_priv, + dummy_write_work.work); + struct snd_soc_component *component = priv->component; + unsigned int mgmt1; + unsigned int mgmt3; + + /* wait 5 LR clocks */ + udelay(5000000 / priv->rate); + + snd_soc_component_read(component, PW_MGMT1, &mgmt1); + snd_soc_component_read(component, PW_MGMT3, &mgmt3); + + snd_soc_component_write(component, PW_MGMT1, mgmt1); + snd_soc_component_write(component, PW_MGMT3, mgmt3); +} + +static int ak4613_dai_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct ak4613_priv *priv = snd_soc_codec_get_drvdata(codec); + + /* + * FIXME + * + * PW_MGMT1 / PW_MGMT3 needs dummy write at least after 5 LR clocks + * from Power Down Release. Otherwise, Playback volume will be 0dB. + * To avoid complex multiple delay/dummy_write method from + * ak4613_set_bias_level() / SND_SOC_DAPM_DAC_E("DACx", ...), + * call it once here. + * + * But, unfortunately, we can't "write" here because here is atomic + * context (It uses I2C access for write). + * Thus, use delayed work with 0 delay to switch to normal context + * immediately, and wait 5 LR clocks and do dummy_write there. + */ + + if ((cmd != SNDRV_PCM_TRIGGER_START) && + (cmd != SNDRV_PCM_TRIGGER_RESUME)) + return 0; + + if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) + return 0; + + priv->component = &codec->component; + schedule_delayed_work(&priv->dummy_write_work, 0); + + return 0; +} + static const struct snd_soc_dai_ops ak4613_dai_ops = { .startup = ak4613_dai_startup, .shutdown = ak4613_dai_shutdown, .set_sysclk = ak4613_dai_set_sysclk, .set_fmt = ak4613_dai_set_fmt, + .trigger = ak4613_dai_trigger, .hw_params = ak4613_dai_hw_params, };
@@ -591,6 +650,7 @@ static int ak4613_i2c_probe(struct i2c_client *i2c, priv->iface = NULL; priv->cnt = 0; priv->sysclk = 0; + INIT_DELAYED_WORK(&priv->dummy_write_work, ak4613_dummy_write);
mutex_init(&priv->lock);
On 2017年12月01日 11:25, Kuninori Morimoto wrote:
This patch uses delayed_work with 0 delay, thus, call-back function will be called imminently. There was 0 jiffies delay between trigger <-> callback in my test.
Just use workqueue.
Additionally, it's better to add code comment about your strategy to wait for the 5 phase of word select clock.
Regards
Takashi Sakamoto
Hi Sakamoto-san
This patch uses delayed_work with 0 delay, thus, call-back function will be called imminently. There was 0 jiffies delay between trigger <-> callback in my test.
Just use workqueue.
Additionally, it's better to add code comment about your strategy to wait for the 5 phase of word select clock.
I think I added it on ak4613_dai_trigger() ?
Best regards --- Kuninori Morimoto
On 2017年12月01日 12:14, Kuninori Morimoto wrote:
Additionally, it's better to add code comment about your strategy to wait for the 5 phase of word select clock.
I think I added it on ak4613_dai_trigger() ?
It doesn't matter as long as you put it into the code, in my opinion.
participants (2)
-
Kuninori Morimoto
-
Takashi Sakamoto