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_BP_FP: 41 adata->master_mode = I2S_MASTER_MODE_ENABLE; 42 break; 43 case SND_SOC_DAIFMT_BC_FC: 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 = snd_soc_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 val, period_bytes, reg_val, ier_val, water_val; 238 u32 buf_size, buf_reg; 239 int ret; 240 241 adata = snd_soc_dai_get_drvdata(dai); 242 rtd = substream->runtime->private_data; 243 period_bytes = frames_to_bytes(substream->runtime, 244 substream->runtime->period_size); 245 buf_size = frames_to_bytes(substream->runtime, 246 substream->runtime->buffer_size); 247 switch (cmd) { 248 case SNDRV_PCM_TRIGGER_START: 249 case SNDRV_PCM_TRIGGER_RESUME: 250 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 251 rtd->bytescount = acp_get_byte_count(rtd, 252 substream->stream); 253 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 254 switch (rtd->i2s_instance) { 255 case I2S_HS_INSTANCE: 256 water_val = 257 ACP_HS_TX_INTR_WATERMARK_SIZE; 258 reg_val = ACP_HSTDM_ITER; 259 ier_val = ACP_HSTDM_IER; 260 buf_reg = ACP_HS_TX_RINGBUFSIZE; 261 break; 262 case I2S_SP_INSTANCE: 263 default: 264 water_val = 265 ACP_I2S_TX_INTR_WATERMARK_SIZE; 266 reg_val = ACP_I2STDM_ITER; 267 ier_val = ACP_I2STDM_IER; 268 buf_reg = ACP_I2S_TX_RINGBUFSIZE; 269 } 270 } else { 271 switch (rtd->i2s_instance) { 272 case I2S_HS_INSTANCE: 273 water_val = 274 ACP_HS_RX_INTR_WATERMARK_SIZE; 275 reg_val = ACP_HSTDM_IRER; 276 ier_val = ACP_HSTDM_IER; 277 buf_reg = ACP_HS_RX_RINGBUFSIZE; 278 break; 279 case I2S_SP_INSTANCE: 280 default: 281 water_val = 282 ACP_I2S_RX_INTR_WATERMARK_SIZE; 283 reg_val = ACP_I2STDM_IRER; 284 ier_val = ACP_I2STDM_IER; 285 buf_reg = ACP_I2S_RX_RINGBUFSIZE; 286 } 287 } 288 acp_writel(period_bytes, rtd->acp5x_base + water_val); 289 acp_writel(buf_size, rtd->acp5x_base + buf_reg); 290 if (adata->master_mode) 291 acp5x_set_i2s_clk(adata, rtd); 292 val = acp_readl(rtd->acp5x_base + reg_val); 293 val = val | BIT(0); 294 acp_writel(val, rtd->acp5x_base + reg_val); 295 acp_writel(1, rtd->acp5x_base + ier_val); 296 ret = 0; 297 break; 298 case SNDRV_PCM_TRIGGER_STOP: 299 case SNDRV_PCM_TRIGGER_SUSPEND: 300 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 301 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 302 switch (rtd->i2s_instance) { 303 case I2S_HS_INSTANCE: 304 reg_val = ACP_HSTDM_ITER; 305 break; 306 case I2S_SP_INSTANCE: 307 default: 308 reg_val = ACP_I2STDM_ITER; 309 } 310 311 } else { 312 switch (rtd->i2s_instance) { 313 case I2S_HS_INSTANCE: 314 reg_val = ACP_HSTDM_IRER; 315 break; 316 case I2S_SP_INSTANCE: 317 default: 318 reg_val = ACP_I2STDM_IRER; 319 } 320 } 321 val = acp_readl(rtd->acp5x_base + reg_val); 322 val = val & ~BIT(0); 323 acp_writel(val, rtd->acp5x_base + reg_val); 324 325 if (!(acp_readl(rtd->acp5x_base + ACP_HSTDM_ITER) & BIT(0)) && 326 !(acp_readl(rtd->acp5x_base + ACP_HSTDM_IRER) & BIT(0))) 327 acp_writel(0, rtd->acp5x_base + ACP_HSTDM_IER); 328 if (!(acp_readl(rtd->acp5x_base + ACP_I2STDM_ITER) & BIT(0)) && 329 !(acp_readl(rtd->acp5x_base + ACP_I2STDM_IRER) & BIT(0))) 330 acp_writel(0, rtd->acp5x_base + ACP_I2STDM_IER); 331 ret = 0; 332 break; 333 default: 334 ret = -EINVAL; 335 break; 336 } 337 return ret; 338 } 339 340 static const struct snd_soc_dai_ops acp5x_i2s_dai_ops = { 341 .hw_params = acp5x_i2s_hwparams, 342 .trigger = acp5x_i2s_trigger, 343 .set_fmt = acp5x_i2s_set_fmt, 344 .set_tdm_slot = acp5x_i2s_set_tdm_slot, 345 }; 346 347 static const struct snd_soc_component_driver acp5x_dai_component = { 348 .name = "acp5x-i2s", 349 .legacy_dai_naming = 1, 350 }; 351 352 static struct snd_soc_dai_driver acp5x_i2s_dai = { 353 .playback = { 354 .rates = SNDRV_PCM_RATE_8000_96000, 355 .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | 356 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, 357 .channels_min = 2, 358 .channels_max = 2, 359 .rate_min = 8000, 360 .rate_max = 96000, 361 }, 362 .capture = { 363 .rates = SNDRV_PCM_RATE_8000_96000, 364 .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | 365 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, 366 .channels_min = 2, 367 .channels_max = 2, 368 .rate_min = 8000, 369 .rate_max = 96000, 370 }, 371 .ops = &acp5x_i2s_dai_ops, 372 }; 373 374 static int acp5x_dai_probe(struct platform_device *pdev) 375 { 376 struct resource *res; 377 struct i2s_dev_data *adata; 378 int ret; 379 380 adata = devm_kzalloc(&pdev->dev, sizeof(struct i2s_dev_data), 381 GFP_KERNEL); 382 if (!adata) 383 return -ENOMEM; 384 385 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 386 if (!res) { 387 dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n"); 388 return -ENOMEM; 389 } 390 adata->acp5x_base = devm_ioremap(&pdev->dev, res->start, 391 resource_size(res)); 392 if (!adata->acp5x_base) 393 return -ENOMEM; 394 395 adata->master_mode = I2S_MASTER_MODE_ENABLE; 396 dev_set_drvdata(&pdev->dev, adata); 397 ret = devm_snd_soc_register_component(&pdev->dev, 398 &acp5x_dai_component, 399 &acp5x_i2s_dai, 1); 400 if (ret) 401 dev_err(&pdev->dev, "Fail to register acp i2s dai\n"); 402 return ret; 403 } 404 405 static struct platform_driver acp5x_dai_driver = { 406 .probe = acp5x_dai_probe, 407 .driver = { 408 .name = "acp5x_i2s_playcap", 409 }, 410 }; 411 412 module_platform_driver(acp5x_dai_driver); 413 414 MODULE_AUTHOR("Vijendar.Mukunda@amd.com"); 415 MODULE_DESCRIPTION("AMD ACP5.x CPU DAI Driver"); 416 MODULE_ALIAS("platform:" DRV_NAME); 417 MODULE_LICENSE("GPL v2"); 418