From 8ac6c7a54ed1b98d142dce24b11c6de6a1e239a5 Mon Sep 17 00:00:00 2001 From: hc <hc@nodka.com> Date: Tue, 22 Oct 2024 10:36:11 +0000 Subject: [PATCH] 修改4g拨号为QMI,需要在系统里后台执行quectel-CM --- kernel/sound/soc/qcom/lpass-platform.c | 615 ++++++++++++++++++++++++++++++++++++++++++------------- 1 files changed, 463 insertions(+), 152 deletions(-) diff --git a/kernel/sound/soc/qcom/lpass-platform.c b/kernel/sound/soc/qcom/lpass-platform.c index 1d06e2b..71122e9 100644 --- a/kernel/sound/soc/qcom/lpass-platform.c +++ b/kernel/sound/soc/qcom/lpass-platform.c @@ -1,14 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. * * lpass-platform.c -- ALSA SoC platform driver for QTi LPASS */ @@ -31,7 +23,7 @@ int i2s_port; }; -#define LPASS_PLATFORM_BUFFER_SIZE (16 * 1024) +#define LPASS_PLATFORM_BUFFER_SIZE (24 * 2 * 1024) #define LPASS_PLATFORM_PERIODS 2 static const struct snd_pcm_hardware lpass_platform_pcm_hardware = { @@ -58,17 +50,67 @@ .fifo_size = 0, }; -static int lpass_platform_pcmops_open(struct snd_pcm_substream *substream) +static int lpass_platform_alloc_dmactl_fields(struct device *dev, + struct regmap *map) +{ + struct lpass_data *drvdata = dev_get_drvdata(dev); + struct lpass_variant *v = drvdata->variant; + struct lpaif_dmactl *rd_dmactl, *wr_dmactl; + int rval; + + drvdata->rd_dmactl = devm_kzalloc(dev, sizeof(struct lpaif_dmactl), + GFP_KERNEL); + if (drvdata->rd_dmactl == NULL) + return -ENOMEM; + + drvdata->wr_dmactl = devm_kzalloc(dev, sizeof(struct lpaif_dmactl), + GFP_KERNEL); + if (drvdata->wr_dmactl == NULL) + return -ENOMEM; + + rd_dmactl = drvdata->rd_dmactl; + wr_dmactl = drvdata->wr_dmactl; + + rval = devm_regmap_field_bulk_alloc(dev, map, &rd_dmactl->intf, + &v->rdma_intf, 6); + if (rval) + return rval; + + return devm_regmap_field_bulk_alloc(dev, map, &wr_dmactl->intf, + &v->wrdma_intf, 6); +} + +static int lpass_platform_alloc_hdmidmactl_fields(struct device *dev, + struct regmap *map) +{ + struct lpass_data *drvdata = dev_get_drvdata(dev); + struct lpass_variant *v = drvdata->variant; + struct lpaif_dmactl *rd_dmactl; + + rd_dmactl = devm_kzalloc(dev, sizeof(struct lpaif_dmactl), GFP_KERNEL); + if (rd_dmactl == NULL) + return -ENOMEM; + + drvdata->hdmi_rd_dmactl = rd_dmactl; + + return devm_regmap_field_bulk_alloc(dev, map, &rd_dmactl->bursten, + &v->hdmi_rdma_bursten, 8); +} + +static int lpass_platform_pcmops_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; - struct snd_soc_dai *cpu_dai = soc_runtime->cpu_dai; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(soc_runtime, DRV_NAME); + struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_runtime, 0); struct lpass_data *drvdata = snd_soc_component_get_drvdata(component); struct lpass_variant *v = drvdata->variant; int ret, dma_ch, dir = substream->stream; struct lpass_pcm_data *data; + 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; @@ -77,7 +119,7 @@ runtime->private_data = data; if (v->alloc_dma_channel) - dma_ch = v->alloc_dma_channel(drvdata, dir); + dma_ch = v->alloc_dma_channel(drvdata, dir, dai_id); else dma_ch = 0; @@ -86,18 +128,21 @@ return dma_ch; } - drvdata->substream[dma_ch] = substream; - - ret = regmap_write(drvdata->lpaif_map, - LPAIF_DMACTL_REG(v, dma_ch, dir), 0); + if (cpu_dai->driver->id == LPASS_DP_RX) { + map = drvdata->hdmiif_map; + drvdata->hdmi_substream[dma_ch] = substream; + } else { + map = drvdata->lpaif_map; + drvdata->substream[dma_ch] = substream; + } + data->dma_ch = dma_ch; + ret = regmap_write(map, + LPAIF_DMACTL_REG(v, dma_ch, dir, data->i2s_port), 0); if (ret) { dev_err(soc_runtime->dev, "error writing to rdmactl reg: %d\n", ret); - return ret; + return ret; } - - data->dma_ch = dma_ch; - snd_soc_set_runtime_hwparams(substream, &lpass_platform_pcm_hardware); runtime->dma_bytes = lpass_platform_pcm_hardware.buffer_bytes_max; @@ -116,29 +161,35 @@ return 0; } -static int lpass_platform_pcmops_close(struct snd_pcm_substream *substream) +static int lpass_platform_pcmops_close(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(soc_runtime, DRV_NAME); + struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_runtime, 0); struct lpass_data *drvdata = snd_soc_component_get_drvdata(component); struct lpass_variant *v = drvdata->variant; struct lpass_pcm_data *data; + unsigned int dai_id = cpu_dai->driver->id; data = runtime->private_data; - drvdata->substream[data->dma_ch] = NULL; + if (dai_id == LPASS_DP_RX) + drvdata->hdmi_substream[data->dma_ch] = NULL; + else + drvdata->substream[data->dma_ch] = NULL; if (v->free_dma_channel) - v->free_dma_channel(drvdata, data->dma_ch); + v->free_dma_channel(drvdata, data->dma_ch, dai_id); kfree(data); return 0; } -static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) +static int lpass_platform_pcmops_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) { - struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(soc_runtime, DRV_NAME); + struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_runtime, 0); struct lpass_data *drvdata = snd_soc_component_get_drvdata(component); struct snd_pcm_runtime *rt = substream->runtime; struct lpass_pcm_data *pcm_data = rt->private_data; @@ -146,11 +197,23 @@ snd_pcm_format_t format = params_format(params); unsigned int channels = params_channels(params); unsigned int regval; - int ch, dir = substream->stream; + struct lpaif_dmactl *dmactl; + int id, dir = substream->stream; int bitwidth; int ret, dma_port = pcm_data->i2s_port + v->dmactl_audif_start; + unsigned int dai_id = cpu_dai->driver->id; - ch = pcm_data->dma_ch; + if (dir == SNDRV_PCM_STREAM_PLAYBACK) { + id = pcm_data->dma_ch; + if (dai_id == LPASS_DP_RX) + dmactl = drvdata->hdmi_rd_dmactl; + else + dmactl = drvdata->rd_dmactl; + + } else { + dmactl = drvdata->wr_dmactl; + id = pcm_data->dma_ch - v->wrdma_channel_start; + } bitwidth = snd_pcm_format_width(format); if (bitwidth < 0) { @@ -159,29 +222,75 @@ return bitwidth; } - regval = LPAIF_DMACTL_BURSTEN_INCR4 | - LPAIF_DMACTL_AUDINTF(dma_port) | - LPAIF_DMACTL_FIFOWM_8; + ret = regmap_fields_write(dmactl->bursten, id, LPAIF_DMACTL_BURSTEN_INCR4); + if (ret) { + dev_err(soc_runtime->dev, "error updating bursten field: %d\n", ret); + return ret; + } + ret = regmap_fields_write(dmactl->fifowm, id, LPAIF_DMACTL_FIFOWM_8); + if (ret) { + dev_err(soc_runtime->dev, "error updating fifowm field: %d\n", ret); + return ret; + } + + switch (dai_id) { + case LPASS_DP_RX: + ret = regmap_fields_write(dmactl->burst8, id, + LPAIF_DMACTL_BURSTEN_INCR4); + if (ret) { + dev_err(soc_runtime->dev, "error updating burst8en field: %d\n", ret); + return ret; + } + ret = regmap_fields_write(dmactl->burst16, id, + LPAIF_DMACTL_BURSTEN_INCR4); + if (ret) { + dev_err(soc_runtime->dev, "error updating burst16en field: %d\n", ret); + return ret; + } + ret = regmap_fields_write(dmactl->dynburst, id, + LPAIF_DMACTL_BURSTEN_INCR4); + if (ret) { + dev_err(soc_runtime->dev, "error updating dynbursten field: %d\n", ret); + return ret; + } + break; + case MI2S_PRIMARY: + case MI2S_SECONDARY: + case MI2S_TERTIARY: + case MI2S_QUATERNARY: + case MI2S_QUINARY: + ret = regmap_fields_write(dmactl->intf, id, + LPAIF_DMACTL_AUDINTF(dma_port)); + if (ret) { + dev_err(soc_runtime->dev, "error updating audio interface field: %d\n", + ret); + return ret; + } + + break; + default: + dev_err(soc_runtime->dev, "%s: invalid interface: %d\n", __func__, dai_id); + break; + } switch (bitwidth) { case 16: switch (channels) { case 1: case 2: - regval |= LPAIF_DMACTL_WPSCNT_ONE; + regval = LPAIF_DMACTL_WPSCNT_ONE; break; case 4: - regval |= LPAIF_DMACTL_WPSCNT_TWO; + regval = LPAIF_DMACTL_WPSCNT_TWO; break; case 6: - regval |= LPAIF_DMACTL_WPSCNT_THREE; + regval = LPAIF_DMACTL_WPSCNT_THREE; break; case 8: - regval |= LPAIF_DMACTL_WPSCNT_FOUR; + regval = LPAIF_DMACTL_WPSCNT_FOUR; break; default: - dev_err(soc_runtime->dev, - "invalid PCM config given: bw=%d, ch=%u\n", + dev_err(soc_runtime->dev, "invalid PCM config given: bw=%d, ch=%u\n", bitwidth, channels); return -EINVAL; } @@ -190,23 +299,30 @@ case 32: switch (channels) { case 1: - regval |= LPAIF_DMACTL_WPSCNT_ONE; + regval = LPAIF_DMACTL_WPSCNT_ONE; break; case 2: - regval |= LPAIF_DMACTL_WPSCNT_TWO; + regval = (dai_id == LPASS_DP_RX ? + LPAIF_DMACTL_WPSCNT_ONE : + LPAIF_DMACTL_WPSCNT_TWO); break; case 4: - regval |= LPAIF_DMACTL_WPSCNT_FOUR; + regval = (dai_id == LPASS_DP_RX ? + LPAIF_DMACTL_WPSCNT_TWO : + LPAIF_DMACTL_WPSCNT_FOUR); break; case 6: - regval |= LPAIF_DMACTL_WPSCNT_SIX; + regval = (dai_id == LPASS_DP_RX ? + LPAIF_DMACTL_WPSCNT_THREE : + LPAIF_DMACTL_WPSCNT_SIX); break; case 8: - regval |= LPAIF_DMACTL_WPSCNT_EIGHT; + regval = (dai_id == LPASS_DP_RX ? + LPAIF_DMACTL_WPSCNT_FOUR : + LPAIF_DMACTL_WPSCNT_EIGHT); break; default: - dev_err(soc_runtime->dev, - "invalid PCM config given: bw=%d, ch=%u\n", + dev_err(soc_runtime->dev, "invalid PCM config given: bw=%d, ch=%u\n", bitwidth, channels); return -EINVAL; } @@ -217,10 +333,9 @@ return -EINVAL; } - ret = regmap_write(drvdata->lpaif_map, - LPAIF_DMACTL_REG(v, ch, dir), regval); + ret = regmap_fields_write(dmactl->wpscnt, id, regval); if (ret) { - dev_err(soc_runtime->dev, "error writing to rdmactl reg: %d\n", + dev_err(soc_runtime->dev, "error writing to dmactl reg: %d\n", ret); return ret; } @@ -228,19 +343,27 @@ return 0; } -static int lpass_platform_pcmops_hw_free(struct snd_pcm_substream *substream) +static int lpass_platform_pcmops_hw_free(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(soc_runtime, DRV_NAME); + struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_runtime, 0); struct lpass_data *drvdata = snd_soc_component_get_drvdata(component); struct snd_pcm_runtime *rt = substream->runtime; struct lpass_pcm_data *pcm_data = rt->private_data; struct lpass_variant *v = drvdata->variant; unsigned int reg; int ret; + struct regmap *map; + unsigned int dai_id = cpu_dai->driver->id; - reg = LPAIF_DMACTL_REG(v, pcm_data->dma_ch, substream->stream); - ret = regmap_write(drvdata->lpaif_map, reg, 0); + if (dai_id == LPASS_DP_RX) + map = drvdata->hdmiif_map; + else + map = drvdata->lpaif_map; + + reg = LPAIF_DMACTL_REG(v, pcm_data->dma_ch, substream->stream, dai_id); + ret = regmap_write(map, reg, 0); if (ret) dev_err(soc_runtime->dev, "error writing to rdmactl reg: %d\n", ret); @@ -248,30 +371,48 @@ return ret; } -static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream) +static int lpass_platform_pcmops_prepare(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(soc_runtime, DRV_NAME); + struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_runtime, 0); struct lpass_data *drvdata = snd_soc_component_get_drvdata(component); struct snd_pcm_runtime *rt = substream->runtime; struct lpass_pcm_data *pcm_data = rt->private_data; struct lpass_variant *v = drvdata->variant; - int ret, ch, dir = substream->stream; + struct lpaif_dmactl *dmactl; + struct regmap *map; + int ret, id, ch, dir = substream->stream; + unsigned int dai_id = cpu_dai->driver->id; + ch = pcm_data->dma_ch; + if (dir == SNDRV_PCM_STREAM_PLAYBACK) { + if (dai_id == LPASS_DP_RX) { + dmactl = drvdata->hdmi_rd_dmactl; + map = drvdata->hdmiif_map; + } else { + dmactl = drvdata->rd_dmactl; + map = drvdata->lpaif_map; + } - ret = regmap_write(drvdata->lpaif_map, - LPAIF_DMABASE_REG(v, ch, dir), - runtime->dma_addr); + id = pcm_data->dma_ch; + } else { + dmactl = drvdata->wr_dmactl; + id = pcm_data->dma_ch - v->wrdma_channel_start; + map = drvdata->lpaif_map; + } + + ret = regmap_write(map, LPAIF_DMABASE_REG(v, ch, dir, dai_id), + runtime->dma_addr); if (ret) { dev_err(soc_runtime->dev, "error writing to rdmabase reg: %d\n", ret); return ret; } - ret = regmap_write(drvdata->lpaif_map, - LPAIF_DMABUFF_REG(v, ch, dir), + ret = regmap_write(map, LPAIF_DMABUFF_REG(v, ch, dir, dai_id), (snd_pcm_lib_buffer_bytes(substream) >> 2) - 1); if (ret) { dev_err(soc_runtime->dev, "error writing to rdmabuff reg: %d\n", @@ -279,8 +420,7 @@ return ret; } - ret = regmap_write(drvdata->lpaif_map, - LPAIF_DMAPER_REG(v, ch, dir), + ret = regmap_write(map, LPAIF_DMAPER_REG(v, ch, dir, dai_id), (snd_pcm_lib_period_bytes(substream) >> 2) - 1); if (ret) { dev_err(soc_runtime->dev, "error writing to rdmaper reg: %d\n", @@ -288,9 +428,7 @@ return ret; } - ret = regmap_update_bits(drvdata->lpaif_map, - LPAIF_DMACTL_REG(v, ch, dir), - LPAIF_DMACTL_ENABLE_MASK, LPAIF_DMACTL_ENABLE_ON); + ret = regmap_fields_write(dmactl->enable, id, LPAIF_DMACTL_ENABLE_ON); if (ret) { dev_err(soc_runtime->dev, "error writing to rdmactl reg: %d\n", ret); @@ -300,69 +438,146 @@ return 0; } -static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream, - int cmd) +static int lpass_platform_pcmops_trigger(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + int cmd) { - struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(soc_runtime, DRV_NAME); + struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_runtime, 0); struct lpass_data *drvdata = snd_soc_component_get_drvdata(component); struct snd_pcm_runtime *rt = substream->runtime; struct lpass_pcm_data *pcm_data = rt->private_data; struct lpass_variant *v = drvdata->variant; - int ret, ch, dir = substream->stream; + struct lpaif_dmactl *dmactl; + struct regmap *map; + int ret, ch, id; + int dir = substream->stream; + 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; ch = pcm_data->dma_ch; + if (dir == SNDRV_PCM_STREAM_PLAYBACK) { + id = pcm_data->dma_ch; + if (dai_id == LPASS_DP_RX) { + dmactl = drvdata->hdmi_rd_dmactl; + 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; + } switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - /* clear status before enabling interrupts */ - ret = regmap_write(drvdata->lpaif_map, - LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST), - LPAIF_IRQ_ALL(ch)); - if (ret) { - dev_err(soc_runtime->dev, - "error writing to irqclear reg: %d\n", ret); - return ret; - } - - ret = regmap_update_bits(drvdata->lpaif_map, - LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST), - LPAIF_IRQ_ALL(ch), - LPAIF_IRQ_ALL(ch)); - if (ret) { - dev_err(soc_runtime->dev, - "error writing to irqen reg: %d\n", ret); - return ret; - } - - ret = regmap_update_bits(drvdata->lpaif_map, - LPAIF_DMACTL_REG(v, ch, dir), - LPAIF_DMACTL_ENABLE_MASK, - LPAIF_DMACTL_ENABLE_ON); + ret = regmap_fields_write(dmactl->enable, id, + LPAIF_DMACTL_ENABLE_ON); if (ret) { dev_err(soc_runtime->dev, "error writing to rdmactl reg: %d\n", ret); + return ret; + } + switch (dai_id) { + case LPASS_DP_RX: + ret = regmap_fields_write(dmactl->dyncclk, id, + LPAIF_DMACTL_DYNCLK_ON); + if (ret) { + dev_err(soc_runtime->dev, + "error writing to rdmactl reg: %d\n", ret); + return ret; + } + reg_irqclr = LPASS_HDMITX_APP_IRQCLEAR_REG(v); + val_irqclr = (LPAIF_IRQ_ALL(ch) | + LPAIF_IRQ_HDMI_REQ_ON_PRELOAD(ch) | + LPAIF_IRQ_HDMI_METADONE | + LPAIF_IRQ_HDMI_SDEEP_AUD_DIS(ch)); + + reg_irqen = LPASS_HDMITX_APP_IRQEN_REG(v); + val_mask = (LPAIF_IRQ_ALL(ch) | + LPAIF_IRQ_HDMI_REQ_ON_PRELOAD(ch) | + LPAIF_IRQ_HDMI_METADONE | + LPAIF_IRQ_HDMI_SDEEP_AUD_DIS(ch)); + val_irqen = (LPAIF_IRQ_ALL(ch) | + LPAIF_IRQ_HDMI_REQ_ON_PRELOAD(ch) | + LPAIF_IRQ_HDMI_METADONE | + LPAIF_IRQ_HDMI_SDEEP_AUD_DIS(ch)); + break; + case MI2S_PRIMARY: + case MI2S_SECONDARY: + case MI2S_TERTIARY: + case MI2S_QUATERNARY: + case MI2S_QUINARY: + reg_irqclr = LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST); + val_irqclr = LPAIF_IRQ_ALL(ch); + + + reg_irqen = LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST); + val_mask = LPAIF_IRQ_ALL(ch); + val_irqen = LPAIF_IRQ_ALL(ch); + break; + default: + dev_err(soc_runtime->dev, "%s: invalid %d interface\n", __func__, dai_id); + return -EINVAL; + } + + ret = regmap_write(map, reg_irqclr, val_irqclr); + if (ret) { + dev_err(soc_runtime->dev, "error writing to irqclear reg: %d\n", ret); + return ret; + } + ret = regmap_update_bits(map, reg_irqen, val_mask, val_irqen); + if (ret) { + dev_err(soc_runtime->dev, "error writing to irqen reg: %d\n", ret); return ret; } break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - ret = regmap_update_bits(drvdata->lpaif_map, - LPAIF_DMACTL_REG(v, ch, dir), - LPAIF_DMACTL_ENABLE_MASK, - LPAIF_DMACTL_ENABLE_OFF); + ret = regmap_fields_write(dmactl->enable, id, + LPAIF_DMACTL_ENABLE_OFF); if (ret) { dev_err(soc_runtime->dev, "error writing to rdmactl reg: %d\n", ret); return ret; } + switch (dai_id) { + case LPASS_DP_RX: + ret = regmap_fields_write(dmactl->dyncclk, id, + LPAIF_DMACTL_DYNCLK_OFF); + if (ret) { + dev_err(soc_runtime->dev, + "error writing to rdmactl reg: %d\n", ret); + return ret; + } + reg_irqen = LPASS_HDMITX_APP_IRQEN_REG(v); + val_mask = (LPAIF_IRQ_ALL(ch) | + LPAIF_IRQ_HDMI_REQ_ON_PRELOAD(ch) | + LPAIF_IRQ_HDMI_METADONE | + LPAIF_IRQ_HDMI_SDEEP_AUD_DIS(ch)); + val_irqen = 0; + break; + case MI2S_PRIMARY: + case MI2S_SECONDARY: + case MI2S_TERTIARY: + case MI2S_QUATERNARY: + case MI2S_QUINARY: + reg_irqen = LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST); + val_mask = LPAIF_IRQ_ALL(ch); + val_irqen = 0; + break; + default: + dev_err(soc_runtime->dev, "%s: invalid %d interface\n", __func__, dai_id); + return -EINVAL; + } - ret = regmap_update_bits(drvdata->lpaif_map, - LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST), - LPAIF_IRQ_ALL(ch), 0); + ret = regmap_update_bits(map, reg_irqen, val_mask, val_irqen); if (ret) { dev_err(soc_runtime->dev, "error writing to irqen reg: %d\n", ret); @@ -375,29 +590,37 @@ } static snd_pcm_uframes_t lpass_platform_pcmops_pointer( + struct snd_soc_component *component, struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(soc_runtime, DRV_NAME); + struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_runtime, 0); struct lpass_data *drvdata = snd_soc_component_get_drvdata(component); struct snd_pcm_runtime *rt = substream->runtime; struct lpass_pcm_data *pcm_data = rt->private_data; struct lpass_variant *v = drvdata->variant; unsigned int base_addr, curr_addr; int ret, ch, dir = substream->stream; + struct regmap *map; + unsigned int dai_id = cpu_dai->driver->id; + + if (dai_id == LPASS_DP_RX) + map = drvdata->hdmiif_map; + else + map = drvdata->lpaif_map; ch = pcm_data->dma_ch; - ret = regmap_read(drvdata->lpaif_map, - LPAIF_DMABASE_REG(v, ch, dir), &base_addr); + ret = regmap_read(map, + LPAIF_DMABASE_REG(v, ch, dir, dai_id), &base_addr); if (ret) { dev_err(soc_runtime->dev, "error reading from rdmabase reg: %d\n", ret); return ret; } - ret = regmap_read(drvdata->lpaif_map, - LPAIF_DMACURR_REG(v, ch, dir), &curr_addr); + ret = regmap_read(map, + LPAIF_DMACURR_REG(v, ch, dir, dai_id), &curr_addr); if (ret) { dev_err(soc_runtime->dev, "error reading from rdmacurr reg: %d\n", ret); @@ -407,42 +630,54 @@ return bytes_to_frames(substream->runtime, curr_addr - base_addr); } -static int lpass_platform_pcmops_mmap(struct snd_pcm_substream *substream, - struct vm_area_struct *vma) +static int lpass_platform_pcmops_mmap(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + struct vm_area_struct *vma) { struct snd_pcm_runtime *runtime = substream->runtime; - return dma_mmap_coherent(substream->pcm->card->dev, vma, - runtime->dma_area, runtime->dma_addr, - runtime->dma_bytes); + return dma_mmap_coherent(component->dev, vma, runtime->dma_area, + runtime->dma_addr, runtime->dma_bytes); } - -static const struct snd_pcm_ops lpass_platform_pcm_ops = { - .open = lpass_platform_pcmops_open, - .close = lpass_platform_pcmops_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = lpass_platform_pcmops_hw_params, - .hw_free = lpass_platform_pcmops_hw_free, - .prepare = lpass_platform_pcmops_prepare, - .trigger = lpass_platform_pcmops_trigger, - .pointer = lpass_platform_pcmops_pointer, - .mmap = lpass_platform_pcmops_mmap, -}; static irqreturn_t lpass_dma_interrupt_handler( struct snd_pcm_substream *substream, struct lpass_data *drvdata, int chan, u32 interrupts) { - struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; + struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_runtime, 0); struct lpass_variant *v = drvdata->variant; irqreturn_t ret = IRQ_NONE; int rv; + unsigned int reg = 0, val = 0; + struct regmap *map; + unsigned int dai_id = cpu_dai->driver->id; + switch (dai_id) { + case LPASS_DP_RX: + map = drvdata->hdmiif_map; + reg = LPASS_HDMITX_APP_IRQCLEAR_REG(v); + val = (LPAIF_IRQ_HDMI_REQ_ON_PRELOAD(chan) | + LPAIF_IRQ_HDMI_METADONE | + LPAIF_IRQ_HDMI_SDEEP_AUD_DIS(chan)); + break; + case MI2S_PRIMARY: + case MI2S_SECONDARY: + case MI2S_TERTIARY: + case MI2S_QUATERNARY: + case MI2S_QUINARY: + map = drvdata->lpaif_map; + reg = LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST); + val = 0; + break; + default: + dev_err(soc_runtime->dev, "%s: invalid %d interface\n", __func__, dai_id); + return -EINVAL; + } if (interrupts & LPAIF_IRQ_PER(chan)) { - rv = regmap_write(drvdata->lpaif_map, - LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST), - LPAIF_IRQ_PER(chan)); + + rv = regmap_write(map, reg, LPAIF_IRQ_PER(chan) | val); if (rv) { dev_err(soc_runtime->dev, "error writing to irqclear reg: %d\n", rv); @@ -453,9 +688,7 @@ } if (interrupts & LPAIF_IRQ_XRUN(chan)) { - rv = regmap_write(drvdata->lpaif_map, - LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST), - LPAIF_IRQ_XRUN(chan)); + rv = regmap_write(map, reg, LPAIF_IRQ_XRUN(chan) | val); if (rv) { dev_err(soc_runtime->dev, "error writing to irqclear reg: %d\n", rv); @@ -467,9 +700,7 @@ } if (interrupts & LPAIF_IRQ_ERR(chan)) { - rv = regmap_write(drvdata->lpaif_map, - LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST), - LPAIF_IRQ_ERR(chan)); + rv = regmap_write(map, reg, LPAIF_IRQ_ERR(chan) | val); if (rv) { dev_err(soc_runtime->dev, "error writing to irqclear reg: %d\n", rv); @@ -477,6 +708,16 @@ } dev_err(soc_runtime->dev, "bus access error\n"); snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED); + ret = IRQ_HANDLED; + } + + if (interrupts & val) { + rv = regmap_write(map, reg, val); + if (rv) { + dev_err(soc_runtime->dev, + "error writing to irqclear reg: %d\n", rv); + return IRQ_NONE; + } ret = IRQ_HANDLED; } @@ -511,11 +752,42 @@ return IRQ_HANDLED; } -static int lpass_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime) +static irqreturn_t lpass_platform_hdmiif_irq(int irq, void *data) +{ + struct lpass_data *drvdata = data; + struct lpass_variant *v = drvdata->variant; + unsigned int irqs; + int rv, chan; + + rv = regmap_read(drvdata->hdmiif_map, + LPASS_HDMITX_APP_IRQSTAT_REG(v), &irqs); + if (rv) { + pr_err("error reading from irqstat reg: %d\n", rv); + return IRQ_NONE; + } + + /* Handle per channel interrupts */ + for (chan = 0; chan < LPASS_MAX_HDMI_DMA_CHANNELS; chan++) { + if (irqs & (LPAIF_IRQ_ALL(chan) | LPAIF_IRQ_HDMI_REQ_ON_PRELOAD(chan) | + LPAIF_IRQ_HDMI_METADONE | + LPAIF_IRQ_HDMI_SDEEP_AUD_DIS(chan)) + && drvdata->hdmi_substream[chan]) { + rv = lpass_dma_interrupt_handler( + drvdata->hdmi_substream[chan], + drvdata, chan, irqs); + if (rv != IRQ_HANDLED) + return rv; + } + } + + return IRQ_HANDLED; +} + +static int lpass_platform_pcm_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *soc_runtime) { struct snd_pcm *pcm = soc_runtime->pcm; struct snd_pcm_substream *psubstream, *csubstream; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(soc_runtime, DRV_NAME); int ret = -EINVAL; size_t size = lpass_platform_pcm_hardware.buffer_bytes_max; @@ -547,12 +819,13 @@ return 0; } -static void lpass_platform_pcm_free(struct snd_pcm *pcm) +static void lpass_platform_pcm_free(struct snd_soc_component *component, + struct snd_pcm *pcm) { struct snd_pcm_substream *substream; int i; - for (i = 0; i < ARRAY_SIZE(pcm->streams); i++) { + for_each_pcm_streams(i) { substream = pcm->streams[i].substream; if (substream) { snd_dma_free_pages(&substream->dma_buffer); @@ -564,9 +837,17 @@ static const struct snd_soc_component_driver lpass_component_driver = { .name = DRV_NAME, - .pcm_new = lpass_platform_pcm_new, - .pcm_free = lpass_platform_pcm_free, - .ops = &lpass_platform_pcm_ops, + .open = lpass_platform_pcmops_open, + .close = lpass_platform_pcmops_close, + .hw_params = lpass_platform_pcmops_hw_params, + .hw_free = lpass_platform_pcmops_hw_free, + .prepare = lpass_platform_pcmops_prepare, + .trigger = lpass_platform_pcmops_trigger, + .pointer = lpass_platform_pcmops_pointer, + .mmap = lpass_platform_pcmops_mmap, + .pcm_construct = lpass_platform_pcm_new, + .pcm_destruct = lpass_platform_pcm_free, + }; int asoc_qcom_lpass_platform_register(struct platform_device *pdev) @@ -576,11 +857,8 @@ int ret; drvdata->lpaif_irq = platform_get_irq_byname(pdev, "lpass-irq-lpaif"); - if (drvdata->lpaif_irq < 0) { - dev_err(&pdev->dev, "error getting irq handle: %d\n", - drvdata->lpaif_irq); + if (drvdata->lpaif_irq < 0) return -ENODEV; - } /* ensure audio hardware is disabled */ ret = regmap_write(drvdata->lpaif_map, @@ -598,7 +876,40 @@ return ret; } + ret = lpass_platform_alloc_dmactl_fields(&pdev->dev, + drvdata->lpaif_map); + if (ret) { + dev_err(&pdev->dev, + "error initializing dmactl fields: %d\n", ret); + return ret; + } + if (drvdata->hdmi_port_enable) { + drvdata->hdmiif_irq = platform_get_irq_byname(pdev, "lpass-irq-hdmi"); + if (drvdata->hdmiif_irq < 0) + return -ENODEV; + + ret = devm_request_irq(&pdev->dev, drvdata->hdmiif_irq, + lpass_platform_hdmiif_irq, 0, "lpass-irq-hdmi", drvdata); + if (ret) { + dev_err(&pdev->dev, "irq hdmi request failed: %d\n", ret); + return ret; + } + ret = regmap_write(drvdata->hdmiif_map, + LPASS_HDMITX_APP_IRQEN_REG(v), 0); + if (ret) { + dev_err(&pdev->dev, "error writing to hdmi irqen reg: %d\n", ret); + return ret; + } + + ret = lpass_platform_alloc_hdmidmactl_fields(&pdev->dev, + drvdata->hdmiif_map); + if (ret) { + dev_err(&pdev->dev, + "error initializing hdmidmactl fields: %d\n", ret); + return ret; + } + } return devm_snd_soc_register_component(&pdev->dev, &lpass_component_driver, NULL, 0); } -- Gitblit v1.6.2