diff options
Diffstat (limited to 'sound/soc/qcom/lpass-platform.c')
-rw-r--r-- | sound/soc/qcom/lpass-platform.c | 244 |
1 files changed, 147 insertions, 97 deletions
diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c index 4aeb8e1a7..6e8665430 100644 --- a/sound/soc/qcom/lpass-platform.c +++ b/sound/soc/qcom/lpass-platform.c @@ -26,6 +26,7 @@ struct lpass_pcm_data { int rdma_ch; + int wrdma_ch; int i2s_port; }; @@ -90,8 +91,14 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream, snd_pcm_format_t format = params_format(params); unsigned int channels = params_channels(params); unsigned int regval; + int ch, dir = substream->stream; int bitwidth; - int ret, rdma_port = pcm_data->i2s_port + v->rdmactl_audif_start; + int ret, dma_port = pcm_data->i2s_port + v->dmactl_audif_start; + + if (dir == SNDRV_PCM_STREAM_PLAYBACK) + ch = pcm_data->rdma_ch; + else + ch = pcm_data->wrdma_ch; bitwidth = snd_pcm_format_width(format); if (bitwidth < 0) { @@ -100,25 +107,25 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream, return bitwidth; } - regval = LPAIF_RDMACTL_BURSTEN_INCR4 | - LPAIF_RDMACTL_AUDINTF(rdma_port) | - LPAIF_RDMACTL_FIFOWM_8; + regval = LPAIF_DMACTL_BURSTEN_INCR4 | + LPAIF_DMACTL_AUDINTF(dma_port) | + LPAIF_DMACTL_FIFOWM_8; switch (bitwidth) { case 16: switch (channels) { case 1: case 2: - regval |= LPAIF_RDMACTL_WPSCNT_ONE; + regval |= LPAIF_DMACTL_WPSCNT_ONE; break; case 4: - regval |= LPAIF_RDMACTL_WPSCNT_TWO; + regval |= LPAIF_DMACTL_WPSCNT_TWO; break; case 6: - regval |= LPAIF_RDMACTL_WPSCNT_THREE; + regval |= LPAIF_DMACTL_WPSCNT_THREE; break; case 8: - regval |= LPAIF_RDMACTL_WPSCNT_FOUR; + regval |= LPAIF_DMACTL_WPSCNT_FOUR; break; default: dev_err(soc_runtime->dev, "%s() invalid PCM config given: bw=%d, ch=%u\n", @@ -130,19 +137,19 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream, case 32: switch (channels) { case 1: - regval |= LPAIF_RDMACTL_WPSCNT_ONE; + regval |= LPAIF_DMACTL_WPSCNT_ONE; break; case 2: - regval |= LPAIF_RDMACTL_WPSCNT_TWO; + regval |= LPAIF_DMACTL_WPSCNT_TWO; break; case 4: - regval |= LPAIF_RDMACTL_WPSCNT_FOUR; + regval |= LPAIF_DMACTL_WPSCNT_FOUR; break; case 6: - regval |= LPAIF_RDMACTL_WPSCNT_SIX; + regval |= LPAIF_DMACTL_WPSCNT_SIX; break; case 8: - regval |= LPAIF_RDMACTL_WPSCNT_EIGHT; + regval |= LPAIF_DMACTL_WPSCNT_EIGHT; break; default: dev_err(soc_runtime->dev, "%s() invalid PCM config given: bw=%d, ch=%u\n", @@ -157,7 +164,7 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream, } ret = regmap_write(drvdata->lpaif_map, - LPAIF_RDMACTL_REG(v, pcm_data->rdma_ch), regval); + LPAIF_DMACTL_REG(v, ch, dir), regval); if (ret) { dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n", __func__, ret); @@ -174,10 +181,15 @@ static int lpass_platform_pcmops_hw_free(struct snd_pcm_substream *substream) struct lpass_data *drvdata = snd_soc_platform_get_drvdata(soc_runtime->platform); struct lpass_variant *v = drvdata->variant; + unsigned int reg; int ret; - ret = regmap_write(drvdata->lpaif_map, - LPAIF_RDMACTL_REG(v, pcm_data->rdma_ch), 0); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + reg = LPAIF_RDMACTL_REG(v, pcm_data->rdma_ch); + else + reg = LPAIF_WRDMACTL_REG(v, pcm_data->wrdma_ch); + + ret = regmap_write(drvdata->lpaif_map, reg, 0); if (ret) dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n", __func__, ret); @@ -193,10 +205,15 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream) struct lpass_data *drvdata = snd_soc_platform_get_drvdata(soc_runtime->platform); struct lpass_variant *v = drvdata->variant; - int ret, ch = pcm_data->rdma_ch; + int ret, ch, dir = substream->stream; + + if (dir == SNDRV_PCM_STREAM_PLAYBACK) + ch = pcm_data->rdma_ch; + else + ch = pcm_data->wrdma_ch; ret = regmap_write(drvdata->lpaif_map, - LPAIF_RDMABASE_REG(v, ch), + LPAIF_DMABASE_REG(v, ch, dir), runtime->dma_addr); if (ret) { dev_err(soc_runtime->dev, "%s() error writing to rdmabase reg: %d\n", @@ -205,7 +222,7 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream) } ret = regmap_write(drvdata->lpaif_map, - LPAIF_RDMABUFF_REG(v, ch), + LPAIF_DMABUFF_REG(v, ch, dir), (snd_pcm_lib_buffer_bytes(substream) >> 2) - 1); if (ret) { dev_err(soc_runtime->dev, "%s() error writing to rdmabuff reg: %d\n", @@ -214,7 +231,7 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream) } ret = regmap_write(drvdata->lpaif_map, - LPAIF_RDMAPER_REG(v, ch), + LPAIF_DMAPER_REG(v, ch, dir), (snd_pcm_lib_period_bytes(substream) >> 2) - 1); if (ret) { dev_err(soc_runtime->dev, "%s() error writing to rdmaper reg: %d\n", @@ -223,8 +240,8 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream) } ret = regmap_update_bits(drvdata->lpaif_map, - LPAIF_RDMACTL_REG(v, ch), - LPAIF_RDMACTL_ENABLE_MASK, LPAIF_RDMACTL_ENABLE_ON); + LPAIF_DMACTL_REG(v, ch, dir), + LPAIF_DMACTL_ENABLE_MASK, LPAIF_DMACTL_ENABLE_ON); if (ret) { dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n", __func__, ret); @@ -242,7 +259,12 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream, struct lpass_data *drvdata = snd_soc_platform_get_drvdata(soc_runtime->platform); struct lpass_variant *v = drvdata->variant; - int ret, ch = pcm_data->rdma_ch; + int ret, ch, dir = substream->stream; + + if (dir == SNDRV_PCM_STREAM_PLAYBACK) + ch = pcm_data->rdma_ch; + else + ch = pcm_data->wrdma_ch; switch (cmd) { case SNDRV_PCM_TRIGGER_START: @@ -269,9 +291,9 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream, } ret = regmap_update_bits(drvdata->lpaif_map, - LPAIF_RDMACTL_REG(v, ch), - LPAIF_RDMACTL_ENABLE_MASK, - LPAIF_RDMACTL_ENABLE_ON); + LPAIF_DMACTL_REG(v, ch, dir), + LPAIF_DMACTL_ENABLE_MASK, + LPAIF_DMACTL_ENABLE_ON); if (ret) { dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n", __func__, ret); @@ -282,9 +304,9 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream, case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: ret = regmap_update_bits(drvdata->lpaif_map, - LPAIF_RDMACTL_REG(v, ch), - LPAIF_RDMACTL_ENABLE_MASK, - LPAIF_RDMACTL_ENABLE_OFF); + LPAIF_DMACTL_REG(v, ch, dir), + LPAIF_DMACTL_ENABLE_MASK, + LPAIF_DMACTL_ENABLE_OFF); if (ret) { dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n", __func__, ret); @@ -314,10 +336,15 @@ static snd_pcm_uframes_t lpass_platform_pcmops_pointer( snd_soc_platform_get_drvdata(soc_runtime->platform); struct lpass_variant *v = drvdata->variant; unsigned int base_addr, curr_addr; - int ret, ch = pcm_data->rdma_ch; + int ret, ch, dir = substream->stream; + + if (dir == SNDRV_PCM_STREAM_PLAYBACK) + ch = pcm_data->rdma_ch; + else + ch = pcm_data->wrdma_ch; ret = regmap_read(drvdata->lpaif_map, - LPAIF_RDMABASE_REG(v, ch), &base_addr); + LPAIF_DMABASE_REG(v, ch, dir), &base_addr); if (ret) { dev_err(soc_runtime->dev, "%s() error reading from rdmabase reg: %d\n", __func__, ret); @@ -325,7 +352,7 @@ static snd_pcm_uframes_t lpass_platform_pcmops_pointer( } ret = regmap_read(drvdata->lpaif_map, - LPAIF_RDMACURR_REG(v, ch), &curr_addr); + LPAIF_DMACURR_REG(v, ch, dir), &curr_addr); if (ret) { dev_err(soc_runtime->dev, "%s() error reading from rdmacurr reg: %d\n", __func__, ret); @@ -439,101 +466,124 @@ static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data) return IRQ_HANDLED; } -static int lpass_platform_alloc_buffer(struct snd_pcm_substream *substream, - struct snd_soc_pcm_runtime *rt) -{ - struct snd_dma_buffer *buf = &substream->dma_buffer; - size_t size = lpass_platform_pcm_hardware.buffer_bytes_max; - - buf->dev.type = SNDRV_DMA_TYPE_DEV; - buf->dev.dev = rt->platform->dev; - buf->private_data = NULL; - buf->area = dma_alloc_coherent(rt->platform->dev, size, &buf->addr, - GFP_KERNEL); - if (!buf->area) { - dev_err(rt->platform->dev, "%s: Could not allocate DMA buffer\n", - __func__); - return -ENOMEM; - } - buf->bytes = size; - - return 0; -} - -static void lpass_platform_free_buffer(struct snd_pcm_substream *substream, - struct snd_soc_pcm_runtime *rt) -{ - struct snd_dma_buffer *buf = &substream->dma_buffer; - - if (buf->area) { - dma_free_coherent(rt->dev, buf->bytes, buf->area, - buf->addr); - } - buf->area = NULL; -} - static int lpass_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime) { struct snd_pcm *pcm = soc_runtime->pcm; - struct snd_pcm_substream *substream = - pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + struct snd_pcm_substream *psubstream, *csubstream; struct snd_soc_dai *cpu_dai = soc_runtime->cpu_dai; struct lpass_data *drvdata = snd_soc_platform_get_drvdata(soc_runtime->platform); struct lpass_variant *v = drvdata->variant; int ret; struct lpass_pcm_data *data; + size_t size = lpass_platform_pcm_hardware.buffer_bytes_max; data = devm_kzalloc(soc_runtime->dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; - if (v->alloc_dma_channel) - data->rdma_ch = v->alloc_dma_channel(drvdata); + data->i2s_port = cpu_dai->driver->id; + snd_soc_pcm_set_drvdata(soc_runtime, data); - if (IS_ERR_VALUE(data->rdma_ch)) - return data->rdma_ch; + psubstream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + if (psubstream) { + if (v->alloc_dma_channel) + data->rdma_ch = v->alloc_dma_channel(drvdata, + SNDRV_PCM_STREAM_PLAYBACK); - drvdata->substream[data->rdma_ch] = substream; - data->i2s_port = cpu_dai->driver->id; + if (IS_ERR_VALUE(data->rdma_ch)) + return data->rdma_ch; - snd_soc_pcm_set_drvdata(soc_runtime, data); + drvdata->substream[data->rdma_ch] = psubstream; - ret = lpass_platform_alloc_buffer(substream, soc_runtime); - if (ret) - return ret; + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, + soc_runtime->platform->dev, + size, &psubstream->dma_buffer); + if (ret) + goto playback_alloc_err; - ret = regmap_write(drvdata->lpaif_map, + ret = regmap_write(drvdata->lpaif_map, LPAIF_RDMACTL_REG(v, data->rdma_ch), 0); - if (ret) { - dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n", + if (ret) { + dev_err(soc_runtime->dev, + "%s() error writing to rdmactl reg: %d\n", + __func__, ret); + goto capture_alloc_err; + } + } + + csubstream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; + if (csubstream) { + if (v->alloc_dma_channel) + data->wrdma_ch = v->alloc_dma_channel(drvdata, + SNDRV_PCM_STREAM_CAPTURE); + + if (IS_ERR_VALUE(data->wrdma_ch)) + goto capture_alloc_err; + + drvdata->substream[data->wrdma_ch] = csubstream; + + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, + soc_runtime->platform->dev, + size, &csubstream->dma_buffer); + if (ret) + goto capture_alloc_err; + + ret = regmap_write(drvdata->lpaif_map, + LPAIF_WRDMACTL_REG(v, data->wrdma_ch), 0); + if (ret) { + dev_err(soc_runtime->dev, + "%s() error writing to wrdmactl reg: %d\n", __func__, ret); - goto err_buf; + goto capture_reg_err; + } } return 0; -err_buf: - lpass_platform_free_buffer(substream, soc_runtime); +capture_reg_err: + if (csubstream) + snd_dma_free_pages(&csubstream->dma_buffer); + +capture_alloc_err: + if (psubstream) + snd_dma_free_pages(&psubstream->dma_buffer); + + playback_alloc_err: + dev_err(soc_runtime->dev, "Cannot allocate buffer(s)\n"); + return ret; } static void lpass_platform_pcm_free(struct snd_pcm *pcm) { - struct snd_pcm_substream *substream = - pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; - struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; - struct lpass_data *drvdata = - snd_soc_platform_get_drvdata(soc_runtime->platform); - struct lpass_pcm_data *data = snd_soc_pcm_get_drvdata(soc_runtime); - struct lpass_variant *v = drvdata->variant; - - drvdata->substream[data->rdma_ch] = NULL; - - if (v->free_dma_channel) - v->free_dma_channel(drvdata, data->rdma_ch); - - lpass_platform_free_buffer(substream, soc_runtime); + struct snd_soc_pcm_runtime *rt; + struct lpass_data *drvdata; + struct lpass_pcm_data *data; + struct lpass_variant *v; + struct snd_pcm_substream *substream; + int ch, i; + + for (i = 0; i < ARRAY_SIZE(pcm->streams); i++) { + substream = pcm->streams[i].substream; + if (substream) { + rt = substream->private_data; + data = snd_soc_pcm_get_drvdata(rt); + drvdata = snd_soc_platform_get_drvdata(rt->platform); + + ch = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + ? data->rdma_ch + : data->wrdma_ch; + v = drvdata->variant; + drvdata->substream[ch] = NULL; + if (v->free_dma_channel) + v->free_dma_channel(drvdata, ch); + + snd_dma_free_pages(&substream->dma_buffer); + substream->dma_buffer.area = NULL; + substream->dma_buffer.addr = 0; + } + } } static struct snd_soc_platform_driver lpass_platform_driver = { |