FILE
cygnus-pcm.c
File preview with syntax highlighting.
linux-6.19.14/sound/soc/bcm/cygnus-pcm.c
1// SPDX-License-Identifier: GPL-2.0-only 2// Copyright (C) 2014-2015 Broadcom Corporation 3#include <linux/debugfs.h> 4#include <linux/dma-mapping.h> 5#include <linux/init.h> 6#include <linux/io.h> 7#include <linux/module.h> 8#include <linux/slab.h> 9#include <linux/timer.h> 10#include <sound/core.h> 11#include <sound/pcm.h> 12#include <sound/pcm_params.h> 13#include <sound/soc.h> 14#include <sound/soc-dai.h> 15 16#include "cygnus-ssp.h" 17 18/* Register offset needed for ASoC PCM module */ 19 20#define INTH_R5F_STATUS_OFFSET 0x040 21#define INTH_R5F_CLEAR_OFFSET 0x048 22#define INTH_R5F_MASK_SET_OFFSET 0x050 23#define INTH_R5F_MASK_CLEAR_OFFSET 0x054 24 25#define BF_REARM_FREE_MARK_OFFSET 0x344 26#define BF_REARM_FULL_MARK_OFFSET 0x348 27 28/* Ring Buffer Ctrl Regs --- Start */ 29/* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_RDADDR_REG_BASE */ 30#define SRC_RBUF_0_RDADDR_OFFSET 0x500 31#define SRC_RBUF_1_RDADDR_OFFSET 0x518 32#define SRC_RBUF_2_RDADDR_OFFSET 0x530 33#define SRC_RBUF_3_RDADDR_OFFSET 0x548 34#define SRC_RBUF_4_RDADDR_OFFSET 0x560 35#define SRC_RBUF_5_RDADDR_OFFSET 0x578 36#define SRC_RBUF_6_RDADDR_OFFSET 0x590 37 38/* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_WRADDR_REG_BASE */ 39#define SRC_RBUF_0_WRADDR_OFFSET 0x504 40#define SRC_RBUF_1_WRADDR_OFFSET 0x51c 41#define SRC_RBUF_2_WRADDR_OFFSET 0x534 42#define SRC_RBUF_3_WRADDR_OFFSET 0x54c 43#define SRC_RBUF_4_WRADDR_OFFSET 0x564 44#define SRC_RBUF_5_WRADDR_OFFSET 0x57c 45#define SRC_RBUF_6_WRADDR_OFFSET 0x594 46 47/* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_BASEADDR_REG_BASE */ 48#define SRC_RBUF_0_BASEADDR_OFFSET 0x508 49#define SRC_RBUF_1_BASEADDR_OFFSET 0x520 50#define SRC_RBUF_2_BASEADDR_OFFSET 0x538 51#define SRC_RBUF_3_BASEADDR_OFFSET 0x550 52#define SRC_RBUF_4_BASEADDR_OFFSET 0x568 53#define SRC_RBUF_5_BASEADDR_OFFSET 0x580 54#define SRC_RBUF_6_BASEADDR_OFFSET 0x598 55 56/* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_ENDADDR_REG_BASE */ 57#define SRC_RBUF_0_ENDADDR_OFFSET 0x50c 58#define SRC_RBUF_1_ENDADDR_OFFSET 0x524 59#define SRC_RBUF_2_ENDADDR_OFFSET 0x53c 60#define SRC_RBUF_3_ENDADDR_OFFSET 0x554 61#define SRC_RBUF_4_ENDADDR_OFFSET 0x56c 62#define SRC_RBUF_5_ENDADDR_OFFSET 0x584 63#define SRC_RBUF_6_ENDADDR_OFFSET 0x59c 64 65/* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_FREE_MARK_REG_BASE */ 66#define SRC_RBUF_0_FREE_MARK_OFFSET 0x510 67#define SRC_RBUF_1_FREE_MARK_OFFSET 0x528 68#define SRC_RBUF_2_FREE_MARK_OFFSET 0x540 69#define SRC_RBUF_3_FREE_MARK_OFFSET 0x558 70#define SRC_RBUF_4_FREE_MARK_OFFSET 0x570 71#define SRC_RBUF_5_FREE_MARK_OFFSET 0x588 72#define SRC_RBUF_6_FREE_MARK_OFFSET 0x5a0 73 74/* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_RDADDR_REG_BASE */ 75#define DST_RBUF_0_RDADDR_OFFSET 0x5c0 76#define DST_RBUF_1_RDADDR_OFFSET 0x5d8 77#define DST_RBUF_2_RDADDR_OFFSET 0x5f0 78#define DST_RBUF_3_RDADDR_OFFSET 0x608 79#define DST_RBUF_4_RDADDR_OFFSET 0x620 80#define DST_RBUF_5_RDADDR_OFFSET 0x638 81 82/* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_WRADDR_REG_BASE */ 83#define DST_RBUF_0_WRADDR_OFFSET 0x5c4 84#define DST_RBUF_1_WRADDR_OFFSET 0x5dc 85#define DST_RBUF_2_WRADDR_OFFSET 0x5f4 86#define DST_RBUF_3_WRADDR_OFFSET 0x60c 87#define DST_RBUF_4_WRADDR_OFFSET 0x624 88#define DST_RBUF_5_WRADDR_OFFSET 0x63c 89 90/* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_BASEADDR_REG_BASE */ 91#define DST_RBUF_0_BASEADDR_OFFSET 0x5c8 92#define DST_RBUF_1_BASEADDR_OFFSET 0x5e0 93#define DST_RBUF_2_BASEADDR_OFFSET 0x5f8 94#define DST_RBUF_3_BASEADDR_OFFSET 0x610 95#define DST_RBUF_4_BASEADDR_OFFSET 0x628 96#define DST_RBUF_5_BASEADDR_OFFSET 0x640 97 98/* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_ENDADDR_REG_BASE */ 99#define DST_RBUF_0_ENDADDR_OFFSET 0x5cc 100#define DST_RBUF_1_ENDADDR_OFFSET 0x5e4 101#define DST_RBUF_2_ENDADDR_OFFSET 0x5fc 102#define DST_RBUF_3_ENDADDR_OFFSET 0x614 103#define DST_RBUF_4_ENDADDR_OFFSET 0x62c 104#define DST_RBUF_5_ENDADDR_OFFSET 0x644 105 106/* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_FULL_MARK_REG_BASE */ 107#define DST_RBUF_0_FULL_MARK_OFFSET 0x5d0 108#define DST_RBUF_1_FULL_MARK_OFFSET 0x5e8 109#define DST_RBUF_2_FULL_MARK_OFFSET 0x600 110#define DST_RBUF_3_FULL_MARK_OFFSET 0x618 111#define DST_RBUF_4_FULL_MARK_OFFSET 0x630 112#define DST_RBUF_5_FULL_MARK_OFFSET 0x648 113/* Ring Buffer Ctrl Regs --- End */ 114 115/* Error Status Regs --- Start */ 116/* AUD_FMM_BF_ESR_ESRX_STATUS_REG_BASE */ 117#define ESR0_STATUS_OFFSET 0x900 118#define ESR1_STATUS_OFFSET 0x918 119#define ESR2_STATUS_OFFSET 0x930 120#define ESR3_STATUS_OFFSET 0x948 121#define ESR4_STATUS_OFFSET 0x960 122 123/* AUD_FMM_BF_ESR_ESRX_STATUS_CLEAR_REG_BASE */ 124#define ESR0_STATUS_CLR_OFFSET 0x908 125#define ESR1_STATUS_CLR_OFFSET 0x920 126#define ESR2_STATUS_CLR_OFFSET 0x938 127#define ESR3_STATUS_CLR_OFFSET 0x950 128#define ESR4_STATUS_CLR_OFFSET 0x968 129 130/* AUD_FMM_BF_ESR_ESRX_MASK_REG_BASE */ 131#define ESR0_MASK_STATUS_OFFSET 0x90c 132#define ESR1_MASK_STATUS_OFFSET 0x924 133#define ESR2_MASK_STATUS_OFFSET 0x93c 134#define ESR3_MASK_STATUS_OFFSET 0x954 135#define ESR4_MASK_STATUS_OFFSET 0x96c 136 137/* AUD_FMM_BF_ESR_ESRX_MASK_SET_REG_BASE */ 138#define ESR0_MASK_SET_OFFSET 0x910 139#define ESR1_MASK_SET_OFFSET 0x928 140#define ESR2_MASK_SET_OFFSET 0x940 141#define ESR3_MASK_SET_OFFSET 0x958 142#define ESR4_MASK_SET_OFFSET 0x970 143 144/* AUD_FMM_BF_ESR_ESRX_MASK_CLEAR_REG_BASE */ 145#define ESR0_MASK_CLR_OFFSET 0x914 146#define ESR1_MASK_CLR_OFFSET 0x92c 147#define ESR2_MASK_CLR_OFFSET 0x944 148#define ESR3_MASK_CLR_OFFSET 0x95c 149#define ESR4_MASK_CLR_OFFSET 0x974 150/* Error Status Regs --- End */ 151 152#define R5F_ESR0_SHIFT 0 /* esr0 = fifo underflow */ 153#define R5F_ESR1_SHIFT 1 /* esr1 = ringbuf underflow */ 154#define R5F_ESR2_SHIFT 2 /* esr2 = ringbuf overflow */ 155#define R5F_ESR3_SHIFT 3 /* esr3 = freemark */ 156#define R5F_ESR4_SHIFT 4 /* esr4 = fullmark */ 157 158 159/* Mask for R5F register. Set all relevant interrupt for playback handler */ 160#define ANY_PLAYBACK_IRQ (BIT(R5F_ESR0_SHIFT) | \ 161 BIT(R5F_ESR1_SHIFT) | \ 162 BIT(R5F_ESR3_SHIFT)) 163 164/* Mask for R5F register. Set all relevant interrupt for capture handler */ 165#define ANY_CAPTURE_IRQ (BIT(R5F_ESR2_SHIFT) | BIT(R5F_ESR4_SHIFT)) 166 167/* 168 * PERIOD_BYTES_MIN is the number of bytes to at which the interrupt will tick. 169 * This number should be a multiple of 256. Minimum value is 256 170 */ 171#define PERIOD_BYTES_MIN 0x100 172 173static const struct snd_pcm_hardware cygnus_pcm_hw = { 174 .info = SNDRV_PCM_INFO_MMAP | 175 SNDRV_PCM_INFO_MMAP_VALID | 176 SNDRV_PCM_INFO_INTERLEAVED, 177 .formats = SNDRV_PCM_FMTBIT_S16_LE | 178 SNDRV_PCM_FMTBIT_S32_LE, 179 180 /* A period is basically an interrupt */ 181 .period_bytes_min = PERIOD_BYTES_MIN, 182 .period_bytes_max = 0x10000, 183 184 /* period_min/max gives range of approx interrupts per buffer */ 185 .periods_min = 2, 186 .periods_max = 8, 187 188 /* 189 * maximum buffer size in bytes = period_bytes_max * periods_max 190 * We allocate this amount of data for each enabled channel 191 */ 192 .buffer_bytes_max = 4 * 0x8000, 193}; 194 195static u64 cygnus_dma_dmamask = DMA_BIT_MASK(32); 196 197static struct cygnus_aio_port *cygnus_dai_get_dma_data( 198 struct snd_pcm_substream *substream) 199{ 200 struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(substream); 201 202 return snd_soc_dai_get_dma_data(snd_soc_rtd_to_cpu(soc_runtime, 0), substream); 203} 204 205static void ringbuf_set_initial(void __iomem *audio_io, 206 struct ringbuf_regs *p_rbuf, 207 bool is_playback, 208 u32 start, 209 u32 periodsize, 210 u32 bufsize) 211{ 212 u32 initial_rd; 213 u32 initial_wr; 214 u32 end; 215 u32 fmark_val; /* free or full mark */ 216 217 p_rbuf->period_bytes = periodsize; 218 p_rbuf->buf_size = bufsize; 219 220 if (is_playback) { 221 /* Set the pointers to indicate full (flip uppermost bit) */ 222 initial_rd = start; 223 initial_wr = initial_rd ^ BIT(31); 224 } else { 225 /* Set the pointers to indicate empty */ 226 initial_wr = start; 227 initial_rd = initial_wr; 228 } 229 230 end = start + bufsize - 1; 231 232 /* 233 * The interrupt will fire when free/full mark is *exceeded* 234 * The fmark value must be multiple of PERIOD_BYTES_MIN so set fmark 235 * to be PERIOD_BYTES_MIN less than the period size. 236 */ 237 fmark_val = periodsize - PERIOD_BYTES_MIN; 238 239 writel(start, audio_io + p_rbuf->baseaddr); 240 writel(end, audio_io + p_rbuf->endaddr); 241 writel(fmark_val, audio_io + p_rbuf->fmark); 242 writel(initial_rd, audio_io + p_rbuf->rdaddr); 243 writel(initial_wr, audio_io + p_rbuf->wraddr); 244} 245 246static int configure_ringbuf_regs(struct snd_pcm_substream *substream) 247{ 248 struct cygnus_aio_port *aio; 249 struct ringbuf_regs *p_rbuf; 250 int status = 0; 251 252 aio = cygnus_dai_get_dma_data(substream); 253 254 /* Map the ssp portnum to a set of ring buffers. */ 255 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 256 p_rbuf = &aio->play_rb_regs; 257 258 switch (aio->portnum) { 259 case 0: 260 *p_rbuf = RINGBUF_REG_PLAYBACK(0); 261 break; 262 case 1: 263 *p_rbuf = RINGBUF_REG_PLAYBACK(2); 264 break; 265 case 2: 266 *p_rbuf = RINGBUF_REG_PLAYBACK(4); 267 break; 268 case 3: /* SPDIF */ 269 *p_rbuf = RINGBUF_REG_PLAYBACK(6); 270 break; 271 default: 272 status = -EINVAL; 273 } 274 } else { 275 p_rbuf = &aio->capture_rb_regs; 276 277 switch (aio->portnum) { 278 case 0: 279 *p_rbuf = RINGBUF_REG_CAPTURE(0); 280 break; 281 case 1: 282 *p_rbuf = RINGBUF_REG_CAPTURE(2); 283 break; 284 case 2: 285 *p_rbuf = RINGBUF_REG_CAPTURE(4); 286 break; 287 default: 288 status = -EINVAL; 289 } 290 } 291 292 return status; 293} 294 295static struct ringbuf_regs *get_ringbuf(struct snd_pcm_substream *substream) 296{ 297 struct cygnus_aio_port *aio; 298 struct ringbuf_regs *p_rbuf = NULL; 299 300 aio = cygnus_dai_get_dma_data(substream); 301 302 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 303 p_rbuf = &aio->play_rb_regs; 304 else 305 p_rbuf = &aio->capture_rb_regs; 306 307 return p_rbuf; 308} 309 310static void enable_intr(struct snd_pcm_substream *substream) 311{ 312 struct cygnus_aio_port *aio; 313 u32 clear_mask; 314 315 aio = cygnus_dai_get_dma_data(substream); 316 317 /* The port number maps to the bit position to be cleared */ 318 clear_mask = BIT(aio->portnum); 319 320 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 321 /* Clear interrupt status before enabling them */ 322 writel(clear_mask, aio->cygaud->audio + ESR0_STATUS_CLR_OFFSET); 323 writel(clear_mask, aio->cygaud->audio + ESR1_STATUS_CLR_OFFSET); 324 writel(clear_mask, aio->cygaud->audio + ESR3_STATUS_CLR_OFFSET); 325 /* Unmask the interrupts of the given port*/ 326 writel(clear_mask, aio->cygaud->audio + ESR0_MASK_CLR_OFFSET); 327 writel(clear_mask, aio->cygaud->audio + ESR1_MASK_CLR_OFFSET); 328 writel(clear_mask, aio->cygaud->audio + ESR3_MASK_CLR_OFFSET); 329 330 writel(ANY_PLAYBACK_IRQ, 331 aio->cygaud->audio + INTH_R5F_MASK_CLEAR_OFFSET); 332 } else { 333 writel(clear_mask, aio->cygaud->audio + ESR2_STATUS_CLR_OFFSET); 334 writel(clear_mask, aio->cygaud->audio + ESR4_STATUS_CLR_OFFSET); 335 writel(clear_mask, aio->cygaud->audio + ESR2_MASK_CLR_OFFSET); 336 writel(clear_mask, aio->cygaud->audio + ESR4_MASK_CLR_OFFSET); 337 338 writel(ANY_CAPTURE_IRQ, 339 aio->cygaud->audio + INTH_R5F_MASK_CLEAR_OFFSET); 340 } 341 342} 343 344static void disable_intr(struct snd_pcm_substream *substream) 345{ 346 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); 347 struct cygnus_aio_port *aio; 348 u32 set_mask; 349 350 aio = cygnus_dai_get_dma_data(substream); 351 352 dev_dbg(snd_soc_rtd_to_cpu(rtd, 0)->dev, "%s on port %d\n", __func__, aio->portnum); 353 354 /* The port number maps to the bit position to be set */ 355 set_mask = BIT(aio->portnum); 356 357 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 358 /* Mask the interrupts of the given port*/ 359 writel(set_mask, aio->cygaud->audio + ESR0_MASK_SET_OFFSET); 360 writel(set_mask, aio->cygaud->audio + ESR1_MASK_SET_OFFSET); 361 writel(set_mask, aio->cygaud->audio + ESR3_MASK_SET_OFFSET); 362 } else { 363 writel(set_mask, aio->cygaud->audio + ESR2_MASK_SET_OFFSET); 364 writel(set_mask, aio->cygaud->audio + ESR4_MASK_SET_OFFSET); 365 } 366 367} 368 369static int cygnus_pcm_trigger(struct snd_soc_component *component, 370 struct snd_pcm_substream *substream, int cmd) 371{ 372 int ret = 0; 373 374 switch (cmd) { 375 case SNDRV_PCM_TRIGGER_START: 376 case SNDRV_PCM_TRIGGER_RESUME: 377 enable_intr(substream); 378 break; 379 380 case SNDRV_PCM_TRIGGER_STOP: 381 case SNDRV_PCM_TRIGGER_SUSPEND: 382 disable_intr(substream); 383 break; 384 default: 385 ret = -EINVAL; 386 } 387 388 return ret; 389} 390 391static void cygnus_pcm_period_elapsed(struct snd_pcm_substream *substream) 392{ 393 struct cygnus_aio_port *aio; 394 struct ringbuf_regs *p_rbuf = NULL; 395 u32 regval; 396 397 aio = cygnus_dai_get_dma_data(substream); 398 399 p_rbuf = get_ringbuf(substream); 400 401 /* 402 * If free/full mark interrupt occurs, provide timestamp 403 * to ALSA and update appropriate idx by period_bytes 404 */ 405 snd_pcm_period_elapsed(substream); 406 407 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 408 /* Set the ring buffer to full */ 409 regval = readl(aio->cygaud->audio + p_rbuf->rdaddr); 410 regval = regval ^ BIT(31); 411 writel(regval, aio->cygaud->audio + p_rbuf->wraddr); 412 } else { 413 /* Set the ring buffer to empty */ 414 regval = readl(aio->cygaud->audio + p_rbuf->wraddr); 415 writel(regval, aio->cygaud->audio + p_rbuf->rdaddr); 416 } 417} 418 419/* 420 * ESR0/1/3 status Description 421 * 0x1 I2S0_out port caused interrupt 422 * 0x2 I2S1_out port caused interrupt 423 * 0x4 I2S2_out port caused interrupt 424 * 0x8 SPDIF_out port caused interrupt 425 */ 426static void handle_playback_irq(struct cygnus_audio *cygaud) 427{ 428 void __iomem *audio_io; 429 u32 port; 430 u32 esr_status0, esr_status1, esr_status3; 431 432 audio_io = cygaud->audio; 433 434 /* 435 * ESR status gets updates with/without interrupts enabled. 436 * So, check the ESR mask, which provides interrupt enable/ 437 * disable status and use it to determine which ESR status 438 * should be serviced. 439 */ 440 esr_status0 = readl(audio_io + ESR0_STATUS_OFFSET); 441 esr_status0 &= ~readl(audio_io + ESR0_MASK_STATUS_OFFSET); 442 esr_status1 = readl(audio_io + ESR1_STATUS_OFFSET); 443 esr_status1 &= ~readl(audio_io + ESR1_MASK_STATUS_OFFSET); 444 esr_status3 = readl(audio_io + ESR3_STATUS_OFFSET); 445 esr_status3 &= ~readl(audio_io + ESR3_MASK_STATUS_OFFSET); 446 447 for (port = 0; port < CYGNUS_MAX_PLAYBACK_PORTS; port++) { 448 u32 esrmask = BIT(port); 449 450 /* 451 * Ringbuffer or FIFO underflow 452 * If we get this interrupt then, it is also true that we have 453 * not yet responded to the freemark interrupt. 454 * Log a debug message. The freemark handler below will 455 * handle getting everything going again. 456 */ 457 if ((esrmask & esr_status1) || (esrmask & esr_status0)) { 458 dev_dbg(cygaud->dev, 459 "Underrun: esr0=0x%x, esr1=0x%x esr3=0x%x\n", 460 esr_status0, esr_status1, esr_status3); 461 } 462 463 /* 464 * Freemark is hit. This is the normal interrupt. 465 * In typical operation the read and write regs will be equal 466 */ 467 if (esrmask & esr_status3) { 468 struct snd_pcm_substream *playstr; 469 470 playstr = cygaud->portinfo[port].play_stream; 471 cygnus_pcm_period_elapsed(playstr); 472 } 473 } 474 475 /* Clear ESR interrupt */ 476 writel(esr_status0, audio_io + ESR0_STATUS_CLR_OFFSET); 477 writel(esr_status1, audio_io + ESR1_STATUS_CLR_OFFSET); 478 writel(esr_status3, audio_io + ESR3_STATUS_CLR_OFFSET); 479 /* Rearm freemark logic by writing 1 to the correct bit */ 480 writel(esr_status3, audio_io + BF_REARM_FREE_MARK_OFFSET); 481} 482 483/* 484 * ESR2/4 status Description 485 * 0x1 I2S0_in port caused interrupt 486 * 0x2 I2S1_in port caused interrupt 487 * 0x4 I2S2_in port caused interrupt 488 */ 489static void handle_capture_irq(struct cygnus_audio *cygaud) 490{ 491 void __iomem *audio_io; 492 u32 port; 493 u32 esr_status2, esr_status4; 494 495 audio_io = cygaud->audio; 496 497 /* 498 * ESR status gets updates with/without interrupts enabled. 499 * So, check the ESR mask, which provides interrupt enable/ 500 * disable status and use it to determine which ESR status 501 * should be serviced. 502 */ 503 esr_status2 = readl(audio_io + ESR2_STATUS_OFFSET); 504 esr_status2 &= ~readl(audio_io + ESR2_MASK_STATUS_OFFSET); 505 esr_status4 = readl(audio_io + ESR4_STATUS_OFFSET); 506 esr_status4 &= ~readl(audio_io + ESR4_MASK_STATUS_OFFSET); 507 508 for (port = 0; port < CYGNUS_MAX_CAPTURE_PORTS; port++) { 509 u32 esrmask = BIT(port); 510 511 /* 512 * Ringbuffer or FIFO overflow 513 * If we get this interrupt then, it is also true that we have 514 * not yet responded to the fullmark interrupt. 515 * Log a debug message. The fullmark handler below will 516 * handle getting everything going again. 517 */ 518 if (esrmask & esr_status2) 519 dev_dbg(cygaud->dev, 520 "Overflow: esr2=0x%x\n", esr_status2); 521 522 if (esrmask & esr_status4) { 523 struct snd_pcm_substream *capstr; 524 525 capstr = cygaud->portinfo[port].capture_stream; 526 cygnus_pcm_period_elapsed(capstr); 527 } 528 } 529 530 writel(esr_status2, audio_io + ESR2_STATUS_CLR_OFFSET); 531 writel(esr_status4, audio_io + ESR4_STATUS_CLR_OFFSET); 532 /* Rearm fullmark logic by writing 1 to the correct bit */ 533 writel(esr_status4, audio_io + BF_REARM_FULL_MARK_OFFSET); 534} 535 536static irqreturn_t cygnus_dma_irq(int irq, void *data) 537{ 538 u32 r5_status; 539 struct cygnus_audio *cygaud = data; 540 541 /* 542 * R5 status bits Description 543 * 0 ESR0 (playback FIFO interrupt) 544 * 1 ESR1 (playback rbuf interrupt) 545 * 2 ESR2 (capture rbuf interrupt) 546 * 3 ESR3 (Freemark play. interrupt) 547 * 4 ESR4 (Fullmark capt. interrupt) 548 */ 549 r5_status = readl(cygaud->audio + INTH_R5F_STATUS_OFFSET); 550 551 if (!(r5_status & (ANY_PLAYBACK_IRQ | ANY_CAPTURE_IRQ))) 552 return IRQ_NONE; 553 554 /* If playback interrupt happened */ 555 if (ANY_PLAYBACK_IRQ & r5_status) { 556 handle_playback_irq(cygaud); 557 writel(ANY_PLAYBACK_IRQ & r5_status, 558 cygaud->audio + INTH_R5F_CLEAR_OFFSET); 559 } 560 561 /* If capture interrupt happened */ 562 if (ANY_CAPTURE_IRQ & r5_status) { 563 handle_capture_irq(cygaud); 564 writel(ANY_CAPTURE_IRQ & r5_status, 565 cygaud->audio + INTH_R5F_CLEAR_OFFSET); 566 } 567 568 return IRQ_HANDLED; 569} 570 571static int cygnus_pcm_open(struct snd_soc_component *component, 572 struct snd_pcm_substream *substream) 573{ 574 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); 575 struct snd_pcm_runtime *runtime = substream->runtime; 576 struct cygnus_aio_port *aio; 577 int ret; 578 579 aio = cygnus_dai_get_dma_data(substream); 580 if (!aio) 581 return -ENODEV; 582 583 dev_dbg(snd_soc_rtd_to_cpu(rtd, 0)->dev, "%s port %d\n", __func__, aio->portnum); 584 585 snd_soc_set_runtime_hwparams(substream, &cygnus_pcm_hw); 586 587 ret = snd_pcm_hw_constraint_step(runtime, 0, 588 SNDRV_PCM_HW_PARAM_PERIOD_BYTES, PERIOD_BYTES_MIN); 589 if (ret < 0) 590 return ret; 591 592 ret = snd_pcm_hw_constraint_step(runtime, 0, 593 SNDRV_PCM_HW_PARAM_BUFFER_BYTES, PERIOD_BYTES_MIN); 594 if (ret < 0) 595 return ret; 596 /* 597 * Keep track of which substream belongs to which port. 598 * This info is needed by snd_pcm_period_elapsed() in irq_handler 599 */ 600 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 601 aio->play_stream = substream; 602 else 603 aio->capture_stream = substream; 604 605 return 0; 606} 607 608static int cygnus_pcm_close(struct snd_soc_component *component, 609 struct snd_pcm_substream *substream) 610{ 611 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); 612 struct cygnus_aio_port *aio; 613 614 aio = cygnus_dai_get_dma_data(substream); 615 616 dev_dbg(snd_soc_rtd_to_cpu(rtd, 0)->dev, "%s port %d\n", __func__, aio->portnum); 617 618 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 619 aio->play_stream = NULL; 620 else 621 aio->capture_stream = NULL; 622 623 if (!aio->play_stream && !aio->capture_stream) 624 dev_dbg(snd_soc_rtd_to_cpu(rtd, 0)->dev, "freed port %d\n", aio->portnum); 625 626 return 0; 627} 628 629static int cygnus_pcm_prepare(struct snd_soc_component *component, 630 struct snd_pcm_substream *substream) 631{ 632 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); 633 struct snd_pcm_runtime *runtime = substream->runtime; 634 struct cygnus_aio_port *aio; 635 unsigned long bufsize, periodsize; 636 bool is_play; 637 u32 start; 638 struct ringbuf_regs *p_rbuf = NULL; 639 640 aio = cygnus_dai_get_dma_data(substream); 641 dev_dbg(snd_soc_rtd_to_cpu(rtd, 0)->dev, "%s port %d\n", __func__, aio->portnum); 642 643 bufsize = snd_pcm_lib_buffer_bytes(substream); 644 periodsize = snd_pcm_lib_period_bytes(substream); 645 646 dev_dbg(snd_soc_rtd_to_cpu(rtd, 0)->dev, "%s (buf_size %lu) (period_size %lu)\n", 647 __func__, bufsize, periodsize); 648 649 configure_ringbuf_regs(substream); 650 651 p_rbuf = get_ringbuf(substream); 652 653 start = runtime->dma_addr; 654 655 is_play = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? 1 : 0; 656 657 ringbuf_set_initial(aio->cygaud->audio, p_rbuf, is_play, start, 658 periodsize, bufsize); 659 660 return 0; 661} 662 663static snd_pcm_uframes_t cygnus_pcm_pointer(struct snd_soc_component *component, 664 struct snd_pcm_substream *substream) 665{ 666 struct cygnus_aio_port *aio; 667 unsigned int res = 0, cur = 0, base = 0; 668 struct ringbuf_regs *p_rbuf = NULL; 669 670 aio = cygnus_dai_get_dma_data(substream); 671 672 /* 673 * Get the offset of the current read (for playack) or write 674 * index (for capture). Report this value back to the asoc framework. 675 */ 676 p_rbuf = get_ringbuf(substream); 677 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 678 cur = readl(aio->cygaud->audio + p_rbuf->rdaddr); 679 else 680 cur = readl(aio->cygaud->audio + p_rbuf->wraddr); 681 682 base = readl(aio->cygaud->audio + p_rbuf->baseaddr); 683 684 /* 685 * Mask off the MSB of the rdaddr,wraddr and baseaddr 686 * since MSB is not part of the address 687 */ 688 res = (cur & 0x7fffffff) - (base & 0x7fffffff); 689 690 return bytes_to_frames(substream->runtime, res); 691} 692 693static int cygnus_dma_new(struct snd_soc_component *component, 694 struct snd_soc_pcm_runtime *rtd) 695{ 696 size_t size = cygnus_pcm_hw.buffer_bytes_max; 697 struct snd_card *card = rtd->card->snd_card; 698 699 if (!card->dev->dma_mask) 700 card->dev->dma_mask = &cygnus_dma_dmamask; 701 if (!card->dev->coherent_dma_mask) 702 card->dev->coherent_dma_mask = DMA_BIT_MASK(32); 703 704 snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV, 705 card->dev, size, size); 706 707 return 0; 708} 709 710static const struct snd_soc_component_driver cygnus_soc_platform = { 711 .open = cygnus_pcm_open, 712 .close = cygnus_pcm_close, 713 .prepare = cygnus_pcm_prepare, 714 .trigger = cygnus_pcm_trigger, 715 .pointer = cygnus_pcm_pointer, 716 .pcm_construct = cygnus_dma_new, 717}; 718 719int cygnus_soc_platform_register(struct device *dev, 720 struct cygnus_audio *cygaud) 721{ 722 int rc; 723 724 dev_dbg(dev, "%s Enter\n", __func__); 725 726 rc = devm_request_irq(dev, cygaud->irq_num, cygnus_dma_irq, 727 IRQF_SHARED, "cygnus-audio", cygaud); 728 if (rc) { 729 dev_err(dev, "%s request_irq error %d\n", __func__, rc); 730 return rc; 731 } 732 733 rc = devm_snd_soc_register_component(dev, &cygnus_soc_platform, 734 NULL, 0); 735 if (rc) { 736 dev_err(dev, "%s failed\n", __func__); 737 return rc; 738 } 739 740 return 0; 741} 742 743int cygnus_soc_platform_unregister(struct device *dev) 744{ 745 return 0; 746} 747 748MODULE_LICENSE("GPL v2"); 749MODULE_AUTHOR("Broadcom"); 750MODULE_DESCRIPTION("Cygnus ASoC PCM module");
~/
Read-only file preview.