1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (c) 2021 The Linux Foundation. All rights reserved. 4 * 5 * lpass-cdc-dma.c -- ALSA SoC CDC DMA CPU DAI driver for QTi LPASS 6 */ 7 8 #include <dt-bindings/sound/qcom,lpass.h> 9 #include <linux/clk.h> 10 #include <linux/module.h> 11 #include <linux/export.h> 12 #include <sound/soc.h> 13 #include <sound/soc-dai.h> 14 15 #include "lpass-lpaif-reg.h" 16 #include "lpass.h" 17 18 #define CODEC_MEM_HZ_NORMAL 153600000 19 20 enum codec_dma_interfaces { 21 LPASS_CDC_DMA_INTERFACE1 = 1, 22 LPASS_CDC_DMA_INTERFACE2, 23 LPASS_CDC_DMA_INTERFACE3, 24 LPASS_CDC_DMA_INTERFACE4, 25 LPASS_CDC_DMA_INTERFACE5, 26 LPASS_CDC_DMA_INTERFACE6, 27 LPASS_CDC_DMA_INTERFACE7, 28 LPASS_CDC_DMA_INTERFACE8, 29 LPASS_CDC_DMA_INTERFACE9, 30 LPASS_CDC_DMA_INTERFACE10, 31 }; 32 33 static void __lpass_get_dmactl_handle(struct snd_pcm_substream *substream, struct snd_soc_dai *dai, 34 struct lpaif_dmactl **dmactl, int *id) 35 { 36 struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(substream); 37 struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(soc_runtime, 0); 38 struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); 39 struct snd_pcm_runtime *rt = substream->runtime; 40 struct lpass_pcm_data *pcm_data = rt->private_data; 41 const struct lpass_variant *v = drvdata->variant; 42 unsigned int dai_id = cpu_dai->driver->id; 43 44 switch (dai_id) { 45 case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9: 46 *dmactl = drvdata->rxtx_rd_dmactl; 47 *id = pcm_data->dma_ch; 48 break; 49 case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8: 50 *dmactl = drvdata->rxtx_wr_dmactl; 51 *id = pcm_data->dma_ch - v->rxtx_wrdma_channel_start; 52 break; 53 case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8: 54 *dmactl = drvdata->va_wr_dmactl; 55 *id = pcm_data->dma_ch - v->va_wrdma_channel_start; 56 break; 57 default: 58 dev_err(soc_runtime->dev, "invalid dai id for dma ctl: %d\n", dai_id); 59 break; 60 } 61 } 62 63 static int __lpass_get_codec_dma_intf_type(int dai_id) 64 { 65 int ret; 66 67 switch (dai_id) { 68 case LPASS_CDC_DMA_RX0: 69 case LPASS_CDC_DMA_TX0: 70 case LPASS_CDC_DMA_VA_TX0: 71 ret = LPASS_CDC_DMA_INTERFACE1; 72 break; 73 case LPASS_CDC_DMA_RX1: 74 case LPASS_CDC_DMA_TX1: 75 case LPASS_CDC_DMA_VA_TX1: 76 ret = LPASS_CDC_DMA_INTERFACE2; 77 break; 78 case LPASS_CDC_DMA_RX2: 79 case LPASS_CDC_DMA_TX2: 80 case LPASS_CDC_DMA_VA_TX2: 81 ret = LPASS_CDC_DMA_INTERFACE3; 82 break; 83 case LPASS_CDC_DMA_RX3: 84 case LPASS_CDC_DMA_TX3: 85 case LPASS_CDC_DMA_VA_TX3: 86 ret = LPASS_CDC_DMA_INTERFACE4; 87 break; 88 case LPASS_CDC_DMA_RX4: 89 case LPASS_CDC_DMA_TX4: 90 case LPASS_CDC_DMA_VA_TX4: 91 ret = LPASS_CDC_DMA_INTERFACE5; 92 break; 93 case LPASS_CDC_DMA_RX5: 94 case LPASS_CDC_DMA_TX5: 95 case LPASS_CDC_DMA_VA_TX5: 96 ret = LPASS_CDC_DMA_INTERFACE6; 97 break; 98 case LPASS_CDC_DMA_RX6: 99 case LPASS_CDC_DMA_TX6: 100 case LPASS_CDC_DMA_VA_TX6: 101 ret = LPASS_CDC_DMA_INTERFACE7; 102 break; 103 case LPASS_CDC_DMA_RX7: 104 case LPASS_CDC_DMA_TX7: 105 case LPASS_CDC_DMA_VA_TX7: 106 ret = LPASS_CDC_DMA_INTERFACE8; 107 break; 108 case LPASS_CDC_DMA_RX8: 109 case LPASS_CDC_DMA_TX8: 110 case LPASS_CDC_DMA_VA_TX8: 111 ret = LPASS_CDC_DMA_INTERFACE9; 112 break; 113 case LPASS_CDC_DMA_RX9: 114 ret = LPASS_CDC_DMA_INTERFACE10; 115 break; 116 default: 117 ret = -EINVAL; 118 break; 119 } 120 return ret; 121 } 122 123 static int __lpass_platform_codec_intf_init(struct snd_soc_dai *dai, 124 struct snd_pcm_substream *substream) 125 { 126 struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(substream); 127 struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(soc_runtime, 0); 128 struct lpaif_dmactl *dmactl = NULL; 129 struct device *dev = soc_runtime->dev; 130 int ret, id, codec_intf; 131 unsigned int dai_id = cpu_dai->driver->id; 132 133 codec_intf = __lpass_get_codec_dma_intf_type(dai_id); 134 if (codec_intf < 0) { 135 dev_err(dev, "failed to get codec_intf: %d\n", codec_intf); 136 return codec_intf; 137 } 138 139 __lpass_get_dmactl_handle(substream, dai, &dmactl, &id); 140 if (!dmactl) 141 return -EINVAL; 142 143 ret = regmap_fields_write(dmactl->codec_intf, id, codec_intf); 144 if (ret) { 145 dev_err(dev, "error writing to dmactl codec_intf reg field: %d\n", ret); 146 return ret; 147 } 148 ret = regmap_fields_write(dmactl->codec_fs_sel, id, 0x0); 149 if (ret) { 150 dev_err(dev, "error writing to dmactl codec_fs_sel reg field: %d\n", ret); 151 return ret; 152 } 153 ret = regmap_fields_write(dmactl->codec_fs_delay, id, 0x0); 154 if (ret) { 155 dev_err(dev, "error writing to dmactl codec_fs_delay reg field: %d\n", ret); 156 return ret; 157 } 158 ret = regmap_fields_write(dmactl->codec_pack, id, 0x1); 159 if (ret) { 160 dev_err(dev, "error writing to dmactl codec_pack reg field: %d\n", ret); 161 return ret; 162 } 163 ret = regmap_fields_write(dmactl->codec_enable, id, LPAIF_DMACTL_ENABLE_ON); 164 if (ret) { 165 dev_err(dev, "error writing to dmactl codec_enable reg field: %d\n", ret); 166 return ret; 167 } 168 return 0; 169 } 170 171 static int lpass_cdc_dma_daiops_startup(struct snd_pcm_substream *substream, 172 struct snd_soc_dai *dai) 173 { 174 struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); 175 struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(substream); 176 177 switch (dai->id) { 178 case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9: 179 case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8: 180 clk_set_rate(drvdata->codec_mem0, CODEC_MEM_HZ_NORMAL); 181 clk_prepare_enable(drvdata->codec_mem0); 182 break; 183 case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX0: 184 clk_set_rate(drvdata->va_mem0, CODEC_MEM_HZ_NORMAL); 185 clk_prepare_enable(drvdata->va_mem0); 186 break; 187 default: 188 dev_err(soc_runtime->dev, "%s: invalid interface: %d\n", __func__, dai->id); 189 break; 190 } 191 return 0; 192 } 193 194 static void lpass_cdc_dma_daiops_shutdown(struct snd_pcm_substream *substream, 195 struct snd_soc_dai *dai) 196 { 197 struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); 198 struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(substream); 199 200 switch (dai->id) { 201 case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9: 202 case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8: 203 clk_disable_unprepare(drvdata->codec_mem0); 204 break; 205 case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX0: 206 clk_disable_unprepare(drvdata->va_mem0); 207 break; 208 default: 209 dev_err(soc_runtime->dev, "%s: invalid interface: %d\n", __func__, dai->id); 210 break; 211 } 212 } 213 214 static int lpass_cdc_dma_daiops_hw_params(struct snd_pcm_substream *substream, 215 struct snd_pcm_hw_params *params, 216 struct snd_soc_dai *dai) 217 { 218 struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(substream); 219 struct lpaif_dmactl *dmactl = NULL; 220 unsigned int ret, regval; 221 unsigned int channels = params_channels(params); 222 int id; 223 224 switch (channels) { 225 case 1: 226 regval = LPASS_CDC_DMA_INTF_ONE_CHANNEL; 227 break; 228 case 2: 229 regval = LPASS_CDC_DMA_INTF_TWO_CHANNEL; 230 break; 231 case 4: 232 regval = LPASS_CDC_DMA_INTF_FOUR_CHANNEL; 233 break; 234 case 6: 235 regval = LPASS_CDC_DMA_INTF_SIX_CHANNEL; 236 break; 237 case 8: 238 regval = LPASS_CDC_DMA_INTF_EIGHT_CHANNEL; 239 break; 240 default: 241 dev_err(soc_runtime->dev, "invalid PCM config\n"); 242 return -EINVAL; 243 } 244 245 __lpass_get_dmactl_handle(substream, dai, &dmactl, &id); 246 if (!dmactl) 247 return -EINVAL; 248 249 ret = regmap_fields_write(dmactl->codec_channel, id, regval); 250 if (ret) { 251 dev_err(soc_runtime->dev, 252 "error writing to dmactl codec_channel reg field: %d\n", ret); 253 return ret; 254 } 255 return 0; 256 } 257 258 static int lpass_cdc_dma_daiops_trigger(struct snd_pcm_substream *substream, 259 int cmd, struct snd_soc_dai *dai) 260 { 261 struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(substream); 262 struct lpaif_dmactl *dmactl = NULL; 263 int ret = 0, id; 264 265 switch (cmd) { 266 case SNDRV_PCM_TRIGGER_START: 267 case SNDRV_PCM_TRIGGER_RESUME: 268 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 269 __lpass_platform_codec_intf_init(dai, substream); 270 break; 271 case SNDRV_PCM_TRIGGER_STOP: 272 case SNDRV_PCM_TRIGGER_SUSPEND: 273 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 274 __lpass_get_dmactl_handle(substream, dai, &dmactl, &id); 275 if (!dmactl) 276 return -EINVAL; 277 278 ret = regmap_fields_write(dmactl->codec_enable, id, LPAIF_DMACTL_ENABLE_OFF); 279 if (ret) { 280 dev_err(soc_runtime->dev, 281 "error writing to dmactl codec_enable reg: %d\n", ret); 282 return ret; 283 } 284 break; 285 default: 286 ret = -EINVAL; 287 dev_err(soc_runtime->dev, "%s: invalid %d interface\n", __func__, cmd); 288 break; 289 } 290 return ret; 291 } 292 293 const struct snd_soc_dai_ops asoc_qcom_lpass_cdc_dma_dai_ops = { 294 .startup = lpass_cdc_dma_daiops_startup, 295 .shutdown = lpass_cdc_dma_daiops_shutdown, 296 .hw_params = lpass_cdc_dma_daiops_hw_params, 297 .trigger = lpass_cdc_dma_daiops_trigger, 298 }; 299 EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cdc_dma_dai_ops); 300 301 MODULE_DESCRIPTION("QTi LPASS CDC DMA Driver"); 302 MODULE_LICENSE("GPL"); 303
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.