1 // SPDX-License-Identifier: GPL-2.0+ 2 // 3 // AMD ALSA SoC PCM Driver 4 // 5 // Copyright (C) 2021 Advanced Micro Devices, Inc. All rights reserved. 6 7 #include <linux/platform_device.h> 8 #include <linux/module.h> 9 #include <linux/err.h> 10 #include <linux/io.h> 11 #include <sound/pcm_params.h> 12 #include <sound/soc.h> 13 #include <sound/soc-dai.h> 14 #include <linux/dma-mapping.h> 15 16 #include "acp5x.h" 17 18 #define DRV_NAME "acp5x_i2s_playcap" 19 20 static int acp5x_i2s_set_fmt(struct snd_soc_dai *cpu_dai, 21 unsigned int fmt) 22 { 23 struct i2s_dev_data *adata; 24 int mode; 25 26 adata = snd_soc_dai_get_drvdata(cpu_dai); 27 mode = fmt & SND_SOC_DAIFMT_FORMAT_MASK; 28 switch (mode) { 29 case SND_SOC_DAIFMT_I2S: 30 adata->tdm_mode = TDM_DISABLE; 31 break; 32 case SND_SOC_DAIFMT_DSP_A: 33 adata->tdm_mode = TDM_ENABLE; 34 break; 35 default: 36 return -EINVAL; 37 } 38 mode = fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK; 39 switch (mode) { 40 case SND_SOC_DAIFMT_CBC_CFC: 41 adata->master_mode = I2S_MASTER_MODE_ENABLE; 42 break; 43 case SND_SOC_DAIFMT_CBP_CFP: 44 adata->master_mode = I2S_MASTER_MODE_DISABLE; 45 break; 46 } 47 return 0; 48 } 49 50 static int acp5x_i2s_set_tdm_slot(struct snd_soc_dai *cpu_dai, 51 u32 tx_mask, u32 rx_mask, 52 int slots, int slot_width) 53 { 54 struct i2s_dev_data *adata; 55 u32 frm_len; 56 u16 slot_len; 57 58 adata = snd_soc_dai_get_drvdata(cpu_dai); 59 60 /* These values are as per Hardware Spec */ 61 switch (slot_width) { 62 case SLOT_WIDTH_8: 63 slot_len = 8; 64 break; 65 case SLOT_WIDTH_16: 66 slot_len = 16; 67 break; 68 case SLOT_WIDTH_24: 69 slot_len = 24; 70 break; 71 case SLOT_WIDTH_32: 72 slot_len = 0; 73 break; 74 default: 75 return -EINVAL; 76 } 77 frm_len = FRM_LEN | (slots << 15) | (slot_len << 18); 78 adata->tdm_fmt = frm_len; 79 return 0; 80 } 81 82 static int acp5x_i2s_hwparams(struct snd_pcm_substream *substream, 83 struct snd_pcm_hw_params *params, 84 struct snd_soc_dai *dai) 85 { 86 struct i2s_stream_instance *rtd; 87 struct snd_soc_pcm_runtime *prtd; 88 struct snd_soc_card *card; 89 struct acp5x_platform_info *pinfo; 90 struct i2s_dev_data *adata; 91 92 u32 val; 93 u32 reg_val, frmt_reg; 94 u32 lrclk_div_val, bclk_div_val; 95 96 lrclk_div_val = 0; 97 bclk_div_val = 0; 98 prtd = asoc_substream_to_rtd(substream); 99 rtd = substream->runtime->private_data; 100 card = prtd->card; 101 adata = snd_soc_dai_get_drvdata(dai); 102 pinfo = snd_soc_card_get_drvdata(card); 103 if (pinfo) { 104 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 105 rtd->i2s_instance = pinfo->play_i2s_instance; 106 else 107 rtd->i2s_instance = pinfo->cap_i2s_instance; 108 } 109 110 /* These values are as per Hardware Spec */ 111 switch (params_format(params)) { 112 case SNDRV_PCM_FORMAT_U8: 113 case SNDRV_PCM_FORMAT_S8: 114 rtd->xfer_resolution = 0x0; 115 break; 116 case SNDRV_PCM_FORMAT_S16_LE: 117 rtd->xfer_resolution = 0x02; 118 break; 119 case SNDRV_PCM_FORMAT_S24_LE: 120 rtd->xfer_resolution = 0x04; 121 break; 122 case SNDRV_PCM_FORMAT_S32_LE: 123 rtd->xfer_resolution = 0x05; 124 break; 125 default: 126 return -EINVAL; 127 } 128 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 129 switch (rtd->i2s_instance) { 130 case I2S_HS_INSTANCE: 131 reg_val = ACP_HSTDM_ITER; 132 frmt_reg = ACP_HSTDM_TXFRMT; 133 break; 134 case I2S_SP_INSTANCE: 135 default: 136 reg_val = ACP_I2STDM_ITER; 137 frmt_reg = ACP_I2STDM_TXFRMT; 138 } 139 } else { 140 switch (rtd->i2s_instance) { 141 case I2S_HS_INSTANCE: 142 reg_val = ACP_HSTDM_IRER; 143 frmt_reg = ACP_HSTDM_RXFRMT; 144 break; 145 case I2S_SP_INSTANCE: 146 default: 147 reg_val = ACP_I2STDM_IRER; 148 frmt_reg = ACP_I2STDM_RXFRMT; 149 } 150 } 151 if (adata->tdm_mode) { 152 val = acp_readl(rtd->acp5x_base + reg_val); 153 acp_writel(val | 0x2, rtd->acp5x_base + reg_val); 154 acp_writel(adata->tdm_fmt, rtd->acp5x_base + frmt_reg); 155 } 156 val = acp_readl(rtd->acp5x_base + reg_val); 157 val &= ~ACP5x_ITER_IRER_SAMP_LEN_MASK; 158 val = val | (rtd->xfer_resolution << 3); 159 acp_writel(val, rtd->acp5x_base + reg_val); 160 161 if (adata->master_mode) { 162 switch (params_format(params)) { 163 case SNDRV_PCM_FORMAT_S16_LE: 164 switch (params_rate(params)) { 165 case 8000: 166 bclk_div_val = 768; 167 break; 168 case 16000: 169 bclk_div_val = 384; 170 break; 171 case 24000: 172 bclk_div_val = 256; 173 break; 174 case 32000: 175 bclk_div_val = 192; 176 break; 177 case 44100: 178 case 48000: 179 bclk_div_val = 128; 180 break; 181 case 88200: 182 case 96000: 183 bclk_div_val = 64; 184 break; 185 case 192000: 186 bclk_div_val = 32; 187 break; 188 default: 189 return -EINVAL; 190 } 191 lrclk_div_val = 32; 192 break; 193 case SNDRV_PCM_FORMAT_S32_LE: 194 switch (params_rate(params)) { 195 case 8000: 196 bclk_div_val = 384; 197 break; 198 case 16000: 199 bclk_div_val = 192; 200 break; 201 case 24000: 202 bclk_div_val = 128; 203 break; 204 case 32000: 205 bclk_div_val = 96; 206 break; 207 case 44100: 208 case 48000: 209 bclk_div_val = 64; 210 break; 211 case 88200: 212 case 96000: 213 bclk_div_val = 32; 214 break; 215 case 192000: 216 bclk_div_val = 16; 217 break; 218 default: 219 return -EINVAL; 220 } 221 lrclk_div_val = 64; 222 break; 223 default: 224 return -EINVAL; 225 } 226 rtd->lrclk_div = lrclk_div_val; 227 rtd->bclk_div = bclk_div_val; 228 } 229 return 0; 230 } 231 232 static int acp5x_i2s_trigger(struct snd_pcm_substream *substream, 233 int cmd, struct snd_soc_dai *dai) 234 { 235 struct i2s_stream_instance *rtd; 236 struct i2s_dev_data *adata; 237 u32 ret, val, period_bytes, reg_val, ier_val, water_val; 238 u32 buf_size, buf_reg; 239 240 adata = snd_soc_dai_get_drvdata(dai); 241 rtd = substream->runtime->private_data; 242 period_bytes = frames_to_bytes(substream->runtime, 243 substream->runtime->period_size); 244 buf_size = frames_to_bytes(substream->runtime, 245 substream->runtime->buffer_size); 246 switch (cmd) { 247 case SNDRV_PCM_TRIGGER_START: 248 case SNDRV_PCM_TRIGGER_RESUME: 249 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 250 rtd->bytescount = acp_get_byte_count(rtd, 251 substream->stream); 252 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 253 switch (rtd->i2s_instance) { 254 case I2S_HS_INSTANCE: 255 water_val = 256 ACP_HS_TX_INTR_WATERMARK_SIZE; 257 reg_val = ACP_HSTDM_ITER; 258 ier_val = ACP_HSTDM_IER; 259 buf_reg = ACP_HS_TX_RINGBUFSIZE; 260 break; 261 case I2S_SP_INSTANCE: 262 default: 263 water_val = 264 ACP_I2S_TX_INTR_WATERMARK_SIZE; 265 reg_val = ACP_I2STDM_ITER; 266 ier_val = ACP_I2STDM_IER; 267 buf_reg = ACP_I2S_TX_RINGBUFSIZE; 268 } 269 } else { 270 switch (rtd->i2s_instance) { 271 case I2S_HS_INSTANCE: 272 water_val = 273 ACP_HS_RX_INTR_WATERMARK_SIZE; 274 reg_val = ACP_HSTDM_IRER; 275 ier_val = ACP_HSTDM_IER; 276 buf_reg = ACP_HS_RX_RINGBUFSIZE; 277 break; 278 case I2S_SP_INSTANCE: 279 default: 280 water_val = 281 ACP_I2S_RX_INTR_WATERMARK_SIZE; 282 reg_val = ACP_I2STDM_IRER; 283 ier_val = ACP_I2STDM_IER; 284 buf_reg = ACP_I2S_RX_RINGBUFSIZE; 285 } 286 } 287 acp_writel(period_bytes, rtd->acp5x_base + water_val); 288 acp_writel(buf_size, rtd->acp5x_base + buf_reg); 289 if (adata->master_mode) 290 acp5x_set_i2s_clk(adata, rtd); 291 val = acp_readl(rtd->acp5x_base + reg_val); 292 val = val | BIT(0); 293 acp_writel(val, rtd->acp5x_base + reg_val); 294 acp_writel(1, rtd->acp5x_base + ier_val); 295 ret = 0; 296 break; 297 case SNDRV_PCM_TRIGGER_STOP: 298 case SNDRV_PCM_TRIGGER_SUSPEND: 299 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 300 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 301 switch (rtd->i2s_instance) { 302 case I2S_HS_INSTANCE: 303 reg_val = ACP_HSTDM_ITER; 304 break; 305 case I2S_SP_INSTANCE: 306 default: 307 reg_val = ACP_I2STDM_ITER; 308 } 309 310 } else { 311 switch (rtd->i2s_instance) { 312 case I2S_HS_INSTANCE: 313 reg_val = ACP_HSTDM_IRER; 314 break; 315 case I2S_SP_INSTANCE: 316 default: 317 reg_val = ACP_I2STDM_IRER; 318 } 319 } 320 val = acp_readl(rtd->acp5x_base + reg_val); 321 val = val & ~BIT(0); 322 acp_writel(val, rtd->acp5x_base + reg_val); 323 324 if (!(acp_readl(rtd->acp5x_base + ACP_HSTDM_ITER) & BIT(0)) && 325 !(acp_readl(rtd->acp5x_base + ACP_HSTDM_IRER) & BIT(0))) 326 acp_writel(0, rtd->acp5x_base + ACP_HSTDM_IER); 327 if (!(acp_readl(rtd->acp5x_base + ACP_I2STDM_ITER) & BIT(0)) && 328 !(acp_readl(rtd->acp5x_base + ACP_I2STDM_IRER) & BIT(0))) 329 acp_writel(0, rtd->acp5x_base + ACP_I2STDM_IER); 330 ret = 0; 331 break; 332 default: 333 ret = -EINVAL; 334 break; 335 } 336 return ret; 337 } 338 339 static const struct snd_soc_dai_ops acp5x_i2s_dai_ops = { 340 .hw_params = acp5x_i2s_hwparams, 341 .trigger = acp5x_i2s_trigger, 342 .set_fmt = acp5x_i2s_set_fmt, 343 .set_tdm_slot = acp5x_i2s_set_tdm_slot, 344 }; 345 346 static const struct snd_soc_component_driver acp5x_dai_component = { 347 .name = "acp5x-i2s", 348 }; 349 350 static struct snd_soc_dai_driver acp5x_i2s_dai = { 351 .playback = { 352 .rates = SNDRV_PCM_RATE_8000_96000, 353 .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | 354 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, 355 .channels_min = 2, 356 .channels_max = 2, 357 .rate_min = 8000, 358 .rate_max = 96000, 359 }, 360 .capture = { 361 .rates = SNDRV_PCM_RATE_8000_96000, 362 .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | 363 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, 364 .channels_min = 2, 365 .channels_max = 2, 366 .rate_min = 8000, 367 .rate_max = 96000, 368 }, 369 .ops = &acp5x_i2s_dai_ops, 370 }; 371 372 static int acp5x_dai_probe(struct platform_device *pdev) 373 { 374 struct resource *res; 375 struct i2s_dev_data *adata; 376 int ret; 377 378 adata = devm_kzalloc(&pdev->dev, sizeof(struct i2s_dev_data), 379 GFP_KERNEL); 380 if (!adata) 381 return -ENOMEM; 382 383 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 384 if (!res) { 385 dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n"); 386 return -ENOMEM; 387 } 388 adata->acp5x_base = devm_ioremap(&pdev->dev, res->start, 389 resource_size(res)); 390 if (!adata->acp5x_base) 391 return -ENOMEM; 392 393 adata->master_mode = I2S_MASTER_MODE_ENABLE; 394 dev_set_drvdata(&pdev->dev, adata); 395 ret = devm_snd_soc_register_component(&pdev->dev, 396 &acp5x_dai_component, 397 &acp5x_i2s_dai, 1); 398 if (ret) 399 dev_err(&pdev->dev, "Fail to register acp i2s dai\n"); 400 return ret; 401 } 402 403 static struct platform_driver acp5x_dai_driver = { 404 .probe = acp5x_dai_probe, 405 .driver = { 406 .name = "acp5x_i2s_playcap", 407 }, 408 }; 409 410 module_platform_driver(acp5x_dai_driver); 411 412 MODULE_AUTHOR("Vijendar.Mukunda@amd.com"); 413 MODULE_DESCRIPTION("AMD ACP5.x CPU DAI Driver"); 414 MODULE_ALIAS("platform:" DRV_NAME); 415 MODULE_LICENSE("GPL v2"); 416