diff options
Diffstat (limited to 'sound/soc/qcom')
-rw-r--r-- | sound/soc/qcom/Kconfig | 24 | ||||
-rw-r--r-- | sound/soc/qcom/Makefile | 4 | ||||
-rw-r--r-- | sound/soc/qcom/common.c | 4 | ||||
-rw-r--r-- | sound/soc/qcom/lpass-apq8016.c | 92 | ||||
-rw-r--r-- | sound/soc/qcom/lpass-cpu.c | 497 | ||||
-rw-r--r-- | sound/soc/qcom/lpass-hdmi.c | 258 | ||||
-rw-r--r-- | sound/soc/qcom/lpass-hdmi.h | 102 | ||||
-rw-r--r-- | sound/soc/qcom/lpass-ipq806x.c | 71 | ||||
-rw-r--r-- | sound/soc/qcom/lpass-lpaif-reg.h | 215 | ||||
-rw-r--r-- | sound/soc/qcom/lpass-platform.c | 514 | ||||
-rw-r--r-- | sound/soc/qcom/lpass-sc7180.c | 305 | ||||
-rw-r--r-- | sound/soc/qcom/lpass.h | 182 | ||||
-rw-r--r-- | sound/soc/qcom/qdsp6/Makefile | 1 | ||||
-rw-r--r-- | sound/soc/qcom/qdsp6/q6adm.c | 2 | ||||
-rw-r--r-- | sound/soc/qcom/qdsp6/q6afe-clocks.c | 273 | ||||
-rw-r--r-- | sound/soc/qcom/qdsp6/q6afe-dai.c | 231 | ||||
-rw-r--r-- | sound/soc/qcom/qdsp6/q6afe.c | 310 | ||||
-rw-r--r-- | sound/soc/qcom/qdsp6/q6afe.h | 33 | ||||
-rw-r--r-- | sound/soc/qcom/qdsp6/q6asm-dai.c | 416 | ||||
-rw-r--r-- | sound/soc/qcom/qdsp6/q6asm.c | 175 | ||||
-rw-r--r-- | sound/soc/qcom/qdsp6/q6asm.h | 49 | ||||
-rw-r--r-- | sound/soc/qcom/qdsp6/q6core.c | 2 | ||||
-rw-r--r-- | sound/soc/qcom/qdsp6/q6routing.c | 123 | ||||
-rw-r--r-- | sound/soc/qcom/sdm845.c | 2 |
24 files changed, 3370 insertions, 515 deletions
diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig index 5d6b2466a2f2..2696ffcba880 100644 --- a/sound/soc/qcom/Kconfig +++ b/sound/soc/qcom/Kconfig @@ -1,15 +1,21 @@ # SPDX-License-Identifier: GPL-2.0-only -config SND_SOC_QCOM +menuconfig SND_SOC_QCOM tristate "ASoC support for QCOM platforms" depends on ARCH_QCOM || COMPILE_TEST help Say Y or M if you want to add support to use audio devices in Qualcomm Technologies SOC-based platforms. +if SND_SOC_QCOM + config SND_SOC_LPASS_CPU tristate select REGMAP_MMIO +config SND_SOC_LPASS_HDMI + tristate + select REGMAP_MMIO + config SND_SOC_LPASS_PLATFORM tristate select REGMAP_MMIO @@ -24,9 +30,14 @@ config SND_SOC_LPASS_APQ8016 select SND_SOC_LPASS_CPU select SND_SOC_LPASS_PLATFORM +config SND_SOC_LPASS_SC7180 + tristate + select SND_SOC_LPASS_CPU + select SND_SOC_LPASS_PLATFORM + select SND_SOC_LPASS_HDMI + config SND_SOC_STORM tristate "ASoC I2S support for Storm boards" - depends on SND_SOC_QCOM select SND_SOC_LPASS_IPQ806X select SND_SOC_MAX98357A help @@ -35,7 +46,6 @@ config SND_SOC_STORM config SND_SOC_APQ8016_SBC tristate "SoC Audio support for APQ8016 SBC platforms" - depends on SND_SOC_QCOM select SND_SOC_LPASS_APQ8016 select SND_SOC_QCOM_COMMON help @@ -58,6 +68,9 @@ config SND_SOC_QDSP6_AFE config SND_SOC_QDSP6_AFE_DAI tristate +config SND_SOC_QDSP6_AFE_CLOCKS + tristate + config SND_SOC_QDSP6_ADM tristate @@ -74,10 +87,12 @@ config SND_SOC_QDSP6_ASM_DAI config SND_SOC_QDSP6 tristate "SoC ALSA audio driver for QDSP6" depends on QCOM_APR + depends on COMMON_CLK select SND_SOC_QDSP6_COMMON select SND_SOC_QDSP6_CORE select SND_SOC_QDSP6_AFE select SND_SOC_QDSP6_AFE_DAI + select SND_SOC_QDSP6_AFE_CLOCKS select SND_SOC_QDSP6_ADM select SND_SOC_QDSP6_ROUTING select SND_SOC_QDSP6_ASM @@ -101,6 +116,7 @@ config SND_SOC_MSM8996 config SND_SOC_SDM845 tristate "SoC Machine driver for SDM845 boards" depends on QCOM_APR && I2C && SOUNDWIRE + depends on COMMON_CLK select SND_SOC_QDSP6 select SND_SOC_QCOM_COMMON select SND_SOC_RT5663 @@ -110,3 +126,5 @@ config SND_SOC_SDM845 To add support for audio on Qualcomm Technologies Inc. SDM845 SoC-based systems. Say Y if you want to use audio device on this SoCs. + +endif #SND_SOC_QCOM diff --git a/sound/soc/qcom/Makefile b/sound/soc/qcom/Makefile index 41b2c7a23a4d..0bd90d74e3db 100644 --- a/sound/soc/qcom/Makefile +++ b/sound/soc/qcom/Makefile @@ -1,14 +1,18 @@ # SPDX-License-Identifier: GPL-2.0 # Platform snd-soc-lpass-cpu-objs := lpass-cpu.o +snd-soc-lpass-hdmi-objs := lpass-hdmi.o snd-soc-lpass-platform-objs := lpass-platform.o snd-soc-lpass-ipq806x-objs := lpass-ipq806x.o snd-soc-lpass-apq8016-objs := lpass-apq8016.o +snd-soc-lpass-sc7180-objs := lpass-sc7180.o obj-$(CONFIG_SND_SOC_LPASS_CPU) += snd-soc-lpass-cpu.o +obj-$(CONFIG_SND_SOC_LPASS_HDMI) += snd-soc-lpass-hdmi.o obj-$(CONFIG_SND_SOC_LPASS_PLATFORM) += snd-soc-lpass-platform.o obj-$(CONFIG_SND_SOC_LPASS_IPQ806X) += snd-soc-lpass-ipq806x.o obj-$(CONFIG_SND_SOC_LPASS_APQ8016) += snd-soc-lpass-apq8016.o +obj-$(CONFIG_SND_SOC_LPASS_SC7180) += snd-soc-lpass-sc7180.o # Machine snd-soc-storm-objs := storm.o diff --git a/sound/soc/qcom/common.c b/sound/soc/qcom/common.c index fd69cf8b1f23..54660f126d09 100644 --- a/sound/soc/qcom/common.c +++ b/sound/soc/qcom/common.c @@ -39,6 +39,10 @@ int qcom_snd_parse_of(struct snd_soc_card *card) return ret; } + ret = snd_soc_of_parse_aux_devs(card, "aux-devs"); + if (ret) + return ret; + /* Populate links */ num_links = of_get_child_count(dev->of_node); diff --git a/sound/soc/qcom/lpass-apq8016.c b/sound/soc/qcom/lpass-apq8016.c index b3610d05b651..0aedb3a0a798 100644 --- a/sound/soc/qcom/lpass-apq8016.c +++ b/sound/soc/qcom/lpass-apq8016.c @@ -125,7 +125,7 @@ static struct snd_soc_dai_driver apq8016_lpass_cpu_dai_driver[] = { }; static int apq8016_lpass_alloc_dma_channel(struct lpass_data *drvdata, - int direction) + int direction, unsigned int dai_id) { struct lpass_variant *v = drvdata->variant; int chan = 0; @@ -151,7 +151,7 @@ static int apq8016_lpass_alloc_dma_channel(struct lpass_data *drvdata, return chan; } -static int apq8016_lpass_free_dma_channel(struct lpass_data *drvdata, int chan) +static int apq8016_lpass_free_dma_channel(struct lpass_data *drvdata, int chan, unsigned int dai_id) { clear_bit(chan, &drvdata->dma_ch_bit_map); @@ -161,44 +161,67 @@ static int apq8016_lpass_free_dma_channel(struct lpass_data *drvdata, int chan) static int apq8016_lpass_init(struct platform_device *pdev) { struct lpass_data *drvdata = platform_get_drvdata(pdev); + struct lpass_variant *variant = drvdata->variant; struct device *dev = &pdev->dev; - int ret; + int ret, i; - drvdata->pcnoc_mport_clk = devm_clk_get(dev, "pcnoc-mport-clk"); - if (IS_ERR(drvdata->pcnoc_mport_clk)) { - dev_err(dev, "error getting pcnoc-mport-clk: %ld\n", - PTR_ERR(drvdata->pcnoc_mport_clk)); - return PTR_ERR(drvdata->pcnoc_mport_clk); + + drvdata->clks = devm_kcalloc(dev, variant->num_clks, + sizeof(*drvdata->clks), GFP_KERNEL); + if (!drvdata->clks) + return -ENOMEM; + drvdata->num_clks = variant->num_clks; + + for (i = 0; i < drvdata->num_clks; i++) + drvdata->clks[i].id = variant->clk_name[i]; + + ret = devm_clk_bulk_get(dev, drvdata->num_clks, drvdata->clks); + if (ret) { + dev_err(dev, "Failed to get clocks %d\n", ret); + return ret; } - ret = clk_prepare_enable(drvdata->pcnoc_mport_clk); + ret = clk_bulk_prepare_enable(drvdata->num_clks, drvdata->clks); if (ret) { - dev_err(dev, "Error enabling pcnoc-mport-clk: %d\n", ret); + dev_err(dev, "apq8016 clk_enable failed\n"); return ret; } - drvdata->pcnoc_sway_clk = devm_clk_get(dev, "pcnoc-sway-clk"); - if (IS_ERR(drvdata->pcnoc_sway_clk)) { - dev_err(dev, "error getting pcnoc-sway-clk: %ld\n", - PTR_ERR(drvdata->pcnoc_sway_clk)); - return PTR_ERR(drvdata->pcnoc_sway_clk); + drvdata->ahbix_clk = devm_clk_get(dev, "ahbix-clk"); + if (IS_ERR(drvdata->ahbix_clk)) { + dev_err(dev, "error getting ahbix-clk: %ld\n", + PTR_ERR(drvdata->ahbix_clk)); + ret = PTR_ERR(drvdata->ahbix_clk); + goto err_ahbix_clk; } - ret = clk_prepare_enable(drvdata->pcnoc_sway_clk); + ret = clk_set_rate(drvdata->ahbix_clk, LPASS_AHBIX_CLOCK_FREQUENCY); if (ret) { - dev_err(dev, "Error enabling pcnoc_sway_clk: %d\n", ret); - return ret; + dev_err(dev, "error setting rate on ahbix_clk: %d\n", ret); + goto err_ahbix_clk; + } + dev_dbg(dev, "set ahbix_clk rate to %lu\n", + clk_get_rate(drvdata->ahbix_clk)); + + ret = clk_prepare_enable(drvdata->ahbix_clk); + if (ret) { + dev_err(dev, "error enabling ahbix_clk: %d\n", ret); + goto err_ahbix_clk; } return 0; + +err_ahbix_clk: + clk_bulk_disable_unprepare(drvdata->num_clks, drvdata->clks); + return ret; } static int apq8016_lpass_exit(struct platform_device *pdev) { struct lpass_data *drvdata = platform_get_drvdata(pdev); - clk_disable_unprepare(drvdata->pcnoc_mport_clk); - clk_disable_unprepare(drvdata->pcnoc_sway_clk); + clk_bulk_disable_unprepare(drvdata->num_clks, drvdata->clks); + clk_disable_unprepare(drvdata->ahbix_clk); return 0; } @@ -219,6 +242,35 @@ static struct lpass_variant apq8016_data = { .wrdma_reg_stride = 0x1000, .wrdma_channel_start = 5, .wrdma_channels = 2, + .loopback = REG_FIELD_ID(0x1000, 15, 15, 4, 0x1000), + .spken = REG_FIELD_ID(0x1000, 14, 14, 4, 0x1000), + .spkmode = REG_FIELD_ID(0x1000, 10, 13, 4, 0x1000), + .spkmono = REG_FIELD_ID(0x1000, 9, 9, 4, 0x1000), + .micen = REG_FIELD_ID(0x1000, 8, 8, 4, 0x1000), + .micmode = REG_FIELD_ID(0x1000, 4, 7, 4, 0x1000), + .micmono = REG_FIELD_ID(0x1000, 3, 3, 4, 0x1000), + .wssrc = REG_FIELD_ID(0x1000, 2, 2, 4, 0x1000), + .bitwidth = REG_FIELD_ID(0x1000, 0, 0, 4, 0x1000), + + .rdma_dyncclk = REG_FIELD_ID(0x8400, 12, 12, 2, 0x1000), + .rdma_bursten = REG_FIELD_ID(0x8400, 11, 11, 2, 0x1000), + .rdma_wpscnt = REG_FIELD_ID(0x8400, 8, 10, 2, 0x1000), + .rdma_intf = REG_FIELD_ID(0x8400, 4, 7, 2, 0x1000), + .rdma_fifowm = REG_FIELD_ID(0x8400, 1, 3, 2, 0x1000), + .rdma_enable = REG_FIELD_ID(0x8400, 0, 0, 2, 0x1000), + + .wrdma_dyncclk = REG_FIELD_ID(0xB000, 12, 12, 2, 0x1000), + .wrdma_bursten = REG_FIELD_ID(0xB000, 11, 11, 2, 0x1000), + .wrdma_wpscnt = REG_FIELD_ID(0xB000, 8, 10, 2, 0x1000), + .wrdma_intf = REG_FIELD_ID(0xB000, 4, 7, 2, 0x1000), + .wrdma_fifowm = REG_FIELD_ID(0xB000, 1, 3, 2, 0x1000), + .wrdma_enable = REG_FIELD_ID(0xB000, 0, 0, 2, 0x1000), + + .clk_name = (const char*[]) { + "pcnoc-mport-clk", + "pcnoc-sway-clk", + }, + .num_clks = 2, .dai_driver = apq8016_lpass_cpu_dai_driver, .num_dai = ARRAY_SIZE(apq8016_lpass_cpu_dai_driver), .dai_osr_clk_names = (const char *[]) { diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c index e00a4af29c13..426235a217ec 100644 --- a/sound/soc/qcom/lpass-cpu.c +++ b/sound/soc/qcom/lpass-cpu.c @@ -29,6 +29,32 @@ #define LPASS_CPU_I2S_SD0_1_2_MASK GENMASK(2, 0) #define LPASS_CPU_I2S_SD0_1_2_3_MASK GENMASK(3, 0) +static int lpass_cpu_init_i2sctl_bitfields(struct device *dev, + struct lpaif_i2sctl *i2sctl, struct regmap *map) +{ + struct lpass_data *drvdata = dev_get_drvdata(dev); + struct lpass_variant *v = drvdata->variant; + + i2sctl->loopback = devm_regmap_field_alloc(dev, map, v->loopback); + i2sctl->spken = devm_regmap_field_alloc(dev, map, v->spken); + i2sctl->spkmode = devm_regmap_field_alloc(dev, map, v->spkmode); + i2sctl->spkmono = devm_regmap_field_alloc(dev, map, v->spkmono); + i2sctl->micen = devm_regmap_field_alloc(dev, map, v->micen); + i2sctl->micmode = devm_regmap_field_alloc(dev, map, v->micmode); + i2sctl->micmono = devm_regmap_field_alloc(dev, map, v->micmono); + i2sctl->wssrc = devm_regmap_field_alloc(dev, map, v->wssrc); + i2sctl->bitwidth = devm_regmap_field_alloc(dev, map, v->bitwidth); + + if (IS_ERR(i2sctl->loopback) || IS_ERR(i2sctl->spken) || + IS_ERR(i2sctl->spkmode) || IS_ERR(i2sctl->spkmono) || + IS_ERR(i2sctl->micen) || IS_ERR(i2sctl->micmode) || + IS_ERR(i2sctl->micmono) || IS_ERR(i2sctl->wssrc) || + IS_ERR(i2sctl->bitwidth)) + return -EINVAL; + + return 0; +} + static int lpass_cpu_daiops_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int freq, int dir) { @@ -54,14 +80,12 @@ static int lpass_cpu_daiops_startup(struct snd_pcm_substream *substream, dev_err(dai->dev, "error in enabling mi2s osr clk: %d\n", ret); return ret; } - - ret = clk_prepare_enable(drvdata->mi2s_bit_clk[dai->driver->id]); + ret = clk_prepare(drvdata->mi2s_bit_clk[dai->driver->id]); if (ret) { dev_err(dai->dev, "error in enabling mi2s bit clk: %d\n", ret); clk_disable_unprepare(drvdata->mi2s_osr_clk[dai->driver->id]); return ret; } - return 0; } @@ -70,15 +94,16 @@ static void lpass_cpu_daiops_shutdown(struct snd_pcm_substream *substream, { struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); - clk_disable_unprepare(drvdata->mi2s_bit_clk[dai->driver->id]); - clk_disable_unprepare(drvdata->mi2s_osr_clk[dai->driver->id]); + clk_unprepare(drvdata->mi2s_bit_clk[dai->driver->id]); } static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, 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; snd_pcm_format_t format = params_format(params); unsigned int channels = params_channels(params); unsigned int rate = params_rate(params); @@ -92,28 +117,45 @@ static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream, return bitwidth; } - regval = LPAIF_I2SCTL_LOOPBACK_DISABLE | - LPAIF_I2SCTL_WSSRC_INTERNAL; + ret = regmap_fields_write(i2sctl->loopback, id, + LPAIF_I2SCTL_LOOPBACK_DISABLE); + if (ret) { + dev_err(dai->dev, "error updating loopback field: %d\n", ret); + return ret; + } + + ret = regmap_fields_write(i2sctl->wssrc, id, + LPAIF_I2SCTL_WSSRC_INTERNAL); + if (ret) { + dev_err(dai->dev, "error updating wssrc field: %d\n", ret); + return ret; + } switch (bitwidth) { case 16: - regval |= LPAIF_I2SCTL_BITWIDTH_16; + regval = LPAIF_I2SCTL_BITWIDTH_16; break; case 24: - regval |= LPAIF_I2SCTL_BITWIDTH_24; + regval = LPAIF_I2SCTL_BITWIDTH_24; break; case 32: - regval |= LPAIF_I2SCTL_BITWIDTH_32; + regval = LPAIF_I2SCTL_BITWIDTH_32; break; default: dev_err(dai->dev, "invalid bitwidth given: %d\n", bitwidth); return -EINVAL; } + ret = regmap_fields_write(i2sctl->bitwidth, id, regval); + if (ret) { + dev_err(dai->dev, "error updating bitwidth field: %d\n", ret); + return ret; + } + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - mode = drvdata->mi2s_playback_sd_mode[dai->driver->id]; + mode = drvdata->mi2s_playback_sd_mode[id]; else - mode = drvdata->mi2s_capture_sd_mode[dai->driver->id]; + mode = drvdata->mi2s_capture_sd_mode[id]; if (!mode) { dev_err(dai->dev, "no line is assigned\n"); @@ -175,30 +217,42 @@ static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream, } if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - regval |= LPAIF_I2SCTL_SPKMODE(mode); - + 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) - regval |= LPAIF_I2SCTL_SPKMONO_STEREO; + ret = regmap_fields_write(i2sctl->spkmono, id, + LPAIF_I2SCTL_SPKMONO_STEREO); else - regval |= LPAIF_I2SCTL_SPKMONO_MONO; + ret = regmap_fields_write(i2sctl->spkmono, id, + LPAIF_I2SCTL_SPKMONO_MONO); } else { - regval |= LPAIF_I2SCTL_MICMODE(mode); - + 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) - regval |= LPAIF_I2SCTL_MICMONO_STEREO; + ret = regmap_fields_write(i2sctl->micmono, id, + LPAIF_I2SCTL_MICMONO_STEREO); else - regval |= LPAIF_I2SCTL_MICMONO_MONO; + ret = regmap_fields_write(i2sctl->micmono, id, + LPAIF_I2SCTL_MICMONO_MONO); } - ret = regmap_write(drvdata->lpaif_map, - LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id), - regval); if (ret) { - dev_err(dai->dev, "error writing to i2sctl reg: %d\n", ret); + dev_err(dai->dev, "error writing to i2sctl channels mode: %d\n", + ret); return ret; } - ret = clk_set_rate(drvdata->mi2s_bit_clk[dai->driver->id], + 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", @@ -209,90 +263,69 @@ static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream, return 0; } -static int lpass_cpu_daiops_hw_free(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); - int ret; - - ret = regmap_write(drvdata->lpaif_map, - LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id), - 0); - if (ret) - dev_err(dai->dev, "error writing to i2sctl reg: %d\n", ret); - - return ret; -} - -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); - int ret; - unsigned int val, mask; - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - val = LPAIF_I2SCTL_SPKEN_ENABLE; - mask = LPAIF_I2SCTL_SPKEN_MASK; - } else { - val = LPAIF_I2SCTL_MICEN_ENABLE; - mask = LPAIF_I2SCTL_MICEN_MASK; - } - - ret = regmap_update_bits(drvdata->lpaif_map, - LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id), - mask, val); - if (ret) - dev_err(dai->dev, "error writing to i2sctl reg: %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, mask; + 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: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - val = LPAIF_I2SCTL_SPKEN_ENABLE; - mask = LPAIF_I2SCTL_SPKEN_MASK; + ret = regmap_fields_write(i2sctl->spken, id, + LPAIF_I2SCTL_SPKEN_ENABLE); } else { - val = LPAIF_I2SCTL_MICEN_ENABLE; - mask = LPAIF_I2SCTL_MICEN_MASK; + ret = regmap_fields_write(i2sctl->micen, id, + LPAIF_I2SCTL_MICEN_ENABLE); } - - ret = regmap_update_bits(drvdata->lpaif_map, - LPAIF_I2SCTL_REG(drvdata->variant, - dai->driver->id), - mask, val); if (ret) dev_err(dai->dev, "error writing to i2sctl reg: %d\n", 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; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - val = LPAIF_I2SCTL_SPKEN_DISABLE; - mask = LPAIF_I2SCTL_SPKEN_MASK; + ret = regmap_fields_write(i2sctl->spken, id, + LPAIF_I2SCTL_SPKEN_DISABLE); } else { - val = LPAIF_I2SCTL_MICEN_DISABLE; - mask = LPAIF_I2SCTL_MICEN_MASK; + ret = regmap_fields_write(i2sctl->micen, id, + LPAIF_I2SCTL_MICEN_DISABLE); } - - ret = regmap_update_bits(drvdata->lpaif_map, - LPAIF_I2SCTL_REG(drvdata->variant, - dai->driver->id), - mask, val); if (ret) dev_err(dai->dev, "error writing to i2sctl reg: %d\n", ret); + 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; } @@ -304,8 +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, - .hw_free = lpass_cpu_daiops_hw_free, - .prepare = lpass_cpu_daiops_prepare, .trigger = lpass_cpu_daiops_trigger, }; EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_dai_ops); @@ -423,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; @@ -448,6 +483,206 @@ static struct regmap_config lpass_cpu_regmap_config = { .cache_type = REGCACHE_FLAT, }; +static int lpass_hdmi_init_bitfields(struct device *dev, struct regmap *map) +{ + struct lpass_data *drvdata = dev_get_drvdata(dev); + struct lpass_variant *v = drvdata->variant; + unsigned int i; + struct lpass_hdmi_tx_ctl *tx_ctl; + struct regmap_field *legacy_en; + struct lpass_vbit_ctrl *vbit_ctl; + struct regmap_field *tx_parity; + struct lpass_dp_metadata_ctl *meta_ctl; + struct lpass_sstream_ctl *sstream_ctl; + struct regmap_field *ch_msb; + struct regmap_field *ch_lsb; + struct lpass_hdmitx_dmactl *tx_dmactl; + int rval; + + tx_ctl = devm_kzalloc(dev, sizeof(*tx_ctl), GFP_KERNEL); + if (!tx_ctl) + return -ENOMEM; + + QCOM_REGMAP_FIELD_ALLOC(dev, map, v->soft_reset, tx_ctl->soft_reset); + QCOM_REGMAP_FIELD_ALLOC(dev, map, v->force_reset, tx_ctl->force_reset); + drvdata->tx_ctl = tx_ctl; + + QCOM_REGMAP_FIELD_ALLOC(dev, map, v->legacy_en, legacy_en); + drvdata->hdmitx_legacy_en = legacy_en; + + vbit_ctl = devm_kzalloc(dev, sizeof(*vbit_ctl), GFP_KERNEL); + if (!vbit_ctl) + return -ENOMEM; + + QCOM_REGMAP_FIELD_ALLOC(dev, map, v->replace_vbit, vbit_ctl->replace_vbit); + QCOM_REGMAP_FIELD_ALLOC(dev, map, v->vbit_stream, vbit_ctl->vbit_stream); + drvdata->vbit_ctl = vbit_ctl; + + + QCOM_REGMAP_FIELD_ALLOC(dev, map, v->calc_en, tx_parity); + drvdata->hdmitx_parity_calc_en = tx_parity; + + meta_ctl = devm_kzalloc(dev, sizeof(*meta_ctl), GFP_KERNEL); + if (!meta_ctl) + return -ENOMEM; + + rval = devm_regmap_field_bulk_alloc(dev, map, &meta_ctl->mute, &v->mute, 7); + if (rval) + return rval; + drvdata->meta_ctl = meta_ctl; + + sstream_ctl = devm_kzalloc(dev, sizeof(*sstream_ctl), GFP_KERNEL); + if (!sstream_ctl) + return -ENOMEM; + + rval = devm_regmap_field_bulk_alloc(dev, map, &sstream_ctl->sstream_en, &v->sstream_en, 9); + if (rval) + return rval; + + drvdata->sstream_ctl = sstream_ctl; + + for (i = 0; i < LPASS_MAX_HDMI_DMA_CHANNELS; i++) { + QCOM_REGMAP_FIELD_ALLOC(dev, map, v->msb_bits, ch_msb); + drvdata->hdmitx_ch_msb[i] = ch_msb; + + QCOM_REGMAP_FIELD_ALLOC(dev, map, v->lsb_bits, ch_lsb); + drvdata->hdmitx_ch_lsb[i] = ch_lsb; + + tx_dmactl = devm_kzalloc(dev, sizeof(*tx_dmactl), GFP_KERNEL); + if (!tx_dmactl) + return -ENOMEM; + + QCOM_REGMAP_FIELD_ALLOC(dev, map, v->use_hw_chs, tx_dmactl->use_hw_chs); + QCOM_REGMAP_FIELD_ALLOC(dev, map, v->use_hw_usr, tx_dmactl->use_hw_usr); + QCOM_REGMAP_FIELD_ALLOC(dev, map, v->hw_chs_sel, tx_dmactl->hw_chs_sel); + QCOM_REGMAP_FIELD_ALLOC(dev, map, v->hw_usr_sel, tx_dmactl->hw_usr_sel); + drvdata->hdmi_tx_dmactl[i] = tx_dmactl; + } + return 0; +} + +static bool lpass_hdmi_regmap_writeable(struct device *dev, unsigned int reg) +{ + struct lpass_data *drvdata = dev_get_drvdata(dev); + struct lpass_variant *v = drvdata->variant; + int i; + + if (reg == LPASS_HDMI_TX_CTL_ADDR(v)) + return true; + if (reg == LPASS_HDMI_TX_LEGACY_ADDR(v)) + return true; + if (reg == LPASS_HDMI_TX_VBIT_CTL_ADDR(v)) + return true; + if (reg == LPASS_HDMI_TX_PARITY_ADDR(v)) + return true; + if (reg == LPASS_HDMI_TX_DP_ADDR(v)) + return true; + if (reg == LPASS_HDMI_TX_SSTREAM_ADDR(v)) + return true; + if (reg == LPASS_HDMITX_APP_IRQEN_REG(v)) + return true; + if (reg == LPASS_HDMITX_APP_IRQCLEAR_REG(v)) + return true; + + for (i = 0; i < v->hdmi_rdma_channels; i++) { + if (reg == LPASS_HDMI_TX_CH_LSB_ADDR(v, i)) + return true; + if (reg == LPASS_HDMI_TX_CH_MSB_ADDR(v, i)) + return true; + if (reg == LPASS_HDMI_TX_DMA_ADDR(v, i)) + return true; + } + + for (i = 0; i < v->rdma_channels; ++i) { + if (reg == LPAIF_HDMI_RDMACTL_REG(v, i)) + return true; + if (reg == LPAIF_HDMI_RDMABASE_REG(v, i)) + return true; + if (reg == LPAIF_HDMI_RDMABUFF_REG(v, i)) + return true; + if (reg == LPAIF_HDMI_RDMAPER_REG(v, i)) + return true; + } + return false; +} + +static bool lpass_hdmi_regmap_readable(struct device *dev, unsigned int reg) +{ + struct lpass_data *drvdata = dev_get_drvdata(dev); + struct lpass_variant *v = drvdata->variant; + int i; + + if (reg == LPASS_HDMI_TX_CTL_ADDR(v)) + return true; + if (reg == LPASS_HDMI_TX_LEGACY_ADDR(v)) + return true; + if (reg == LPASS_HDMI_TX_VBIT_CTL_ADDR(v)) + return true; + + for (i = 0; i < v->hdmi_rdma_channels; i++) { + if (reg == LPASS_HDMI_TX_CH_LSB_ADDR(v, i)) + return true; + if (reg == LPASS_HDMI_TX_CH_MSB_ADDR(v, i)) + return true; + if (reg == LPASS_HDMI_TX_DMA_ADDR(v, i)) + return true; + } + + if (reg == LPASS_HDMI_TX_PARITY_ADDR(v)) + return true; + if (reg == LPASS_HDMI_TX_DP_ADDR(v)) + return true; + if (reg == LPASS_HDMI_TX_SSTREAM_ADDR(v)) + return true; + if (reg == LPASS_HDMITX_APP_IRQEN_REG(v)) + return true; + if (reg == LPASS_HDMITX_APP_IRQSTAT_REG(v)) + return true; + + for (i = 0; i < v->rdma_channels; ++i) { + if (reg == LPAIF_HDMI_RDMACTL_REG(v, i)) + return true; + if (reg == LPAIF_HDMI_RDMABASE_REG(v, i)) + return true; + if (reg == LPAIF_HDMI_RDMABUFF_REG(v, i)) + return true; + if (reg == LPAIF_HDMI_RDMAPER_REG(v, i)) + return true; + if (reg == LPAIF_HDMI_RDMACURR_REG(v, i)) + return true; + } + + return false; +} + +static bool lpass_hdmi_regmap_volatile(struct device *dev, unsigned int reg) +{ + struct lpass_data *drvdata = dev_get_drvdata(dev); + struct lpass_variant *v = drvdata->variant; + int i; + + if (reg == LPASS_HDMITX_APP_IRQSTAT_REG(v)) + return true; + if (reg == LPASS_HDMI_TX_LEGACY_ADDR(v)) + return true; + + for (i = 0; i < v->rdma_channels; ++i) { + if (reg == LPAIF_HDMI_RDMACURR_REG(v, i)) + return true; + } + return false; +} + +struct regmap_config lpass_hdmi_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .writeable_reg = lpass_hdmi_regmap_writeable, + .readable_reg = lpass_hdmi_regmap_readable, + .volatile_reg = lpass_hdmi_regmap_volatile, + .cache_type = REGCACHE_FLAT, +}; + static unsigned int of_lpass_cpu_parse_sd_lines(struct device *dev, struct device_node *node, const char *name) @@ -505,13 +740,17 @@ static void of_lpass_cpu_parse_dai_data(struct device *dev, dev_err(dev, "valid dai id not found: %d\n", ret); continue; } - - 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, + if (id == LPASS_DP_RX) { + data->hdmi_port_enable = 1; + dev_err(dev, "HDMI Port is enabled: %d\n", id); + } 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"); + } } } @@ -566,11 +805,40 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev) return PTR_ERR(drvdata->lpaif_map); } - if (variant->init) - variant->init(pdev); + if (drvdata->hdmi_port_enable) { + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpass-hdmiif"); + + drvdata->hdmiif = devm_ioremap_resource(dev, res); + if (IS_ERR((void const __force *)drvdata->hdmiif)) { + dev_err(dev, "error mapping reg resource: %ld\n", + PTR_ERR((void const __force *)drvdata->hdmiif)); + return PTR_ERR((void const __force *)drvdata->hdmiif); + } + + lpass_hdmi_regmap_config.max_register = LPAIF_HDMI_RDMAPER_REG(variant, + variant->hdmi_rdma_channels); + drvdata->hdmiif_map = devm_regmap_init_mmio(dev, drvdata->hdmiif, + &lpass_hdmi_regmap_config); + if (IS_ERR(drvdata->hdmiif_map)) { + dev_err(dev, "error initializing regmap: %ld\n", + PTR_ERR(drvdata->hdmiif_map)); + return PTR_ERR(drvdata->hdmiif_map); + } + } + + if (variant->init) { + ret = variant->init(pdev); + if (ret) { + dev_err(dev, "error initializing variant: %d\n", ret); + return ret; + } + } for (i = 0; i < variant->num_dai; i++) { dai_id = variant->dai_driver[i].id; + if (dai_id == LPASS_DP_RX) + continue; + drvdata->mi2s_osr_clk[dai_id] = devm_clk_get(dev, variant->dai_osr_clk_names[i]); if (IS_ERR(drvdata->mi2s_osr_clk[dai_id])) { @@ -592,48 +860,44 @@ 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; } - drvdata->ahbix_clk = devm_clk_get(dev, "ahbix-clk"); - if (IS_ERR(drvdata->ahbix_clk)) { - dev_err(dev, "error getting ahbix-clk: %ld\n", - PTR_ERR(drvdata->ahbix_clk)); - return PTR_ERR(drvdata->ahbix_clk); - } + /* Allocation for i2sctl regmap fields */ + drvdata->i2sctl = devm_kzalloc(&pdev->dev, sizeof(struct lpaif_i2sctl), + GFP_KERNEL); - ret = clk_set_rate(drvdata->ahbix_clk, LPASS_AHBIX_CLOCK_FREQUENCY); + /* Initialize bitfields for dai I2SCTL register */ + ret = lpass_cpu_init_i2sctl_bitfields(dev, drvdata->i2sctl, + drvdata->lpaif_map); if (ret) { - dev_err(dev, "error setting rate on ahbix_clk: %d\n", ret); + dev_err(dev, "error init i2sctl field: %d\n", ret); return ret; } - dev_dbg(dev, "set ahbix_clk rate to %lu\n", - clk_get_rate(drvdata->ahbix_clk)); - ret = clk_prepare_enable(drvdata->ahbix_clk); - if (ret) { - dev_err(dev, "error enabling ahbix_clk: %d\n", ret); - return ret; + if (drvdata->hdmi_port_enable) { + ret = lpass_hdmi_init_bitfields(dev, drvdata->hdmiif_map); + if (ret) { + dev_err(dev, "%s error hdmi init failed\n", __func__); + return ret; + } } - ret = devm_snd_soc_register_component(dev, &lpass_cpu_comp_driver, variant->dai_driver, variant->num_dai); if (ret) { dev_err(dev, "error registering cpu driver: %d\n", ret); - goto err_clk; + goto err; } ret = asoc_qcom_lpass_platform_register(pdev); if (ret) { dev_err(dev, "error registering platform driver: %d\n", ret); - goto err_clk; + goto err; } - return 0; - -err_clk: - clk_disable_unprepare(drvdata->ahbix_clk); +err: return ret; } EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_platform_probe); @@ -645,7 +909,6 @@ int asoc_qcom_lpass_cpu_platform_remove(struct platform_device *pdev) if (drvdata->variant->exit) drvdata->variant->exit(pdev); - clk_disable_unprepare(drvdata->ahbix_clk); return 0; } diff --git a/sound/soc/qcom/lpass-hdmi.c b/sound/soc/qcom/lpass-hdmi.c new file mode 100644 index 000000000000..172952d3a5d6 --- /dev/null +++ b/sound/soc/qcom/lpass-hdmi.c @@ -0,0 +1,258 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2020 The Linux Foundation. All rights reserved. + * + * lpass-hdmi.c -- ALSA SoC HDMI-CPU DAI driver for QTi LPASS HDMI + */ + + +#include <linux/kernel.h> +#include <linux/module.h> +#include <sound/pcm_params.h> +#include <linux/regmap.h> +#include <sound/soc.h> +#include <sound/soc-dai.h> +#include <dt-bindings/sound/sc7180-lpass.h> +#include "lpass-lpaif-reg.h" +#include "lpass.h" + +static int lpass_hdmi_daiops_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); + snd_pcm_format_t format = params_format(params); + unsigned int rate = params_rate(params); + unsigned int channels = params_channels(params); + unsigned int ret; + unsigned int bitwidth; + unsigned int word_length; + unsigned int ch_sts_buf0; + unsigned int ch_sts_buf1; + unsigned int data_format; + unsigned int sampling_freq; + unsigned int ch = 0; + struct lpass_dp_metadata_ctl *meta_ctl = drvdata->meta_ctl; + struct lpass_sstream_ctl *sstream_ctl = drvdata->sstream_ctl; + + bitwidth = snd_pcm_format_width(format); + if (bitwidth < 0) { + dev_err(dai->dev, "%s invalid bit width given : %d\n", + __func__, bitwidth); + return bitwidth; + } + + switch (bitwidth) { + case 16: + word_length = LPASS_DP_AUDIO_BITWIDTH16; + break; + case 24: + word_length = LPASS_DP_AUDIO_BITWIDTH24; + break; + default: + dev_err(dai->dev, "%s invalid bit width given : %d\n", + __func__, bitwidth); + return -EINVAL; + } + + switch (rate) { + case 32000: + sampling_freq = LPASS_SAMPLING_FREQ32; + break; + case 44100: + sampling_freq = LPASS_SAMPLING_FREQ44; + break; + case 48000: + sampling_freq = LPASS_SAMPLING_FREQ48; + break; + default: + dev_err(dai->dev, "%s invalid bit width given : %d\n", + __func__, bitwidth); + return -EINVAL; + } + data_format = LPASS_DATA_FORMAT_LINEAR; + ch_sts_buf0 = (((data_format << LPASS_DATA_FORMAT_SHIFT) & LPASS_DATA_FORMAT_MASK) + | ((sampling_freq << LPASS_FREQ_BIT_SHIFT) & LPASS_FREQ_BIT_MASK)); + ch_sts_buf1 = (word_length) & LPASS_WORDLENGTH_MASK; + + ret = regmap_field_write(drvdata->tx_ctl->soft_reset, LPASS_TX_CTL_RESET); + if (ret) + return ret; + + ret = regmap_field_write(drvdata->tx_ctl->soft_reset, LPASS_TX_CTL_CLEAR); + if (ret) + return ret; + + ret = regmap_field_write(drvdata->hdmitx_legacy_en, LPASS_HDMITX_LEGACY_DISABLE); + if (ret) + return ret; + + ret = regmap_field_write(drvdata->hdmitx_parity_calc_en, HDMITX_PARITY_CALC_EN); + if (ret) + return ret; + + ret = regmap_field_write(drvdata->vbit_ctl->replace_vbit, REPLACE_VBIT); + if (ret) + return ret; + + ret = regmap_field_write(drvdata->vbit_ctl->vbit_stream, LINEAR_PCM_DATA); + if (ret) + return ret; + + ret = regmap_field_write(drvdata->hdmitx_ch_msb[0], ch_sts_buf1); + if (ret) + return ret; + + ret = regmap_field_write(drvdata->hdmitx_ch_lsb[0], ch_sts_buf0); + if (ret) + return ret; + + ret = regmap_field_write(drvdata->hdmi_tx_dmactl[0]->use_hw_chs, HW_MODE); + if (ret) + return ret; + + ret = regmap_field_write(drvdata->hdmi_tx_dmactl[0]->hw_chs_sel, SW_MODE); + if (ret) + return ret; + + ret = regmap_field_write(drvdata->hdmi_tx_dmactl[0]->use_hw_usr, HW_MODE); + if (ret) + return ret; + + ret = regmap_field_write(drvdata->hdmi_tx_dmactl[0]->hw_usr_sel, SW_MODE); + if (ret) + return ret; + + ret = regmap_field_write(meta_ctl->mute, LPASS_MUTE_ENABLE); + if (ret) + return ret; + + ret = regmap_field_write(meta_ctl->as_sdp_cc, channels - 1); + if (ret) + return ret; + + ret = regmap_field_write(meta_ctl->as_sdp_ct, LPASS_META_DEFAULT_VAL); + if (ret) + return ret; + + ret = regmap_field_write(meta_ctl->aif_db4, LPASS_META_DEFAULT_VAL); + if (ret) + return ret; + + ret = regmap_field_write(meta_ctl->frequency, sampling_freq); + if (ret) + return ret; + + ret = regmap_field_write(meta_ctl->mst_index, LPASS_META_DEFAULT_VAL); + if (ret) + return ret; + + ret = regmap_field_write(meta_ctl->dptx_index, LPASS_META_DEFAULT_VAL); + if (ret) + return ret; + + ret = regmap_field_write(sstream_ctl->sstream_en, LPASS_SSTREAM_DISABLE); + if (ret) + return ret; + + ret = regmap_field_write(sstream_ctl->dma_sel, ch); + if (ret) + return ret; + + ret = regmap_field_write(sstream_ctl->auto_bbit_en, LPASS_SSTREAM_DEFAULT_ENABLE); + if (ret) + return ret; + + ret = regmap_field_write(sstream_ctl->layout, LPASS_SSTREAM_DEFAULT_DISABLE); + if (ret) + return ret; + + ret = regmap_field_write(sstream_ctl->layout_sp, LPASS_LAYOUT_SP_DEFAULT); + if (ret) + return ret; + + ret = regmap_field_write(sstream_ctl->dp_audio, LPASS_SSTREAM_DEFAULT_ENABLE); + if (ret) + return ret; + + ret = regmap_field_write(sstream_ctl->set_sp_on_en, LPASS_SSTREAM_DEFAULT_ENABLE); + if (ret) + return ret; + + ret = regmap_field_write(sstream_ctl->dp_sp_b_hw_en, LPASS_SSTREAM_DEFAULT_ENABLE); + if (ret) + return ret; + + ret = regmap_field_write(sstream_ctl->dp_staffing_en, LPASS_SSTREAM_DEFAULT_ENABLE); + if (ret) + return ret; + + return ret; +} + +static int lpass_hdmi_daiops_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + int ret; + struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); + + ret = regmap_field_write(drvdata->sstream_ctl->sstream_en, LPASS_SSTREAM_ENABLE); + if (ret) + return ret; + + ret = regmap_field_write(drvdata->meta_ctl->mute, LPASS_MUTE_DISABLE); + if (ret) + return ret; + + return ret; +} + +static int lpass_hdmi_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 lpass_dp_metadata_ctl *meta_ctl = drvdata->meta_ctl; + struct lpass_sstream_ctl *sstream_ctl = drvdata->sstream_ctl; + int ret = -EINVAL; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + ret = regmap_field_write(sstream_ctl->sstream_en, LPASS_SSTREAM_ENABLE); + if (ret) + return ret; + + ret = regmap_field_write(meta_ctl->mute, LPASS_MUTE_DISABLE); + if (ret) + return ret; + + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + ret = regmap_field_write(sstream_ctl->sstream_en, LPASS_SSTREAM_DISABLE); + if (ret) + return ret; + + ret = regmap_field_write(meta_ctl->mute, LPASS_MUTE_ENABLE); + if (ret) + return ret; + + ret = regmap_field_write(sstream_ctl->dp_audio, 0); + if (ret) + return ret; + + break; + } + return ret; +} + +const struct snd_soc_dai_ops asoc_qcom_lpass_hdmi_dai_ops = { + .hw_params = lpass_hdmi_daiops_hw_params, + .prepare = lpass_hdmi_daiops_prepare, + .trigger = lpass_hdmi_daiops_trigger, +}; +EXPORT_SYMBOL_GPL(asoc_qcom_lpass_hdmi_dai_ops); + +MODULE_DESCRIPTION("QTi LPASS HDMI Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/qcom/lpass-hdmi.h b/sound/soc/qcom/lpass-hdmi.h new file mode 100644 index 000000000000..ee74d783027a --- /dev/null +++ b/sound/soc/qcom/lpass-hdmi.h @@ -0,0 +1,102 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2020 The Linux Foundation. All rights reserved. + * + * lpass_hdmi.h - Definitions for the QTi LPASS HDMI + */ + +#ifndef __LPASS_HDMI_H__ +#define __LPASS_HDMI_H__ + +#include <linux/regmap.h> + +#define LPASS_HDMITX_LEGACY_DISABLE 0x0 +#define LPASS_HDMITX_LEGACY_ENABLE 0x1 +#define LPASS_DP_AUDIO_BITWIDTH16 0x0 +#define LPASS_DP_AUDIO_BITWIDTH24 0xb +#define LPASS_DATA_FORMAT_SHIFT 0x1 +#define LPASS_FREQ_BIT_SHIFT 24 +#define LPASS_DATA_FORMAT_LINEAR 0x0 +#define LPASS_DATA_FORMAT_NON_LINEAR 0x1 +#define LPASS_SAMPLING_FREQ32 0x3 +#define LPASS_SAMPLING_FREQ44 0x0 +#define LPASS_SAMPLING_FREQ48 0x2 +#define LPASS_TX_CTL_RESET 0x1 +#define LPASS_TX_CTL_CLEAR 0x0 +#define LPASS_SSTREAM_ENABLE 1 +#define LPASS_SSTREAM_DISABLE 0 +#define LPASS_LAYOUT_SP_DEFAULT 0xf +#define LPASS_SSTREAM_DEFAULT_ENABLE 1 +#define LPASS_SSTREAM_DEFAULT_DISABLE 0 +#define LPASS_MUTE_ENABLE 1 +#define LPASS_MUTE_DISABLE 0 +#define LPASS_META_DEFAULT_VAL 0 +#define HW_MODE 1 +#define SW_MODE 0 +#define LEGACY_LPASS_LPAIF 1 +#define LEGACY_LPASS_HDMI 0 +#define REPLACE_VBIT 0x1 +#define LINEAR_PCM_DATA 0x0 +#define NON_LINEAR_PCM_DATA 0x1 +#define HDMITX_PARITY_CALC_EN 0x1 +#define HDMITX_PARITY_CALC_DIS 0x0 +#define LPASS_DATA_FORMAT_MASK GENMASK(1, 1) +#define LPASS_WORDLENGTH_MASK GENMASK(3, 0) +#define LPASS_FREQ_BIT_MASK GENMASK(27, 24) + +#define LPASS_HDMI_TX_CTL_ADDR(v) (v->hdmi_tx_ctl_addr) +#define LPASS_HDMI_TX_LEGACY_ADDR(v) (v->hdmi_legacy_addr) +#define LPASS_HDMI_TX_VBIT_CTL_ADDR(v) (v->hdmi_vbit_addr) +#define LPASS_HDMI_TX_PARITY_ADDR(v) (v->hdmi_parity_addr) +#define LPASS_HDMI_TX_DP_ADDR(v) (v->hdmi_DP_addr) +#define LPASS_HDMI_TX_SSTREAM_ADDR(v) (v->hdmi_sstream_addr) + +#define LPASS_HDMI_TX_CH_LSB_ADDR(v, port) \ + (v->hdmi_ch_lsb_addr + v->ch_stride * (port)) +#define LPASS_HDMI_TX_CH_MSB_ADDR(v, port) \ + (v->hdmi_ch_msb_addr + v->ch_stride * (port)) +#define LPASS_HDMI_TX_DMA_ADDR(v, port) \ + (v->hdmi_dmactl_addr + v->hdmi_dma_stride * (port)) + +struct lpass_sstream_ctl { + struct regmap_field *sstream_en; + struct regmap_field *dma_sel; + struct regmap_field *auto_bbit_en; + struct regmap_field *layout; + struct regmap_field *layout_sp; + struct regmap_field *set_sp_on_en; + struct regmap_field *dp_audio; + struct regmap_field *dp_staffing_en; + struct regmap_field *dp_sp_b_hw_en; +}; + +struct lpass_dp_metadata_ctl { + struct regmap_field *mute; + struct regmap_field *as_sdp_cc; + struct regmap_field *as_sdp_ct; + struct regmap_field *aif_db4; + struct regmap_field *frequency; + struct regmap_field *mst_index; + struct regmap_field *dptx_index; +}; + +struct lpass_hdmi_tx_ctl { + struct regmap_field *soft_reset; + struct regmap_field *force_reset; +}; + +struct lpass_hdmitx_dmactl { + struct regmap_field *use_hw_chs; + struct regmap_field *use_hw_usr; + struct regmap_field *hw_chs_sel; + struct regmap_field *hw_usr_sel; +}; + +struct lpass_vbit_ctrl { + struct regmap_field *replace_vbit; + struct regmap_field *vbit_stream; +}; + +extern const struct snd_soc_dai_ops asoc_qcom_lpass_hdmi_dai_ops; + +#endif /* __LPASS_HDMI_H__ */ diff --git a/sound/soc/qcom/lpass-ipq806x.c b/sound/soc/qcom/lpass-ipq806x.c index 1987605482f7..832a9161484e 100644 --- a/sound/soc/qcom/lpass-ipq806x.c +++ b/sound/soc/qcom/lpass-ipq806x.c @@ -55,7 +55,48 @@ static struct snd_soc_dai_driver ipq806x_lpass_cpu_dai_driver = { .ops = &asoc_qcom_lpass_cpu_dai_ops, }; -static int ipq806x_lpass_alloc_dma_channel(struct lpass_data *drvdata, int dir) +static int ipq806x_lpass_init(struct platform_device *pdev) +{ + struct lpass_data *drvdata = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + int ret; + + drvdata->ahbix_clk = devm_clk_get(dev, "ahbix-clk"); + if (IS_ERR(drvdata->ahbix_clk)) { + dev_err(dev, "error getting ahbix-clk: %ld\n", + PTR_ERR(drvdata->ahbix_clk)); + ret = PTR_ERR(drvdata->ahbix_clk); + goto err_ahbix_clk; + } + + ret = clk_set_rate(drvdata->ahbix_clk, LPASS_AHBIX_CLOCK_FREQUENCY); + if (ret) { + dev_err(dev, "error setting rate on ahbix_clk: %d\n", ret); + goto err_ahbix_clk; + } + dev_dbg(dev, "set ahbix_clk rate to %lu\n", + clk_get_rate(drvdata->ahbix_clk)); + + ret = clk_prepare_enable(drvdata->ahbix_clk); + if (ret) { + dev_err(dev, "error enabling ahbix_clk: %d\n", ret); + goto err_ahbix_clk; + } + +err_ahbix_clk: + return ret; +} + +static int ipq806x_lpass_exit(struct platform_device *pdev) +{ + struct lpass_data *drvdata = platform_get_drvdata(pdev); + + clk_disable_unprepare(drvdata->ahbix_clk); + + return 0; +} + +static int ipq806x_lpass_alloc_dma_channel(struct lpass_data *drvdata, int dir, unsigned int dai_id) { if (dir == SNDRV_PCM_STREAM_PLAYBACK) return IPQ806X_LPAIF_RDMA_CHAN_MI2S; @@ -63,7 +104,7 @@ static int ipq806x_lpass_alloc_dma_channel(struct lpass_data *drvdata, int dir) return -EINVAL; } -static int ipq806x_lpass_free_dma_channel(struct lpass_data *drvdata, int chan) +static int ipq806x_lpass_free_dma_channel(struct lpass_data *drvdata, int chan, unsigned int dai_id) { return 0; } @@ -82,6 +123,30 @@ static struct lpass_variant ipq806x_data = { .wrdma_reg_stride = 0x1000, .wrdma_channel_start = 5, .wrdma_channels = 4, + .loopback = REG_FIELD_ID(0x0010, 15, 15, 5, 0x4), + .spken = REG_FIELD_ID(0x0010, 14, 14, 5, 0x4), + .spkmode = REG_FIELD_ID(0x0010, 10, 13, 5, 0x4), + .spkmono = REG_FIELD_ID(0x0010, 9, 9, 5, 0x4), + .micen = REG_FIELD_ID(0x0010, 8, 8, 5, 0x4), + .micmode = REG_FIELD_ID(0x0010, 4, 7, 5, 0x4), + .micmono = REG_FIELD_ID(0x0010, 3, 3, 5, 0x4), + .wssrc = REG_FIELD_ID(0x0010, 2, 2, 5, 0x4), + .bitwidth = REG_FIELD_ID(0x0010, 0, 0, 5, 0x4), + + .rdma_dyncclk = REG_FIELD_ID(0x6000, 12, 12, 4, 0x1000), + .rdma_bursten = REG_FIELD_ID(0x6000, 11, 11, 4, 0x1000), + .rdma_wpscnt = REG_FIELD_ID(0x6000, 8, 10, 4, 0x1000), + .rdma_intf = REG_FIELD_ID(0x6000, 4, 7, 4, 0x1000), + .rdma_fifowm = REG_FIELD_ID(0x6000, 1, 3, 4, 0x1000), + .rdma_enable = REG_FIELD_ID(0x6000, 0, 0, 4, 0x1000), + + .wrdma_dyncclk = REG_FIELD_ID(0xB000, 12, 12, 4, 0x1000), + .wrdma_bursten = REG_FIELD_ID(0xB000, 11, 11, 4, 0x1000), + .wrdma_wpscnt = REG_FIELD_ID(0xB000, 8, 10, 4, 0x1000), + .wrdma_intf = REG_FIELD_ID(0xB000, 4, 7, 4, 0x1000), + .wrdma_fifowm = REG_FIELD_ID(0xB000, 1, 3, 4, 0x1000), + .wrdma_enable = REG_FIELD_ID(0xB000, 0, 0, 4, 0x1000), + .dai_driver = &ipq806x_lpass_cpu_dai_driver, .num_dai = 1, .dai_osr_clk_names = (const char *[]) { @@ -90,6 +155,8 @@ static struct lpass_variant ipq806x_data = { .dai_bit_clk_names = (const char *[]) { "mi2s-bit-clk", }, + .init = ipq806x_lpass_init, + .exit = ipq806x_lpass_exit, .alloc_dma_channel = ipq806x_lpass_alloc_dma_channel, .free_dma_channel = ipq806x_lpass_free_dma_channel, }; diff --git a/sound/soc/qcom/lpass-lpaif-reg.h b/sound/soc/qcom/lpass-lpaif-reg.h index 72a3e2f69572..405542832e99 100644 --- a/sound/soc/qcom/lpass-lpaif-reg.h +++ b/sound/soc/qcom/lpass-lpaif-reg.h @@ -12,15 +12,12 @@ (v->i2sctrl_reg_base + (addr) + v->i2sctrl_reg_stride * (port)) #define LPAIF_I2SCTL_REG(v, port) LPAIF_I2SCTL_REG_ADDR(v, 0x0, (port)) -#define LPAIF_I2SCTL_LOOPBACK_MASK 0x8000 -#define LPAIF_I2SCTL_LOOPBACK_SHIFT 15 -#define LPAIF_I2SCTL_LOOPBACK_DISABLE (0 << LPAIF_I2SCTL_LOOPBACK_SHIFT) -#define LPAIF_I2SCTL_LOOPBACK_ENABLE (1 << LPAIF_I2SCTL_LOOPBACK_SHIFT) -#define LPAIF_I2SCTL_SPKEN_MASK 0x4000 -#define LPAIF_I2SCTL_SPKEN_SHIFT 14 -#define LPAIF_I2SCTL_SPKEN_DISABLE (0 << LPAIF_I2SCTL_SPKEN_SHIFT) -#define LPAIF_I2SCTL_SPKEN_ENABLE (1 << LPAIF_I2SCTL_SPKEN_SHIFT) +#define LPAIF_I2SCTL_LOOPBACK_DISABLE 0 +#define LPAIF_I2SCTL_LOOPBACK_ENABLE 1 + +#define LPAIF_I2SCTL_SPKEN_DISABLE 0 +#define LPAIF_I2SCTL_SPKEN_ENABLE 1 #define LPAIF_I2SCTL_MODE_NONE 0 #define LPAIF_I2SCTL_MODE_SD0 1 @@ -31,40 +28,44 @@ #define LPAIF_I2SCTL_MODE_QUAD23 6 #define LPAIF_I2SCTL_MODE_6CH 7 #define LPAIF_I2SCTL_MODE_8CH 8 +#define LPAIF_I2SCTL_MODE_10CH 9 +#define LPAIF_I2SCTL_MODE_12CH 10 +#define LPAIF_I2SCTL_MODE_14CH 11 +#define LPAIF_I2SCTL_MODE_16CH 12 +#define LPAIF_I2SCTL_MODE_SD4 13 +#define LPAIF_I2SCTL_MODE_SD5 14 +#define LPAIF_I2SCTL_MODE_SD6 15 +#define LPAIF_I2SCTL_MODE_SD7 16 +#define LPAIF_I2SCTL_MODE_QUAD45 17 +#define LPAIF_I2SCTL_MODE_QUAD47 18 +#define LPAIF_I2SCTL_MODE_8CH_2 19 + +#define LPAIF_I2SCTL_SPKMODE(mode) mode + +#define LPAIF_I2SCTL_SPKMONO_STEREO 0 +#define LPAIF_I2SCTL_SPKMONO_MONO 1 -#define LPAIF_I2SCTL_SPKMODE_MASK 0x3C00 -#define LPAIF_I2SCTL_SPKMODE_SHIFT 10 -#define LPAIF_I2SCTL_SPKMODE(mode) ((mode) << LPAIF_I2SCTL_SPKMODE_SHIFT) +#define LPAIF_I2SCTL_MICEN_DISABLE 0 +#define LPAIF_I2SCTL_MICEN_ENABLE 1 -#define LPAIF_I2SCTL_SPKMONO_MASK 0x0200 -#define LPAIF_I2SCTL_SPKMONO_SHIFT 9 -#define LPAIF_I2SCTL_SPKMONO_STEREO (0 << LPAIF_I2SCTL_SPKMONO_SHIFT) -#define LPAIF_I2SCTL_SPKMONO_MONO (1 << LPAIF_I2SCTL_SPKMONO_SHIFT) +#define LPAIF_I2SCTL_MICMODE(mode) mode -#define LPAIF_I2SCTL_MICEN_MASK GENMASK(8, 8) -#define LPAIF_I2SCTL_MICEN_SHIFT 8 -#define LPAIF_I2SCTL_MICEN_DISABLE (0 << LPAIF_I2SCTL_MICEN_SHIFT) -#define LPAIF_I2SCTL_MICEN_ENABLE (1 << LPAIF_I2SCTL_MICEN_SHIFT) +#define LPAIF_I2SCTL_MICMONO_STEREO 0 +#define LPAIF_I2SCTL_MICMONO_MONO 1 -#define LPAIF_I2SCTL_MICMODE_MASK GENMASK(7, 4) -#define LPAIF_I2SCTL_MICMODE_SHIFT 4 -#define LPAIF_I2SCTL_MICMODE(mode) ((mode) << LPAIF_I2SCTL_MICMODE_SHIFT) +#define LPAIF_I2SCTL_WSSRC_INTERNAL 0 +#define LPAIF_I2SCTL_WSSRC_EXTERNAL 1 -#define LPAIF_I2SCTL_MIMONO_MASK GENMASK(3, 3) -#define LPAIF_I2SCTL_MICMONO_SHIFT 3 -#define LPAIF_I2SCTL_MICMONO_STEREO (0 << LPAIF_I2SCTL_MICMONO_SHIFT) -#define LPAIF_I2SCTL_MICMONO_MONO (1 << LPAIF_I2SCTL_MICMONO_SHIFT) +#define LPAIF_I2SCTL_BITWIDTH_16 0 +#define LPAIF_I2SCTL_BITWIDTH_24 1 +#define LPAIF_I2SCTL_BITWIDTH_32 2 -#define LPAIF_I2SCTL_WSSRC_MASK 0x0004 -#define LPAIF_I2SCTL_WSSRC_SHIFT 2 -#define LPAIF_I2SCTL_WSSRC_INTERNAL (0 << LPAIF_I2SCTL_WSSRC_SHIFT) -#define LPAIF_I2SCTL_WSSRC_EXTERNAL (1 << LPAIF_I2SCTL_WSSRC_SHIFT) +#define LPAIF_BIT_CLK_DISABLE 0 +#define LPAIF_BIT_CLK_ENABLE 1 + +#define LPAIF_I2SCTL_RESET_STATE 0x003C0004 +#define LPAIF_DMACTL_RESET_STATE 0x00200000 -#define LPAIF_I2SCTL_BITWIDTH_MASK 0x0003 -#define LPAIF_I2SCTL_BITWIDTH_SHIFT 0 -#define LPAIF_I2SCTL_BITWIDTH_16 (0 << LPAIF_I2SCTL_BITWIDTH_SHIFT) -#define LPAIF_I2SCTL_BITWIDTH_24 (1 << LPAIF_I2SCTL_BITWIDTH_SHIFT) -#define LPAIF_I2SCTL_BITWIDTH_32 (2 << LPAIF_I2SCTL_BITWIDTH_SHIFT) /* LPAIF IRQ */ #define LPAIF_IRQ_REG_ADDR(v, addr, port) \ @@ -76,6 +77,14 @@ #define LPAIF_IRQSTAT_REG(v, port) LPAIF_IRQ_REG_ADDR(v, 0x4, (port)) #define LPAIF_IRQCLEAR_REG(v, port) LPAIF_IRQ_REG_ADDR(v, 0xC, (port)) + +#define LPASS_HDMITX_APP_IRQ_REG_ADDR(v, addr) \ + ((v->hdmi_irq_reg_base) + (addr)) + +#define LPASS_HDMITX_APP_IRQEN_REG(v) LPASS_HDMITX_APP_IRQ_REG_ADDR(v, 0x4) +#define LPASS_HDMITX_APP_IRQSTAT_REG(v) LPASS_HDMITX_APP_IRQ_REG_ADDR(v, 0x8) +#define LPASS_HDMITX_APP_IRQCLEAR_REG(v) LPASS_HDMITX_APP_IRQ_REG_ADDR(v, 0xC) + #define LPAIF_IRQ_BITSTRIDE 3 #define LPAIF_IRQ_PER(chan) (1 << (LPAIF_IRQ_BITSTRIDE * (chan))) @@ -83,8 +92,22 @@ #define LPAIF_IRQ_ERR(chan) (4 << (LPAIF_IRQ_BITSTRIDE * (chan))) #define LPAIF_IRQ_ALL(chan) (7 << (LPAIF_IRQ_BITSTRIDE * (chan))) +#define LPAIF_IRQ_HDMI_REQ_ON_PRELOAD(chan) (1 << (14 + chan)) +#define LPAIF_IRQ_HDMI_SDEEP_AUD_DIS(chan) (1 << (24 + chan)) +#define LPAIF_IRQ_HDMI_METADONE BIT(23) /* LPAIF DMA */ +#define LPAIF_HDMI_RDMA_REG_ADDR(v, addr, chan) \ + (v->hdmi_rdma_reg_base + (addr) + v->hdmi_rdma_reg_stride * (chan)) + +#define LPAIF_HDMI_RDMACTL_AUDINTF(id) (id << LPAIF_RDMACTL_AUDINTF_SHIFT) + +#define LPAIF_HDMI_RDMACTL_REG(v, chan) LPAIF_HDMI_RDMA_REG_ADDR(v, 0x00, (chan)) +#define LPAIF_HDMI_RDMABASE_REG(v, chan) LPAIF_HDMI_RDMA_REG_ADDR(v, 0x04, (chan)) +#define LPAIF_HDMI_RDMABUFF_REG(v, chan) LPAIF_HDMI_RDMA_REG_ADDR(v, 0x08, (chan)) +#define LPAIF_HDMI_RDMACURR_REG(v, chan) LPAIF_HDMI_RDMA_REG_ADDR(v, 0x0C, (chan)) +#define LPAIF_HDMI_RDMAPER_REG(v, chan) LPAIF_HDMI_RDMA_REG_ADDR(v, 0x10, (chan)) +#define LPAIF_HDMI_RDMAPERCNT_REG(v, chan) LPAIF_HDMI_RDMA_REG_ADDR(v, 0x14, (chan)) #define LPAIF_RDMA_REG_ADDR(v, addr, chan) \ (v->rdma_reg_base + (addr) + v->rdma_reg_stride * (chan)) @@ -109,54 +132,76 @@ #define LPAIF_WRDMAPER_REG(v, chan) LPAIF_WRDMA_REG_ADDR(v, 0x10, (chan)) #define LPAIF_WRDMAPERCNT_REG(v, chan) LPAIF_WRDMA_REG_ADDR(v, 0x14, (chan)) -#define __LPAIF_DMA_REG(v, chan, dir, reg) \ - (dir == SNDRV_PCM_STREAM_PLAYBACK) ? \ - LPAIF_RDMA##reg##_REG(v, chan) : \ - LPAIF_WRDMA##reg##_REG(v, chan) - -#define LPAIF_DMACTL_REG(v, chan, dir) __LPAIF_DMA_REG(v, chan, dir, CTL) -#define LPAIF_DMABASE_REG(v, chan, dir) __LPAIF_DMA_REG(v, chan, dir, BASE) -#define LPAIF_DMABUFF_REG(v, chan, dir) __LPAIF_DMA_REG(v, chan, dir, BUFF) -#define LPAIF_DMACURR_REG(v, chan, dir) __LPAIF_DMA_REG(v, chan, dir, CURR) -#define LPAIF_DMAPER_REG(v, chan, dir) __LPAIF_DMA_REG(v, chan, dir, PER) -#define LPAIF_DMAPERCNT_REG(v, chan, dir) __LPAIF_DMA_REG(v, chan, dir, PERCNT) - -#define LPAIF_DMACTL_BURSTEN_MASK 0x800 -#define LPAIF_DMACTL_BURSTEN_SHIFT 11 -#define LPAIF_DMACTL_BURSTEN_SINGLE (0 << LPAIF_DMACTL_BURSTEN_SHIFT) -#define LPAIF_DMACTL_BURSTEN_INCR4 (1 << LPAIF_DMACTL_BURSTEN_SHIFT) - -#define LPAIF_DMACTL_WPSCNT_MASK 0x700 -#define LPAIF_DMACTL_WPSCNT_SHIFT 8 -#define LPAIF_DMACTL_WPSCNT_ONE (0 << LPAIF_DMACTL_WPSCNT_SHIFT) -#define LPAIF_DMACTL_WPSCNT_TWO (1 << LPAIF_DMACTL_WPSCNT_SHIFT) -#define LPAIF_DMACTL_WPSCNT_THREE (2 << LPAIF_DMACTL_WPSCNT_SHIFT) -#define LPAIF_DMACTL_WPSCNT_FOUR (3 << LPAIF_DMACTL_WPSCNT_SHIFT) -#define LPAIF_DMACTL_WPSCNT_SIX (5 << LPAIF_DMACTL_WPSCNT_SHIFT) -#define LPAIF_DMACTL_WPSCNT_EIGHT (7 << LPAIF_DMACTL_WPSCNT_SHIFT) - -#define LPAIF_DMACTL_AUDINTF_MASK 0x0F0 -#define LPAIF_DMACTL_AUDINTF_SHIFT 4 -#define LPAIF_DMACTL_AUDINTF(id) (id << LPAIF_DMACTL_AUDINTF_SHIFT) - -#define LPAIF_DMACTL_FIFOWM_MASK 0x00E -#define LPAIF_DMACTL_FIFOWM_SHIFT 1 -#define LPAIF_DMACTL_FIFOWM_1 (0 << LPAIF_DMACTL_FIFOWM_SHIFT) -#define LPAIF_DMACTL_FIFOWM_2 (1 << LPAIF_DMACTL_FIFOWM_SHIFT) -#define LPAIF_DMACTL_FIFOWM_3 (2 << LPAIF_DMACTL_FIFOWM_SHIFT) -#define LPAIF_DMACTL_FIFOWM_4 (3 << LPAIF_DMACTL_FIFOWM_SHIFT) -#define LPAIF_DMACTL_FIFOWM_5 (4 << LPAIF_DMACTL_FIFOWM_SHIFT) -#define LPAIF_DMACTL_FIFOWM_6 (5 << LPAIF_DMACTL_FIFOWM_SHIFT) -#define LPAIF_DMACTL_FIFOWM_7 (6 << LPAIF_DMACTL_FIFOWM_SHIFT) -#define LPAIF_DMACTL_FIFOWM_8 (7 << LPAIF_DMACTL_FIFOWM_SHIFT) - -#define LPAIF_DMACTL_ENABLE_MASK 0x1 -#define LPAIF_DMACTL_ENABLE_SHIFT 0 -#define LPAIF_DMACTL_ENABLE_OFF (0 << LPAIF_DMACTL_ENABLE_SHIFT) -#define LPAIF_DMACTL_ENABLE_ON (1 << LPAIF_DMACTL_ENABLE_SHIFT) - -#define LPAIF_DMACTL_DYNCLK_MASK BIT(12) -#define LPAIF_DMACTL_DYNCLK_SHIFT 12 -#define LPAIF_DMACTL_DYNCLK_OFF (0 << LPAIF_DMACTL_DYNCLK_SHIFT) -#define LPAIF_DMACTL_DYNCLK_ON (1 << LPAIF_DMACTL_DYNCLK_SHIFT) +#define LPAIF_INTFDMA_REG(v, chan, reg, dai_id) \ + ((v->dai_driver[dai_id].id == LPASS_DP_RX) ? \ + LPAIF_HDMI_RDMA##reg##_REG(v, chan) : \ + LPAIF_RDMA##reg##_REG(v, chan)) + +#define __LPAIF_DMA_REG(v, chan, dir, reg, dai_id) \ + ((dir == SNDRV_PCM_STREAM_PLAYBACK) ? \ + (LPAIF_INTFDMA_REG(v, chan, reg, dai_id)) : \ + LPAIF_WRDMA##reg##_REG(v, chan)) + +#define LPAIF_DMACTL_REG(v, chan, dir, dai_id) __LPAIF_DMA_REG(v, chan, dir, CTL, dai_id) +#define LPAIF_DMABASE_REG(v, chan, dir, dai_id) __LPAIF_DMA_REG(v, chan, dir, BASE, dai_id) +#define LPAIF_DMABUFF_REG(v, chan, dir, dai_id) __LPAIF_DMA_REG(v, chan, dir, BUFF, dai_id) +#define LPAIF_DMACURR_REG(v, chan, dir, dai_id) __LPAIF_DMA_REG(v, chan, dir, CURR, dai_id) +#define LPAIF_DMAPER_REG(v, chan, dir, dai_id) __LPAIF_DMA_REG(v, chan, dir, PER, dai_id) +#define LPAIF_DMAPERCNT_REG(v, chan, dir, dai_id) __LPAIF_DMA_REG(v, chan, dir, PERCNT, dai_id) + +#define LPAIF_DMACTL_BURSTEN_SINGLE 0 +#define LPAIF_DMACTL_BURSTEN_INCR4 1 + +#define LPAIF_DMACTL_WPSCNT_ONE 0 +#define LPAIF_DMACTL_WPSCNT_TWO 1 +#define LPAIF_DMACTL_WPSCNT_THREE 2 +#define LPAIF_DMACTL_WPSCNT_FOUR 3 +#define LPAIF_DMACTL_WPSCNT_SIX 5 +#define LPAIF_DMACTL_WPSCNT_EIGHT 7 +#define LPAIF_DMACTL_WPSCNT_TEN 9 +#define LPAIF_DMACTL_WPSCNT_TWELVE 11 +#define LPAIF_DMACTL_WPSCNT_FOURTEEN 13 +#define LPAIF_DMACTL_WPSCNT_SIXTEEN 15 + +#define LPAIF_DMACTL_AUDINTF(id) id + +#define LPAIF_DMACTL_FIFOWM_1 0 +#define LPAIF_DMACTL_FIFOWM_2 1 +#define LPAIF_DMACTL_FIFOWM_3 2 +#define LPAIF_DMACTL_FIFOWM_4 3 +#define LPAIF_DMACTL_FIFOWM_5 4 +#define LPAIF_DMACTL_FIFOWM_6 5 +#define LPAIF_DMACTL_FIFOWM_7 6 +#define LPAIF_DMACTL_FIFOWM_8 7 +#define LPAIF_DMACTL_FIFOWM_9 8 +#define LPAIF_DMACTL_FIFOWM_10 9 +#define LPAIF_DMACTL_FIFOWM_11 10 +#define LPAIF_DMACTL_FIFOWM_12 11 +#define LPAIF_DMACTL_FIFOWM_13 12 +#define LPAIF_DMACTL_FIFOWM_14 13 +#define LPAIF_DMACTL_FIFOWM_15 14 +#define LPAIF_DMACTL_FIFOWM_16 15 +#define LPAIF_DMACTL_FIFOWM_17 16 +#define LPAIF_DMACTL_FIFOWM_18 17 +#define LPAIF_DMACTL_FIFOWM_19 18 +#define LPAIF_DMACTL_FIFOWM_20 19 +#define LPAIF_DMACTL_FIFOWM_21 20 +#define LPAIF_DMACTL_FIFOWM_22 21 +#define LPAIF_DMACTL_FIFOWM_23 22 +#define LPAIF_DMACTL_FIFOWM_24 23 +#define LPAIF_DMACTL_FIFOWM_25 24 +#define LPAIF_DMACTL_FIFOWM_26 25 +#define LPAIF_DMACTL_FIFOWM_27 26 +#define LPAIF_DMACTL_FIFOWM_28 27 +#define LPAIF_DMACTL_FIFOWM_29 28 +#define LPAIF_DMACTL_FIFOWM_30 29 +#define LPAIF_DMACTL_FIFOWM_31 30 +#define LPAIF_DMACTL_FIFOWM_32 31 + +#define LPAIF_DMACTL_ENABLE_OFF 0 +#define LPAIF_DMACTL_ENABLE_ON 1 + +#define LPAIF_DMACTL_DYNCLK_OFF 0 +#define LPAIF_DMACTL_DYNCLK_ON 1 + #endif /* __LPASS_LPAIF_REG_H__ */ diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c index 01179bc0e5e5..80b09dede5f9 100644 --- a/sound/soc/qcom/lpass-platform.c +++ b/sound/soc/qcom/lpass-platform.c @@ -23,7 +23,7 @@ struct lpass_pcm_data { 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 = { @@ -50,6 +50,53 @@ static const struct snd_pcm_hardware lpass_platform_pcm_hardware = { .fifo_size = 0, }; +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) { @@ -60,8 +107,11 @@ static int lpass_platform_pcmops_open(struct snd_soc_component *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; - data = devm_kzalloc(soc_runtime->dev, sizeof(*data), GFP_KERNEL); + component->id = dai_id; + data = kzalloc(sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; @@ -69,25 +119,30 @@ static int lpass_platform_pcmops_open(struct snd_soc_component *component, 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; - if (dma_ch < 0) + if (dma_ch < 0) { + kfree(data); 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; } - - 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; @@ -95,6 +150,7 @@ static int lpass_platform_pcmops_open(struct snd_soc_component *component, ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); if (ret < 0) { + kfree(data); dev_err(soc_runtime->dev, "setting constraints failed: %d\n", ret); return -EINVAL; @@ -109,15 +165,22 @@ 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 = 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; } @@ -126,6 +189,7 @@ static int lpass_platform_pcmops_hw_params(struct snd_soc_component *component, struct snd_pcm_hw_params *params) { 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; @@ -133,11 +197,23 @@ static int lpass_platform_pcmops_hw_params(struct snd_soc_component *component, 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; - - ch = pcm_data->dma_ch; + unsigned int dai_id = cpu_dai->driver->id; + + 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) { @@ -146,29 +222,72 @@ static int lpass_platform_pcmops_hw_params(struct snd_soc_component *component, 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: + 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; } @@ -177,23 +296,30 @@ static int lpass_platform_pcmops_hw_params(struct snd_soc_component *component, 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; } @@ -204,10 +330,9 @@ static int lpass_platform_pcmops_hw_params(struct snd_soc_component *component, 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; } @@ -219,15 +344,23 @@ static int lpass_platform_pcmops_hw_free(struct snd_soc_component *component, struct snd_pcm_substream *substream) { 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; + + 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); - ret = regmap_write(drvdata->lpaif_map, reg, 0); + 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); @@ -240,25 +373,43 @@ static int lpass_platform_pcmops_prepare(struct snd_soc_component *component, { struct snd_pcm_runtime *runtime = substream->runtime; 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", @@ -266,8 +417,7 @@ static int lpass_platform_pcmops_prepare(struct snd_soc_component *component, 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", @@ -275,9 +425,7 @@ static int lpass_platform_pcmops_prepare(struct snd_soc_component *component, 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); @@ -292,64 +440,150 @@ static int lpass_platform_pcmops_trigger(struct snd_soc_component *component, int cmd) { 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; + 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) { + 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; + } + 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: 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)); + ret = regmap_fields_write(dmactl->enable, id, + LPAIF_DMACTL_ENABLE_ON); if (ret) { dev_err(soc_runtime->dev, - "error writing to irqclear reg: %d\n", ret); + "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; + } + map = drvdata->hdmiif_map; + 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: + map = drvdata->lpaif_map; + reg_irqclr = LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST); + val_irqclr = LPAIF_IRQ_ALL(ch); - ret = regmap_update_bits(drvdata->lpaif_map, - LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST), - LPAIF_IRQ_ALL(ch), - 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 irqen reg: %d\n", ret); + dev_err(soc_runtime->dev, "error writing to irqclear 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_update_bits(map, reg_irqen, val_mask, val_irqen); if (ret) { - dev_err(soc_runtime->dev, - "error writing to rdmactl reg: %d\n", 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; + } + map = drvdata->hdmiif_map; + 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: + map = drvdata->lpaif_map; + 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); @@ -366,25 +600,33 @@ static snd_pcm_uframes_t lpass_platform_pcmops_pointer( struct snd_pcm_substream *substream) { 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); @@ -400,9 +642,8 @@ static int lpass_platform_pcmops_mmap(struct snd_soc_component *component, { 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 irqreturn_t lpass_dma_interrupt_handler( @@ -411,14 +652,35 @@ static irqreturn_t lpass_dma_interrupt_handler( int chan, u32 interrupts) { 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: + 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); @@ -429,9 +691,7 @@ static irqreturn_t lpass_dma_interrupt_handler( } 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); @@ -443,9 +703,7 @@ static irqreturn_t lpass_dma_interrupt_handler( } 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); @@ -456,6 +714,16 @@ static irqreturn_t lpass_dma_interrupt_handler( 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; + } + return ret; } @@ -487,6 +755,37 @@ static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data) return IRQ_HANDLED; } +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) { @@ -580,7 +879,40 @@ int asoc_qcom_lpass_platform_register(struct platform_device *pdev) 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); } diff --git a/sound/soc/qcom/lpass-sc7180.c b/sound/soc/qcom/lpass-sc7180.c new file mode 100644 index 000000000000..bc998d501600 --- /dev/null +++ b/sound/soc/qcom/lpass-sc7180.c @@ -0,0 +1,305 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * + * lpass-sc7180.c -- ALSA SoC platform-machine driver for QTi LPASS + */ + +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <dt-bindings/sound/sc7180-lpass.h> +#include <sound/pcm.h> +#include <sound/soc.h> + +#include "lpass-lpaif-reg.h" +#include "lpass.h" + +static struct snd_soc_dai_driver sc7180_lpass_cpu_dai_driver[] = { + [MI2S_PRIMARY] = { + .id = MI2S_PRIMARY, + .name = "Primary MI2S", + .playback = { + .stream_name = "Primary Playback", + .formats = SNDRV_PCM_FMTBIT_S16, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + }, + .capture = { + .stream_name = "Primary Capture", + .formats = SNDRV_PCM_FMTBIT_S16, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + }, + .probe = &asoc_qcom_lpass_cpu_dai_probe, + .ops = &asoc_qcom_lpass_cpu_dai_ops, + }, + + [MI2S_SECONDARY] = { + .id = MI2S_SECONDARY, + .name = "Secondary MI2S", + .playback = { + .stream_name = "Secondary Playback", + .formats = SNDRV_PCM_FMTBIT_S16, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + }, + .probe = &asoc_qcom_lpass_cpu_dai_probe, + .ops = &asoc_qcom_lpass_cpu_dai_ops, + }, + [LPASS_DP_RX] = { + .id = LPASS_DP_RX, + .name = "Hdmi", + .playback = { + .stream_name = "Hdmi Playback", + .formats = SNDRV_PCM_FMTBIT_S24, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + }, + .ops = &asoc_qcom_lpass_hdmi_dai_ops, + }, +}; + +static int sc7180_lpass_alloc_dma_channel(struct lpass_data *drvdata, + int direction, unsigned int dai_id) +{ + struct lpass_variant *v = drvdata->variant; + int chan = 0; + + if (dai_id == LPASS_DP_RX) { + if (direction == SNDRV_PCM_STREAM_PLAYBACK) { + chan = find_first_zero_bit(&drvdata->hdmi_dma_ch_bit_map, + v->hdmi_rdma_channels); + + if (chan >= v->hdmi_rdma_channels) + return -EBUSY; + } + set_bit(chan, &drvdata->hdmi_dma_ch_bit_map); + } else { + if (direction == SNDRV_PCM_STREAM_PLAYBACK) { + chan = find_first_zero_bit(&drvdata->dma_ch_bit_map, + v->rdma_channels); + + if (chan >= v->rdma_channels) + return -EBUSY; + } else { + chan = find_next_zero_bit(&drvdata->dma_ch_bit_map, + v->wrdma_channel_start + + v->wrdma_channels, + v->wrdma_channel_start); + + if (chan >= v->wrdma_channel_start + v->wrdma_channels) + return -EBUSY; + } + + set_bit(chan, &drvdata->dma_ch_bit_map); + } + return chan; +} + +static int sc7180_lpass_free_dma_channel(struct lpass_data *drvdata, int chan, unsigned int dai_id) +{ + if (dai_id == LPASS_DP_RX) + clear_bit(chan, &drvdata->hdmi_dma_ch_bit_map); + else + clear_bit(chan, &drvdata->dma_ch_bit_map); + + return 0; +} + +static int sc7180_lpass_init(struct platform_device *pdev) +{ + struct lpass_data *drvdata = platform_get_drvdata(pdev); + struct lpass_variant *variant = drvdata->variant; + struct device *dev = &pdev->dev; + int ret, i; + + drvdata->clks = devm_kcalloc(dev, variant->num_clks, + sizeof(*drvdata->clks), GFP_KERNEL); + drvdata->num_clks = variant->num_clks; + + for (i = 0; i < drvdata->num_clks; i++) + drvdata->clks[i].id = variant->clk_name[i]; + + ret = devm_clk_bulk_get(dev, drvdata->num_clks, drvdata->clks); + if (ret) { + dev_err(dev, "Failed to get clocks %d\n", ret); + return ret; + } + + ret = clk_bulk_prepare_enable(drvdata->num_clks, drvdata->clks); + if (ret) { + dev_err(dev, "sc7180 clk_enable failed\n"); + return ret; + } + + return 0; +} + +static int sc7180_lpass_exit(struct platform_device *pdev) +{ + struct lpass_data *drvdata = platform_get_drvdata(pdev); + + clk_bulk_disable_unprepare(drvdata->num_clks, drvdata->clks); + + return 0; +} + +static struct lpass_variant sc7180_data = { + .i2sctrl_reg_base = 0x1000, + .i2sctrl_reg_stride = 0x1000, + .i2s_ports = 3, + .irq_reg_base = 0x9000, + .irq_reg_stride = 0x1000, + .irq_ports = 3, + .rdma_reg_base = 0xC000, + .rdma_reg_stride = 0x1000, + .rdma_channels = 5, + .hdmi_rdma_reg_base = 0x64000, + .hdmi_rdma_reg_stride = 0x1000, + .hdmi_rdma_channels = 4, + .dmactl_audif_start = 1, + .wrdma_reg_base = 0x18000, + .wrdma_reg_stride = 0x1000, + .wrdma_channel_start = 5, + .wrdma_channels = 4, + + .loopback = REG_FIELD_ID(0x1000, 17, 17, 3, 0x1000), + .spken = REG_FIELD_ID(0x1000, 16, 16, 3, 0x1000), + .spkmode = REG_FIELD_ID(0x1000, 11, 15, 3, 0x1000), + .spkmono = REG_FIELD_ID(0x1000, 10, 10, 3, 0x1000), + .micen = REG_FIELD_ID(0x1000, 9, 9, 3, 0x1000), + .micmode = REG_FIELD_ID(0x1000, 4, 8, 3, 0x1000), + .micmono = REG_FIELD_ID(0x1000, 3, 3, 3, 0x1000), + .wssrc = REG_FIELD_ID(0x1000, 2, 2, 3, 0x1000), + .bitwidth = REG_FIELD_ID(0x1000, 0, 1, 3, 0x1000), + + .rdma_dyncclk = REG_FIELD_ID(0xC000, 21, 21, 5, 0x1000), + .rdma_bursten = REG_FIELD_ID(0xC000, 20, 20, 5, 0x1000), + .rdma_wpscnt = REG_FIELD_ID(0xC000, 16, 19, 5, 0x1000), + .rdma_intf = REG_FIELD_ID(0xC000, 12, 15, 5, 0x1000), + .rdma_fifowm = REG_FIELD_ID(0xC000, 1, 5, 5, 0x1000), + .rdma_enable = REG_FIELD_ID(0xC000, 0, 0, 5, 0x1000), + + .wrdma_dyncclk = REG_FIELD_ID(0x18000, 22, 22, 4, 0x1000), + .wrdma_bursten = REG_FIELD_ID(0x18000, 21, 21, 4, 0x1000), + .wrdma_wpscnt = REG_FIELD_ID(0x18000, 17, 20, 4, 0x1000), + .wrdma_intf = REG_FIELD_ID(0x18000, 12, 16, 4, 0x1000), + .wrdma_fifowm = REG_FIELD_ID(0x18000, 1, 5, 4, 0x1000), + .wrdma_enable = REG_FIELD_ID(0x18000, 0, 0, 4, 0x1000), + + .hdmi_tx_ctl_addr = 0x1000, + .hdmi_legacy_addr = 0x1008, + .hdmi_vbit_addr = 0x610c0, + .hdmi_ch_lsb_addr = 0x61048, + .hdmi_ch_msb_addr = 0x6104c, + .ch_stride = 0x8, + .hdmi_parity_addr = 0x61034, + .hdmi_dmactl_addr = 0x61038, + .hdmi_dma_stride = 0x4, + .hdmi_DP_addr = 0x610c8, + .hdmi_sstream_addr = 0x6101c, + .hdmi_irq_reg_base = 0x63000, + .hdmi_irq_ports = 1, + + .hdmi_rdma_dyncclk = REG_FIELD_ID(0x64000, 14, 14, 4, 0x1000), + .hdmi_rdma_bursten = REG_FIELD_ID(0x64000, 13, 13, 4, 0x1000), + .hdmi_rdma_burst8 = REG_FIELD_ID(0x64000, 15, 15, 4, 0x1000), + .hdmi_rdma_burst16 = REG_FIELD_ID(0x64000, 16, 16, 4, 0x1000), + .hdmi_rdma_dynburst = REG_FIELD_ID(0x64000, 18, 18, 4, 0x1000), + .hdmi_rdma_wpscnt = REG_FIELD_ID(0x64000, 10, 12, 4, 0x1000), + .hdmi_rdma_fifowm = REG_FIELD_ID(0x64000, 1, 5, 4, 0x1000), + .hdmi_rdma_enable = REG_FIELD_ID(0x64000, 0, 0, 4, 0x1000), + + .sstream_en = REG_FIELD(0x6101c, 0, 0), + .dma_sel = REG_FIELD(0x6101c, 1, 2), + .auto_bbit_en = REG_FIELD(0x6101c, 3, 3), + .layout = REG_FIELD(0x6101c, 4, 4), + .layout_sp = REG_FIELD(0x6101c, 5, 8), + .set_sp_on_en = REG_FIELD(0x6101c, 10, 10), + .dp_audio = REG_FIELD(0x6101c, 11, 11), + .dp_staffing_en = REG_FIELD(0x6101c, 12, 12), + .dp_sp_b_hw_en = REG_FIELD(0x6101c, 13, 13), + + .mute = REG_FIELD(0x610c8, 0, 0), + .as_sdp_cc = REG_FIELD(0x610c8, 1, 3), + .as_sdp_ct = REG_FIELD(0x610c8, 4, 7), + .aif_db4 = REG_FIELD(0x610c8, 8, 15), + .frequency = REG_FIELD(0x610c8, 16, 21), + .mst_index = REG_FIELD(0x610c8, 28, 29), + .dptx_index = REG_FIELD(0x610c8, 30, 31), + + .soft_reset = REG_FIELD(0x1000, 31, 31), + .force_reset = REG_FIELD(0x1000, 30, 30), + + .use_hw_chs = REG_FIELD(0x61038, 0, 0), + .use_hw_usr = REG_FIELD(0x61038, 1, 1), + .hw_chs_sel = REG_FIELD(0x61038, 2, 4), + .hw_usr_sel = REG_FIELD(0x61038, 5, 6), + + .replace_vbit = REG_FIELD(0x610c0, 0, 0), + .vbit_stream = REG_FIELD(0x610c0, 1, 1), + + .legacy_en = REG_FIELD(0x1008, 0, 0), + .calc_en = REG_FIELD(0x61034, 0, 0), + .lsb_bits = REG_FIELD(0x61048, 0, 31), + .msb_bits = REG_FIELD(0x6104c, 0, 31), + + + .clk_name = (const char*[]) { + "pcnoc-sway-clk", + "audio-core", + "pcnoc-mport-clk", + }, + .num_clks = 3, + .dai_driver = sc7180_lpass_cpu_dai_driver, + .num_dai = ARRAY_SIZE(sc7180_lpass_cpu_dai_driver), + .dai_osr_clk_names = (const char *[]) { + "mclk0", + "null", + }, + .dai_bit_clk_names = (const char *[]) { + "mi2s-bit-clk0", + "mi2s-bit-clk1", + }, + .init = sc7180_lpass_init, + .exit = sc7180_lpass_exit, + .alloc_dma_channel = sc7180_lpass_alloc_dma_channel, + .free_dma_channel = sc7180_lpass_free_dma_channel, +}; + +static const struct of_device_id sc7180_lpass_cpu_device_id[] = { + {.compatible = "qcom,sc7180-lpass-cpu", .data = &sc7180_data}, + {} +}; +MODULE_DEVICE_TABLE(of, sc7180_lpass_cpu_device_id); + +static struct platform_driver sc7180_lpass_cpu_platform_driver = { + .driver = { + .name = "sc7180-lpass-cpu", + .of_match_table = of_match_ptr(sc7180_lpass_cpu_device_id), + }, + .probe = asoc_qcom_lpass_cpu_platform_probe, + .remove = asoc_qcom_lpass_cpu_platform_remove, +}; + +module_platform_driver(sc7180_lpass_cpu_platform_driver); + +MODULE_DESCRIPTION("SC7180 LPASS CPU DRIVER"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/qcom/lpass.h b/sound/soc/qcom/lpass.h index bd19ec57c73d..bccd1a05d771 100644 --- a/sound/soc/qcom/lpass.h +++ b/sound/soc/qcom/lpass.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved. + * Copyright (c) 2010-2011,2013-2015,2020 The Linux Foundation. All rights reserved. * * lpass.h - Definitions for the QTi LPASS */ @@ -12,10 +12,45 @@ #include <linux/compiler.h> #include <linux/platform_device.h> #include <linux/regmap.h> +#include <dt-bindings/sound/sc7180-lpass.h> +#include "lpass-hdmi.h" #define LPASS_AHBIX_CLOCK_FREQUENCY 131072000 #define LPASS_MAX_MI2S_PORTS (8) #define LPASS_MAX_DMA_CHANNELS (8) +#define LPASS_MAX_HDMI_DMA_CHANNELS (4) + +#define QCOM_REGMAP_FIELD_ALLOC(d, m, f, mf) \ + do { \ + mf = devm_regmap_field_alloc(d, m, f); \ + if (IS_ERR(mf)) \ + return -EINVAL; \ + } while (0) + +struct lpaif_i2sctl { + struct regmap_field *loopback; + struct regmap_field *spken; + struct regmap_field *spkmode; + struct regmap_field *spkmono; + struct regmap_field *micen; + struct regmap_field *micmode; + struct regmap_field *micmono; + struct regmap_field *wssrc; + struct regmap_field *bitwidth; +}; + + +struct lpaif_dmactl { + struct regmap_field *intf; + struct regmap_field *bursten; + struct regmap_field *wpscnt; + struct regmap_field *fifowm; + struct regmap_field *enable; + struct regmap_field *dyncclk; + struct regmap_field *burst8; + struct regmap_field *burst16; + struct regmap_field *dynburst; +}; /* Both the CPU DAI and platform drivers will access this data */ struct lpass_data { @@ -32,45 +67,168 @@ struct lpass_data { /* MI2S SD lines to use for playback/capture */ 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; + void __iomem *hdmiif; /* regmap backed by the low-power audio interface (LPAIF) registers */ struct regmap *lpaif_map; + struct regmap *hdmiif_map; /* interrupts from the low-power audio interface (LPAIF) */ int lpaif_irq; - + int hdmiif_irq; /* SOC specific variations in the LPASS IP integration */ struct lpass_variant *variant; /* bit map to keep track of static channel allocations */ unsigned long dma_ch_bit_map; + unsigned long hdmi_dma_ch_bit_map; /* used it for handling interrupt per dma channel */ struct snd_pcm_substream *substream[LPASS_MAX_DMA_CHANNELS]; + struct snd_pcm_substream *hdmi_substream[LPASS_MAX_HDMI_DMA_CHANNELS]; - /* 8016 specific */ - struct clk *pcnoc_mport_clk; - struct clk *pcnoc_sway_clk; + /* SOC specific clock list */ + struct clk_bulk_data *clks; + int num_clks; + /* Regmap fields of I2SCTL & DMACTL registers bitfields */ + struct lpaif_i2sctl *i2sctl; + struct lpaif_dmactl *rd_dmactl; + struct lpaif_dmactl *wr_dmactl; + struct lpaif_dmactl *hdmi_rd_dmactl; + /* Regmap fields of HDMI_CTRL registers*/ + struct regmap_field *hdmitx_legacy_en; + struct regmap_field *hdmitx_parity_calc_en; + struct regmap_field *hdmitx_ch_msb[LPASS_MAX_HDMI_DMA_CHANNELS]; + struct regmap_field *hdmitx_ch_lsb[LPASS_MAX_HDMI_DMA_CHANNELS]; + struct lpass_hdmi_tx_ctl *tx_ctl; + struct lpass_vbit_ctrl *vbit_ctl; + struct lpass_hdmitx_dmactl *hdmi_tx_dmactl[LPASS_MAX_HDMI_DMA_CHANNELS]; + struct lpass_dp_metadata_ctl *meta_ctl; + struct lpass_sstream_ctl *sstream_ctl; }; /* Vairant data per each SOC */ struct lpass_variant { - u32 i2sctrl_reg_base; - u32 i2sctrl_reg_stride; - u32 i2s_ports; u32 irq_reg_base; u32 irq_reg_stride; u32 irq_ports; u32 rdma_reg_base; u32 rdma_reg_stride; u32 rdma_channels; + u32 hdmi_rdma_reg_base; + u32 hdmi_rdma_reg_stride; + u32 hdmi_rdma_channels; u32 wrdma_reg_base; u32 wrdma_reg_stride; u32 wrdma_channels; + u32 i2sctrl_reg_base; + u32 i2sctrl_reg_stride; + u32 i2s_ports; + + /* I2SCTL Register fields */ + struct reg_field loopback; + struct reg_field spken; + struct reg_field spkmode; + struct reg_field spkmono; + struct reg_field micen; + struct reg_field micmode; + struct reg_field micmono; + struct reg_field wssrc; + struct reg_field bitwidth; + + u32 hdmi_irq_reg_base; + u32 hdmi_irq_reg_stride; + u32 hdmi_irq_ports; + + /* HDMI specific controls */ + u32 hdmi_tx_ctl_addr; + u32 hdmi_legacy_addr; + u32 hdmi_vbit_addr; + u32 hdmi_ch_lsb_addr; + u32 hdmi_ch_msb_addr; + u32 ch_stride; + u32 hdmi_parity_addr; + u32 hdmi_dmactl_addr; + u32 hdmi_dma_stride; + u32 hdmi_DP_addr; + u32 hdmi_sstream_addr; + + /* HDMI SSTREAM CTRL fields */ + struct reg_field sstream_en; + struct reg_field dma_sel; + struct reg_field auto_bbit_en; + struct reg_field layout; + struct reg_field layout_sp; + struct reg_field set_sp_on_en; + struct reg_field dp_audio; + struct reg_field dp_staffing_en; + struct reg_field dp_sp_b_hw_en; + + /* HDMI DP METADATA CTL fields */ + struct reg_field mute; + struct reg_field as_sdp_cc; + struct reg_field as_sdp_ct; + struct reg_field aif_db4; + struct reg_field frequency; + struct reg_field mst_index; + struct reg_field dptx_index; + + /* HDMI TX CTRL fields */ + struct reg_field soft_reset; + struct reg_field force_reset; + + /* HDMI TX DMA CTRL */ + struct reg_field use_hw_chs; + struct reg_field use_hw_usr; + struct reg_field hw_chs_sel; + struct reg_field hw_usr_sel; + + /* HDMI VBIT CTRL */ + struct reg_field replace_vbit; + struct reg_field vbit_stream; + + /* HDMI TX LEGACY */ + struct reg_field legacy_en; + + /* HDMI TX PARITY */ + struct reg_field calc_en; + + /* HDMI CH LSB */ + struct reg_field lsb_bits; + + /* HDMI CH MSB */ + struct reg_field msb_bits; + + struct reg_field hdmi_rdma_bursten; + struct reg_field hdmi_rdma_wpscnt; + struct reg_field hdmi_rdma_fifowm; + struct reg_field hdmi_rdma_enable; + struct reg_field hdmi_rdma_dyncclk; + struct reg_field hdmi_rdma_burst8; + struct reg_field hdmi_rdma_burst16; + struct reg_field hdmi_rdma_dynburst; + + /* RD_DMA Register fields */ + struct reg_field rdma_intf; + struct reg_field rdma_bursten; + struct reg_field rdma_wpscnt; + struct reg_field rdma_fifowm; + struct reg_field rdma_enable; + struct reg_field rdma_dyncclk; + + /* WR_DMA Register fields */ + struct reg_field wrdma_intf; + struct reg_field wrdma_bursten; + struct reg_field wrdma_wpscnt; + struct reg_field wrdma_fifowm; + struct reg_field wrdma_enable; + struct reg_field wrdma_dyncclk; /** * on SOCs like APQ8016 the channel control bits start @@ -81,14 +239,18 @@ struct lpass_variant { /* SOC specific initialization like clocks */ int (*init)(struct platform_device *pdev); int (*exit)(struct platform_device *pdev); - int (*alloc_dma_channel)(struct lpass_data *data, int direction); - int (*free_dma_channel)(struct lpass_data *data, int ch); + int (*alloc_dma_channel)(struct lpass_data *data, int direction, unsigned int dai_id); + int (*free_dma_channel)(struct lpass_data *data, int ch, unsigned int dai_id); /* SOC specific dais */ struct snd_soc_dai_driver *dai_driver; int num_dai; const char * const *dai_osr_clk_names; const char * const *dai_bit_clk_names; + + /* SOC specific clocks configuration */ + const char **clk_name; + int num_clks; }; /* register the platform driver from the CPU DAI driver */ diff --git a/sound/soc/qcom/qdsp6/Makefile b/sound/soc/qcom/qdsp6/Makefile index 7e91e96f7ad5..3c1dd9f32f1d 100644 --- a/sound/soc/qcom/qdsp6/Makefile +++ b/sound/soc/qcom/qdsp6/Makefile @@ -3,6 +3,7 @@ obj-$(CONFIG_SND_SOC_QDSP6_COMMON) += q6dsp-common.o obj-$(CONFIG_SND_SOC_QDSP6_CORE) += q6core.o obj-$(CONFIG_SND_SOC_QDSP6_AFE) += q6afe.o obj-$(CONFIG_SND_SOC_QDSP6_AFE_DAI) += q6afe-dai.o +obj-$(CONFIG_SND_SOC_QDSP6_AFE_CLOCKS) += q6afe-clocks.o obj-$(CONFIG_SND_SOC_QDSP6_ADM) += q6adm.o obj-$(CONFIG_SND_SOC_QDSP6_ROUTING) += q6routing.o obj-$(CONFIG_SND_SOC_QDSP6_ASM) += q6asm.o diff --git a/sound/soc/qcom/qdsp6/q6adm.c b/sound/soc/qcom/qdsp6/q6adm.c index 2f3ea6beb066..72f29720398c 100644 --- a/sound/soc/qcom/qdsp6/q6adm.c +++ b/sound/soc/qcom/qdsp6/q6adm.c @@ -611,11 +611,13 @@ static int q6adm_remove(struct apr_device *adev) return 0; } +#ifdef CONFIG_OF static const struct of_device_id q6adm_device_id[] = { { .compatible = "qcom,q6adm" }, {}, }; MODULE_DEVICE_TABLE(of, q6adm_device_id); +#endif static struct apr_driver qcom_q6adm_driver = { .probe = q6adm_probe, diff --git a/sound/soc/qcom/qdsp6/q6afe-clocks.c b/sound/soc/qcom/qdsp6/q6afe-clocks.c new file mode 100644 index 000000000000..acfc0c698f6a --- /dev/null +++ b/sound/soc/qcom/qdsp6/q6afe-clocks.c @@ -0,0 +1,273 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2020, Linaro Limited + +#include <linux/err.h> +#include <linux/init.h> +#include <linux/clk-provider.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/slab.h> +#include "q6afe.h" + +#define Q6AFE_CLK(id) &(struct q6afe_clk) { \ + .clk_id = id, \ + .afe_clk_id = Q6AFE_##id, \ + .name = #id, \ + .attributes = LPASS_CLK_ATTRIBUTE_COUPLE_NO, \ + .rate = 19200000, \ + .hw.init = &(struct clk_init_data) { \ + .ops = &clk_q6afe_ops, \ + .name = #id, \ + }, \ + } + +#define Q6AFE_VOTE_CLK(id, blkid, n) &(struct q6afe_clk) { \ + .clk_id = id, \ + .afe_clk_id = blkid, \ + .name = #n, \ + .hw.init = &(struct clk_init_data) { \ + .ops = &clk_vote_q6afe_ops, \ + .name = #id, \ + }, \ + } + +struct q6afe_clk { + struct device *dev; + int clk_id; + int afe_clk_id; + char *name; + int attributes; + int rate; + uint32_t handle; + struct clk_hw hw; +}; + +#define to_q6afe_clk(_hw) container_of(_hw, struct q6afe_clk, hw) + +struct q6afe_cc { + struct device *dev; + struct q6afe_clk **clks; + int num_clks; +}; + +static int clk_q6afe_prepare(struct clk_hw *hw) +{ + struct q6afe_clk *clk = to_q6afe_clk(hw); + + return q6afe_set_lpass_clock(clk->dev, clk->afe_clk_id, clk->attributes, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, clk->rate); +} + +static void clk_q6afe_unprepare(struct clk_hw *hw) +{ + struct q6afe_clk *clk = to_q6afe_clk(hw); + + q6afe_set_lpass_clock(clk->dev, clk->afe_clk_id, clk->attributes, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, 0); +} + +static int clk_q6afe_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct q6afe_clk *clk = to_q6afe_clk(hw); + + clk->rate = rate; + + return 0; +} + +static unsigned long clk_q6afe_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct q6afe_clk *clk = to_q6afe_clk(hw); + + return clk->rate; +} + +static long clk_q6afe_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + return rate; +} + +static const struct clk_ops clk_q6afe_ops = { + .prepare = clk_q6afe_prepare, + .unprepare = clk_q6afe_unprepare, + .set_rate = clk_q6afe_set_rate, + .round_rate = clk_q6afe_round_rate, + .recalc_rate = clk_q6afe_recalc_rate, +}; + +static int clk_vote_q6afe_block(struct clk_hw *hw) +{ + struct q6afe_clk *clk = to_q6afe_clk(hw); + + return q6afe_vote_lpass_core_hw(clk->dev, clk->afe_clk_id, + clk->name, &clk->handle); +} + +static void clk_unvote_q6afe_block(struct clk_hw *hw) +{ + struct q6afe_clk *clk = to_q6afe_clk(hw); + + q6afe_unvote_lpass_core_hw(clk->dev, clk->afe_clk_id, clk->handle); +} + +static const struct clk_ops clk_vote_q6afe_ops = { + .prepare = clk_vote_q6afe_block, + .unprepare = clk_unvote_q6afe_block, +}; + +struct q6afe_clk *q6afe_clks[Q6AFE_MAX_CLK_ID] = { + [LPASS_CLK_ID_PRI_MI2S_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_PRI_MI2S_IBIT), + [LPASS_CLK_ID_PRI_MI2S_EBIT] = Q6AFE_CLK(LPASS_CLK_ID_PRI_MI2S_EBIT), + [LPASS_CLK_ID_SEC_MI2S_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_SEC_MI2S_IBIT), + [LPASS_CLK_ID_SEC_MI2S_EBIT] = Q6AFE_CLK(LPASS_CLK_ID_SEC_MI2S_EBIT), + [LPASS_CLK_ID_TER_MI2S_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_TER_MI2S_IBIT), + [LPASS_CLK_ID_TER_MI2S_EBIT] = Q6AFE_CLK(LPASS_CLK_ID_TER_MI2S_EBIT), + [LPASS_CLK_ID_QUAD_MI2S_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_QUAD_MI2S_IBIT), + [LPASS_CLK_ID_QUAD_MI2S_EBIT] = Q6AFE_CLK(LPASS_CLK_ID_QUAD_MI2S_EBIT), + [LPASS_CLK_ID_SPEAKER_I2S_IBIT] = + Q6AFE_CLK(LPASS_CLK_ID_SPEAKER_I2S_IBIT), + [LPASS_CLK_ID_SPEAKER_I2S_EBIT] = + Q6AFE_CLK(LPASS_CLK_ID_SPEAKER_I2S_EBIT), + [LPASS_CLK_ID_SPEAKER_I2S_OSR] = + Q6AFE_CLK(LPASS_CLK_ID_SPEAKER_I2S_OSR), + [LPASS_CLK_ID_QUI_MI2S_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_QUI_MI2S_IBIT), + [LPASS_CLK_ID_QUI_MI2S_EBIT] = Q6AFE_CLK(LPASS_CLK_ID_QUI_MI2S_EBIT), + [LPASS_CLK_ID_SEN_MI2S_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_SEN_MI2S_IBIT), + [LPASS_CLK_ID_SEN_MI2S_EBIT] = Q6AFE_CLK(LPASS_CLK_ID_SEN_MI2S_EBIT), + [LPASS_CLK_ID_INT0_MI2S_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_INT0_MI2S_IBIT), + [LPASS_CLK_ID_INT1_MI2S_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_INT1_MI2S_IBIT), + [LPASS_CLK_ID_INT2_MI2S_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_INT2_MI2S_IBIT), + [LPASS_CLK_ID_INT3_MI2S_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_INT3_MI2S_IBIT), + [LPASS_CLK_ID_INT4_MI2S_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_INT4_MI2S_IBIT), + [LPASS_CLK_ID_INT5_MI2S_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_INT5_MI2S_IBIT), + [LPASS_CLK_ID_INT6_MI2S_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_INT6_MI2S_IBIT), + [LPASS_CLK_ID_QUI_MI2S_OSR] = Q6AFE_CLK(LPASS_CLK_ID_QUI_MI2S_OSR), + [LPASS_CLK_ID_PRI_PCM_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_PRI_PCM_IBIT), + [LPASS_CLK_ID_PRI_PCM_EBIT] = Q6AFE_CLK(LPASS_CLK_ID_PRI_PCM_EBIT), + [LPASS_CLK_ID_SEC_PCM_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_SEC_PCM_IBIT), + [LPASS_CLK_ID_SEC_PCM_EBIT] = Q6AFE_CLK(LPASS_CLK_ID_SEC_PCM_EBIT), + [LPASS_CLK_ID_TER_PCM_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_TER_PCM_IBIT), + [LPASS_CLK_ID_TER_PCM_EBIT] = Q6AFE_CLK(LPASS_CLK_ID_TER_PCM_EBIT), + [LPASS_CLK_ID_QUAD_PCM_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_QUAD_PCM_IBIT), + [LPASS_CLK_ID_QUAD_PCM_EBIT] = Q6AFE_CLK(LPASS_CLK_ID_QUAD_PCM_EBIT), + [LPASS_CLK_ID_QUIN_PCM_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_QUIN_PCM_IBIT), + [LPASS_CLK_ID_QUIN_PCM_EBIT] = Q6AFE_CLK(LPASS_CLK_ID_QUIN_PCM_EBIT), + [LPASS_CLK_ID_QUI_PCM_OSR] = Q6AFE_CLK(LPASS_CLK_ID_QUI_PCM_OSR), + [LPASS_CLK_ID_PRI_TDM_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_PRI_TDM_IBIT), + [LPASS_CLK_ID_PRI_TDM_EBIT] = Q6AFE_CLK(LPASS_CLK_ID_PRI_TDM_EBIT), + [LPASS_CLK_ID_SEC_TDM_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_SEC_TDM_IBIT), + [LPASS_CLK_ID_SEC_TDM_EBIT] = Q6AFE_CLK(LPASS_CLK_ID_SEC_TDM_EBIT), + [LPASS_CLK_ID_TER_TDM_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_TER_TDM_IBIT), + [LPASS_CLK_ID_TER_TDM_EBIT] = Q6AFE_CLK(LPASS_CLK_ID_TER_TDM_EBIT), + [LPASS_CLK_ID_QUAD_TDM_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_QUAD_TDM_IBIT), + [LPASS_CLK_ID_QUAD_TDM_EBIT] = Q6AFE_CLK(LPASS_CLK_ID_QUAD_TDM_EBIT), + [LPASS_CLK_ID_QUIN_TDM_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_QUIN_TDM_IBIT), + [LPASS_CLK_ID_QUIN_TDM_EBIT] = Q6AFE_CLK(LPASS_CLK_ID_QUIN_TDM_EBIT), + [LPASS_CLK_ID_QUIN_TDM_OSR] = Q6AFE_CLK(LPASS_CLK_ID_QUIN_TDM_OSR), + [LPASS_CLK_ID_MCLK_1] = Q6AFE_CLK(LPASS_CLK_ID_MCLK_1), + [LPASS_CLK_ID_MCLK_2] = Q6AFE_CLK(LPASS_CLK_ID_MCLK_2), + [LPASS_CLK_ID_MCLK_3] = Q6AFE_CLK(LPASS_CLK_ID_MCLK_3), + [LPASS_CLK_ID_MCLK_4] = Q6AFE_CLK(LPASS_CLK_ID_MCLK_4), + [LPASS_CLK_ID_INTERNAL_DIGITAL_CODEC_CORE] = + Q6AFE_CLK(LPASS_CLK_ID_INTERNAL_DIGITAL_CODEC_CORE), + [LPASS_CLK_ID_INT_MCLK_0] = Q6AFE_CLK(LPASS_CLK_ID_INT_MCLK_0), + [LPASS_CLK_ID_INT_MCLK_1] = Q6AFE_CLK(LPASS_CLK_ID_INT_MCLK_1), + [LPASS_CLK_ID_WSA_CORE_MCLK] = Q6AFE_CLK(LPASS_CLK_ID_WSA_CORE_MCLK), + [LPASS_CLK_ID_WSA_CORE_NPL_MCLK] = + Q6AFE_CLK(LPASS_CLK_ID_WSA_CORE_NPL_MCLK), + [LPASS_CLK_ID_VA_CORE_MCLK] = Q6AFE_CLK(LPASS_CLK_ID_VA_CORE_MCLK), + [LPASS_CLK_ID_TX_CORE_MCLK] = Q6AFE_CLK(LPASS_CLK_ID_TX_CORE_MCLK), + [LPASS_CLK_ID_TX_CORE_NPL_MCLK] = + Q6AFE_CLK(LPASS_CLK_ID_TX_CORE_NPL_MCLK), + [LPASS_CLK_ID_RX_CORE_MCLK] = Q6AFE_CLK(LPASS_CLK_ID_RX_CORE_MCLK), + [LPASS_CLK_ID_RX_CORE_NPL_MCLK] = + Q6AFE_CLK(LPASS_CLK_ID_RX_CORE_NPL_MCLK), + [LPASS_CLK_ID_VA_CORE_2X_MCLK] = + Q6AFE_CLK(LPASS_CLK_ID_VA_CORE_2X_MCLK), + [LPASS_HW_AVTIMER_VOTE] = Q6AFE_VOTE_CLK(LPASS_HW_AVTIMER_VOTE, + Q6AFE_LPASS_CORE_AVTIMER_BLOCK, + "LPASS_AVTIMER_MACRO"), + [LPASS_HW_MACRO_VOTE] = Q6AFE_VOTE_CLK(LPASS_HW_MACRO_VOTE, + Q6AFE_LPASS_CORE_HW_MACRO_BLOCK, + "LPASS_HW_MACRO"), + [LPASS_HW_DCODEC_VOTE] = Q6AFE_VOTE_CLK(LPASS_HW_DCODEC_VOTE, + Q6AFE_LPASS_CORE_HW_DCODEC_BLOCK, + "LPASS_HW_DCODEC"), +}; + +static struct clk_hw *q6afe_of_clk_hw_get(struct of_phandle_args *clkspec, + void *data) +{ + struct q6afe_cc *cc = data; + unsigned int idx = clkspec->args[0]; + unsigned int attr = clkspec->args[1]; + + if (idx >= cc->num_clks || attr > LPASS_CLK_ATTRIBUTE_COUPLE_DIVISOR) { + dev_err(cc->dev, "Invalid clk specifier (%d, %d)\n", idx, attr); + return ERR_PTR(-EINVAL); + } + + if (cc->clks[idx]) { + cc->clks[idx]->attributes = attr; + return &cc->clks[idx]->hw; + } + + return ERR_PTR(-ENOENT); +} + +static int q6afe_clock_dev_probe(struct platform_device *pdev) +{ + struct q6afe_cc *cc; + struct device *dev = &pdev->dev; + int i, ret; + + cc = devm_kzalloc(dev, sizeof(*cc), GFP_KERNEL); + if (!cc) + return -ENOMEM; + + cc->clks = &q6afe_clks[0]; + cc->num_clks = ARRAY_SIZE(q6afe_clks); + for (i = 0; i < ARRAY_SIZE(q6afe_clks); i++) { + if (!q6afe_clks[i]) + continue; + + q6afe_clks[i]->dev = dev; + + ret = devm_clk_hw_register(dev, &q6afe_clks[i]->hw); + if (ret) + return ret; + } + + ret = of_clk_add_hw_provider(dev->of_node, q6afe_of_clk_hw_get, cc); + if (ret) + return ret; + + dev_set_drvdata(dev, cc); + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id q6afe_clock_device_id[] = { + { .compatible = "qcom,q6afe-clocks" }, + {}, +}; +MODULE_DEVICE_TABLE(of, q6afe_clock_device_id); +#endif + +static struct platform_driver q6afe_clock_platform_driver = { + .driver = { + .name = "q6afe-clock", + .of_match_table = of_match_ptr(q6afe_clock_device_id), + }, + .probe = q6afe_clock_dev_probe, +}; +module_platform_driver(q6afe_clock_platform_driver); + +MODULE_DESCRIPTION("Q6 Audio Frontend clock driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/qcom/qdsp6/q6afe-dai.c b/sound/soc/qcom/qdsp6/q6afe-dai.c index 0168af849272..4e1f101281e7 100644 --- a/sound/soc/qcom/qdsp6/q6afe-dai.c +++ b/sound/soc/qcom/qdsp6/q6afe-dai.c @@ -55,6 +55,48 @@ .remove = msm_dai_q6_dai_remove, \ } +#define Q6AFE_CDC_DMA_RX_DAI(did) { \ + .playback = { \ + .stream_name = #did" Playback", \ + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_176400, \ + .formats = SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE, \ + .channels_min = 1, \ + .channels_max = 8, \ + .rate_min = 8000, \ + .rate_max = 176400, \ + }, \ + .name = #did, \ + .ops = &q6dma_ops, \ + .id = did, \ + .probe = msm_dai_q6_dai_probe, \ + .remove = msm_dai_q6_dai_remove, \ + } + +#define Q6AFE_CDC_DMA_TX_DAI(did) { \ + .capture = { \ + .stream_name = #did" Capture", \ + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_176400, \ + .formats = SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE, \ + .channels_min = 1, \ + .channels_max = 8, \ + .rate_min = 8000, \ + .rate_max = 176400, \ + }, \ + .name = #did, \ + .ops = &q6dma_ops, \ + .id = did, \ + .probe = msm_dai_q6_dai_probe, \ + .remove = msm_dai_q6_dai_remove, \ + } + struct q6afe_dai_priv_data { uint32_t sd_line_mask; uint32_t sync_mode; @@ -307,6 +349,90 @@ static int q6tdm_hw_params(struct snd_pcm_substream *substream, return 0; } + +static int q6dma_set_channel_map(struct snd_soc_dai *dai, + unsigned int tx_num, unsigned int *tx_ch_mask, + unsigned int rx_num, unsigned int *rx_ch_mask) +{ + + struct q6afe_dai_data *dai_data = dev_get_drvdata(dai->dev); + struct q6afe_cdc_dma_cfg *cfg = &dai_data->port_config[dai->id].dma_cfg; + int ch_mask; + int rc = 0; + + switch (dai->id) { + case WSA_CODEC_DMA_TX_0: + case WSA_CODEC_DMA_TX_1: + case WSA_CODEC_DMA_TX_2: + case VA_CODEC_DMA_TX_0: + case VA_CODEC_DMA_TX_1: + case VA_CODEC_DMA_TX_2: + case TX_CODEC_DMA_TX_0: + case TX_CODEC_DMA_TX_1: + case TX_CODEC_DMA_TX_2: + case TX_CODEC_DMA_TX_3: + case TX_CODEC_DMA_TX_4: + case TX_CODEC_DMA_TX_5: + if (!tx_ch_mask) { + dev_err(dai->dev, "tx slot not found\n"); + return -EINVAL; + } + + if (tx_num > AFE_PORT_MAX_AUDIO_CHAN_CNT) { + dev_err(dai->dev, "invalid tx num %d\n", + tx_num); + return -EINVAL; + } + ch_mask = *tx_ch_mask; + + break; + case WSA_CODEC_DMA_RX_0: + case WSA_CODEC_DMA_RX_1: + case RX_CODEC_DMA_RX_0: + case RX_CODEC_DMA_RX_1: + case RX_CODEC_DMA_RX_2: + case RX_CODEC_DMA_RX_3: + case RX_CODEC_DMA_RX_4: + case RX_CODEC_DMA_RX_5: + case RX_CODEC_DMA_RX_6: + case RX_CODEC_DMA_RX_7: + /* rx */ + if (!rx_ch_mask) { + dev_err(dai->dev, "rx slot not found\n"); + return -EINVAL; + } + if (rx_num > AFE_PORT_MAX_AUDIO_CHAN_CNT) { + dev_err(dai->dev, "invalid rx num %d\n", + rx_num); + return -EINVAL; + } + ch_mask = *rx_ch_mask; + + break; + default: + dev_err(dai->dev, "%s: invalid dai id 0x%x\n", + __func__, dai->id); + return -EINVAL; + } + + cfg->active_channels_mask = ch_mask; + + return rc; +} + +static int q6dma_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct q6afe_dai_data *dai_data = dev_get_drvdata(dai->dev); + struct q6afe_cdc_dma_cfg *cfg = &dai_data->port_config[dai->id].dma_cfg; + + cfg->bit_width = params_width(params); + cfg->sample_rate = params_rate(params); + cfg->num_channels = params_channels(params); + + return 0; +} static void q6afe_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { @@ -362,6 +488,10 @@ static int q6afe_dai_prepare(struct snd_pcm_substream *substream, q6afe_tdm_port_prepare(dai_data->port[dai->id], &dai_data->port_config[dai->id].tdm); break; + case WSA_CODEC_DMA_RX_0 ... RX_CODEC_DMA_RX_7: + q6afe_cdc_dma_port_prepare(dai_data->port[dai->id], + &dai_data->port_config[dai->id].dma_cfg); + break; default: return -EINVAL; } @@ -430,6 +560,7 @@ static int q6afe_mi2s_set_sysclk(struct snd_soc_dai *dai, freq, dir); case Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT ... Q6AFE_LPASS_CLK_ID_QUI_MI2S_OSR: case Q6AFE_LPASS_CLK_ID_MCLK_1 ... Q6AFE_LPASS_CLK_ID_INT_MCLK_1: + case Q6AFE_LPASS_CLK_ID_WSA_CORE_MCLK ... Q6AFE_LPASS_CLK_ID_VA_CORE_2X_MCLK: return q6afe_port_set_sysclk(port, clk_id, Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, Q6AFE_LPASS_CLK_ROOT_DEFAULT, @@ -562,6 +693,29 @@ static const struct snd_soc_dapm_route q6afe_dapm_routes[] = { {"PRI_MI2S_TX", NULL, "Primary MI2S Capture"}, {"SEC_MI2S_TX", NULL, "Secondary MI2S Capture"}, {"QUAT_MI2S_TX", NULL, "Quaternary MI2S Capture"}, + + {"WSA_CODEC_DMA_RX_0 Playback", NULL, "WSA_CODEC_DMA_RX_0"}, + {"WSA_CODEC_DMA_TX_0", NULL, "WSA_CODEC_DMA_TX_0 Capture"}, + {"WSA_CODEC_DMA_RX_1 Playback", NULL, "WSA_CODEC_DMA_RX_1"}, + {"WSA_CODEC_DMA_TX_1", NULL, "WSA_CODEC_DMA_TX_1 Capture"}, + {"WSA_CODEC_DMA_TX_2", NULL, "WSA_CODEC_DMA_TX_2 Capture"}, + {"VA_CODEC_DMA_TX_0", NULL, "VA_CODEC_DMA_TX_0 Capture"}, + {"VA_CODEC_DMA_TX_1", NULL, "VA_CODEC_DMA_TX_1 Capture"}, + {"VA_CODEC_DMA_TX_2", NULL, "VA_CODEC_DMA_TX_2 Capture"}, + {"RX_CODEC_DMA_RX_0 Playback", NULL, "RX_CODEC_DMA_RX_0"}, + {"TX_CODEC_DMA_TX_0", NULL, "TX_CODEC_DMA_TX_0 Capture"}, + {"RX_CODEC_DMA_RX_1 Playback", NULL, "RX_CODEC_DMA_RX_1"}, + {"TX_CODEC_DMA_TX_1", NULL, "TX_CODEC_DMA_TX_1 Capture"}, + {"RX_CODEC_DMA_RX_2 Playback", NULL, "RX_CODEC_DMA_RX_2"}, + {"TX_CODEC_DMA_TX_2", NULL, "TX_CODEC_DMA_TX_2 Capture"}, + {"RX_CODEC_DMA_RX_3 Playback", NULL, "RX_CODEC_DMA_RX_3"}, + {"TX_CODEC_DMA_TX_3", NULL, "TX_CODEC_DMA_TX_3 Capture"}, + {"RX_CODEC_DMA_RX_4 Playback", NULL, "RX_CODEC_DMA_RX_4"}, + {"TX_CODEC_DMA_TX_4", NULL, "TX_CODEC_DMA_TX_4 Capture"}, + {"RX_CODEC_DMA_RX_5 Playback", NULL, "RX_CODEC_DMA_RX_5"}, + {"TX_CODEC_DMA_TX_5", NULL, "TX_CODEC_DMA_TX_5 Capture"}, + {"RX_CODEC_DMA_RX_6 Playback", NULL, "RX_CODEC_DMA_RX_6"}, + {"RX_CODEC_DMA_RX_7 Playback", NULL, "RX_CODEC_DMA_RX_7"}, }; static const struct snd_soc_dai_ops q6hdmi_ops = { @@ -594,6 +748,14 @@ static const struct snd_soc_dai_ops q6tdm_ops = { .hw_params = q6tdm_hw_params, }; +static const struct snd_soc_dai_ops q6dma_ops = { + .prepare = q6afe_dai_prepare, + .shutdown = q6afe_dai_shutdown, + .set_sysclk = q6afe_mi2s_set_sysclk, + .set_channel_map = q6dma_set_channel_map, + .hw_params = q6dma_hw_params, +}; + static int msm_dai_q6_dai_probe(struct snd_soc_dai *dai) { struct q6afe_dai_data *dai_data = dev_get_drvdata(dai->dev); @@ -1128,6 +1290,28 @@ static struct snd_soc_dai_driver q6afe_dais[] = { .probe = msm_dai_q6_dai_probe, .remove = msm_dai_q6_dai_remove, }, + Q6AFE_CDC_DMA_RX_DAI(WSA_CODEC_DMA_RX_0), + Q6AFE_CDC_DMA_TX_DAI(WSA_CODEC_DMA_TX_0), + Q6AFE_CDC_DMA_RX_DAI(WSA_CODEC_DMA_RX_1), + Q6AFE_CDC_DMA_TX_DAI(WSA_CODEC_DMA_TX_1), + Q6AFE_CDC_DMA_TX_DAI(WSA_CODEC_DMA_TX_2), + Q6AFE_CDC_DMA_TX_DAI(VA_CODEC_DMA_TX_0), + Q6AFE_CDC_DMA_TX_DAI(VA_CODEC_DMA_TX_1), + Q6AFE_CDC_DMA_TX_DAI(VA_CODEC_DMA_TX_2), + Q6AFE_CDC_DMA_RX_DAI(RX_CODEC_DMA_RX_0), + Q6AFE_CDC_DMA_TX_DAI(TX_CODEC_DMA_TX_0), + Q6AFE_CDC_DMA_RX_DAI(RX_CODEC_DMA_RX_1), + Q6AFE_CDC_DMA_TX_DAI(TX_CODEC_DMA_TX_1), + Q6AFE_CDC_DMA_RX_DAI(RX_CODEC_DMA_RX_2), + Q6AFE_CDC_DMA_TX_DAI(TX_CODEC_DMA_TX_2), + Q6AFE_CDC_DMA_RX_DAI(RX_CODEC_DMA_RX_3), + Q6AFE_CDC_DMA_TX_DAI(TX_CODEC_DMA_TX_3), + Q6AFE_CDC_DMA_RX_DAI(RX_CODEC_DMA_RX_4), + Q6AFE_CDC_DMA_TX_DAI(TX_CODEC_DMA_TX_4), + Q6AFE_CDC_DMA_RX_DAI(RX_CODEC_DMA_RX_5), + Q6AFE_CDC_DMA_TX_DAI(TX_CODEC_DMA_TX_5), + Q6AFE_CDC_DMA_RX_DAI(RX_CODEC_DMA_RX_6), + Q6AFE_CDC_DMA_RX_DAI(RX_CODEC_DMA_RX_7), }; static int q6afe_of_xlate_dai_name(struct snd_soc_component *component, @@ -1350,6 +1534,51 @@ static const struct snd_soc_dapm_widget q6afe_dai_widgets[] = { SND_SOC_DAPM_AIF_OUT("QUIN_TDM_TX_7", NULL, 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_AIF_OUT("DISPLAY_PORT_RX", "NULL", 0, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_AIF_IN("WSA_CODEC_DMA_RX_0", "NULL", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("WSA_CODEC_DMA_TX_0", "NULL", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("WSA_CODEC_DMA_RX_1", "NULL", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("WSA_CODEC_DMA_TX_1", "NULL", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("WSA_CODEC_DMA_TX_2", "NULL", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("VA_CODEC_DMA_TX_0", "NULL", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("VA_CODEC_DMA_TX_1", "NULL", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("VA_CODEC_DMA_TX_2", "NULL", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("RX_CODEC_DMA_RX_0", "NULL", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("TX_CODEC_DMA_TX_0", "NULL", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("RX_CODEC_DMA_RX_1", "NULL", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("TX_CODEC_DMA_TX_1", "NULL", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("RX_CODEC_DMA_RX_2", "NULL", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("TX_CODEC_DMA_TX_2", "NULL", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("RX_CODEC_DMA_RX_3", "NULL", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("TX_CODEC_DMA_TX_3", "NULL", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("RX_CODEC_DMA_RX_4", "NULL", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("TX_CODEC_DMA_TX_4", "NULL", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("RX_CODEC_DMA_RX_5", "NULL", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("TX_CODEC_DMA_TX_5", "NULL", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("RX_CODEC_DMA_RX_6", "NULL", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("RX_CODEC_DMA_RX_7", "NULL", + 0, SND_SOC_NOPM, 0, 0), }; static const struct snd_soc_component_driver q6afe_dai_component = { @@ -1460,11 +1689,13 @@ static int q6afe_dai_dev_probe(struct platform_device *pdev) q6afe_dais, ARRAY_SIZE(q6afe_dais)); } +#ifdef CONFIG_OF static const struct of_device_id q6afe_dai_device_id[] = { { .compatible = "qcom,q6afe-dais" }, {}, }; MODULE_DEVICE_TABLE(of, q6afe_dai_device_id); +#endif static struct platform_driver q6afe_dai_platform_driver = { .driver = { diff --git a/sound/soc/qcom/qdsp6/q6afe.c b/sound/soc/qcom/qdsp6/q6afe.c index e0945f7a58c8..0ca1e4aae518 100644 --- a/sound/soc/qcom/qdsp6/q6afe.c +++ b/sound/soc/qcom/qdsp6/q6afe.c @@ -42,6 +42,10 @@ #define AFE_PARAM_ID_I2S_CONFIG 0x0001020D #define AFE_PARAM_ID_TDM_CONFIG 0x0001029D #define AFE_PARAM_ID_PORT_SLOT_MAPPING_CONFIG 0x00010297 +#define AFE_PARAM_ID_CODEC_DMA_CONFIG 0x000102B8 +#define AFE_CMD_REMOTE_LPASS_CORE_HW_VOTE_REQUEST 0x000100f4 +#define AFE_CMD_RSP_REMOTE_LPASS_CORE_HW_VOTE_REQUEST 0x000100f5 +#define AFE_CMD_REMOTE_LPASS_CORE_HW_DEVOTE_REQUEST 0x000100f6 /* I2S config specific */ #define AFE_API_VERSION_I2S_CONFIG 0x1 @@ -299,22 +303,71 @@ #define AFE_PORT_ID_QUINARY_TDM_TX_7 \ (AFE_PORT_ID_QUINARY_TDM_TX + 0x0E) +/* AFE WSA Codec DMA Rx port 0 */ +#define AFE_PORT_ID_WSA_CODEC_DMA_RX_0 0xB000 +/* AFE WSA Codec DMA Tx port 0 */ +#define AFE_PORT_ID_WSA_CODEC_DMA_TX_0 0xB001 +/* AFE WSA Codec DMA Rx port 1 */ +#define AFE_PORT_ID_WSA_CODEC_DMA_RX_1 0xB002 +/* AFE WSA Codec DMA Tx port 1 */ +#define AFE_PORT_ID_WSA_CODEC_DMA_TX_1 0xB003 +/* AFE WSA Codec DMA Tx port 2 */ +#define AFE_PORT_ID_WSA_CODEC_DMA_TX_2 0xB005 +/* AFE VA Codec DMA Tx port 0 */ +#define AFE_PORT_ID_VA_CODEC_DMA_TX_0 0xB021 +/* AFE VA Codec DMA Tx port 1 */ +#define AFE_PORT_ID_VA_CODEC_DMA_TX_1 0xB023 +/* AFE VA Codec DMA Tx port 2 */ +#define AFE_PORT_ID_VA_CODEC_DMA_TX_2 0xB025 +/* AFE Rx Codec DMA Rx port 0 */ +#define AFE_PORT_ID_RX_CODEC_DMA_RX_0 0xB030 +/* AFE Tx Codec DMA Tx port 0 */ +#define AFE_PORT_ID_TX_CODEC_DMA_TX_0 0xB031 +/* AFE Rx Codec DMA Rx port 1 */ +#define AFE_PORT_ID_RX_CODEC_DMA_RX_1 0xB032 +/* AFE Tx Codec DMA Tx port 1 */ +#define AFE_PORT_ID_TX_CODEC_DMA_TX_1 0xB033 +/* AFE Rx Codec DMA Rx port 2 */ +#define AFE_PORT_ID_RX_CODEC_DMA_RX_2 0xB034 +/* AFE Tx Codec DMA Tx port 2 */ +#define AFE_PORT_ID_TX_CODEC_DMA_TX_2 0xB035 +/* AFE Rx Codec DMA Rx port 3 */ +#define AFE_PORT_ID_RX_CODEC_DMA_RX_3 0xB036 +/* AFE Tx Codec DMA Tx port 3 */ +#define AFE_PORT_ID_TX_CODEC_DMA_TX_3 0xB037 +/* AFE Rx Codec DMA Rx port 4 */ +#define AFE_PORT_ID_RX_CODEC_DMA_RX_4 0xB038 +/* AFE Tx Codec DMA Tx port 4 */ +#define AFE_PORT_ID_TX_CODEC_DMA_TX_4 0xB039 +/* AFE Rx Codec DMA Rx port 5 */ +#define AFE_PORT_ID_RX_CODEC_DMA_RX_5 0xB03A +/* AFE Tx Codec DMA Tx port 5 */ +#define AFE_PORT_ID_TX_CODEC_DMA_TX_5 0xB03B +/* AFE Rx Codec DMA Rx port 6 */ +#define AFE_PORT_ID_RX_CODEC_DMA_RX_6 0xB03C +/* AFE Rx Codec DMA Rx port 7 */ +#define AFE_PORT_ID_RX_CODEC_DMA_RX_7 0xB03E + #define Q6AFE_LPASS_MODE_CLK1_VALID 1 #define Q6AFE_LPASS_MODE_CLK2_VALID 2 #define Q6AFE_LPASS_CLK_SRC_INTERNAL 1 #define Q6AFE_LPASS_CLK_ROOT_DEFAULT 0 #define AFE_API_VERSION_TDM_CONFIG 1 #define AFE_API_VERSION_SLOT_MAPPING_CONFIG 1 +#define AFE_API_VERSION_CODEC_DMA_CONFIG 1 #define TIMEOUT_MS 1000 #define AFE_CMD_RESP_AVAIL 0 #define AFE_CMD_RESP_NONE 1 +#define AFE_CLK_TOKEN 1024 struct q6afe { struct apr_device *apr; struct device *dev; struct q6core_svc_api_info ainfo; struct mutex lock; + struct aprv2_ibasic_rsp_result_t result; + wait_queue_head_t wait; struct list_head port_list; spinlock_t port_list_lock; }; @@ -448,11 +501,21 @@ struct afe_param_id_tdm_cfg { u32 slot_mask; } __packed; +struct afe_param_id_cdc_dma_cfg { + u32 cdc_dma_cfg_minor_version; + u32 sample_rate; + u16 bit_width; + u16 data_format; + u16 num_channels; + u16 active_channels_mask; +} __packed; + union afe_port_config { struct afe_param_id_hdmi_multi_chan_audio_cfg hdmi_multi_ch; struct afe_param_id_slimbus_cfg slim_cfg; struct afe_param_id_i2s_cfg i2s_cfg; struct afe_param_id_tdm_cfg tdm_cfg; + struct afe_param_id_cdc_dma_cfg dma_cfg; } __packed; @@ -486,6 +549,18 @@ struct q6afe_port { struct list_head node; }; +struct afe_cmd_remote_lpass_core_hw_vote_request { + uint32_t hw_block_id; + char client_name[8]; +} __packed; + +struct afe_cmd_remote_lpass_core_hw_devote_request { + uint32_t hw_block_id; + uint32_t client_handle; +} __packed; + + + struct afe_port_map { int port_id; int token; @@ -707,6 +782,50 @@ static struct afe_port_map port_maps[AFE_PORT_MAX] = { QUINARY_TDM_TX_7, 0, 1}, [DISPLAY_PORT_RX] = { AFE_PORT_ID_HDMI_OVER_DP_RX, DISPLAY_PORT_RX, 1, 1}, + [WSA_CODEC_DMA_RX_0] = { AFE_PORT_ID_WSA_CODEC_DMA_RX_0, + WSA_CODEC_DMA_RX_0, 1, 1}, + [WSA_CODEC_DMA_TX_0] = { AFE_PORT_ID_WSA_CODEC_DMA_TX_0, + WSA_CODEC_DMA_TX_0, 0, 1}, + [WSA_CODEC_DMA_RX_1] = { AFE_PORT_ID_WSA_CODEC_DMA_RX_1, + WSA_CODEC_DMA_RX_1, 1, 1}, + [WSA_CODEC_DMA_TX_1] = { AFE_PORT_ID_WSA_CODEC_DMA_TX_1, + WSA_CODEC_DMA_TX_1, 0, 1}, + [WSA_CODEC_DMA_TX_2] = { AFE_PORT_ID_WSA_CODEC_DMA_TX_2, + WSA_CODEC_DMA_TX_2, 0, 1}, + [VA_CODEC_DMA_TX_0] = { AFE_PORT_ID_VA_CODEC_DMA_TX_0, + VA_CODEC_DMA_TX_0, 0, 1}, + [VA_CODEC_DMA_TX_1] = { AFE_PORT_ID_VA_CODEC_DMA_TX_1, + VA_CODEC_DMA_TX_1, 0, 1}, + [VA_CODEC_DMA_TX_2] = { AFE_PORT_ID_VA_CODEC_DMA_TX_2, + VA_CODEC_DMA_TX_2, 0, 1}, + [RX_CODEC_DMA_RX_0] = { AFE_PORT_ID_RX_CODEC_DMA_RX_0, + RX_CODEC_DMA_RX_0, 1, 1}, + [TX_CODEC_DMA_TX_0] = { AFE_PORT_ID_TX_CODEC_DMA_TX_0, + TX_CODEC_DMA_TX_0, 0, 1}, + [RX_CODEC_DMA_RX_1] = { AFE_PORT_ID_RX_CODEC_DMA_RX_1, + RX_CODEC_DMA_RX_1, 1, 1}, + [TX_CODEC_DMA_TX_1] = { AFE_PORT_ID_TX_CODEC_DMA_TX_1, + TX_CODEC_DMA_TX_1, 0, 1}, + [RX_CODEC_DMA_RX_2] = { AFE_PORT_ID_RX_CODEC_DMA_RX_2, + RX_CODEC_DMA_RX_2, 1, 1}, + [TX_CODEC_DMA_TX_2] = { AFE_PORT_ID_TX_CODEC_DMA_TX_2, + TX_CODEC_DMA_TX_2, 0, 1}, + [RX_CODEC_DMA_RX_3] = { AFE_PORT_ID_RX_CODEC_DMA_RX_3, + RX_CODEC_DMA_RX_3, 1, 1}, + [TX_CODEC_DMA_TX_3] = { AFE_PORT_ID_TX_CODEC_DMA_TX_3, + TX_CODEC_DMA_TX_3, 0, 1}, + [RX_CODEC_DMA_RX_4] = { AFE_PORT_ID_RX_CODEC_DMA_RX_4, + RX_CODEC_DMA_RX_4, 1, 1}, + [TX_CODEC_DMA_TX_4] = { AFE_PORT_ID_TX_CODEC_DMA_TX_4, + TX_CODEC_DMA_TX_4, 0, 1}, + [RX_CODEC_DMA_RX_5] = { AFE_PORT_ID_RX_CODEC_DMA_RX_5, + RX_CODEC_DMA_RX_5, 1, 1}, + [TX_CODEC_DMA_TX_5] = { AFE_PORT_ID_TX_CODEC_DMA_TX_5, + TX_CODEC_DMA_TX_5, 0, 1}, + [RX_CODEC_DMA_RX_6] = { AFE_PORT_ID_RX_CODEC_DMA_RX_6, + RX_CODEC_DMA_RX_6, 1, 1}, + [RX_CODEC_DMA_RX_7] = { AFE_PORT_ID_RX_CODEC_DMA_RX_7, + RX_CODEC_DMA_RX_7, 1, 1}, }; static void q6afe_port_free(struct kref *ref) @@ -769,6 +888,9 @@ static int q6afe_callback(struct apr_device *adev, struct apr_resp_pkt *data) port->result = *res; wake_up(&port->wait); kref_put(&port->refcount, q6afe_port_free); + } else if (hdr->token == AFE_CLK_TOKEN) { + afe->result = *res; + wake_up(&afe->wait); } break; default: @@ -777,6 +899,11 @@ static int q6afe_callback(struct apr_device *adev, struct apr_resp_pkt *data) } } break; + case AFE_CMD_RSP_REMOTE_LPASS_CORE_HW_VOTE_REQUEST: + afe->result.opcode = hdr->opcode; + afe->result.status = res->status; + wake_up(&afe->wait); + break; default: break; } @@ -801,15 +928,23 @@ int q6afe_get_port_id(int index) EXPORT_SYMBOL_GPL(q6afe_get_port_id); static int afe_apr_send_pkt(struct q6afe *afe, struct apr_pkt *pkt, - struct q6afe_port *port) + struct q6afe_port *port, uint32_t rsp_opcode) { wait_queue_head_t *wait = &port->wait; - struct apr_hdr *hdr = &pkt->hdr; + struct aprv2_ibasic_rsp_result_t *result; int ret; mutex_lock(&afe->lock); - port->result.opcode = 0; - port->result.status = 0; + if (port) { + wait = &port->wait; + result = &port->result; + } else { + result = &afe->result; + wait = &afe->wait; + } + + result->opcode = 0; + result->status = 0; ret = apr_send_pkt(afe->apr, pkt); if (ret < 0) { @@ -818,13 +953,13 @@ static int afe_apr_send_pkt(struct q6afe *afe, struct apr_pkt *pkt, goto err; } - ret = wait_event_timeout(*wait, (port->result.opcode == hdr->opcode), + ret = wait_event_timeout(*wait, (result->opcode == rsp_opcode), msecs_to_jiffies(TIMEOUT_MS)); if (!ret) { ret = -ETIMEDOUT; - } else if (port->result.status > 0) { + } else if (result->status > 0) { dev_err(afe->dev, "DSP returned error[%x]\n", - port->result.status); + result->status); ret = -EINVAL; } else { ret = 0; @@ -836,14 +971,13 @@ err: return ret; } -static int q6afe_port_set_param(struct q6afe_port *port, void *data, - int param_id, int module_id, int psize) +static int q6afe_set_param(struct q6afe *afe, struct q6afe_port *port, + void *data, int param_id, int module_id, int psize, + int token) { struct afe_svc_cmd_set_param *param; struct afe_port_param_data_v2 *pdata; - struct q6afe *afe = port->afe; struct apr_pkt *pkt; - u16 port_id = port->id; int ret, pkt_size; void *p, *pl; @@ -864,7 +998,7 @@ static int q6afe_port_set_param(struct q6afe_port *port, void *data, pkt->hdr.pkt_size = pkt_size; pkt->hdr.src_port = 0; pkt->hdr.dest_port = 0; - pkt->hdr.token = port->token; + pkt->hdr.token = token; pkt->hdr.opcode = AFE_SVC_CMD_SET_PARAM; param->payload_size = sizeof(*pdata) + psize; @@ -875,15 +1009,21 @@ static int q6afe_port_set_param(struct q6afe_port *port, void *data, pdata->param_id = param_id; pdata->param_size = psize; - ret = afe_apr_send_pkt(afe, pkt, port); + ret = afe_apr_send_pkt(afe, pkt, port, AFE_SVC_CMD_SET_PARAM); if (ret) - dev_err(afe->dev, "AFE enable for port 0x%x failed %d\n", - port_id, ret); + dev_err(afe->dev, "AFE set params failed %d\n", ret); kfree(pkt); return ret; } +static int q6afe_port_set_param(struct q6afe_port *port, void *data, + int param_id, int module_id, int psize) +{ + return q6afe_set_param(port->afe, port, data, param_id, module_id, + psize, port->token); +} + static int q6afe_port_set_param_v2(struct q6afe_port *port, void *data, int param_id, int module_id, int psize) { @@ -924,7 +1064,7 @@ static int q6afe_port_set_param_v2(struct q6afe_port *port, void *data, pdata->param_id = param_id; pdata->param_size = psize; - ret = afe_apr_send_pkt(afe, pkt, port); + ret = afe_apr_send_pkt(afe, pkt, port, AFE_PORT_CMD_SET_PARAM_V2); if (ret) dev_err(afe->dev, "AFE enable for port 0x%x failed %d\n", port_id, ret); @@ -933,7 +1073,7 @@ static int q6afe_port_set_param_v2(struct q6afe_port *port, void *data, return ret; } -static int q6afe_set_lpass_clock(struct q6afe_port *port, +static int q6afe_port_set_lpass_clock(struct q6afe_port *port, struct afe_clk_cfg *cfg) { return q6afe_port_set_param_v2(port, cfg, @@ -958,6 +1098,25 @@ static int q6afe_set_digital_codec_core_clock(struct q6afe_port *port, sizeof(*cfg)); } +int q6afe_set_lpass_clock(struct device *dev, int clk_id, int attri, + int clk_root, unsigned int freq) +{ + struct q6afe *afe = dev_get_drvdata(dev->parent); + struct afe_clk_set cset = {0,}; + + cset.clk_set_minor_version = AFE_API_VERSION_CLOCK_SET; + cset.clk_id = clk_id; + cset.clk_freq_in_hz = freq; + cset.clk_attri = attri; + cset.clk_root = clk_root; + cset.enable = !!freq; + + return q6afe_set_param(afe, NULL, &cset, AFE_PARAM_ID_CLOCK_SET, + AFE_MODULE_CLOCK_SET, sizeof(cset), + AFE_CLK_TOKEN); +} +EXPORT_SYMBOL_GPL(q6afe_set_lpass_clock); + int q6afe_port_set_sysclk(struct q6afe_port *port, int clk_id, int clk_src, int clk_root, unsigned int freq, int dir) @@ -980,7 +1139,7 @@ int q6afe_port_set_sysclk(struct q6afe_port *port, int clk_id, ccfg.clk_src = clk_src; ccfg.clk_root = clk_root; ccfg.clk_set_mode = Q6AFE_LPASS_MODE_CLK1_VALID; - ret = q6afe_set_lpass_clock(port, &ccfg); + ret = q6afe_port_set_lpass_clock(port, &ccfg); break; case LPAIF_OSR_CLK: @@ -989,11 +1148,12 @@ int q6afe_port_set_sysclk(struct q6afe_port *port, int clk_id, ccfg.clk_src = clk_src; ccfg.clk_root = clk_root; ccfg.clk_set_mode = Q6AFE_LPASS_MODE_CLK2_VALID; - ret = q6afe_set_lpass_clock(port, &ccfg); + ret = q6afe_port_set_lpass_clock(port, &ccfg); break; case Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT ... Q6AFE_LPASS_CLK_ID_QUI_MI2S_OSR: case Q6AFE_LPASS_CLK_ID_MCLK_1 ... Q6AFE_LPASS_CLK_ID_INT_MCLK_1: case Q6AFE_LPASS_CLK_ID_PRI_TDM_IBIT ... Q6AFE_LPASS_CLK_ID_QUIN_TDM_EBIT: + case Q6AFE_LPASS_CLK_ID_WSA_CORE_MCLK ... Q6AFE_LPASS_CLK_ID_VA_CORE_2X_MCLK: cset.clk_set_minor_version = AFE_API_VERSION_CLOCK_SET; cset.clk_id = clk_id; cset.clk_freq_in_hz = freq; @@ -1054,7 +1214,7 @@ int q6afe_port_stop(struct q6afe_port *port) stop->port_id = port_id; stop->reserved = 0; - ret = afe_apr_send_pkt(afe, pkt, port); + ret = afe_apr_send_pkt(afe, pkt, port, AFE_PORT_CMD_DEVICE_STOP); if (ret) dev_err(afe->dev, "AFE close failed %d\n", ret); @@ -1289,6 +1449,28 @@ int q6afe_i2s_port_prepare(struct q6afe_port *port, struct q6afe_i2s_cfg *cfg) EXPORT_SYMBOL_GPL(q6afe_i2s_port_prepare); /** + * q6afe_dam_port_prepare() - Prepare dma afe port. + * + * @port: Instance of afe port + * @cfg: DMA configuration for the afe port + * + */ +void q6afe_cdc_dma_port_prepare(struct q6afe_port *port, + struct q6afe_cdc_dma_cfg *cfg) +{ + union afe_port_config *pcfg = &port->port_cfg; + struct afe_param_id_cdc_dma_cfg *dma_cfg = &pcfg->dma_cfg; + + dma_cfg->cdc_dma_cfg_minor_version = AFE_API_VERSION_CODEC_DMA_CONFIG; + dma_cfg->sample_rate = cfg->sample_rate; + dma_cfg->bit_width = cfg->bit_width; + dma_cfg->data_format = cfg->data_format; + dma_cfg->num_channels = cfg->num_channels; + if (!cfg->active_channels_mask) + dma_cfg->active_channels_mask = (1 << cfg->num_channels) - 1; +} +EXPORT_SYMBOL_GPL(q6afe_cdc_dma_port_prepare); +/** * q6afe_port_start() - Start a afe port * * @port: Instance of port to start @@ -1344,7 +1526,7 @@ int q6afe_port_start(struct q6afe_port *port) start->port_id = port_id; - ret = afe_apr_send_pkt(afe, pkt, port); + ret = afe_apr_send_pkt(afe, pkt, port, AFE_PORT_CMD_DEVICE_START); if (ret) dev_err(afe->dev, "AFE enable for port 0x%x failed %d\n", port_id, ret); @@ -1420,7 +1602,9 @@ struct q6afe_port *q6afe_port_get_from_id(struct device *dev, int id) case AFE_PORT_ID_PRIMARY_TDM_RX ... AFE_PORT_ID_QUINARY_TDM_TX_7: cfg_type = AFE_PARAM_ID_TDM_CONFIG; break; - + case AFE_PORT_ID_WSA_CODEC_DMA_RX_0 ... AFE_PORT_ID_RX_CODEC_DMA_RX_7: + cfg_type = AFE_PARAM_ID_CODEC_DMA_CONFIG; + break; default: dev_err(dev, "Invalid port id 0x%x\n", port_id); return ERR_PTR(-EINVAL); @@ -1458,6 +1642,85 @@ void q6afe_port_put(struct q6afe_port *port) } EXPORT_SYMBOL_GPL(q6afe_port_put); +int q6afe_unvote_lpass_core_hw(struct device *dev, uint32_t hw_block_id, + uint32_t client_handle) +{ + struct q6afe *afe = dev_get_drvdata(dev->parent); + struct afe_cmd_remote_lpass_core_hw_devote_request *vote_cfg; + struct apr_pkt *pkt; + int ret = 0; + int pkt_size; + void *p; + + pkt_size = APR_HDR_SIZE + sizeof(*vote_cfg); + p = kzalloc(pkt_size, GFP_KERNEL); + if (!p) + return -ENOMEM; + + pkt = p; + vote_cfg = p + APR_HDR_SIZE; + + pkt->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + pkt->hdr.pkt_size = pkt_size; + pkt->hdr.src_port = 0; + pkt->hdr.dest_port = 0; + pkt->hdr.token = hw_block_id; + pkt->hdr.opcode = AFE_CMD_REMOTE_LPASS_CORE_HW_DEVOTE_REQUEST; + vote_cfg->hw_block_id = hw_block_id; + vote_cfg->client_handle = client_handle; + + ret = apr_send_pkt(afe->apr, pkt); + if (ret < 0) + dev_err(afe->dev, "AFE failed to unvote (%d)\n", hw_block_id); + + kfree(pkt); + return ret; +} +EXPORT_SYMBOL(q6afe_unvote_lpass_core_hw); + +int q6afe_vote_lpass_core_hw(struct device *dev, uint32_t hw_block_id, + char *client_name, uint32_t *client_handle) +{ + struct q6afe *afe = dev_get_drvdata(dev->parent); + struct afe_cmd_remote_lpass_core_hw_vote_request *vote_cfg; + struct apr_pkt *pkt; + int ret = 0; + int pkt_size; + void *p; + + pkt_size = APR_HDR_SIZE + sizeof(*vote_cfg); + p = kzalloc(pkt_size, GFP_KERNEL); + if (!p) + return -ENOMEM; + + pkt = p; + vote_cfg = p + APR_HDR_SIZE; + + pkt->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + pkt->hdr.pkt_size = pkt_size; + pkt->hdr.src_port = 0; + pkt->hdr.dest_port = 0; + pkt->hdr.token = hw_block_id; + pkt->hdr.opcode = AFE_CMD_REMOTE_LPASS_CORE_HW_VOTE_REQUEST; + vote_cfg->hw_block_id = hw_block_id; + strlcpy(vote_cfg->client_name, client_name, + sizeof(vote_cfg->client_name)); + + ret = afe_apr_send_pkt(afe, pkt, NULL, + AFE_CMD_RSP_REMOTE_LPASS_CORE_HW_VOTE_REQUEST); + if (ret) + dev_err(afe->dev, "AFE failed to vote (%d)\n", hw_block_id); + + + kfree(pkt); + return ret; +} +EXPORT_SYMBOL(q6afe_vote_lpass_core_hw); + static int q6afe_probe(struct apr_device *adev) { struct q6afe *afe; @@ -1470,6 +1733,7 @@ static int q6afe_probe(struct apr_device *adev) q6core_get_svc_api_info(adev->svc_id, &afe->ainfo); afe->apr = adev; mutex_init(&afe->lock); + init_waitqueue_head(&afe->wait); afe->dev = dev; INIT_LIST_HEAD(&afe->port_list); spin_lock_init(&afe->port_list_lock); @@ -1486,11 +1750,13 @@ static int q6afe_remove(struct apr_device *adev) return 0; } +#ifdef CONFIG_OF static const struct of_device_id q6afe_device_id[] = { { .compatible = "qcom,q6afe" }, {}, }; MODULE_DEVICE_TABLE(of, q6afe_device_id); +#endif static struct apr_driver qcom_q6afe_driver = { .probe = q6afe_probe, diff --git a/sound/soc/qcom/qdsp6/q6afe.h b/sound/soc/qcom/qdsp6/q6afe.h index c7ed5422baff..22e10269aa10 100644 --- a/sound/soc/qcom/qdsp6/q6afe.h +++ b/sound/soc/qcom/qdsp6/q6afe.h @@ -5,7 +5,7 @@ #include <dt-bindings/sound/qcom,q6afe.h> -#define AFE_PORT_MAX 105 +#define AFE_PORT_MAX 127 #define MSM_AFE_PORT_TYPE_RX 0 #define MSM_AFE_PORT_TYPE_TX 1 @@ -133,6 +133,19 @@ /* Clock ID for INT MCLK1 */ #define Q6AFE_LPASS_CLK_ID_INT_MCLK_1 0x306 +#define Q6AFE_LPASS_CLK_ID_WSA_CORE_MCLK 0x309 +#define Q6AFE_LPASS_CLK_ID_WSA_CORE_NPL_MCLK 0x30a +#define Q6AFE_LPASS_CLK_ID_TX_CORE_MCLK 0x30c +#define Q6AFE_LPASS_CLK_ID_TX_CORE_NPL_MCLK 0x30d +#define Q6AFE_LPASS_CLK_ID_RX_CORE_MCLK 0x30e +#define Q6AFE_LPASS_CLK_ID_RX_CORE_NPL_MCLK 0x30f +#define Q6AFE_LPASS_CLK_ID_VA_CORE_MCLK 0x30b +#define Q6AFE_LPASS_CLK_ID_VA_CORE_2X_MCLK 0x310 + +#define Q6AFE_LPASS_CORE_AVTIMER_BLOCK 0x2 +#define Q6AFE_LPASS_CORE_HW_MACRO_BLOCK 0x3 +#define Q6AFE_LPASS_CORE_HW_DCODEC_BLOCK 0x4 + /* Clock attribute for invalid use (reserved for internal usage) */ #define Q6AFE_LPASS_CLK_ATTRIBUTE_INVALID 0x0 /* Clock attribute for no couple case */ @@ -184,11 +197,21 @@ struct q6afe_tdm_cfg { u16 ch_mapping[AFE_MAX_CHAN_COUNT]; }; +struct q6afe_cdc_dma_cfg { + u16 sample_rate; + u16 bit_width; + u16 data_format; + u16 num_channels; + u16 active_channels_mask; +}; + + struct q6afe_port_config { struct q6afe_hdmi_cfg hdmi; struct q6afe_slim_cfg slim; struct q6afe_i2s_cfg i2s_cfg; struct q6afe_tdm_cfg tdm; + struct q6afe_cdc_dma_cfg dma_cfg; }; struct q6afe_port; @@ -204,8 +227,16 @@ void q6afe_slim_port_prepare(struct q6afe_port *port, struct q6afe_slim_cfg *cfg); int q6afe_i2s_port_prepare(struct q6afe_port *port, struct q6afe_i2s_cfg *cfg); void q6afe_tdm_port_prepare(struct q6afe_port *port, struct q6afe_tdm_cfg *cfg); +void q6afe_cdc_dma_port_prepare(struct q6afe_port *port, + struct q6afe_cdc_dma_cfg *cfg); int q6afe_port_set_sysclk(struct q6afe_port *port, int clk_id, int clk_src, int clk_root, unsigned int freq, int dir); +int q6afe_set_lpass_clock(struct device *dev, int clk_id, int clk_src, + int clk_root, unsigned int freq); +int q6afe_vote_lpass_core_hw(struct device *dev, uint32_t hw_block_id, + char *client_name, uint32_t *client_handle); +int q6afe_unvote_lpass_core_hw(struct device *dev, uint32_t hw_block_id, + uint32_t client_handle); #endif /* __Q6AFE_H__ */ diff --git a/sound/soc/qcom/qdsp6/q6asm-dai.c b/sound/soc/qcom/qdsp6/q6asm-dai.c index 9b7b218f2a20..c9ac9c1d26c4 100644 --- a/sound/soc/qcom/qdsp6/q6asm-dai.c +++ b/sound/soc/qcom/qdsp6/q6asm-dai.c @@ -50,7 +50,7 @@ enum stream_state { struct q6asm_dai_rtd { struct snd_pcm_substream *substream; struct snd_compr_stream *cstream; - struct snd_compr_params codec_param; + struct snd_codec codec; struct snd_dma_buffer dma_buffer; spinlock_t lock; phys_addr_t phys; @@ -64,8 +64,14 @@ struct q6asm_dai_rtd { uint16_t bits_per_sample; uint16_t source; /* Encoding source bit mask */ struct audio_client *audio_client; + uint32_t next_track_stream_id; + bool next_track; + uint32_t stream_id; uint16_t session_id; enum stream_state state; + uint32_t initial_samples_drop; + uint32_t trailing_samples_drop; + bool notify_on_drain; }; struct q6asm_dai_data { @@ -181,8 +187,8 @@ static void event_handler(uint32_t opcode, uint32_t token, switch (opcode) { case ASM_CLIENT_EVENT_CMD_RUN_DONE: if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - q6asm_write_async(prtd->audio_client, - prtd->pcm_count, 0, 0, NO_TIMESTAMP); + q6asm_write_async(prtd->audio_client, prtd->stream_id, + prtd->pcm_count, 0, 0, 0); break; case ASM_CLIENT_EVENT_CMD_EOS_DONE: prtd->state = Q6ASM_STREAM_STOPPED; @@ -191,8 +197,8 @@ static void event_handler(uint32_t opcode, uint32_t token, prtd->pcm_irq_pos += prtd->pcm_count; snd_pcm_period_elapsed(substream); if (prtd->state == Q6ASM_STREAM_RUNNING) - q6asm_write_async(prtd->audio_client, - prtd->pcm_count, 0, 0, NO_TIMESTAMP); + q6asm_write_async(prtd->audio_client, prtd->stream_id, + prtd->pcm_count, 0, 0, 0); break; } @@ -200,7 +206,7 @@ static void event_handler(uint32_t opcode, uint32_t token, prtd->pcm_irq_pos += prtd->pcm_count; snd_pcm_period_elapsed(substream); if (prtd->state == Q6ASM_STREAM_RUNNING) - q6asm_read(prtd->audio_client); + q6asm_read(prtd->audio_client, prtd->stream_id); break; default: @@ -233,7 +239,7 @@ static int q6asm_dai_prepare(struct snd_soc_component *component, /* rate and channels are sent to audio driver */ if (prtd->state) { /* clear the previous setup if any */ - q6asm_cmd(prtd->audio_client, CMD_CLOSE); + q6asm_cmd(prtd->audio_client, prtd->stream_id, CMD_CLOSE); q6asm_unmap_memory_regions(substream->stream, prtd->audio_client); q6routing_stream_close(soc_prtd->dai_link->id, @@ -252,11 +258,13 @@ static int q6asm_dai_prepare(struct snd_soc_component *component, } if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - ret = q6asm_open_write(prtd->audio_client, FORMAT_LINEAR_PCM, - 0, prtd->bits_per_sample); + ret = q6asm_open_write(prtd->audio_client, prtd->stream_id, + FORMAT_LINEAR_PCM, + 0, prtd->bits_per_sample, false); } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { - ret = q6asm_open_read(prtd->audio_client, FORMAT_LINEAR_PCM, - prtd->bits_per_sample); + ret = q6asm_open_read(prtd->audio_client, prtd->stream_id, + FORMAT_LINEAR_PCM, + prtd->bits_per_sample); } if (ret < 0) { @@ -276,17 +284,19 @@ static int q6asm_dai_prepare(struct snd_soc_component *component, if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { ret = q6asm_media_format_block_multi_ch_pcm( - prtd->audio_client, runtime->rate, - runtime->channels, NULL, + prtd->audio_client, prtd->stream_id, + runtime->rate, runtime->channels, NULL, prtd->bits_per_sample); } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { ret = q6asm_enc_cfg_blk_pcm_format_support(prtd->audio_client, - runtime->rate, runtime->channels, - prtd->bits_per_sample); + prtd->stream_id, + runtime->rate, + runtime->channels, + prtd->bits_per_sample); /* Queue the buffers */ for (i = 0; i < runtime->periods; i++) - q6asm_read(prtd->audio_client); + q6asm_read(prtd->audio_client, prtd->stream_id); } if (ret < 0) @@ -308,15 +318,18 @@ static int q6asm_dai_trigger(struct snd_soc_component *component, case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - ret = q6asm_run_nowait(prtd->audio_client, 0, 0, 0); + ret = q6asm_run_nowait(prtd->audio_client, prtd->stream_id, + 0, 0, 0); break; case SNDRV_PCM_TRIGGER_STOP: prtd->state = Q6ASM_STREAM_STOPPED; - ret = q6asm_cmd_nowait(prtd->audio_client, CMD_EOS); + ret = q6asm_cmd_nowait(prtd->audio_client, prtd->stream_id, + CMD_EOS); break; case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - ret = q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE); + ret = q6asm_cmd_nowait(prtd->audio_client, prtd->stream_id, + CMD_PAUSE); break; default: ret = -EINVAL; @@ -361,6 +374,9 @@ static int q6asm_dai_open(struct snd_soc_component *component, return ret; } + /* DSP expects stream id from 1 */ + prtd->stream_id = 1; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) runtime->hw = q6asm_dai_hardware_playback; else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) @@ -427,7 +443,8 @@ static int q6asm_dai_close(struct snd_soc_component *component, if (prtd->audio_client) { if (prtd->state) - q6asm_cmd(prtd->audio_client, CMD_CLOSE); + q6asm_cmd(prtd->audio_client, prtd->stream_id, + CMD_CLOSE); q6asm_unmap_memory_regions(substream->stream, prtd->audio_client); @@ -493,14 +510,21 @@ static void compress_event_handler(uint32_t opcode, uint32_t token, struct q6asm_dai_rtd *prtd = priv; struct snd_compr_stream *substream = prtd->cstream; unsigned long flags; + u32 wflags = 0; uint64_t avail; + uint32_t bytes_written, bytes_to_write; + bool is_last_buffer = false; switch (opcode) { case ASM_CLIENT_EVENT_CMD_RUN_DONE: spin_lock_irqsave(&prtd->lock, flags); if (!prtd->bytes_sent) { - q6asm_write_async(prtd->audio_client, prtd->pcm_count, - 0, 0, NO_TIMESTAMP); + q6asm_stream_remove_initial_silence(prtd->audio_client, + prtd->stream_id, + prtd->initial_samples_drop); + + q6asm_write_async(prtd->audio_client, prtd->stream_id, + prtd->pcm_count, 0, 0, 0); prtd->bytes_sent += prtd->pcm_count; } @@ -508,13 +532,37 @@ static void compress_event_handler(uint32_t opcode, uint32_t token, break; case ASM_CLIENT_EVENT_CMD_EOS_DONE: - prtd->state = Q6ASM_STREAM_STOPPED; + spin_lock_irqsave(&prtd->lock, flags); + if (prtd->notify_on_drain) { + if (substream->partial_drain) { + /* + * Close old stream and make it stale, switch + * the active stream now! + */ + q6asm_cmd_nowait(prtd->audio_client, + prtd->stream_id, + CMD_CLOSE); + /* + * vaild stream ids start from 1, So we are + * toggling this between 1 and 2. + */ + prtd->stream_id = (prtd->stream_id == 1 ? 2 : 1); + } + + snd_compr_drain_notify(prtd->cstream); + prtd->notify_on_drain = false; + + } else { + prtd->state = Q6ASM_STREAM_STOPPED; + } + spin_unlock_irqrestore(&prtd->lock, flags); break; case ASM_CLIENT_EVENT_DATA_WRITE_DONE: spin_lock_irqsave(&prtd->lock, flags); - prtd->copied_total += prtd->pcm_count; + bytes_written = token >> ASM_WRITE_TOKEN_LEN_SHIFT; + prtd->copied_total += bytes_written; snd_compr_fragment_elapsed(substream); if (prtd->state != Q6ASM_STREAM_RUNNING) { @@ -523,13 +571,32 @@ static void compress_event_handler(uint32_t opcode, uint32_t token, } avail = prtd->bytes_received - prtd->bytes_sent; + if (avail > prtd->pcm_count) { + bytes_to_write = prtd->pcm_count; + } else { + if (substream->partial_drain || prtd->notify_on_drain) + is_last_buffer = true; + bytes_to_write = avail; + } - if (avail >= prtd->pcm_count) { - q6asm_write_async(prtd->audio_client, - prtd->pcm_count, 0, 0, NO_TIMESTAMP); - prtd->bytes_sent += prtd->pcm_count; + if (bytes_to_write) { + if (substream->partial_drain && is_last_buffer) { + wflags |= ASM_LAST_BUFFER_FLAG; + q6asm_stream_remove_trailing_silence(prtd->audio_client, + prtd->stream_id, + prtd->trailing_samples_drop); + } + + q6asm_write_async(prtd->audio_client, prtd->stream_id, + bytes_to_write, 0, 0, wflags); + + prtd->bytes_sent += bytes_to_write; } + if (prtd->notify_on_drain && is_last_buffer) + q6asm_cmd_nowait(prtd->audio_client, + prtd->stream_id, CMD_EOS); + spin_unlock_irqrestore(&prtd->lock, flags); break; @@ -560,6 +627,9 @@ static int q6asm_dai_compr_open(struct snd_soc_component *component, if (!prtd) return -ENOMEM; + /* DSP expects stream id from 1 */ + prtd->stream_id = 1; + prtd->cstream = stream; prtd->audio_client = q6asm_audio_client_alloc(dev, (q6asm_cb)compress_event_handler, @@ -606,8 +676,15 @@ static int q6asm_dai_compr_free(struct snd_soc_component *component, struct snd_soc_pcm_runtime *rtd = stream->private_data; if (prtd->audio_client) { - if (prtd->state) - q6asm_cmd(prtd->audio_client, CMD_CLOSE); + if (prtd->state) { + q6asm_cmd(prtd->audio_client, prtd->stream_id, + CMD_CLOSE); + if (prtd->next_track_stream_id) { + q6asm_cmd(prtd->audio_client, + prtd->next_track_stream_id, + CMD_CLOSE); + } + } snd_dma_free_pages(&prtd->dma_buffer); q6asm_unmap_memory_regions(stream->direction, @@ -621,15 +698,13 @@ static int q6asm_dai_compr_free(struct snd_soc_component *component, return 0; } -static int q6asm_dai_compr_set_params(struct snd_soc_component *component, - struct snd_compr_stream *stream, - struct snd_compr_params *params) +static int __q6asm_dai_compr_set_codec_params(struct snd_soc_component *component, + struct snd_compr_stream *stream, + struct snd_codec *codec, + int stream_id) { struct snd_compr_runtime *runtime = stream->runtime; struct q6asm_dai_rtd *prtd = runtime->private_data; - struct snd_soc_pcm_runtime *rtd = stream->private_data; - int dir = stream->direction; - struct q6asm_dai_data *pdata; struct q6asm_flac_cfg flac_cfg; struct q6asm_wma_cfg wma_cfg; struct q6asm_alac_cfg alac_cfg; @@ -643,52 +718,18 @@ static int q6asm_dai_compr_set_params(struct snd_soc_component *component, struct snd_dec_alac *alac; struct snd_dec_ape *ape; - codec_options = &(prtd->codec_param.codec.options); - - - memcpy(&prtd->codec_param, params, sizeof(*params)); - - pdata = snd_soc_component_get_drvdata(component); - if (!pdata) - return -EINVAL; - - if (!prtd || !prtd->audio_client) { - dev_err(dev, "private data null or audio client freed\n"); - return -EINVAL; - } - - prtd->periods = runtime->fragments; - prtd->pcm_count = runtime->fragment_size; - prtd->pcm_size = runtime->fragments * runtime->fragment_size; - prtd->bits_per_sample = 16; - if (dir == SND_COMPRESS_PLAYBACK) { - ret = q6asm_open_write(prtd->audio_client, params->codec.id, - params->codec.profile, prtd->bits_per_sample); - - if (ret < 0) { - dev_err(dev, "q6asm_open_write failed\n"); - q6asm_audio_client_free(prtd->audio_client); - prtd->audio_client = NULL; - return ret; - } - } + codec_options = &(prtd->codec.options); - prtd->session_id = q6asm_get_session_id(prtd->audio_client); - ret = q6routing_stream_open(rtd->dai_link->id, LEGACY_PCM_MODE, - prtd->session_id, dir); - if (ret) { - dev_err(dev, "Stream reg failed ret:%d\n", ret); - return ret; - } + memcpy(&prtd->codec, codec, sizeof(*codec)); - switch (params->codec.id) { + switch (codec->id) { case SND_AUDIOCODEC_FLAC: memset(&flac_cfg, 0x0, sizeof(struct q6asm_flac_cfg)); flac = &codec_options->flac_d; - flac_cfg.ch_cfg = params->codec.ch_in; - flac_cfg.sample_rate = params->codec.sample_rate; + flac_cfg.ch_cfg = codec->ch_in; + flac_cfg.sample_rate = codec->sample_rate; flac_cfg.stream_info_present = 1; flac_cfg.sample_size = flac->sample_size; flac_cfg.min_blk_size = flac->min_blk_size; @@ -697,6 +738,7 @@ static int q6asm_dai_compr_set_params(struct snd_soc_component *component, flac_cfg.min_frame_size = flac->min_frame_size; ret = q6asm_stream_media_format_block_flac(prtd->audio_client, + stream_id, &flac_cfg); if (ret < 0) { dev_err(dev, "FLAC CMD Format block failed:%d\n", ret); @@ -709,10 +751,10 @@ static int q6asm_dai_compr_set_params(struct snd_soc_component *component, memset(&wma_cfg, 0x0, sizeof(struct q6asm_wma_cfg)); - wma_cfg.sample_rate = params->codec.sample_rate; - wma_cfg.num_channels = params->codec.ch_in; - wma_cfg.bytes_per_sec = params->codec.bit_rate / 8; - wma_cfg.block_align = params->codec.align; + wma_cfg.sample_rate = codec->sample_rate; + wma_cfg.num_channels = codec->ch_in; + wma_cfg.bytes_per_sec = codec->bit_rate / 8; + wma_cfg.block_align = codec->align; wma_cfg.bits_per_sample = prtd->bits_per_sample; wma_cfg.enc_options = wma->encoder_option; wma_cfg.adv_enc_options = wma->adv_encoder_option; @@ -726,7 +768,7 @@ static int q6asm_dai_compr_set_params(struct snd_soc_component *component, return -EINVAL; /* check the codec profile */ - switch (params->codec.profile) { + switch (codec->profile) { case SND_AUDIOPROFILE_WMA9: wma_cfg.fmtag = 0x161; wma_v9 = 1; @@ -750,16 +792,18 @@ static int q6asm_dai_compr_set_params(struct snd_soc_component *component, default: dev_err(dev, "Unknown WMA profile:%x\n", - params->codec.profile); + codec->profile); return -EIO; } if (wma_v9) ret = q6asm_stream_media_format_block_wma_v9( - prtd->audio_client, &wma_cfg); + prtd->audio_client, stream_id, + &wma_cfg); else ret = q6asm_stream_media_format_block_wma_v10( - prtd->audio_client, &wma_cfg); + prtd->audio_client, stream_id, + &wma_cfg); if (ret < 0) { dev_err(dev, "WMA9 CMD failed:%d\n", ret); return -EIO; @@ -770,10 +814,10 @@ static int q6asm_dai_compr_set_params(struct snd_soc_component *component, memset(&alac_cfg, 0x0, sizeof(alac_cfg)); alac = &codec_options->alac_d; - alac_cfg.sample_rate = params->codec.sample_rate; - alac_cfg.avg_bit_rate = params->codec.bit_rate; + alac_cfg.sample_rate = codec->sample_rate; + alac_cfg.avg_bit_rate = codec->bit_rate; alac_cfg.bit_depth = prtd->bits_per_sample; - alac_cfg.num_channels = params->codec.ch_in; + alac_cfg.num_channels = codec->ch_in; alac_cfg.frame_length = alac->frame_length; alac_cfg.pb = alac->pb; @@ -783,7 +827,7 @@ static int q6asm_dai_compr_set_params(struct snd_soc_component *component, alac_cfg.compatible_version = alac->compatible_version; alac_cfg.max_frame_bytes = alac->max_frame_bytes; - switch (params->codec.ch_in) { + switch (codec->ch_in) { case 1: alac_cfg.channel_layout_tag = ALAC_CH_LAYOUT_MONO; break; @@ -792,6 +836,7 @@ static int q6asm_dai_compr_set_params(struct snd_soc_component *component, break; } ret = q6asm_stream_media_format_block_alac(prtd->audio_client, + stream_id, &alac_cfg); if (ret < 0) { dev_err(dev, "ALAC CMD Format block failed:%d\n", ret); @@ -803,8 +848,8 @@ static int q6asm_dai_compr_set_params(struct snd_soc_component *component, memset(&ape_cfg, 0x0, sizeof(ape_cfg)); ape = &codec_options->ape_d; - ape_cfg.sample_rate = params->codec.sample_rate; - ape_cfg.num_channels = params->codec.ch_in; + ape_cfg.sample_rate = codec->sample_rate; + ape_cfg.num_channels = codec->ch_in; ape_cfg.bits_per_sample = prtd->bits_per_sample; ape_cfg.compatible_version = ape->compatible_version; @@ -816,6 +861,7 @@ static int q6asm_dai_compr_set_params(struct snd_soc_component *component, ape_cfg.seek_table_present = ape->seek_table_present; ret = q6asm_stream_media_format_block_ape(prtd->audio_client, + stream_id, &ape_cfg); if (ret < 0) { dev_err(dev, "APE CMD Format block failed:%d\n", ret); @@ -827,6 +873,64 @@ static int q6asm_dai_compr_set_params(struct snd_soc_component *component, break; } + return 0; +} + +static int q6asm_dai_compr_set_params(struct snd_soc_component *component, + struct snd_compr_stream *stream, + struct snd_compr_params *params) +{ + struct snd_compr_runtime *runtime = stream->runtime; + struct q6asm_dai_rtd *prtd = runtime->private_data; + struct snd_soc_pcm_runtime *rtd = stream->private_data; + int dir = stream->direction; + struct q6asm_dai_data *pdata; + struct device *dev = component->dev; + int ret; + + pdata = snd_soc_component_get_drvdata(component); + if (!pdata) + return -EINVAL; + + if (!prtd || !prtd->audio_client) { + dev_err(dev, "private data null or audio client freed\n"); + return -EINVAL; + } + + prtd->periods = runtime->fragments; + prtd->pcm_count = runtime->fragment_size; + prtd->pcm_size = runtime->fragments * runtime->fragment_size; + prtd->bits_per_sample = 16; + + if (dir == SND_COMPRESS_PLAYBACK) { + ret = q6asm_open_write(prtd->audio_client, prtd->stream_id, params->codec.id, + params->codec.profile, prtd->bits_per_sample, + true); + + if (ret < 0) { + dev_err(dev, "q6asm_open_write failed\n"); + q6asm_audio_client_free(prtd->audio_client); + prtd->audio_client = NULL; + return ret; + } + } + + prtd->session_id = q6asm_get_session_id(prtd->audio_client); + ret = q6routing_stream_open(rtd->dai_link->id, LEGACY_PCM_MODE, + prtd->session_id, dir); + if (ret) { + dev_err(dev, "Stream reg failed ret:%d\n", ret); + return ret; + } + + ret = __q6asm_dai_compr_set_codec_params(component, stream, + ¶ms->codec, + prtd->stream_id); + if (ret) { + dev_err(dev, "codec param setup failed ret:%d\n", ret); + return ret; + } + ret = q6asm_map_memory_regions(dir, prtd->audio_client, prtd->phys, (prtd->pcm_size / prtd->periods), prtd->periods); @@ -841,6 +945,55 @@ static int q6asm_dai_compr_set_params(struct snd_soc_component *component, return 0; } +static int q6asm_dai_compr_set_metadata(struct snd_soc_component *component, + struct snd_compr_stream *stream, + struct snd_compr_metadata *metadata) +{ + struct snd_compr_runtime *runtime = stream->runtime; + struct q6asm_dai_rtd *prtd = runtime->private_data; + int ret = 0; + + switch (metadata->key) { + case SNDRV_COMPRESS_ENCODER_PADDING: + prtd->trailing_samples_drop = metadata->value[0]; + break; + case SNDRV_COMPRESS_ENCODER_DELAY: + prtd->initial_samples_drop = metadata->value[0]; + if (prtd->next_track_stream_id) { + ret = q6asm_open_write(prtd->audio_client, + prtd->next_track_stream_id, + prtd->codec.id, + prtd->codec.profile, + prtd->bits_per_sample, + true); + if (ret < 0) { + dev_err(component->dev, "q6asm_open_write failed\n"); + return ret; + } + ret = __q6asm_dai_compr_set_codec_params(component, stream, + &prtd->codec, + prtd->next_track_stream_id); + if (ret < 0) { + dev_err(component->dev, "q6asm_open_write failed\n"); + return ret; + } + + ret = q6asm_stream_remove_initial_silence(prtd->audio_client, + prtd->next_track_stream_id, + prtd->initial_samples_drop); + prtd->next_track_stream_id = 0; + + } + + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + static int q6asm_dai_compr_trigger(struct snd_soc_component *component, struct snd_compr_stream *stream, int cmd) { @@ -852,15 +1005,26 @@ static int q6asm_dai_compr_trigger(struct snd_soc_component *component, case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - ret = q6asm_run_nowait(prtd->audio_client, 0, 0, 0); + ret = q6asm_run_nowait(prtd->audio_client, prtd->stream_id, + 0, 0, 0); break; case SNDRV_PCM_TRIGGER_STOP: prtd->state = Q6ASM_STREAM_STOPPED; - ret = q6asm_cmd_nowait(prtd->audio_client, CMD_EOS); + ret = q6asm_cmd_nowait(prtd->audio_client, prtd->stream_id, + CMD_EOS); break; case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - ret = q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE); + ret = q6asm_cmd_nowait(prtd->audio_client, prtd->stream_id, + CMD_PAUSE); + break; + case SND_COMPR_TRIGGER_NEXT_TRACK: + prtd->next_track = true; + prtd->next_track_stream_id = (prtd->stream_id == 1 ? 2 : 1); + break; + case SND_COMPR_TRIGGER_DRAIN: + case SND_COMPR_TRIGGER_PARTIAL_DRAIN: + prtd->notify_on_drain = true; break; default: ret = -EINVAL; @@ -888,16 +1052,71 @@ static int q6asm_dai_compr_pointer(struct snd_soc_component *component, return 0; } -static int q6asm_dai_compr_ack(struct snd_soc_component *component, - struct snd_compr_stream *stream, - size_t count) +static int q6asm_compr_copy(struct snd_soc_component *component, + struct snd_compr_stream *stream, char __user *buf, + size_t count) { struct snd_compr_runtime *runtime = stream->runtime; struct q6asm_dai_rtd *prtd = runtime->private_data; unsigned long flags; + u32 wflags = 0; + int avail, bytes_in_flight = 0; + void *dstn; + size_t copy; + u32 app_pointer; + u32 bytes_received; + + bytes_received = prtd->bytes_received; + + /** + * Make sure that next track data pointer is aligned at 32 bit boundary + * This is a Mandatory requirement from DSP data buffers alignment + */ + if (prtd->next_track) + bytes_received = ALIGN(prtd->bytes_received, prtd->pcm_count); + + app_pointer = bytes_received/prtd->pcm_size; + app_pointer = bytes_received - (app_pointer * prtd->pcm_size); + dstn = prtd->dma_buffer.area + app_pointer; + + if (count < prtd->pcm_size - app_pointer) { + if (copy_from_user(dstn, buf, count)) + return -EFAULT; + } else { + copy = prtd->pcm_size - app_pointer; + if (copy_from_user(dstn, buf, copy)) + return -EFAULT; + if (copy_from_user(prtd->dma_buffer.area, buf + copy, + count - copy)) + return -EFAULT; + } spin_lock_irqsave(&prtd->lock, flags); - prtd->bytes_received += count; + + bytes_in_flight = prtd->bytes_received - prtd->copied_total; + + if (prtd->next_track) { + prtd->next_track = false; + prtd->copied_total = ALIGN(prtd->copied_total, prtd->pcm_count); + prtd->bytes_sent = ALIGN(prtd->bytes_sent, prtd->pcm_count); + } + + prtd->bytes_received = bytes_received + count; + + /* Kick off the data to dsp if its starving!! */ + if (prtd->state == Q6ASM_STREAM_RUNNING && (bytes_in_flight == 0)) { + uint32_t bytes_to_write = prtd->pcm_count; + + avail = prtd->bytes_received - prtd->bytes_sent; + + if (avail < prtd->pcm_count) + bytes_to_write = avail; + + q6asm_write_async(prtd->audio_client, prtd->stream_id, + bytes_to_write, 0, 0, wflags); + prtd->bytes_sent += bytes_to_write; + } + spin_unlock_irqrestore(&prtd->lock, flags); return count; @@ -954,12 +1173,13 @@ static struct snd_compress_ops q6asm_dai_compress_ops = { .open = q6asm_dai_compr_open, .free = q6asm_dai_compr_free, .set_params = q6asm_dai_compr_set_params, + .set_metadata = q6asm_dai_compr_set_metadata, .pointer = q6asm_dai_compr_pointer, .trigger = q6asm_dai_compr_trigger, .get_caps = q6asm_dai_compr_get_caps, .get_codec_caps = q6asm_dai_compr_get_codec_caps, .mmap = q6asm_dai_compr_mmap, - .ack = q6asm_dai_compr_ack, + .copy = q6asm_compr_copy, }; static int q6asm_dai_pcm_new(struct snd_soc_component *component, @@ -1114,11 +1334,13 @@ static int q6asm_dai_probe(struct platform_device *pdev) pdata->dais, pdata->num_dais); } +#ifdef CONFIG_OF static const struct of_device_id q6asm_dai_device_id[] = { { .compatible = "qcom,q6asm-dais" }, {}, }; MODULE_DEVICE_TABLE(of, q6asm_dai_device_id); +#endif static struct platform_driver q6asm_dai_platform_driver = { .driver = { diff --git a/sound/soc/qcom/qdsp6/q6asm.c b/sound/soc/qcom/qdsp6/q6asm.c index 755062eadcc8..c547c560cb24 100644 --- a/sound/soc/qcom/qdsp6/q6asm.c +++ b/sound/soc/qcom/qdsp6/q6asm.c @@ -51,6 +51,8 @@ #define ASM_STREAM_CMD_OPEN_READWRITE_V2 0x00010D8D #define ASM_MEDIA_FMT_ALAC 0x00012f31 #define ASM_MEDIA_FMT_APE 0x00012f32 +#define ASM_DATA_CMD_REMOVE_INITIAL_SILENCE 0x00010D67 +#define ASM_DATA_CMD_REMOVE_TRAILING_SILENCE 0x00010D68 #define ASM_LEGACY_STREAM_SESSION 0 @@ -270,7 +272,6 @@ struct audio_client { wait_queue_head_t cmd_wait; struct aprv2_ibasic_rsp_result_t result; int perf_mode; - int stream_id; struct q6asm *q6asm; struct device *dev; }; @@ -640,6 +641,8 @@ static int32_t q6asm_stream_callback(struct apr_device *adev, case ASM_STREAM_CMD_OPEN_READWRITE_V2: case ASM_STREAM_CMD_SET_ENCDEC_PARAM: case ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2: + case ASM_DATA_CMD_REMOVE_INITIAL_SILENCE: + case ASM_DATA_CMD_REMOVE_TRAILING_SILENCE: if (result->status != 0) { dev_err(ac->dev, "cmd = 0x%x returned error = 0x%x\n", @@ -671,6 +674,7 @@ static int32_t q6asm_stream_callback(struct apr_device *adev, if (ac->io_mode & ASM_SYNC_IO_MODE) { phys_addr_t phys; unsigned long flags; + int token = hdr->token & ASM_WRITE_TOKEN_MASK; spin_lock_irqsave(&ac->lock, flags); @@ -682,12 +686,12 @@ static int32_t q6asm_stream_callback(struct apr_device *adev, goto done; } - phys = port->buf[hdr->token].phys; + phys = port->buf[token].phys; if (lower_32_bits(phys) != result->opcode || upper_32_bits(phys) != result->status) { dev_err(ac->dev, "Expected addr %pa\n", - &port->buf[hdr->token].phys); + &port->buf[token].phys); spin_unlock_irqrestore(&ac->lock, flags); ret = -EINVAL; goto done; @@ -828,21 +832,21 @@ EXPORT_SYMBOL_GPL(q6asm_get_session_id); * @dev: Pointer to asm child device. * @cb: event callback. * @priv: private data associated with this client. - * @stream_id: stream id + * @session_id: session id * @perf_mode: performace mode for this client * * Return: Will be an error pointer on error or a valid audio client * on success. */ struct audio_client *q6asm_audio_client_alloc(struct device *dev, q6asm_cb cb, - void *priv, int stream_id, + void *priv, int session_id, int perf_mode) { struct q6asm *a = dev_get_drvdata(dev->parent); struct audio_client *ac; unsigned long flags; - ac = q6asm_get_audio_client(a, stream_id + 1); + ac = q6asm_get_audio_client(a, session_id + 1); if (ac) { dev_err(dev, "Audio Client already active\n"); return ac; @@ -853,17 +857,15 @@ struct audio_client *q6asm_audio_client_alloc(struct device *dev, q6asm_cb cb, return ERR_PTR(-ENOMEM); spin_lock_irqsave(&a->slock, flags); - a->session[stream_id + 1] = ac; + a->session[session_id + 1] = ac; spin_unlock_irqrestore(&a->slock, flags); - ac->session = stream_id + 1; + ac->session = session_id + 1; ac->cb = cb; ac->dev = dev; ac->q6asm = a; ac->priv = priv; ac->io_mode = ASM_SYNC_IO_MODE; ac->perf_mode = perf_mode; - /* DSP expects stream id from 1 */ - ac->stream_id = 1; ac->adev = a->adev; kref_init(&ac->refcount); @@ -913,14 +915,17 @@ err: /** * q6asm_open_write() - Open audio client for writing * @ac: audio client pointer + * @stream_id: stream id of q6asm session * @format: audio sample format * @codec_profile: compressed format profile * @bits_per_sample: bits per sample + * @is_gapless: flag to indicate if this is a gapless stream * * Return: Will be an negative value on error or zero on success */ -int q6asm_open_write(struct audio_client *ac, uint32_t format, - u32 codec_profile, uint16_t bits_per_sample) +int q6asm_open_write(struct audio_client *ac, uint32_t stream_id, + uint32_t format, u32 codec_profile, + uint16_t bits_per_sample, bool is_gapless) { struct asm_stream_cmd_open_write_v3 *open; struct apr_pkt *pkt; @@ -935,11 +940,13 @@ int q6asm_open_write(struct audio_client *ac, uint32_t format, pkt = p; open = p + APR_HDR_SIZE; - q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, ac->stream_id); + q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id); pkt->hdr.opcode = ASM_STREAM_CMD_OPEN_WRITE_V3; open->mode_flags = 0x00; open->mode_flags |= ASM_LEGACY_STREAM_SESSION; + if (is_gapless) + open->mode_flags |= BIT(ASM_SHIFT_GAPLESS_MODE_FLAG); /* source endpoint : matrix */ open->sink_endpointype = ASM_END_POINT_DEVICE_MATRIX; @@ -998,8 +1005,9 @@ err: } EXPORT_SYMBOL_GPL(q6asm_open_write); -static int __q6asm_run(struct audio_client *ac, uint32_t flags, - uint32_t msw_ts, uint32_t lsw_ts, bool wait) +static int __q6asm_run(struct audio_client *ac, uint32_t stream_id, + uint32_t flags, uint32_t msw_ts, uint32_t lsw_ts, + bool wait) { struct asm_session_cmd_run_v2 *run; struct apr_pkt *pkt; @@ -1014,7 +1022,7 @@ static int __q6asm_run(struct audio_client *ac, uint32_t flags, pkt = p; run = p + APR_HDR_SIZE; - q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, ac->stream_id); + q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id); pkt->hdr.opcode = ASM_SESSION_CMD_RUN_V2; run->flags = flags; @@ -1036,16 +1044,17 @@ static int __q6asm_run(struct audio_client *ac, uint32_t flags, * q6asm_run() - start the audio client * * @ac: audio client pointer + * @stream_id: stream id of q6asm session * @flags: flags associated with write * @msw_ts: timestamp msw * @lsw_ts: timestamp lsw * * Return: Will be an negative value on error or zero on success */ -int q6asm_run(struct audio_client *ac, uint32_t flags, +int q6asm_run(struct audio_client *ac, uint32_t stream_id, uint32_t flags, uint32_t msw_ts, uint32_t lsw_ts) { - return __q6asm_run(ac, flags, msw_ts, lsw_ts, true); + return __q6asm_run(ac, stream_id, flags, msw_ts, lsw_ts, true); } EXPORT_SYMBOL_GPL(q6asm_run); @@ -1053,16 +1062,17 @@ EXPORT_SYMBOL_GPL(q6asm_run); * q6asm_run_nowait() - start the audio client withou blocking * * @ac: audio client pointer + * @stream_id: stream id * @flags: flags associated with write * @msw_ts: timestamp msw * @lsw_ts: timestamp lsw * * Return: Will be an negative value on error or zero on success */ -int q6asm_run_nowait(struct audio_client *ac, uint32_t flags, - uint32_t msw_ts, uint32_t lsw_ts) +int q6asm_run_nowait(struct audio_client *ac, uint32_t stream_id, + uint32_t flags, uint32_t msw_ts, uint32_t lsw_ts) { - return __q6asm_run(ac, flags, msw_ts, lsw_ts, false); + return __q6asm_run(ac, stream_id, flags, msw_ts, lsw_ts, false); } EXPORT_SYMBOL_GPL(q6asm_run_nowait); @@ -1070,6 +1080,7 @@ EXPORT_SYMBOL_GPL(q6asm_run_nowait); * q6asm_media_format_block_multi_ch_pcm() - setup pcm configuration * * @ac: audio client pointer + * @stream_id: stream id * @rate: audio sample rate * @channels: number of audio channels. * @channel_map: channel map pointer @@ -1078,6 +1089,7 @@ EXPORT_SYMBOL_GPL(q6asm_run_nowait); * Return: Will be an negative value on error or zero on success */ int q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac, + uint32_t stream_id, uint32_t rate, uint32_t channels, u8 channel_map[PCM_MAX_NUM_CHANNEL], uint16_t bits_per_sample) @@ -1096,7 +1108,7 @@ int q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac, pkt = p; fmt = p + APR_HDR_SIZE; - q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, ac->stream_id); + q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id); pkt->hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; fmt->fmt_blk.fmt_blk_size = sizeof(*fmt) - sizeof(fmt->fmt_blk); @@ -1125,8 +1137,8 @@ err: } EXPORT_SYMBOL_GPL(q6asm_media_format_block_multi_ch_pcm); - int q6asm_stream_media_format_block_flac(struct audio_client *ac, + uint32_t stream_id, struct q6asm_flac_cfg *cfg) { struct asm_flac_fmt_blk_v2 *fmt; @@ -1142,7 +1154,7 @@ int q6asm_stream_media_format_block_flac(struct audio_client *ac, pkt = p; fmt = p + APR_HDR_SIZE; - q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, ac->stream_id); + q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id); pkt->hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; fmt->fmt_blk.fmt_blk_size = sizeof(*fmt) - sizeof(fmt->fmt_blk); @@ -1163,6 +1175,7 @@ int q6asm_stream_media_format_block_flac(struct audio_client *ac, EXPORT_SYMBOL_GPL(q6asm_stream_media_format_block_flac); int q6asm_stream_media_format_block_wma_v9(struct audio_client *ac, + uint32_t stream_id, struct q6asm_wma_cfg *cfg) { struct asm_wmastdv9_fmt_blk_v2 *fmt; @@ -1178,7 +1191,7 @@ int q6asm_stream_media_format_block_wma_v9(struct audio_client *ac, pkt = p; fmt = p + APR_HDR_SIZE; - q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, ac->stream_id); + q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id); pkt->hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; fmt->fmt_blk.fmt_blk_size = sizeof(*fmt) - sizeof(fmt->fmt_blk); @@ -1200,6 +1213,7 @@ int q6asm_stream_media_format_block_wma_v9(struct audio_client *ac, EXPORT_SYMBOL_GPL(q6asm_stream_media_format_block_wma_v9); int q6asm_stream_media_format_block_wma_v10(struct audio_client *ac, + uint32_t stream_id, struct q6asm_wma_cfg *cfg) { struct asm_wmaprov10_fmt_blk_v2 *fmt; @@ -1215,7 +1229,7 @@ int q6asm_stream_media_format_block_wma_v10(struct audio_client *ac, pkt = p; fmt = p + APR_HDR_SIZE; - q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, ac->stream_id); + q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id); pkt->hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; fmt->fmt_blk.fmt_blk_size = sizeof(*fmt) - sizeof(fmt->fmt_blk); @@ -1238,6 +1252,7 @@ int q6asm_stream_media_format_block_wma_v10(struct audio_client *ac, EXPORT_SYMBOL_GPL(q6asm_stream_media_format_block_wma_v10); int q6asm_stream_media_format_block_alac(struct audio_client *ac, + uint32_t stream_id, struct q6asm_alac_cfg *cfg) { struct asm_alac_fmt_blk_v2 *fmt; @@ -1253,7 +1268,7 @@ int q6asm_stream_media_format_block_alac(struct audio_client *ac, pkt = p; fmt = p + APR_HDR_SIZE; - q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, ac->stream_id); + q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id); pkt->hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; fmt->fmt_blk.fmt_blk_size = sizeof(*fmt) - sizeof(fmt->fmt_blk); @@ -1279,6 +1294,7 @@ int q6asm_stream_media_format_block_alac(struct audio_client *ac, EXPORT_SYMBOL_GPL(q6asm_stream_media_format_block_alac); int q6asm_stream_media_format_block_ape(struct audio_client *ac, + uint32_t stream_id, struct q6asm_ape_cfg *cfg) { struct asm_ape_fmt_blk_v2 *fmt; @@ -1294,7 +1310,7 @@ int q6asm_stream_media_format_block_ape(struct audio_client *ac, pkt = p; fmt = p + APR_HDR_SIZE; - q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, ac->stream_id); + q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id); pkt->hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; fmt->fmt_blk.fmt_blk_size = sizeof(*fmt) - sizeof(fmt->fmt_blk); @@ -1317,10 +1333,60 @@ int q6asm_stream_media_format_block_ape(struct audio_client *ac, } EXPORT_SYMBOL_GPL(q6asm_stream_media_format_block_ape); +static int q6asm_stream_remove_silence(struct audio_client *ac, uint32_t stream_id, + uint32_t cmd, + uint32_t num_samples) +{ + uint32_t *samples; + struct apr_pkt *pkt; + void *p; + int rc, pkt_size; + + pkt_size = APR_HDR_SIZE + sizeof(uint32_t); + p = kzalloc(pkt_size, GFP_ATOMIC); + if (!p) + return -ENOMEM; + + pkt = p; + samples = p + APR_HDR_SIZE; + + q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id); + + pkt->hdr.opcode = cmd; + *samples = num_samples; + rc = apr_send_pkt(ac->adev, pkt); + if (rc == pkt_size) + rc = 0; + + kfree(pkt); + + return rc; +} + +int q6asm_stream_remove_initial_silence(struct audio_client *ac, + uint32_t stream_id, + uint32_t initial_samples) +{ + return q6asm_stream_remove_silence(ac, stream_id, + ASM_DATA_CMD_REMOVE_INITIAL_SILENCE, + initial_samples); +} +EXPORT_SYMBOL_GPL(q6asm_stream_remove_initial_silence); + +int q6asm_stream_remove_trailing_silence(struct audio_client *ac, uint32_t stream_id, + uint32_t trailing_samples) +{ + return q6asm_stream_remove_silence(ac, stream_id, + ASM_DATA_CMD_REMOVE_TRAILING_SILENCE, + trailing_samples); +} +EXPORT_SYMBOL_GPL(q6asm_stream_remove_trailing_silence); + /** * q6asm_enc_cfg_blk_pcm_format_support() - setup pcm configuration for capture * * @ac: audio client pointer + * @stream_id: stream id * @rate: audio sample rate * @channels: number of audio channels. * @bits_per_sample: bits per sample @@ -1328,7 +1394,9 @@ EXPORT_SYMBOL_GPL(q6asm_stream_media_format_block_ape); * Return: Will be an negative value on error or zero on success */ int q6asm_enc_cfg_blk_pcm_format_support(struct audio_client *ac, - uint32_t rate, uint32_t channels, uint16_t bits_per_sample) + uint32_t stream_id, uint32_t rate, + uint32_t channels, + uint16_t bits_per_sample) { struct asm_multi_channel_pcm_enc_cfg_v2 *enc_cfg; struct apr_pkt *pkt; @@ -1344,7 +1412,7 @@ int q6asm_enc_cfg_blk_pcm_format_support(struct audio_client *ac, pkt = p; enc_cfg = p + APR_HDR_SIZE; - q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, ac->stream_id); + q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id); pkt->hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM; enc_cfg->encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2; @@ -1376,10 +1444,11 @@ EXPORT_SYMBOL_GPL(q6asm_enc_cfg_blk_pcm_format_support); * q6asm_read() - read data of period size from audio client * * @ac: audio client pointer + * @stream_id: stream id * * Return: Will be an negative value on error or zero on success */ -int q6asm_read(struct audio_client *ac) +int q6asm_read(struct audio_client *ac, uint32_t stream_id) { struct asm_data_cmd_read_v2 *read; struct audio_port_data *port; @@ -1400,7 +1469,7 @@ int q6asm_read(struct audio_client *ac) spin_lock_irqsave(&ac->lock, flags); port = &ac->port[SNDRV_PCM_STREAM_CAPTURE]; - q6asm_add_hdr(ac, &pkt->hdr, pkt_size, false, ac->stream_id); + q6asm_add_hdr(ac, &pkt->hdr, pkt_size, false, stream_id); ab = &port->buf[port->dsp_buf]; pkt->hdr.opcode = ASM_DATA_CMD_READ_V2; read->buf_addr_lsw = lower_32_bits(ab->phys); @@ -1428,7 +1497,7 @@ int q6asm_read(struct audio_client *ac) } EXPORT_SYMBOL_GPL(q6asm_read); -static int __q6asm_open_read(struct audio_client *ac, +static int __q6asm_open_read(struct audio_client *ac, uint32_t stream_id, uint32_t format, uint16_t bits_per_sample) { struct asm_stream_cmd_open_read_v3 *open; @@ -1444,7 +1513,7 @@ static int __q6asm_open_read(struct audio_client *ac, pkt = p; open = p + APR_HDR_SIZE; - q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, ac->stream_id); + q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id); pkt->hdr.opcode = ASM_STREAM_CMD_OPEN_READ_V3; /* Stream prio : High, provide meta info with encoded frames */ open->src_endpointype = ASM_END_POINT_DEVICE_MATRIX; @@ -1475,15 +1544,16 @@ static int __q6asm_open_read(struct audio_client *ac, * q6asm_open_read() - Open audio client for reading * * @ac: audio client pointer + * @stream_id: stream id * @format: audio sample format * @bits_per_sample: bits per sample * * Return: Will be an negative value on error or zero on success */ -int q6asm_open_read(struct audio_client *ac, uint32_t format, - uint16_t bits_per_sample) +int q6asm_open_read(struct audio_client *ac, uint32_t stream_id, + uint32_t format, uint16_t bits_per_sample) { - return __q6asm_open_read(ac, format, bits_per_sample); + return __q6asm_open_read(ac, stream_id, format, bits_per_sample); } EXPORT_SYMBOL_GPL(q6asm_open_read); @@ -1491,6 +1561,7 @@ EXPORT_SYMBOL_GPL(q6asm_open_read); * q6asm_write_async() - non blocking write * * @ac: audio client pointer + * @stream_id: stream id * @len: length in bytes * @msw_ts: timestamp msw * @lsw_ts: timestamp lsw @@ -1498,8 +1569,8 @@ EXPORT_SYMBOL_GPL(q6asm_open_read); * * Return: Will be an negative value on error or zero on success */ -int q6asm_write_async(struct audio_client *ac, uint32_t len, uint32_t msw_ts, - uint32_t lsw_ts, uint32_t wflags) +int q6asm_write_async(struct audio_client *ac, uint32_t stream_id, uint32_t len, + uint32_t msw_ts, uint32_t lsw_ts, uint32_t wflags) { struct asm_data_cmd_write_v2 *write; struct audio_port_data *port; @@ -1520,10 +1591,10 @@ int q6asm_write_async(struct audio_client *ac, uint32_t len, uint32_t msw_ts, spin_lock_irqsave(&ac->lock, flags); port = &ac->port[SNDRV_PCM_STREAM_PLAYBACK]; - q6asm_add_hdr(ac, &pkt->hdr, pkt_size, false, ac->stream_id); + q6asm_add_hdr(ac, &pkt->hdr, pkt_size, false, stream_id); ab = &port->buf[port->dsp_buf]; - pkt->hdr.token = port->dsp_buf; + pkt->hdr.token = port->dsp_buf | (len << ASM_WRITE_TOKEN_LEN_SHIFT); pkt->hdr.opcode = ASM_DATA_CMD_WRITE_V2; write->buf_addr_lsw = lower_32_bits(ab->phys); write->buf_addr_msw = upper_32_bits(ab->phys); @@ -1534,10 +1605,7 @@ int q6asm_write_async(struct audio_client *ac, uint32_t len, uint32_t msw_ts, write->mem_map_handle = ac->port[SNDRV_PCM_STREAM_PLAYBACK].mem_map_handle; - if (wflags == NO_TIMESTAMP) - write->flags = (wflags & 0x800000FF); - else - write->flags = (0x80000000 | wflags); + write->flags = wflags; port->dsp_buf++; @@ -1567,9 +1635,9 @@ static void q6asm_reset_buf_state(struct audio_client *ac) spin_unlock_irqrestore(&ac->lock, flags); } -static int __q6asm_cmd(struct audio_client *ac, int cmd, bool wait) +static int __q6asm_cmd(struct audio_client *ac, uint32_t stream_id, int cmd, + bool wait) { - int stream_id = ac->stream_id; struct apr_pkt pkt; int rc; @@ -1616,13 +1684,14 @@ static int __q6asm_cmd(struct audio_client *ac, int cmd, bool wait) * q6asm_cmd() - run cmd on audio client * * @ac: audio client pointer + * @stream_id: stream id * @cmd: command to run on audio client. * * Return: Will be an negative value on error or zero on success */ -int q6asm_cmd(struct audio_client *ac, int cmd) +int q6asm_cmd(struct audio_client *ac, uint32_t stream_id, int cmd) { - return __q6asm_cmd(ac, cmd, true); + return __q6asm_cmd(ac, stream_id, cmd, true); } EXPORT_SYMBOL_GPL(q6asm_cmd); @@ -1630,13 +1699,14 @@ EXPORT_SYMBOL_GPL(q6asm_cmd); * q6asm_cmd_nowait() - non blocking, run cmd on audio client * * @ac: audio client pointer + * @stream_id: stream id * @cmd: command to run on audio client. * * Return: Will be an negative value on error or zero on success */ -int q6asm_cmd_nowait(struct audio_client *ac, int cmd) +int q6asm_cmd_nowait(struct audio_client *ac, uint32_t stream_id, int cmd) { - return __q6asm_cmd(ac, cmd, false); + return __q6asm_cmd(ac, stream_id, cmd, false); } EXPORT_SYMBOL_GPL(q6asm_cmd_nowait); @@ -1666,11 +1736,14 @@ static int q6asm_remove(struct apr_device *adev) return 0; } + +#ifdef CONFIG_OF static const struct of_device_id q6asm_device_id[] = { { .compatible = "qcom,q6asm" }, {}, }; MODULE_DEVICE_TABLE(of, q6asm_device_id); +#endif static struct apr_driver qcom_q6asm_driver = { .probe = q6asm_probe, diff --git a/sound/soc/qcom/qdsp6/q6asm.h b/sound/soc/qcom/qdsp6/q6asm.h index 38a207d6cd95..82e584aa534f 100644 --- a/sound/soc/qcom/qdsp6/q6asm.h +++ b/sound/soc/qcom/qdsp6/q6asm.h @@ -20,6 +20,9 @@ #define ASM_CLIENT_EVENT_CMD_RUN_DONE 0x1008 #define ASM_CLIENT_EVENT_DATA_WRITE_DONE 0x1009 #define ASM_CLIENT_EVENT_DATA_READ_DONE 0x100a +#define ASM_WRITE_TOKEN_MASK GENMASK(15, 0) +#define ASM_WRITE_TOKEN_LEN_MASK GENMASK(31, 16) +#define ASM_WRITE_TOKEN_LEN_SHIFT 16 enum { LEGACY_PCM_MODE = 0, @@ -29,8 +32,8 @@ enum { }; #define MAX_SESSIONS 8 -#define NO_TIMESTAMP 0xFF00 #define FORMAT_LINEAR_PCM 0x0000 +#define ASM_LAST_BUFFER_FLAG BIT(30) struct q6asm_flac_cfg { u32 sample_rate; @@ -93,37 +96,53 @@ struct audio_client *q6asm_audio_client_alloc(struct device *dev, q6asm_cb cb, void *priv, int session_id, int perf_mode); void q6asm_audio_client_free(struct audio_client *ac); -int q6asm_write_async(struct audio_client *ac, uint32_t len, uint32_t msw_ts, - uint32_t lsw_ts, uint32_t flags); -int q6asm_open_write(struct audio_client *ac, uint32_t format, - u32 codec_profile, uint16_t bits_per_sample); +int q6asm_write_async(struct audio_client *ac, uint32_t stream_id, uint32_t len, + uint32_t msw_ts, uint32_t lsw_ts, uint32_t flags); +int q6asm_open_write(struct audio_client *ac, uint32_t stream_id, + uint32_t format, u32 codec_profile, + uint16_t bits_per_sample, bool is_gapless); -int q6asm_open_read(struct audio_client *ac, uint32_t format, - uint16_t bits_per_sample); +int q6asm_open_read(struct audio_client *ac, uint32_t stream_id, + uint32_t format, uint16_t bits_per_sample); int q6asm_enc_cfg_blk_pcm_format_support(struct audio_client *ac, - uint32_t rate, uint32_t channels, uint16_t bits_per_sample); -int q6asm_read(struct audio_client *ac); + uint32_t stream_id, uint32_t rate, + uint32_t channels, + uint16_t bits_per_sample); + +int q6asm_read(struct audio_client *ac, uint32_t stream_id); int q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac, + uint32_t stream_id, uint32_t rate, uint32_t channels, u8 channel_map[PCM_MAX_NUM_CHANNEL], uint16_t bits_per_sample); int q6asm_stream_media_format_block_flac(struct audio_client *ac, + uint32_t stream_id, struct q6asm_flac_cfg *cfg); int q6asm_stream_media_format_block_wma_v9(struct audio_client *ac, + uint32_t stream_id, struct q6asm_wma_cfg *cfg); int q6asm_stream_media_format_block_wma_v10(struct audio_client *ac, + uint32_t stream_id, struct q6asm_wma_cfg *cfg); int q6asm_stream_media_format_block_alac(struct audio_client *ac, + uint32_t stream_id, struct q6asm_alac_cfg *cfg); int q6asm_stream_media_format_block_ape(struct audio_client *ac, + uint32_t stream_id, struct q6asm_ape_cfg *cfg); -int q6asm_run(struct audio_client *ac, uint32_t flags, uint32_t msw_ts, - uint32_t lsw_ts); -int q6asm_run_nowait(struct audio_client *ac, uint32_t flags, uint32_t msw_ts, - uint32_t lsw_ts); -int q6asm_cmd(struct audio_client *ac, int cmd); -int q6asm_cmd_nowait(struct audio_client *ac, int cmd); +int q6asm_run(struct audio_client *ac, uint32_t stream_id, uint32_t flags, + uint32_t msw_ts, uint32_t lsw_ts); +int q6asm_run_nowait(struct audio_client *ac, uint32_t stream_id, + uint32_t flags, uint32_t msw_ts, uint32_t lsw_ts); +int q6asm_stream_remove_initial_silence(struct audio_client *ac, + uint32_t stream_id, + uint32_t initial_samples); +int q6asm_stream_remove_trailing_silence(struct audio_client *ac, + uint32_t stream_id, + uint32_t trailing_samples); +int q6asm_cmd(struct audio_client *ac, uint32_t stream_id, int cmd); +int q6asm_cmd_nowait(struct audio_client *ac, uint32_t stream_id, int cmd); int q6asm_get_session_id(struct audio_client *ac); int q6asm_map_memory_regions(unsigned int dir, struct audio_client *ac, diff --git a/sound/soc/qcom/qdsp6/q6core.c b/sound/soc/qcom/qdsp6/q6core.c index ae314a652efe..5358fefd4210 100644 --- a/sound/soc/qcom/qdsp6/q6core.c +++ b/sound/soc/qcom/qdsp6/q6core.c @@ -354,11 +354,13 @@ static int q6core_exit(struct apr_device *adev) return 0; } +#ifdef CONFIG_OF static const struct of_device_id q6core_device_id[] = { { .compatible = "qcom,q6core" }, {}, }; MODULE_DEVICE_TABLE(of, q6core_device_id); +#endif static struct apr_driver qcom_q6core_driver = { .probe = q6core_probe, diff --git a/sound/soc/qcom/qdsp6/q6routing.c b/sound/soc/qcom/qdsp6/q6routing.c index 25d23e0266c7..53185e26fea1 100644 --- a/sound/soc/qcom/qdsp6/q6routing.c +++ b/sound/soc/qcom/qdsp6/q6routing.c @@ -113,7 +113,19 @@ { mix_name, "QUIN_TDM_TX_4", "QUIN_TDM_TX_4"}, \ { mix_name, "QUIN_TDM_TX_5", "QUIN_TDM_TX_5"}, \ { mix_name, "QUIN_TDM_TX_6", "QUIN_TDM_TX_6"}, \ - { mix_name, "QUIN_TDM_TX_7", "QUIN_TDM_TX_7"} + { mix_name, "QUIN_TDM_TX_7", "QUIN_TDM_TX_7"}, \ + { mix_name, "WSA_CODEC_DMA_TX_0", "WSA_CODEC_DMA_TX_0"}, \ + { mix_name, "WSA_CODEC_DMA_TX_1", "WSA_CODEC_DMA_TX_1"}, \ + { mix_name, "WSA_CODEC_DMA_TX_2", "WSA_CODEC_DMA_TX_2"}, \ + { mix_name, "VA_CODEC_DMA_TX_0", "VA_CODEC_DMA_TX_0"}, \ + { mix_name, "VA_CODEC_DMA_TX_1", "VA_CODEC_DMA_TX_1"}, \ + { mix_name, "VA_CODEC_DMA_TX_2", "VA_CODEC_DMA_TX_2"}, \ + { mix_name, "TX_CODEC_DMA_TX_0", "TX_CODEC_DMA_TX_0"}, \ + { mix_name, "TX_CODEC_DMA_TX_1", "TX_CODEC_DMA_TX_1"}, \ + { mix_name, "TX_CODEC_DMA_TX_2", "TX_CODEC_DMA_TX_2"}, \ + { mix_name, "TX_CODEC_DMA_TX_3", "TX_CODEC_DMA_TX_3"}, \ + { mix_name, "TX_CODEC_DMA_TX_4", "TX_CODEC_DMA_TX_4"}, \ + { mix_name, "TX_CODEC_DMA_TX_5", "TX_CODEC_DMA_TX_5"} #define Q6ROUTING_TX_MIXERS(id) \ SOC_SINGLE_EXT("PRI_MI2S_TX", PRIMARY_MI2S_TX, \ @@ -268,6 +280,42 @@ msm_routing_put_audio_mixer), \ SOC_SINGLE_EXT("QUIN_TDM_TX_7", QUINARY_TDM_TX_7, \ id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("WSA_CODEC_DMA_TX_0", WSA_CODEC_DMA_TX_0, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("WSA_CODEC_DMA_TX_1", WSA_CODEC_DMA_TX_1, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("WSA_CODEC_DMA_TX_2", WSA_CODEC_DMA_TX_2, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("VA_CODEC_DMA_TX_0", VA_CODEC_DMA_TX_0, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("VA_CODEC_DMA_TX_1", VA_CODEC_DMA_TX_1, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("VA_CODEC_DMA_TX_2", VA_CODEC_DMA_TX_2, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("TX_CODEC_DMA_TX_0", TX_CODEC_DMA_TX_0, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("TX_CODEC_DMA_TX_1", TX_CODEC_DMA_TX_1, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("TX_CODEC_DMA_TX_2", TX_CODEC_DMA_TX_2, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("TX_CODEC_DMA_TX_3", TX_CODEC_DMA_TX_3, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("TX_CODEC_DMA_TX_4", TX_CODEC_DMA_TX_4, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("TX_CODEC_DMA_TX_5", TX_CODEC_DMA_TX_5, \ + id, 1, 0, msm_routing_get_audio_mixer, \ msm_routing_put_audio_mixer), struct session_data { @@ -609,6 +657,36 @@ static const struct snd_kcontrol_new quin_tdm_rx_6_mixer_controls[] = { static const struct snd_kcontrol_new quin_tdm_rx_7_mixer_controls[] = { Q6ROUTING_RX_MIXERS(QUINARY_TDM_RX_7) }; +static const struct snd_kcontrol_new wsa_codec_dma_rx_0_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(WSA_CODEC_DMA_RX_0) }; + +static const struct snd_kcontrol_new wsa_codec_dma_rx_1_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(WSA_CODEC_DMA_RX_1) }; + +static const struct snd_kcontrol_new rx_codec_dma_rx_0_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(RX_CODEC_DMA_RX_0) }; + +static const struct snd_kcontrol_new rx_codec_dma_rx_1_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(RX_CODEC_DMA_RX_1) }; + +static const struct snd_kcontrol_new rx_codec_dma_rx_2_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(RX_CODEC_DMA_RX_2) }; + +static const struct snd_kcontrol_new rx_codec_dma_rx_3_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(RX_CODEC_DMA_RX_3) }; + +static const struct snd_kcontrol_new rx_codec_dma_rx_4_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(RX_CODEC_DMA_RX_4) }; + +static const struct snd_kcontrol_new rx_codec_dma_rx_5_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(RX_CODEC_DMA_RX_5) }; + +static const struct snd_kcontrol_new rxcodec_dma_rx_6_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(RX_CODEC_DMA_RX_6) }; + +static const struct snd_kcontrol_new rx_codec_dma_rx_7_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(RX_CODEC_DMA_RX_7) }; + static const struct snd_kcontrol_new mmul1_mixer_controls[] = { Q6ROUTING_TX_MIXERS(MSM_FRONTEND_DAI_MULTIMEDIA1) }; @@ -819,6 +897,37 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = { SND_SOC_DAPM_MIXER("QUIN_TDM_RX_7 Audio Mixer", SND_SOC_NOPM, 0, 0, quin_tdm_rx_7_mixer_controls, ARRAY_SIZE(quin_tdm_rx_7_mixer_controls)), + + SND_SOC_DAPM_MIXER("WSA_CODEC_DMA_RX_0 Audio Mixer", SND_SOC_NOPM, 0, 0, + wsa_codec_dma_rx_0_mixer_controls, + ARRAY_SIZE(wsa_codec_dma_rx_0_mixer_controls)), + SND_SOC_DAPM_MIXER("WSA_CODEC_DMA_RX_1 Audio Mixer", SND_SOC_NOPM, 0, 0, + wsa_codec_dma_rx_1_mixer_controls, + ARRAY_SIZE(wsa_codec_dma_rx_1_mixer_controls)), + SND_SOC_DAPM_MIXER("RX_CODEC_DMA_RX_0 Audio Mixer", SND_SOC_NOPM, 0, 0, + rx_codec_dma_rx_0_mixer_controls, + ARRAY_SIZE(rx_codec_dma_rx_0_mixer_controls)), + SND_SOC_DAPM_MIXER("RX_CODEC_DMA_RX_1 Audio Mixer", SND_SOC_NOPM, 0, 0, + rx_codec_dma_rx_1_mixer_controls, + ARRAY_SIZE(rx_codec_dma_rx_1_mixer_controls)), + SND_SOC_DAPM_MIXER("RX_CODEC_DMA_RX_2 Audio Mixer", SND_SOC_NOPM, 0, 0, + rx_codec_dma_rx_2_mixer_controls, + ARRAY_SIZE(rx_codec_dma_rx_2_mixer_controls)), + SND_SOC_DAPM_MIXER("RX_CODEC_DMA_RX_3 Audio Mixer", SND_SOC_NOPM, 0, 0, + rx_codec_dma_rx_3_mixer_controls, + ARRAY_SIZE(rx_codec_dma_rx_3_mixer_controls)), + SND_SOC_DAPM_MIXER("RX_CODEC_DMA_RX_4 Audio Mixer", SND_SOC_NOPM, 0, 0, + rx_codec_dma_rx_4_mixer_controls, + ARRAY_SIZE(rx_codec_dma_rx_4_mixer_controls)), + SND_SOC_DAPM_MIXER("RX_CODEC_DMA_RX_5 Audio Mixer", SND_SOC_NOPM, 0, 0, + rx_codec_dma_rx_5_mixer_controls, + ARRAY_SIZE(rx_codec_dma_rx_5_mixer_controls)), + SND_SOC_DAPM_MIXER("RX_CODEC_DMA_RX_6 Audio Mixer", SND_SOC_NOPM, 0, 0, + rxcodec_dma_rx_6_mixer_controls, + ARRAY_SIZE(rxcodec_dma_rx_6_mixer_controls)), + SND_SOC_DAPM_MIXER("RX_CODEC_DMA_RX_7 Audio Mixer", SND_SOC_NOPM, 0, 0, + rx_codec_dma_rx_7_mixer_controls, + ARRAY_SIZE(rx_codec_dma_rx_7_mixer_controls)), SND_SOC_DAPM_MIXER("MultiMedia1 Mixer", SND_SOC_NOPM, 0, 0, mmul1_mixer_controls, ARRAY_SIZE(mmul1_mixer_controls)), SND_SOC_DAPM_MIXER("MultiMedia2 Mixer", SND_SOC_NOPM, 0, 0, @@ -901,6 +1010,16 @@ static const struct snd_soc_dapm_route intercon[] = { Q6ROUTING_RX_DAPM_ROUTE("QUIN_TDM_RX_5 Audio Mixer", "QUIN_TDM_RX_5"), Q6ROUTING_RX_DAPM_ROUTE("QUIN_TDM_RX_6 Audio Mixer", "QUIN_TDM_RX_6"), Q6ROUTING_RX_DAPM_ROUTE("QUIN_TDM_RX_7 Audio Mixer", "QUIN_TDM_RX_7"), + Q6ROUTING_RX_DAPM_ROUTE("WSA_CODEC_DMA_RX_0 Audio Mixer", "WSA_CODEC_DMA_RX_0"), + Q6ROUTING_RX_DAPM_ROUTE("WSA_CODEC_DMA_RX_1 Audio Mixer", "WSA_CODEC_DMA_RX_1"), + Q6ROUTING_RX_DAPM_ROUTE("RX_CODEC_DMA_RX_0 Audio Mixer", "RX_CODEC_DMA_RX_0"), + Q6ROUTING_RX_DAPM_ROUTE("RX_CODEC_DMA_RX_1 Audio Mixer", "RX_CODEC_DMA_RX_1"), + Q6ROUTING_RX_DAPM_ROUTE("RX_CODEC_DMA_RX_2 Audio Mixer", "RX_CODEC_DMA_RX_2"), + Q6ROUTING_RX_DAPM_ROUTE("RX_CODEC_DMA_RX_3 Audio Mixer", "RX_CODEC_DMA_RX_3"), + Q6ROUTING_RX_DAPM_ROUTE("RX_CODEC_DMA_RX_4 Audio Mixer", "RX_CODEC_DMA_RX_4"), + Q6ROUTING_RX_DAPM_ROUTE("RX_CODEC_DMA_RX_5 Audio Mixer", "RX_CODEC_DMA_RX_5"), + Q6ROUTING_RX_DAPM_ROUTE("RX_CODEC_DMA_RX_6 Audio Mixer", "RX_CODEC_DMA_RX_6"), + Q6ROUTING_RX_DAPM_ROUTE("RX_CODEC_DMA_RX_7 Audio Mixer", "RX_CODEC_DMA_RX_7"), Q6ROUTING_TX_DAPM_ROUTE("MultiMedia1 Mixer"), Q6ROUTING_TX_DAPM_ROUTE("MultiMedia2 Mixer"), Q6ROUTING_TX_DAPM_ROUTE("MultiMedia3 Mixer"), @@ -1024,11 +1143,13 @@ static int q6pcm_routing_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_OF static const struct of_device_id q6pcm_routing_device_id[] = { { .compatible = "qcom,q6adm-routing" }, {}, }; MODULE_DEVICE_TABLE(of, q6pcm_routing_device_id); +#endif static struct platform_driver q6pcm_routing_platform_driver = { .driver = { diff --git a/sound/soc/qcom/sdm845.c b/sound/soc/qcom/sdm845.c index ab1bf23c21a6..6c2760e27ea6 100644 --- a/sound/soc/qcom/sdm845.c +++ b/sound/soc/qcom/sdm845.c @@ -17,6 +17,7 @@ #include "qdsp6/q6afe.h" #include "../codecs/rt5663.h" +#define DRIVER_NAME "sdm845" #define DEFAULT_SAMPLE_RATE_48K 48000 #define DEFAULT_MCLK_RATE 24576000 #define TDM_BCLK_RATE 6144000 @@ -552,6 +553,7 @@ static int sdm845_snd_platform_probe(struct platform_device *pdev) if (!data) return -ENOMEM; + card->driver_name = DRIVER_NAME; card->dapm_widgets = sdm845_snd_widgets; card->num_dapm_widgets = ARRAY_SIZE(sdm845_snd_widgets); card->dev = dev; |