Skip to content

Commit

Permalink
ASoC: qcom: Fix enabling BCLK and LRCLK in LPAIF invalid state
Browse files Browse the repository at this point in the history
Fix enabling BCLK and LRCLK only when LPAIF is invalid state and
bit clock in enable state.
In device suspend/resume scenario LPAIF is going to reset state.
which is causing LRCLK disable and BCLK enable.
Avoid such inconsitency by removing unnecessary cpu dai prepare API,
which is doing LRCLK enable, and by maintaining BLCK  state information.

Fixes: 7e6799d ("ASoC: qcom: lpass-cpu: Enable MI2S BCLK and LRCLK together")

Signed-off-by: V Sujith Kumar Reddy <[email protected]>
Signed-off-by: Srinivasa Rao Mandadapu <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
Signed-off-by: Mark Brown <[email protected]>
  • Loading branch information
Srinivasa Rao Mandadapu authored and broonie committed Nov 23, 2020
1 parent fbdae7d commit b182496
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 33 deletions.
62 changes: 31 additions & 31 deletions sound/soc/qcom/lpass-cpu.c
Original file line number Diff line number Diff line change
Expand Up @@ -263,35 +263,25 @@ static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream,
return 0;
}

static int lpass_cpu_daiops_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
struct lpaif_i2sctl *i2sctl = drvdata->i2sctl;
unsigned int id = dai->driver->id;
int ret;

if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
ret = regmap_fields_write(i2sctl->spken, id,
LPAIF_I2SCTL_SPKEN_ENABLE);
} else {
ret = regmap_fields_write(i2sctl->micen, id,
LPAIF_I2SCTL_MICEN_ENABLE);
}

if (ret)
dev_err(dai->dev, "error writing to i2sctl enable: %d\n", ret);

return ret;
}

static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream,
int cmd, struct snd_soc_dai *dai)
{
struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
struct lpaif_i2sctl *i2sctl = drvdata->i2sctl;
unsigned int id = dai->driver->id;
int ret = -EINVAL;
unsigned int val = 0;

ret = regmap_read(drvdata->lpaif_map,
LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id), &val);
if (ret) {
dev_err(dai->dev, "error reading from i2sctl reg: %d\n", ret);
return ret;
}
if (val == LPAIF_I2SCTL_RESET_STATE) {
dev_err(dai->dev, "error in i2sctl register state\n");
return -ENOTRECOVERABLE;
}

switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
Expand All @@ -308,11 +298,14 @@ static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream,
dev_err(dai->dev, "error writing to i2sctl reg: %d\n",
ret);

ret = clk_enable(drvdata->mi2s_bit_clk[id]);
if (ret) {
dev_err(dai->dev, "error in enabling mi2s bit clk: %d\n", ret);
clk_disable(drvdata->mi2s_osr_clk[id]);
return ret;
if (drvdata->bit_clk_state[id] == LPAIF_BIT_CLK_DISABLE) {
ret = clk_enable(drvdata->mi2s_bit_clk[id]);
if (ret) {
dev_err(dai->dev, "error in enabling mi2s bit clk: %d\n", ret);
clk_disable(drvdata->mi2s_osr_clk[id]);
return ret;
}
drvdata->bit_clk_state[id] = LPAIF_BIT_CLK_ENABLE;
}

break;
Expand All @@ -329,7 +322,10 @@ static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream,
if (ret)
dev_err(dai->dev, "error writing to i2sctl reg: %d\n",
ret);
clk_disable(drvdata->mi2s_bit_clk[dai->driver->id]);
if (drvdata->bit_clk_state[id] == LPAIF_BIT_CLK_ENABLE) {
clk_disable(drvdata->mi2s_bit_clk[dai->driver->id]);
drvdata->bit_clk_state[id] = LPAIF_BIT_CLK_DISABLE;
}
break;
}

Expand All @@ -341,7 +337,6 @@ const struct snd_soc_dai_ops asoc_qcom_lpass_cpu_dai_ops = {
.startup = lpass_cpu_daiops_startup,
.shutdown = lpass_cpu_daiops_shutdown,
.hw_params = lpass_cpu_daiops_hw_params,
.prepare = lpass_cpu_daiops_prepare,
.trigger = lpass_cpu_daiops_trigger,
};
EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_dai_ops);
Expand Down Expand Up @@ -459,16 +454,20 @@ static bool lpass_cpu_regmap_volatile(struct device *dev, unsigned int reg)
struct lpass_variant *v = drvdata->variant;
int i;

