162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2021 The Linux Foundation. All rights reserved. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * lpass-cdc-dma.c -- ALSA SoC CDC DMA CPU DAI driver for QTi LPASS 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/clk.h> 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/export.h> 1162306a36Sopenharmony_ci#include <sound/soc.h> 1262306a36Sopenharmony_ci#include <sound/soc-dai.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include "lpass-lpaif-reg.h" 1562306a36Sopenharmony_ci#include "lpass.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#define CODEC_MEM_HZ_NORMAL 153600000 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cienum codec_dma_interfaces { 2062306a36Sopenharmony_ci LPASS_CDC_DMA_INTERFACE1 = 1, 2162306a36Sopenharmony_ci LPASS_CDC_DMA_INTERFACE2, 2262306a36Sopenharmony_ci LPASS_CDC_DMA_INTERFACE3, 2362306a36Sopenharmony_ci LPASS_CDC_DMA_INTERFACE4, 2462306a36Sopenharmony_ci LPASS_CDC_DMA_INTERFACE5, 2562306a36Sopenharmony_ci LPASS_CDC_DMA_INTERFACE6, 2662306a36Sopenharmony_ci LPASS_CDC_DMA_INTERFACE7, 2762306a36Sopenharmony_ci LPASS_CDC_DMA_INTERFACE8, 2862306a36Sopenharmony_ci LPASS_CDC_DMA_INTERFACE9, 2962306a36Sopenharmony_ci LPASS_CDC_DMA_INTERFACE10, 3062306a36Sopenharmony_ci}; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic void __lpass_get_dmactl_handle(struct snd_pcm_substream *substream, struct snd_soc_dai *dai, 3362306a36Sopenharmony_ci struct lpaif_dmactl **dmactl, int *id) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(substream); 3662306a36Sopenharmony_ci struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(soc_runtime, 0); 3762306a36Sopenharmony_ci struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); 3862306a36Sopenharmony_ci struct snd_pcm_runtime *rt = substream->runtime; 3962306a36Sopenharmony_ci struct lpass_pcm_data *pcm_data = rt->private_data; 4062306a36Sopenharmony_ci struct lpass_variant *v = drvdata->variant; 4162306a36Sopenharmony_ci unsigned int dai_id = cpu_dai->driver->id; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci switch (dai_id) { 4462306a36Sopenharmony_ci case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9: 4562306a36Sopenharmony_ci *dmactl = drvdata->rxtx_rd_dmactl; 4662306a36Sopenharmony_ci *id = pcm_data->dma_ch; 4762306a36Sopenharmony_ci break; 4862306a36Sopenharmony_ci case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8: 4962306a36Sopenharmony_ci *dmactl = drvdata->rxtx_wr_dmactl; 5062306a36Sopenharmony_ci *id = pcm_data->dma_ch - v->rxtx_wrdma_channel_start; 5162306a36Sopenharmony_ci break; 5262306a36Sopenharmony_ci case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8: 5362306a36Sopenharmony_ci *dmactl = drvdata->va_wr_dmactl; 5462306a36Sopenharmony_ci *id = pcm_data->dma_ch - v->va_wrdma_channel_start; 5562306a36Sopenharmony_ci break; 5662306a36Sopenharmony_ci default: 5762306a36Sopenharmony_ci dev_err(soc_runtime->dev, "invalid dai id for dma ctl: %d\n", dai_id); 5862306a36Sopenharmony_ci break; 5962306a36Sopenharmony_ci } 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistatic int __lpass_get_codec_dma_intf_type(int dai_id) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci int ret; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci switch (dai_id) { 6762306a36Sopenharmony_ci case LPASS_CDC_DMA_RX0: 6862306a36Sopenharmony_ci case LPASS_CDC_DMA_TX0: 6962306a36Sopenharmony_ci case LPASS_CDC_DMA_VA_TX0: 7062306a36Sopenharmony_ci ret = LPASS_CDC_DMA_INTERFACE1; 7162306a36Sopenharmony_ci break; 7262306a36Sopenharmony_ci case LPASS_CDC_DMA_RX1: 7362306a36Sopenharmony_ci case LPASS_CDC_DMA_TX1: 7462306a36Sopenharmony_ci case LPASS_CDC_DMA_VA_TX1: 7562306a36Sopenharmony_ci ret = LPASS_CDC_DMA_INTERFACE2; 7662306a36Sopenharmony_ci break; 7762306a36Sopenharmony_ci case LPASS_CDC_DMA_RX2: 7862306a36Sopenharmony_ci case LPASS_CDC_DMA_TX2: 7962306a36Sopenharmony_ci case LPASS_CDC_DMA_VA_TX2: 8062306a36Sopenharmony_ci ret = LPASS_CDC_DMA_INTERFACE3; 8162306a36Sopenharmony_ci break; 8262306a36Sopenharmony_ci case LPASS_CDC_DMA_RX3: 8362306a36Sopenharmony_ci case LPASS_CDC_DMA_TX3: 8462306a36Sopenharmony_ci case LPASS_CDC_DMA_VA_TX3: 8562306a36Sopenharmony_ci ret = LPASS_CDC_DMA_INTERFACE4; 8662306a36Sopenharmony_ci break; 8762306a36Sopenharmony_ci case LPASS_CDC_DMA_RX4: 8862306a36Sopenharmony_ci case LPASS_CDC_DMA_TX4: 8962306a36Sopenharmony_ci case LPASS_CDC_DMA_VA_TX4: 9062306a36Sopenharmony_ci ret = LPASS_CDC_DMA_INTERFACE5; 9162306a36Sopenharmony_ci break; 9262306a36Sopenharmony_ci case LPASS_CDC_DMA_RX5: 9362306a36Sopenharmony_ci case LPASS_CDC_DMA_TX5: 9462306a36Sopenharmony_ci case LPASS_CDC_DMA_VA_TX5: 9562306a36Sopenharmony_ci ret = LPASS_CDC_DMA_INTERFACE6; 9662306a36Sopenharmony_ci break; 9762306a36Sopenharmony_ci case LPASS_CDC_DMA_RX6: 9862306a36Sopenharmony_ci case LPASS_CDC_DMA_TX6: 9962306a36Sopenharmony_ci case LPASS_CDC_DMA_VA_TX6: 10062306a36Sopenharmony_ci ret = LPASS_CDC_DMA_INTERFACE7; 10162306a36Sopenharmony_ci break; 10262306a36Sopenharmony_ci case LPASS_CDC_DMA_RX7: 10362306a36Sopenharmony_ci case LPASS_CDC_DMA_TX7: 10462306a36Sopenharmony_ci case LPASS_CDC_DMA_VA_TX7: 10562306a36Sopenharmony_ci ret = LPASS_CDC_DMA_INTERFACE8; 10662306a36Sopenharmony_ci break; 10762306a36Sopenharmony_ci case LPASS_CDC_DMA_RX8: 10862306a36Sopenharmony_ci case LPASS_CDC_DMA_TX8: 10962306a36Sopenharmony_ci case LPASS_CDC_DMA_VA_TX8: 11062306a36Sopenharmony_ci ret = LPASS_CDC_DMA_INTERFACE9; 11162306a36Sopenharmony_ci break; 11262306a36Sopenharmony_ci case LPASS_CDC_DMA_RX9: 11362306a36Sopenharmony_ci ret = LPASS_CDC_DMA_INTERFACE10; 11462306a36Sopenharmony_ci break; 11562306a36Sopenharmony_ci default: 11662306a36Sopenharmony_ci ret = -EINVAL; 11762306a36Sopenharmony_ci break; 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci return ret; 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic int __lpass_platform_codec_intf_init(struct snd_soc_dai *dai, 12362306a36Sopenharmony_ci struct snd_pcm_substream *substream) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(substream); 12662306a36Sopenharmony_ci struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(soc_runtime, 0); 12762306a36Sopenharmony_ci struct lpaif_dmactl *dmactl = NULL; 12862306a36Sopenharmony_ci struct device *dev = soc_runtime->dev; 12962306a36Sopenharmony_ci int ret, id, codec_intf; 13062306a36Sopenharmony_ci unsigned int dai_id = cpu_dai->driver->id; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci codec_intf = __lpass_get_codec_dma_intf_type(dai_id); 13362306a36Sopenharmony_ci if (codec_intf < 0) { 13462306a36Sopenharmony_ci dev_err(dev, "failed to get codec_intf: %d\n", codec_intf); 13562306a36Sopenharmony_ci return codec_intf; 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci __lpass_get_dmactl_handle(substream, dai, &dmactl, &id); 13962306a36Sopenharmony_ci if (!dmactl) 14062306a36Sopenharmony_ci return -EINVAL; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci ret = regmap_fields_write(dmactl->codec_intf, id, codec_intf); 14362306a36Sopenharmony_ci if (ret) { 14462306a36Sopenharmony_ci dev_err(dev, "error writing to dmactl codec_intf reg field: %d\n", ret); 14562306a36Sopenharmony_ci return ret; 14662306a36Sopenharmony_ci } 14762306a36Sopenharmony_ci ret = regmap_fields_write(dmactl->codec_fs_sel, id, 0x0); 14862306a36Sopenharmony_ci if (ret) { 14962306a36Sopenharmony_ci dev_err(dev, "error writing to dmactl codec_fs_sel reg field: %d\n", ret); 15062306a36Sopenharmony_ci return ret; 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci ret = regmap_fields_write(dmactl->codec_fs_delay, id, 0x0); 15362306a36Sopenharmony_ci if (ret) { 15462306a36Sopenharmony_ci dev_err(dev, "error writing to dmactl codec_fs_delay reg field: %d\n", ret); 15562306a36Sopenharmony_ci return ret; 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci ret = regmap_fields_write(dmactl->codec_pack, id, 0x1); 15862306a36Sopenharmony_ci if (ret) { 15962306a36Sopenharmony_ci dev_err(dev, "error writing to dmactl codec_pack reg field: %d\n", ret); 16062306a36Sopenharmony_ci return ret; 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci ret = regmap_fields_write(dmactl->codec_enable, id, LPAIF_DMACTL_ENABLE_ON); 16362306a36Sopenharmony_ci if (ret) { 16462306a36Sopenharmony_ci dev_err(dev, "error writing to dmactl codec_enable reg field: %d\n", ret); 16562306a36Sopenharmony_ci return ret; 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci return 0; 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_cistatic int lpass_cdc_dma_daiops_startup(struct snd_pcm_substream *substream, 17162306a36Sopenharmony_ci struct snd_soc_dai *dai) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); 17462306a36Sopenharmony_ci struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(substream); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci switch (dai->id) { 17762306a36Sopenharmony_ci case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9: 17862306a36Sopenharmony_ci case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8: 17962306a36Sopenharmony_ci clk_set_rate(drvdata->codec_mem0, CODEC_MEM_HZ_NORMAL); 18062306a36Sopenharmony_ci clk_prepare_enable(drvdata->codec_mem0); 18162306a36Sopenharmony_ci break; 18262306a36Sopenharmony_ci case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX0: 18362306a36Sopenharmony_ci clk_set_rate(drvdata->va_mem0, CODEC_MEM_HZ_NORMAL); 18462306a36Sopenharmony_ci clk_prepare_enable(drvdata->va_mem0); 18562306a36Sopenharmony_ci break; 18662306a36Sopenharmony_ci default: 18762306a36Sopenharmony_ci dev_err(soc_runtime->dev, "%s: invalid interface: %d\n", __func__, dai->id); 18862306a36Sopenharmony_ci break; 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci return 0; 19162306a36Sopenharmony_ci} 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_cistatic void lpass_cdc_dma_daiops_shutdown(struct snd_pcm_substream *substream, 19462306a36Sopenharmony_ci struct snd_soc_dai *dai) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); 19762306a36Sopenharmony_ci struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(substream); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci switch (dai->id) { 20062306a36Sopenharmony_ci case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9: 20162306a36Sopenharmony_ci case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8: 20262306a36Sopenharmony_ci clk_disable_unprepare(drvdata->codec_mem0); 20362306a36Sopenharmony_ci break; 20462306a36Sopenharmony_ci case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX0: 20562306a36Sopenharmony_ci clk_disable_unprepare(drvdata->va_mem0); 20662306a36Sopenharmony_ci break; 20762306a36Sopenharmony_ci default: 20862306a36Sopenharmony_ci dev_err(soc_runtime->dev, "%s: invalid interface: %d\n", __func__, dai->id); 20962306a36Sopenharmony_ci break; 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_cistatic int lpass_cdc_dma_daiops_hw_params(struct snd_pcm_substream *substream, 21462306a36Sopenharmony_ci struct snd_pcm_hw_params *params, 21562306a36Sopenharmony_ci struct snd_soc_dai *dai) 21662306a36Sopenharmony_ci{ 21762306a36Sopenharmony_ci struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(substream); 21862306a36Sopenharmony_ci struct lpaif_dmactl *dmactl = NULL; 21962306a36Sopenharmony_ci unsigned int ret, regval; 22062306a36Sopenharmony_ci unsigned int channels = params_channels(params); 22162306a36Sopenharmony_ci int id; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci switch (channels) { 22462306a36Sopenharmony_ci case 1: 22562306a36Sopenharmony_ci regval = LPASS_CDC_DMA_INTF_ONE_CHANNEL; 22662306a36Sopenharmony_ci break; 22762306a36Sopenharmony_ci case 2: 22862306a36Sopenharmony_ci regval = LPASS_CDC_DMA_INTF_TWO_CHANNEL; 22962306a36Sopenharmony_ci break; 23062306a36Sopenharmony_ci case 4: 23162306a36Sopenharmony_ci regval = LPASS_CDC_DMA_INTF_FOUR_CHANNEL; 23262306a36Sopenharmony_ci break; 23362306a36Sopenharmony_ci case 6: 23462306a36Sopenharmony_ci regval = LPASS_CDC_DMA_INTF_SIX_CHANNEL; 23562306a36Sopenharmony_ci break; 23662306a36Sopenharmony_ci case 8: 23762306a36Sopenharmony_ci regval = LPASS_CDC_DMA_INTF_EIGHT_CHANNEL; 23862306a36Sopenharmony_ci break; 23962306a36Sopenharmony_ci default: 24062306a36Sopenharmony_ci dev_err(soc_runtime->dev, "invalid PCM config\n"); 24162306a36Sopenharmony_ci return -EINVAL; 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci __lpass_get_dmactl_handle(substream, dai, &dmactl, &id); 24562306a36Sopenharmony_ci if (!dmactl) 24662306a36Sopenharmony_ci return -EINVAL; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci ret = regmap_fields_write(dmactl->codec_channel, id, regval); 24962306a36Sopenharmony_ci if (ret) { 25062306a36Sopenharmony_ci dev_err(soc_runtime->dev, 25162306a36Sopenharmony_ci "error writing to dmactl codec_channel reg field: %d\n", ret); 25262306a36Sopenharmony_ci return ret; 25362306a36Sopenharmony_ci } 25462306a36Sopenharmony_ci return 0; 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_cistatic int lpass_cdc_dma_daiops_trigger(struct snd_pcm_substream *substream, 25862306a36Sopenharmony_ci int cmd, struct snd_soc_dai *dai) 25962306a36Sopenharmony_ci{ 26062306a36Sopenharmony_ci struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(substream); 26162306a36Sopenharmony_ci struct lpaif_dmactl *dmactl = NULL; 26262306a36Sopenharmony_ci int ret = 0, id; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci switch (cmd) { 26562306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 26662306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 26762306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 26862306a36Sopenharmony_ci __lpass_platform_codec_intf_init(dai, substream); 26962306a36Sopenharmony_ci break; 27062306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 27162306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 27262306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 27362306a36Sopenharmony_ci __lpass_get_dmactl_handle(substream, dai, &dmactl, &id); 27462306a36Sopenharmony_ci if (!dmactl) 27562306a36Sopenharmony_ci return -EINVAL; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci ret = regmap_fields_write(dmactl->codec_enable, id, LPAIF_DMACTL_ENABLE_OFF); 27862306a36Sopenharmony_ci if (ret) { 27962306a36Sopenharmony_ci dev_err(soc_runtime->dev, 28062306a36Sopenharmony_ci "error writing to dmactl codec_enable reg: %d\n", ret); 28162306a36Sopenharmony_ci return ret; 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_ci break; 28462306a36Sopenharmony_ci default: 28562306a36Sopenharmony_ci ret = -EINVAL; 28662306a36Sopenharmony_ci dev_err(soc_runtime->dev, "%s: invalid %d interface\n", __func__, cmd); 28762306a36Sopenharmony_ci break; 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci return ret; 29062306a36Sopenharmony_ci} 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ciconst struct snd_soc_dai_ops asoc_qcom_lpass_cdc_dma_dai_ops = { 29362306a36Sopenharmony_ci .startup = lpass_cdc_dma_daiops_startup, 29462306a36Sopenharmony_ci .shutdown = lpass_cdc_dma_daiops_shutdown, 29562306a36Sopenharmony_ci .hw_params = lpass_cdc_dma_daiops_hw_params, 29662306a36Sopenharmony_ci .trigger = lpass_cdc_dma_daiops_trigger, 29762306a36Sopenharmony_ci}; 29862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(asoc_qcom_lpass_cdc_dma_dai_ops); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ciMODULE_DESCRIPTION("QTi LPASS CDC DMA Driver"); 30162306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 302