// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved. * * lpass-cpu.c -- ALSA SoC CPU DAI driver for QTi LPASS
*/
clk_disable_unprepare(drvdata->mi2s_osr_clk[dai->driver->id]); /* * Ensure LRCLK is disabled even in device node validation. * Will not impact if disabled in lpass_cpu_daiops_trigger() * suspend.
*/ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
regmap_fields_write(i2sctl->spken, id, LPAIF_I2SCTL_SPKEN_DISABLE); else
regmap_fields_write(i2sctl->micen, id, LPAIF_I2SCTL_MICEN_DISABLE);
/* * BCLK may not be enabled if lpass_cpu_daiops_prepare is called before * lpass_cpu_daiops_shutdown. It's paired with the clk_enable in * lpass_cpu_daiops_prepare.
*/ if (drvdata->mi2s_was_prepared[dai->driver->id]) {
drvdata->mi2s_was_prepared[dai->driver->id] = false;
clk_disable(drvdata->mi2s_bit_clk[dai->driver->id]);
}
if (!mode) {
dev_err(dai->dev, "no line is assigned\n"); return -EINVAL;
}
switch (channels) { case 1: case 2: switch (mode) { case LPAIF_I2SCTL_MODE_QUAD01: case LPAIF_I2SCTL_MODE_6CH: case LPAIF_I2SCTL_MODE_8CH:
mode = LPAIF_I2SCTL_MODE_SD0; break; case LPAIF_I2SCTL_MODE_QUAD23:
mode = LPAIF_I2SCTL_MODE_SD2; break;
}
break; case 4: if (mode < LPAIF_I2SCTL_MODE_QUAD01) {
dev_err(dai->dev, "cannot configure 4 channels with mode %d\n",
mode); return -EINVAL;
}
switch (mode) { case LPAIF_I2SCTL_MODE_6CH: case LPAIF_I2SCTL_MODE_8CH:
mode = LPAIF_I2SCTL_MODE_QUAD01; break;
} break; case 6: if (mode < LPAIF_I2SCTL_MODE_6CH) {
dev_err(dai->dev, "cannot configure 6 channels with mode %d\n",
mode); return -EINVAL;
}
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
ret = regmap_fields_write(i2sctl->spkmode, id,
LPAIF_I2SCTL_SPKMODE(mode)); if (ret) {
dev_err(dai->dev, "error writing to i2sctl spkr mode: %d\n",
ret); return ret;
} if (channels >= 2)
ret = regmap_fields_write(i2sctl->spkmono, id,
LPAIF_I2SCTL_SPKMONO_STEREO); else
ret = regmap_fields_write(i2sctl->spkmono, id,
LPAIF_I2SCTL_SPKMONO_MONO);
} else {
ret = regmap_fields_write(i2sctl->micmode, id,
LPAIF_I2SCTL_MICMODE(mode)); if (ret) {
dev_err(dai->dev, "error writing to i2sctl mic mode: %d\n",
ret); return ret;
} if (channels >= 2)
ret = regmap_fields_write(i2sctl->micmono, id,
LPAIF_I2SCTL_MICMONO_STEREO); else
ret = regmap_fields_write(i2sctl->micmono, id,
LPAIF_I2SCTL_MICMONO_MONO);
}
if (ret) {
dev_err(dai->dev, "error writing to i2sctl channels mode: %d\n",
ret); return ret;
}
ret = clk_set_rate(drvdata->mi2s_bit_clk[id],
rate * bitwidth * 2); if (ret) {
dev_err(dai->dev, "error setting mi2s bitclk to %u: %d\n",
rate * bitwidth * 2, ret); return ret;
}
return 0;
}
staticint 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; unsignedint id = dai->driver->id; int ret = -EINVAL;
switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: /* * Ensure lpass BCLK/LRCLK is enabled during * device resume as lpass_cpu_daiops_prepare() is not called * after the device resumes. We don't check mi2s_was_prepared before * enable/disable BCLK in trigger events because: * 1. These trigger events are paired, so the BCLK * enable_count is balanced. * 2. the BCLK can be shared (ex: headset and headset mic), * we need to increase the enable_count so that we don't * turn off the shared BCLK while other devices are using * it.
*/ 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 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;
} break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: /* * To ensure lpass BCLK/LRCLK is disabled during * device suspend.
*/ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
ret = regmap_fields_write(i2sctl->spken, id,
LPAIF_I2SCTL_SPKEN_DISABLE);
} else {
ret = regmap_fields_write(i2sctl->micen, id,
LPAIF_I2SCTL_MICEN_DISABLE);
} if (ret)
dev_err(dai->dev, "error writing to i2sctl reg: %d\n",
ret);
/* * Ensure lpass BCLK/LRCLK is enabled bit before playback/capture * data flow starts. This allows other codec to have some delay before * the data flow. * (ex: to drop start up pop noise before capture starts).
*/ 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 reg: %d\n", ret); return ret;
}
/* * Check mi2s_was_prepared before enabling BCLK as lpass_cpu_daiops_prepare can * be called multiple times. It's paired with the clk_disable in * lpass_cpu_daiops_shutdown.
*/ if (!drvdata->mi2s_was_prepared[dai->driver->id]) {
ret = clk_enable(drvdata->mi2s_bit_clk[id]); if (ret) {
dev_err(dai->dev, "error in enabling mi2s bit clk: %d\n", ret); return ret;
}
drvdata->mi2s_was_prepared[dai->driver->id] = true;
} return 0;
}
for (i = 0; i < v->i2s_ports; ++i) if (reg == LPAIF_I2SCTL_REG(v, i)) returntrue;
for (i = 0; i < v->irq_ports; ++i) { if (reg == LPAIF_IRQEN_REG(v, i)) returntrue; if (reg == LPAIF_IRQCLEAR_REG(v, i)) returntrue;
}
for (i = 0; i < v->rdma_channels; ++i) { if (reg == LPAIF_RDMACTL_REG(v, i)) returntrue; if (reg == LPAIF_RDMABASE_REG(v, i)) returntrue; if (reg == LPAIF_RDMABUFF_REG(v, i)) returntrue; if (reg == LPAIF_RDMAPER_REG(v, i)) returntrue;
}
for (i = 0; i < v->wrdma_channels; ++i) { if (reg == LPAIF_WRDMACTL_REG(v, i + v->wrdma_channel_start)) returntrue; if (reg == LPAIF_WRDMABASE_REG(v, i + v->wrdma_channel_start)) returntrue; if (reg == LPAIF_WRDMABUFF_REG(v, i + v->wrdma_channel_start)) returntrue; if (reg == LPAIF_WRDMAPER_REG(v, i + v->wrdma_channel_start)) returntrue;
}
for (i = 0; i < v->i2s_ports; ++i) if (reg == LPAIF_I2SCTL_REG(v, i)) returntrue;
for (i = 0; i < v->irq_ports; ++i) { if (reg == LPAIF_IRQCLEAR_REG(v, i)) returntrue; if (reg == LPAIF_IRQEN_REG(v, i)) returntrue; if (reg == LPAIF_IRQSTAT_REG(v, i)) returntrue;
}
for (i = 0; i < v->rdma_channels; ++i) { if (reg == LPAIF_RDMACTL_REG(v, i)) returntrue; if (reg == LPAIF_RDMABASE_REG(v, i)) returntrue; if (reg == LPAIF_RDMABUFF_REG(v, i)) returntrue; if (reg == LPAIF_RDMACURR_REG(v, i)) returntrue; if (reg == LPAIF_RDMAPER_REG(v, i)) returntrue;
}
for (i = 0; i < v->wrdma_channels; ++i) { if (reg == LPAIF_WRDMACTL_REG(v, i + v->wrdma_channel_start)) returntrue; if (reg == LPAIF_WRDMABASE_REG(v, i + v->wrdma_channel_start)) returntrue; if (reg == LPAIF_WRDMABUFF_REG(v, i + v->wrdma_channel_start)) returntrue; if (reg == LPAIF_WRDMACURR_REG(v, i + v->wrdma_channel_start)) returntrue; if (reg == LPAIF_WRDMAPER_REG(v, i + v->wrdma_channel_start)) returntrue;
}
if (reg == LPASS_HDMI_TX_CTL_ADDR(v)) returntrue; if (reg == LPASS_HDMI_TX_LEGACY_ADDR(v)) returntrue; if (reg == LPASS_HDMI_TX_VBIT_CTL_ADDR(v)) returntrue; if (reg == LPASS_HDMI_TX_PARITY_ADDR(v)) returntrue; if (reg == LPASS_HDMI_TX_DP_ADDR(v)) returntrue; if (reg == LPASS_HDMI_TX_SSTREAM_ADDR(v)) returntrue; if (reg == LPASS_HDMITX_APP_IRQEN_REG(v)) returntrue; if (reg == LPASS_HDMITX_APP_IRQCLEAR_REG(v)) returntrue;
for (i = 0; i < v->hdmi_rdma_channels; i++) { if (reg == LPASS_HDMI_TX_CH_LSB_ADDR(v, i)) returntrue; if (reg == LPASS_HDMI_TX_CH_MSB_ADDR(v, i)) returntrue; if (reg == LPASS_HDMI_TX_DMA_ADDR(v, i)) returntrue;
}
for (i = 0; i < v->hdmi_rdma_channels; ++i) { if (reg == LPAIF_HDMI_RDMACTL_REG(v, i)) returntrue; if (reg == LPAIF_HDMI_RDMABASE_REG(v, i)) returntrue; if (reg == LPAIF_HDMI_RDMABUFF_REG(v, i)) returntrue; if (reg == LPAIF_HDMI_RDMAPER_REG(v, i)) returntrue;
} returnfalse;
}
if (reg == LPASS_HDMI_TX_CTL_ADDR(v)) returntrue; if (reg == LPASS_HDMI_TX_LEGACY_ADDR(v)) returntrue; if (reg == LPASS_HDMI_TX_VBIT_CTL_ADDR(v)) returntrue;
for (i = 0; i < v->hdmi_rdma_channels; i++) { if (reg == LPASS_HDMI_TX_CH_LSB_ADDR(v, i)) returntrue; if (reg == LPASS_HDMI_TX_CH_MSB_ADDR(v, i)) returntrue; if (reg == LPASS_HDMI_TX_DMA_ADDR(v, i)) returntrue;
}
if (reg == LPASS_HDMI_TX_PARITY_ADDR(v)) returntrue; if (reg == LPASS_HDMI_TX_DP_ADDR(v)) returntrue; if (reg == LPASS_HDMI_TX_SSTREAM_ADDR(v)) returntrue; if (reg == LPASS_HDMITX_APP_IRQEN_REG(v)) returntrue; if (reg == LPASS_HDMITX_APP_IRQSTAT_REG(v)) returntrue;
for (i = 0; i < v->hdmi_rdma_channels; ++i) { if (reg == LPAIF_HDMI_RDMACTL_REG(v, i)) returntrue; if (reg == LPAIF_HDMI_RDMABASE_REG(v, i)) returntrue; if (reg == LPAIF_HDMI_RDMABUFF_REG(v, i)) returntrue; if (reg == LPAIF_HDMI_RDMAPER_REG(v, i)) returntrue; if (reg == LPAIF_HDMI_RDMACURR_REG(v, i)) returntrue;
}
if (reg == LPASS_HDMITX_APP_IRQSTAT_REG(v)) returntrue; if (reg == LPASS_HDMI_TX_LEGACY_ADDR(v)) returntrue; if (reg == LPASS_HDMI_TX_VBIT_CTL_ADDR(v)) returntrue; if (reg == LPASS_HDMI_TX_PARITY_ADDR(v)) returntrue;
for (i = 0; i < v->hdmi_rdma_channels; ++i) { if (reg == LPAIF_HDMI_RDMACURR_REG(v, i)) returntrue; if (reg == LPASS_HDMI_TX_DMA_ADDR(v, i)) returntrue; if (reg == LPASS_HDMI_TX_CH_LSB_ADDR(v, i)) returntrue; if (reg == LPASS_HDMI_TX_CH_MSB_ADDR(v, i)) returntrue;
} returnfalse;
}
for (i = 0; i < v->rxtx_irq_ports; ++i) { if (reg == LPAIF_RXTX_IRQCLEAR_REG(v, i)) returntrue; if (reg == LPAIF_RXTX_IRQEN_REG(v, i)) returntrue; if (reg == LPAIF_RXTX_IRQSTAT_REG(v, i)) returntrue;
}
for (i = 0; i < v->rxtx_rdma_channels; ++i) { if (reg == LPAIF_CDC_RXTX_RDMACTL_REG(v, i, LPASS_CDC_DMA_RX0)) returntrue; if (reg == LPAIF_CDC_RXTX_RDMABASE_REG(v, i, LPASS_CDC_DMA_RX0)) returntrue; if (reg == LPAIF_CDC_RXTX_RDMABUFF_REG(v, i, LPASS_CDC_DMA_RX0)) returntrue; if (rw == LPASS_REG_READ) { if (reg == LPAIF_CDC_RXTX_RDMACURR_REG(v, i, LPASS_CDC_DMA_RX0)) returntrue;
} if (reg == LPAIF_CDC_RXTX_RDMAPER_REG(v, i, LPASS_CDC_DMA_RX0)) returntrue; if (reg == LPAIF_CDC_RXTX_RDMA_INTF_REG(v, i, LPASS_CDC_DMA_RX0)) returntrue;
}
for (i = 0; i < v->rxtx_wrdma_channels; ++i) { if (reg == LPAIF_CDC_RXTX_WRDMACTL_REG(v, i + v->rxtx_wrdma_channel_start,
LPASS_CDC_DMA_TX3)) returntrue; if (reg == LPAIF_CDC_RXTX_WRDMABASE_REG(v, i + v->rxtx_wrdma_channel_start,
LPASS_CDC_DMA_TX3)) returntrue; if (reg == LPAIF_CDC_RXTX_WRDMABUFF_REG(v, i + v->rxtx_wrdma_channel_start,
LPASS_CDC_DMA_TX3)) returntrue; if (rw == LPASS_REG_READ) { if (reg == LPAIF_CDC_RXTX_WRDMACURR_REG(v, i, LPASS_CDC_DMA_RX0)) returntrue;
} if (reg == LPAIF_CDC_RXTX_WRDMAPER_REG(v, i + v->rxtx_wrdma_channel_start,
LPASS_CDC_DMA_TX3)) returntrue; if (reg == LPAIF_CDC_RXTX_WRDMA_INTF_REG(v, i + v->rxtx_wrdma_channel_start,
LPASS_CDC_DMA_TX3)) returntrue;
} returnfalse;
}
for (i = 0; i < v->rxtx_irq_ports; ++i) { if (reg == LPAIF_RXTX_IRQCLEAR_REG(v, i)) returntrue; if (reg == LPAIF_RXTX_IRQSTAT_REG(v, i)) returntrue;
}
for (i = 0; i < v->rxtx_rdma_channels; ++i) if (reg == LPAIF_CDC_RXTX_RDMACURR_REG(v, i, LPASS_CDC_DMA_RX0)) returntrue;
for (i = 0; i < v->rxtx_wrdma_channels; ++i) if (reg == LPAIF_CDC_RXTX_WRDMACURR_REG(v, i + v->rxtx_wrdma_channel_start,
LPASS_CDC_DMA_TX3)) returntrue;
for (i = 0; i < v->va_irq_ports; ++i) { if (reg == LPAIF_VA_IRQCLEAR_REG(v, i)) returntrue; if (reg == LPAIF_VA_IRQEN_REG(v, i)) returntrue; if (reg == LPAIF_VA_IRQSTAT_REG(v, i)) returntrue;
}
for (i = 0; i < v->va_wrdma_channels; ++i) { if (reg == LPAIF_CDC_VA_WRDMACTL_REG(v, i + v->va_wrdma_channel_start,
LPASS_CDC_DMA_VA_TX0)) returntrue; if (reg == LPAIF_CDC_VA_WRDMABASE_REG(v, i + v->va_wrdma_channel_start,
LPASS_CDC_DMA_VA_TX0)) returntrue; if (reg == LPAIF_CDC_VA_WRDMABUFF_REG(v, i + v->va_wrdma_channel_start,
LPASS_CDC_DMA_VA_TX0)) returntrue; if (rw == LPASS_REG_READ) { if (reg == LPAIF_CDC_VA_WRDMACURR_REG(v, i + v->va_wrdma_channel_start,
LPASS_CDC_DMA_VA_TX0)) returntrue;
} if (reg == LPAIF_CDC_VA_WRDMAPER_REG(v, i + v->va_wrdma_channel_start,
LPASS_CDC_DMA_VA_TX0)) returntrue; if (reg == LPAIF_CDC_VA_WRDMA_INTF_REG(v, i + v->va_wrdma_channel_start,
LPASS_CDC_DMA_VA_TX0)) returntrue;
} returnfalse;
}
for (i = 0; i < v->va_irq_ports; ++i) { if (reg == LPAIF_VA_IRQCLEAR_REG(v, i)) returntrue; if (reg == LPAIF_VA_IRQSTAT_REG(v, i)) returntrue;
}
for (i = 0; i < v->va_wrdma_channels; ++i) { if (reg == LPAIF_CDC_VA_WRDMACURR_REG(v, i + v->va_wrdma_channel_start,
LPASS_CDC_DMA_VA_TX0)) returntrue;
}
for (i = 0; i < num_lines; i++)
sd_line_mask |= BIT(lines[i]);
switch (sd_line_mask) { case LPASS_CPU_I2S_SD0_MASK: return LPAIF_I2SCTL_MODE_SD0; case LPASS_CPU_I2S_SD1_MASK: return LPAIF_I2SCTL_MODE_SD1; case LPASS_CPU_I2S_SD2_MASK: return LPAIF_I2SCTL_MODE_SD2; case LPASS_CPU_I2S_SD3_MASK: return LPAIF_I2SCTL_MODE_SD3; case LPASS_CPU_I2S_SD0_1_MASK: return LPAIF_I2SCTL_MODE_QUAD01; case LPASS_CPU_I2S_SD2_3_MASK: return LPAIF_I2SCTL_MODE_QUAD23; case LPASS_CPU_I2S_SD0_1_2_MASK: return LPAIF_I2SCTL_MODE_6CH; case LPASS_CPU_I2S_SD0_1_2_3_MASK: return LPAIF_I2SCTL_MODE_8CH; default:
dev_err(dev, "Unsupported SD line mask: %#x\n", sd_line_mask); return LPAIF_I2SCTL_MODE_NONE;
}
}
staticvoid of_lpass_cpu_parse_dai_data(struct device *dev, struct lpass_data *data)
{ struct device_node *node; int ret, i, id;
/* Allow all channels by default for backwards compatibility */ for (i = 0; i < data->variant->num_dai; i++) {
id = data->variant->dai_driver[i].id;
data->mi2s_playback_sd_mode[id] = LPAIF_I2SCTL_MODE_8CH;
data->mi2s_capture_sd_mode[id] = LPAIF_I2SCTL_MODE_8CH;
}
for_each_child_of_node(dev->of_node, node) {
ret = of_property_read_u32(node, "reg", &id); if (ret || id < 0) {
dev_err(dev, "valid dai id not found: %d\n", ret); continue;
} if (id == LPASS_DP_RX) {
data->hdmi_port_enable = 1;
} elseif (is_cdc_dma_port(id)) {
data->codec_dma_enable = 1;
} else {
data->mi2s_playback_sd_mode[id] =
of_lpass_cpu_parse_sd_lines(dev, node, "qcom,playback-sd-lines");
data->mi2s_capture_sd_mode[id] =
of_lpass_cpu_parse_sd_lines(dev, node, "qcom,capture-sd-lines");
}
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.