for (i = 0; i < v->i2s_ports; ++i)
if (reg == LPAIF_I2SCTL_REG(v, i))
return true;
for (i = 0; i < v->irq_ports; ++i)
if (reg == LPAIF_IRQSTAT_REG(v, i))
return true;

for (i = 0; i < v->rdma_channels; ++i)
if (reg == LPAIF_RDMACURR_REG(v, i))
if (reg == LPAIF_RDMACURR_REG(v, i) || reg == LPAIF_RDMACTL_REG(v, i))
return true;

for (i = 0; i < v->wrdma_channels; ++i)
if (reg == LPAIF_WRDMACURR_REG(v, i + v->wrdma_channel_start))
if (reg == LPAIF_WRDMACURR_REG(v, i + v->wrdma_channel_start) ||
reg == LPAIF_WRDMACTL_REG(v, i + v->wrdma_channel_start))
return true;

return false;
Expand Down Expand Up @@ -861,6 +860,7 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)
PTR_ERR(drvdata->mi2s_bit_clk[dai_id]));
return PTR_ERR(drvdata->mi2s_bit_clk[dai_id]);
}
drvdata->bit_clk_state[dai_id] = LPAIF_BIT_CLK_DISABLE;
}

/* Allocation for i2sctl regmap fields */
Expand Down
7 changes: 7 additions & 0 deletions sound/soc/qcom/lpass-lpaif-reg.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,13 @@
#define LPAIF_I2SCTL_BITWIDTH_24 1
#define LPAIF_I2SCTL_BITWIDTH_32 2

#define LPAIF_BIT_CLK_DISABLE 0
#define LPAIF_BIT_CLK_ENABLE 1

#define LPAIF_I2SCTL_RESET_STATE 0x003C0004
#define LPAIF_DMACTL_RESET_STATE 0x00200000


/* LPAIF IRQ */
#define LPAIF_IRQ_REG_ADDR(v, addr, port) \
(v->irq_reg_base + (addr) + v->irq_reg_stride * (port))
Expand Down
20 changes: 18 additions & 2 deletions sound/soc/qcom/lpass-platform.c
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ static int lpass_platform_pcmops_open(struct snd_soc_component *component,
struct regmap *map;
unsigned int dai_id = cpu_dai->driver->id;

component->id = dai_id;
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
Expand Down Expand Up @@ -451,19 +452,34 @@ static int lpass_platform_pcmops_trigger(struct snd_soc_component *component,
unsigned int reg_irqclr = 0, val_irqclr = 0;
unsigned int reg_irqen = 0, val_irqen = 0, val_mask = 0;
unsigned int dai_id = cpu_dai->driver->id;
unsigned int dma_ctrl_reg = 0;

ch = pcm_data->dma_ch;
if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
id = pcm_data->dma_ch;
if (dai_id == LPASS_DP_RX)
if (dai_id == LPASS_DP_RX) {
dmactl = drvdata->hdmi_rd_dmactl;
else
map = drvdata->hdmiif_map;
} else {
dmactl = drvdata->rd_dmactl;
map = drvdata->lpaif_map;
}
} else {
dmactl = drvdata->wr_dmactl;
id = pcm_data->dma_ch - v->wrdma_channel_start;
map = drvdata->lpaif_map;
}
ret = regmap_read(map, LPAIF_DMACTL_REG(v, ch, dir, dai_id), &dma_ctrl_reg);
if (ret) {
dev_err(soc_runtime->dev, "error reading from rdmactl reg: %d\n", ret);
return ret;
}

if (dma_ctrl_reg == LPAIF_DMACTL_RESET_STATE ||
dma_ctrl_reg == LPAIF_DMACTL_RESET_STATE + 1) {
dev_err(soc_runtime->dev, "error in rdmactl register state\n");
return -ENOTRECOVERABLE;
}
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
Expand Down
1 change: 1 addition & 0 deletions sound/soc/qcom/lpass.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ struct lpass_data {
unsigned int mi2s_playback_sd_mode[LPASS_MAX_MI2S_PORTS];
unsigned int mi2s_capture_sd_mode[LPASS_MAX_MI2S_PORTS];
int hdmi_port_enable;
int bit_clk_state[LPASS_MAX_MI2S_PORTS];

/* low-power audio interface (LPAIF) registers */
void __iomem *lpaif;
Expand Down

0 comments on commit b182496

Please sign in to comment.