| .. | .. |
|---|
| 9 | 9 | #include <linux/module.h> |
|---|
| 10 | 10 | #include <linux/of_irq.h> |
|---|
| 11 | 11 | #include <linux/of_platform.h> |
|---|
| 12 | +#include <linux/pm_runtime.h> |
|---|
| 12 | 13 | #include <sound/dmaengine_pcm.h> |
|---|
| 13 | 14 | #include <sound/pcm_params.h> |
|---|
| 14 | 15 | |
|---|
| .. | .. |
|---|
| 21 | 22 | SNDRV_PCM_FMTBIT_S24_LE) |
|---|
| 22 | 23 | |
|---|
| 23 | 24 | /** |
|---|
| 24 | | - * fsl_esai: ESAI private data |
|---|
| 25 | | - * |
|---|
| 25 | + * struct fsl_esai_soc_data - soc specific data |
|---|
| 26 | + * @imx: for imx platform |
|---|
| 27 | + * @reset_at_xrun: flags for enable reset operaton |
|---|
| 28 | + */ |
|---|
| 29 | +struct fsl_esai_soc_data { |
|---|
| 30 | + bool imx; |
|---|
| 31 | + bool reset_at_xrun; |
|---|
| 32 | +}; |
|---|
| 33 | + |
|---|
| 34 | +/** |
|---|
| 35 | + * struct fsl_esai - ESAI private data |
|---|
| 26 | 36 | * @dma_params_rx: DMA parameters for receive channel |
|---|
| 27 | 37 | * @dma_params_tx: DMA parameters for transmit channel |
|---|
| 28 | 38 | * @pdev: platform device pointer |
|---|
| .. | .. |
|---|
| 31 | 41 | * @extalclk: esai clock source to derive HCK, SCK and FS |
|---|
| 32 | 42 | * @fsysclk: system clock source to derive HCK, SCK and FS |
|---|
| 33 | 43 | * @spbaclk: SPBA clock (optional, depending on SoC design) |
|---|
| 44 | + * @work: work to handle the reset operation |
|---|
| 45 | + * @soc: soc specific data |
|---|
| 46 | + * @lock: spin lock between hw_reset() and trigger() |
|---|
| 34 | 47 | * @fifo_depth: depth of tx/rx FIFO |
|---|
| 35 | 48 | * @slot_width: width of each DAI slot |
|---|
| 36 | 49 | * @slots: number of slots |
|---|
| 50 | + * @tx_mask: slot mask for TX |
|---|
| 51 | + * @rx_mask: slot mask for RX |
|---|
| 52 | + * @channels: channel num for tx or rx |
|---|
| 37 | 53 | * @hck_rate: clock rate of desired HCKx clock |
|---|
| 38 | 54 | * @sck_rate: clock rate of desired SCKx clock |
|---|
| 39 | 55 | * @hck_dir: the direction of HCKx pads |
|---|
| .. | .. |
|---|
| 51 | 67 | struct clk *extalclk; |
|---|
| 52 | 68 | struct clk *fsysclk; |
|---|
| 53 | 69 | struct clk *spbaclk; |
|---|
| 70 | + struct work_struct work; |
|---|
| 71 | + const struct fsl_esai_soc_data *soc; |
|---|
| 72 | + spinlock_t lock; /* Protect hw_reset and trigger */ |
|---|
| 54 | 73 | u32 fifo_depth; |
|---|
| 55 | 74 | u32 slot_width; |
|---|
| 56 | 75 | u32 slots; |
|---|
| 57 | 76 | u32 tx_mask; |
|---|
| 58 | 77 | u32 rx_mask; |
|---|
| 78 | + u32 channels[2]; |
|---|
| 59 | 79 | u32 hck_rate[2]; |
|---|
| 60 | 80 | u32 sck_rate[2]; |
|---|
| 61 | 81 | bool hck_dir[2]; |
|---|
| .. | .. |
|---|
| 65 | 85 | char name[32]; |
|---|
| 66 | 86 | }; |
|---|
| 67 | 87 | |
|---|
| 88 | +static struct fsl_esai_soc_data fsl_esai_vf610 = { |
|---|
| 89 | + .imx = false, |
|---|
| 90 | + .reset_at_xrun = true, |
|---|
| 91 | +}; |
|---|
| 92 | + |
|---|
| 93 | +static struct fsl_esai_soc_data fsl_esai_imx35 = { |
|---|
| 94 | + .imx = true, |
|---|
| 95 | + .reset_at_xrun = true, |
|---|
| 96 | +}; |
|---|
| 97 | + |
|---|
| 98 | +static struct fsl_esai_soc_data fsl_esai_imx6ull = { |
|---|
| 99 | + .imx = true, |
|---|
| 100 | + .reset_at_xrun = false, |
|---|
| 101 | +}; |
|---|
| 102 | + |
|---|
| 68 | 103 | static irqreturn_t esai_isr(int irq, void *devid) |
|---|
| 69 | 104 | { |
|---|
| 70 | 105 | struct fsl_esai *esai_priv = (struct fsl_esai *)devid; |
|---|
| 71 | 106 | struct platform_device *pdev = esai_priv->pdev; |
|---|
| 72 | 107 | u32 esr; |
|---|
| 108 | + u32 saisr; |
|---|
| 73 | 109 | |
|---|
| 74 | 110 | regmap_read(esai_priv->regmap, REG_ESAI_ESR, &esr); |
|---|
| 111 | + regmap_read(esai_priv->regmap, REG_ESAI_SAISR, &saisr); |
|---|
| 112 | + |
|---|
| 113 | + if ((saisr & (ESAI_SAISR_TUE | ESAI_SAISR_ROE)) && |
|---|
| 114 | + esai_priv->soc->reset_at_xrun) { |
|---|
| 115 | + dev_dbg(&pdev->dev, "reset module for xrun\n"); |
|---|
| 116 | + regmap_update_bits(esai_priv->regmap, REG_ESAI_TCR, |
|---|
| 117 | + ESAI_xCR_xEIE_MASK, 0); |
|---|
| 118 | + regmap_update_bits(esai_priv->regmap, REG_ESAI_RCR, |
|---|
| 119 | + ESAI_xCR_xEIE_MASK, 0); |
|---|
| 120 | + schedule_work(&esai_priv->work); |
|---|
| 121 | + } |
|---|
| 75 | 122 | |
|---|
| 76 | 123 | if (esr & ESAI_ESR_TINIT_MASK) |
|---|
| 77 | 124 | dev_dbg(&pdev->dev, "isr: Transmission Initialized\n"); |
|---|
| .. | .. |
|---|
| 110 | 157 | } |
|---|
| 111 | 158 | |
|---|
| 112 | 159 | /** |
|---|
| 113 | | - * This function is used to calculate the divisors of psr, pm, fp and it is |
|---|
| 114 | | - * supposed to be called in set_dai_sysclk() and set_bclk(). |
|---|
| 160 | + * fsl_esai_divisor_cal - This function is used to calculate the |
|---|
| 161 | + * divisors of psr, pm, fp and it is supposed to be called in |
|---|
| 162 | + * set_dai_sysclk() and set_bclk(). |
|---|
| 115 | 163 | * |
|---|
| 164 | + * @dai: pointer to DAI |
|---|
| 165 | + * @tx: current setting is for playback or capture |
|---|
| 116 | 166 | * @ratio: desired overall ratio for the paticipating dividers |
|---|
| 117 | 167 | * @usefp: for HCK setting, there is no need to set fp divider |
|---|
| 118 | 168 | * @fp: bypass other dividers by setting fp directly if fp != 0 |
|---|
| 119 | | - * @tx: current setting is for playback or capture |
|---|
| 120 | 169 | */ |
|---|
| 121 | 170 | static int fsl_esai_divisor_cal(struct snd_soc_dai *dai, bool tx, u32 ratio, |
|---|
| 122 | 171 | bool usefp, u32 fp) |
|---|
| .. | .. |
|---|
| 203 | 252 | } |
|---|
| 204 | 253 | |
|---|
| 205 | 254 | /** |
|---|
| 206 | | - * This function mainly configures the clock frequency of MCLK (HCKT/HCKR) |
|---|
| 207 | | - * |
|---|
| 208 | | - * @Parameters: |
|---|
| 209 | | - * clk_id: The clock source of HCKT/HCKR |
|---|
| 255 | + * fsl_esai_set_dai_sysclk - configure the clock frequency of MCLK (HCKT/HCKR) |
|---|
| 256 | + * @dai: pointer to DAI |
|---|
| 257 | + * @clk_id: The clock source of HCKT/HCKR |
|---|
| 210 | 258 | * (Input from outside; output from inside, FSYS or EXTAL) |
|---|
| 211 | | - * freq: The required clock rate of HCKT/HCKR |
|---|
| 212 | | - * dir: The clock direction of HCKT/HCKR |
|---|
| 259 | + * @freq: The required clock rate of HCKT/HCKR |
|---|
| 260 | + * @dir: The clock direction of HCKT/HCKR |
|---|
| 213 | 261 | * |
|---|
| 214 | 262 | * Note: If the direction is input, we do not care about clk_id. |
|---|
| 215 | 263 | */ |
|---|
| .. | .. |
|---|
| 218 | 266 | { |
|---|
| 219 | 267 | struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); |
|---|
| 220 | 268 | struct clk *clksrc = esai_priv->extalclk; |
|---|
| 221 | | - bool tx = clk_id <= ESAI_HCKT_EXTAL; |
|---|
| 269 | + bool tx = (clk_id <= ESAI_HCKT_EXTAL || esai_priv->synchronous); |
|---|
| 222 | 270 | bool in = dir == SND_SOC_CLOCK_IN; |
|---|
| 223 | 271 | u32 ratio, ecr = 0; |
|---|
| 224 | 272 | unsigned long clk_rate; |
|---|
| .. | .. |
|---|
| 253 | 301 | ecr |= ESAI_ECR_ETI; |
|---|
| 254 | 302 | break; |
|---|
| 255 | 303 | case ESAI_HCKR_EXTAL: |
|---|
| 256 | | - ecr |= ESAI_ECR_ERI; |
|---|
| 304 | + ecr |= esai_priv->synchronous ? ESAI_ECR_ETI : ESAI_ECR_ERI; |
|---|
| 257 | 305 | break; |
|---|
| 258 | 306 | default: |
|---|
| 259 | 307 | return -EINVAL; |
|---|
| .. | .. |
|---|
| 311 | 359 | } |
|---|
| 312 | 360 | |
|---|
| 313 | 361 | /** |
|---|
| 314 | | - * This function configures the related dividers according to the bclk rate |
|---|
| 362 | + * fsl_esai_set_bclk - configure the related dividers according to the bclk rate |
|---|
| 363 | + * @dai: pointer to DAI |
|---|
| 364 | + * @tx: direction boolean |
|---|
| 365 | + * @freq: bclk freq |
|---|
| 315 | 366 | */ |
|---|
| 316 | 367 | static int fsl_esai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq) |
|---|
| 317 | 368 | { |
|---|
| .. | .. |
|---|
| 466 | 517 | struct snd_soc_dai *dai) |
|---|
| 467 | 518 | { |
|---|
| 468 | 519 | struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); |
|---|
| 469 | | - int ret; |
|---|
| 470 | 520 | |
|---|
| 471 | | - /* |
|---|
| 472 | | - * Some platforms might use the same bit to gate all three or two of |
|---|
| 473 | | - * clocks, so keep all clocks open/close at the same time for safety |
|---|
| 474 | | - */ |
|---|
| 475 | | - ret = clk_prepare_enable(esai_priv->coreclk); |
|---|
| 476 | | - if (ret) |
|---|
| 477 | | - return ret; |
|---|
| 478 | | - if (!IS_ERR(esai_priv->spbaclk)) { |
|---|
| 479 | | - ret = clk_prepare_enable(esai_priv->spbaclk); |
|---|
| 480 | | - if (ret) |
|---|
| 481 | | - goto err_spbaclk; |
|---|
| 482 | | - } |
|---|
| 483 | | - if (!IS_ERR(esai_priv->extalclk)) { |
|---|
| 484 | | - ret = clk_prepare_enable(esai_priv->extalclk); |
|---|
| 485 | | - if (ret) |
|---|
| 486 | | - goto err_extalck; |
|---|
| 487 | | - } |
|---|
| 488 | | - if (!IS_ERR(esai_priv->fsysclk)) { |
|---|
| 489 | | - ret = clk_prepare_enable(esai_priv->fsysclk); |
|---|
| 490 | | - if (ret) |
|---|
| 491 | | - goto err_fsysclk; |
|---|
| 492 | | - } |
|---|
| 493 | | - |
|---|
| 494 | | - if (!dai->active) { |
|---|
| 521 | + if (!snd_soc_dai_active(dai)) { |
|---|
| 495 | 522 | /* Set synchronous mode */ |
|---|
| 496 | 523 | regmap_update_bits(esai_priv->regmap, REG_ESAI_SAICR, |
|---|
| 497 | 524 | ESAI_SAICR_SYNC, esai_priv->synchronous ? |
|---|
| .. | .. |
|---|
| 508 | 535 | |
|---|
| 509 | 536 | return 0; |
|---|
| 510 | 537 | |
|---|
| 511 | | -err_fsysclk: |
|---|
| 512 | | - if (!IS_ERR(esai_priv->extalclk)) |
|---|
| 513 | | - clk_disable_unprepare(esai_priv->extalclk); |
|---|
| 514 | | -err_extalck: |
|---|
| 515 | | - if (!IS_ERR(esai_priv->spbaclk)) |
|---|
| 516 | | - clk_disable_unprepare(esai_priv->spbaclk); |
|---|
| 517 | | -err_spbaclk: |
|---|
| 518 | | - clk_disable_unprepare(esai_priv->coreclk); |
|---|
| 519 | | - |
|---|
| 520 | | - return ret; |
|---|
| 521 | 538 | } |
|---|
| 522 | 539 | |
|---|
| 523 | 540 | static int fsl_esai_hw_params(struct snd_pcm_substream *substream, |
|---|
| .. | .. |
|---|
| 539 | 556 | |
|---|
| 540 | 557 | bclk = params_rate(params) * slot_width * esai_priv->slots; |
|---|
| 541 | 558 | |
|---|
| 542 | | - ret = fsl_esai_set_bclk(dai, tx, bclk); |
|---|
| 559 | + ret = fsl_esai_set_bclk(dai, esai_priv->synchronous || tx, bclk); |
|---|
| 543 | 560 | if (ret) |
|---|
| 544 | 561 | return ret; |
|---|
| 562 | + |
|---|
| 563 | + mask = ESAI_xCR_xSWS_MASK; |
|---|
| 564 | + val = ESAI_xCR_xSWS(slot_width, width); |
|---|
| 565 | + |
|---|
| 566 | + regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), mask, val); |
|---|
| 567 | + /* Recording in synchronous mode needs to set TCR also */ |
|---|
| 568 | + if (!tx && esai_priv->synchronous) |
|---|
| 569 | + regmap_update_bits(esai_priv->regmap, REG_ESAI_TCR, mask, val); |
|---|
| 545 | 570 | |
|---|
| 546 | 571 | /* Use Normal mode to support monaural audio */ |
|---|
| 547 | 572 | regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), |
|---|
| .. | .. |
|---|
| 558 | 583 | |
|---|
| 559 | 584 | regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx), mask, val); |
|---|
| 560 | 585 | |
|---|
| 561 | | - mask = ESAI_xCR_xSWS_MASK | (tx ? ESAI_xCR_PADC : 0); |
|---|
| 562 | | - val = ESAI_xCR_xSWS(slot_width, width) | (tx ? ESAI_xCR_PADC : 0); |
|---|
| 563 | | - |
|---|
| 564 | | - regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), mask, val); |
|---|
| 586 | + if (tx) |
|---|
| 587 | + regmap_update_bits(esai_priv->regmap, REG_ESAI_TCR, |
|---|
| 588 | + ESAI_xCR_PADC, ESAI_xCR_PADC); |
|---|
| 565 | 589 | |
|---|
| 566 | 590 | /* Remove ESAI personal reset by configuring ESAI_PCRC and ESAI_PRRC */ |
|---|
| 567 | 591 | regmap_update_bits(esai_priv->regmap, REG_ESAI_PRRC, |
|---|
| .. | .. |
|---|
| 571 | 595 | return 0; |
|---|
| 572 | 596 | } |
|---|
| 573 | 597 | |
|---|
| 574 | | -static void fsl_esai_shutdown(struct snd_pcm_substream *substream, |
|---|
| 575 | | - struct snd_soc_dai *dai) |
|---|
| 598 | +static int fsl_esai_hw_init(struct fsl_esai *esai_priv) |
|---|
| 576 | 599 | { |
|---|
| 577 | | - struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); |
|---|
| 600 | + struct platform_device *pdev = esai_priv->pdev; |
|---|
| 601 | + int ret; |
|---|
| 578 | 602 | |
|---|
| 579 | | - if (!IS_ERR(esai_priv->fsysclk)) |
|---|
| 580 | | - clk_disable_unprepare(esai_priv->fsysclk); |
|---|
| 581 | | - if (!IS_ERR(esai_priv->extalclk)) |
|---|
| 582 | | - clk_disable_unprepare(esai_priv->extalclk); |
|---|
| 583 | | - if (!IS_ERR(esai_priv->spbaclk)) |
|---|
| 584 | | - clk_disable_unprepare(esai_priv->spbaclk); |
|---|
| 585 | | - clk_disable_unprepare(esai_priv->coreclk); |
|---|
| 603 | + /* Reset ESAI unit */ |
|---|
| 604 | + ret = regmap_update_bits(esai_priv->regmap, REG_ESAI_ECR, |
|---|
| 605 | + ESAI_ECR_ESAIEN_MASK | ESAI_ECR_ERST_MASK, |
|---|
| 606 | + ESAI_ECR_ESAIEN | ESAI_ECR_ERST); |
|---|
| 607 | + if (ret) { |
|---|
| 608 | + dev_err(&pdev->dev, "failed to reset ESAI: %d\n", ret); |
|---|
| 609 | + return ret; |
|---|
| 610 | + } |
|---|
| 611 | + |
|---|
| 612 | + /* |
|---|
| 613 | + * We need to enable ESAI so as to access some of its registers. |
|---|
| 614 | + * Otherwise, we would fail to dump regmap from user space. |
|---|
| 615 | + */ |
|---|
| 616 | + ret = regmap_update_bits(esai_priv->regmap, REG_ESAI_ECR, |
|---|
| 617 | + ESAI_ECR_ESAIEN_MASK | ESAI_ECR_ERST_MASK, |
|---|
| 618 | + ESAI_ECR_ESAIEN); |
|---|
| 619 | + if (ret) { |
|---|
| 620 | + dev_err(&pdev->dev, "failed to enable ESAI: %d\n", ret); |
|---|
| 621 | + return ret; |
|---|
| 622 | + } |
|---|
| 623 | + |
|---|
| 624 | + regmap_update_bits(esai_priv->regmap, REG_ESAI_PRRC, |
|---|
| 625 | + ESAI_PRRC_PDC_MASK, 0); |
|---|
| 626 | + regmap_update_bits(esai_priv->regmap, REG_ESAI_PCRC, |
|---|
| 627 | + ESAI_PCRC_PC_MASK, 0); |
|---|
| 628 | + |
|---|
| 629 | + return 0; |
|---|
| 630 | +} |
|---|
| 631 | + |
|---|
| 632 | +static int fsl_esai_register_restore(struct fsl_esai *esai_priv) |
|---|
| 633 | +{ |
|---|
| 634 | + int ret; |
|---|
| 635 | + |
|---|
| 636 | + /* FIFO reset for safety */ |
|---|
| 637 | + regmap_update_bits(esai_priv->regmap, REG_ESAI_TFCR, |
|---|
| 638 | + ESAI_xFCR_xFR, ESAI_xFCR_xFR); |
|---|
| 639 | + regmap_update_bits(esai_priv->regmap, REG_ESAI_RFCR, |
|---|
| 640 | + ESAI_xFCR_xFR, ESAI_xFCR_xFR); |
|---|
| 641 | + |
|---|
| 642 | + regcache_mark_dirty(esai_priv->regmap); |
|---|
| 643 | + ret = regcache_sync(esai_priv->regmap); |
|---|
| 644 | + if (ret) |
|---|
| 645 | + return ret; |
|---|
| 646 | + |
|---|
| 647 | + /* FIFO reset done */ |
|---|
| 648 | + regmap_update_bits(esai_priv->regmap, REG_ESAI_TFCR, ESAI_xFCR_xFR, 0); |
|---|
| 649 | + regmap_update_bits(esai_priv->regmap, REG_ESAI_RFCR, ESAI_xFCR_xFR, 0); |
|---|
| 650 | + |
|---|
| 651 | + return 0; |
|---|
| 652 | +} |
|---|
| 653 | + |
|---|
| 654 | +static void fsl_esai_trigger_start(struct fsl_esai *esai_priv, bool tx) |
|---|
| 655 | +{ |
|---|
| 656 | + u8 i, channels = esai_priv->channels[tx]; |
|---|
| 657 | + u32 pins = DIV_ROUND_UP(channels, esai_priv->slots); |
|---|
| 658 | + u32 mask; |
|---|
| 659 | + |
|---|
| 660 | + regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx), |
|---|
| 661 | + ESAI_xFCR_xFEN_MASK, ESAI_xFCR_xFEN); |
|---|
| 662 | + |
|---|
| 663 | + /* Write initial words reqiured by ESAI as normal procedure */ |
|---|
| 664 | + for (i = 0; tx && i < channels; i++) |
|---|
| 665 | + regmap_write(esai_priv->regmap, REG_ESAI_ETDR, 0x0); |
|---|
| 666 | + |
|---|
| 667 | + /* |
|---|
| 668 | + * When set the TE/RE in the end of enablement flow, there |
|---|
| 669 | + * will be channel swap issue for multi data line case. |
|---|
| 670 | + * In order to workaround this issue, we switch the bit |
|---|
| 671 | + * enablement sequence to below sequence |
|---|
| 672 | + * 1) clear the xSMB & xSMA: which is done in probe and |
|---|
| 673 | + * stop state. |
|---|
| 674 | + * 2) set TE/RE |
|---|
| 675 | + * 3) set xSMB |
|---|
| 676 | + * 4) set xSMA: xSMA is the last one in this flow, which |
|---|
| 677 | + * will trigger esai to start. |
|---|
| 678 | + */ |
|---|
| 679 | + regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), |
|---|
| 680 | + tx ? ESAI_xCR_TE_MASK : ESAI_xCR_RE_MASK, |
|---|
| 681 | + tx ? ESAI_xCR_TE(pins) : ESAI_xCR_RE(pins)); |
|---|
| 682 | + mask = tx ? esai_priv->tx_mask : esai_priv->rx_mask; |
|---|
| 683 | + |
|---|
| 684 | + regmap_update_bits(esai_priv->regmap, REG_ESAI_xSMB(tx), |
|---|
| 685 | + ESAI_xSMB_xS_MASK, ESAI_xSMB_xS(mask)); |
|---|
| 686 | + regmap_update_bits(esai_priv->regmap, REG_ESAI_xSMA(tx), |
|---|
| 687 | + ESAI_xSMA_xS_MASK, ESAI_xSMA_xS(mask)); |
|---|
| 688 | + |
|---|
| 689 | + /* Enable Exception interrupt */ |
|---|
| 690 | + regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), |
|---|
| 691 | + ESAI_xCR_xEIE_MASK, ESAI_xCR_xEIE); |
|---|
| 692 | +} |
|---|
| 693 | + |
|---|
| 694 | +static void fsl_esai_trigger_stop(struct fsl_esai *esai_priv, bool tx) |
|---|
| 695 | +{ |
|---|
| 696 | + regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), |
|---|
| 697 | + ESAI_xCR_xEIE_MASK, 0); |
|---|
| 698 | + |
|---|
| 699 | + regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), |
|---|
| 700 | + tx ? ESAI_xCR_TE_MASK : ESAI_xCR_RE_MASK, 0); |
|---|
| 701 | + regmap_update_bits(esai_priv->regmap, REG_ESAI_xSMA(tx), |
|---|
| 702 | + ESAI_xSMA_xS_MASK, 0); |
|---|
| 703 | + regmap_update_bits(esai_priv->regmap, REG_ESAI_xSMB(tx), |
|---|
| 704 | + ESAI_xSMB_xS_MASK, 0); |
|---|
| 705 | + |
|---|
| 706 | + /* Disable and reset FIFO */ |
|---|
| 707 | + regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx), |
|---|
| 708 | + ESAI_xFCR_xFR | ESAI_xFCR_xFEN, ESAI_xFCR_xFR); |
|---|
| 709 | + regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx), |
|---|
| 710 | + ESAI_xFCR_xFR, 0); |
|---|
| 711 | +} |
|---|
| 712 | + |
|---|
| 713 | +static void fsl_esai_hw_reset(struct work_struct *work) |
|---|
| 714 | +{ |
|---|
| 715 | + struct fsl_esai *esai_priv = container_of(work, struct fsl_esai, work); |
|---|
| 716 | + bool tx = true, rx = false, enabled[2]; |
|---|
| 717 | + unsigned long lock_flags; |
|---|
| 718 | + u32 tfcr, rfcr; |
|---|
| 719 | + |
|---|
| 720 | + spin_lock_irqsave(&esai_priv->lock, lock_flags); |
|---|
| 721 | + /* Save the registers */ |
|---|
| 722 | + regmap_read(esai_priv->regmap, REG_ESAI_TFCR, &tfcr); |
|---|
| 723 | + regmap_read(esai_priv->regmap, REG_ESAI_RFCR, &rfcr); |
|---|
| 724 | + enabled[tx] = tfcr & ESAI_xFCR_xFEN; |
|---|
| 725 | + enabled[rx] = rfcr & ESAI_xFCR_xFEN; |
|---|
| 726 | + |
|---|
| 727 | + /* Stop the tx & rx */ |
|---|
| 728 | + fsl_esai_trigger_stop(esai_priv, tx); |
|---|
| 729 | + fsl_esai_trigger_stop(esai_priv, rx); |
|---|
| 730 | + |
|---|
| 731 | + /* Reset the esai, and ignore return value */ |
|---|
| 732 | + fsl_esai_hw_init(esai_priv); |
|---|
| 733 | + |
|---|
| 734 | + /* Enforce ESAI personal resets for both TX and RX */ |
|---|
| 735 | + regmap_update_bits(esai_priv->regmap, REG_ESAI_TCR, |
|---|
| 736 | + ESAI_xCR_xPR_MASK, ESAI_xCR_xPR); |
|---|
| 737 | + regmap_update_bits(esai_priv->regmap, REG_ESAI_RCR, |
|---|
| 738 | + ESAI_xCR_xPR_MASK, ESAI_xCR_xPR); |
|---|
| 739 | + |
|---|
| 740 | + /* Restore registers by regcache_sync, and ignore return value */ |
|---|
| 741 | + fsl_esai_register_restore(esai_priv); |
|---|
| 742 | + |
|---|
| 743 | + /* Remove ESAI personal resets by configuring PCRC and PRRC also */ |
|---|
| 744 | + regmap_update_bits(esai_priv->regmap, REG_ESAI_TCR, |
|---|
| 745 | + ESAI_xCR_xPR_MASK, 0); |
|---|
| 746 | + regmap_update_bits(esai_priv->regmap, REG_ESAI_RCR, |
|---|
| 747 | + ESAI_xCR_xPR_MASK, 0); |
|---|
| 748 | + regmap_update_bits(esai_priv->regmap, REG_ESAI_PRRC, |
|---|
| 749 | + ESAI_PRRC_PDC_MASK, ESAI_PRRC_PDC(ESAI_GPIO)); |
|---|
| 750 | + regmap_update_bits(esai_priv->regmap, REG_ESAI_PCRC, |
|---|
| 751 | + ESAI_PCRC_PC_MASK, ESAI_PCRC_PC(ESAI_GPIO)); |
|---|
| 752 | + |
|---|
| 753 | + /* Restart tx / rx, if they already enabled */ |
|---|
| 754 | + if (enabled[tx]) |
|---|
| 755 | + fsl_esai_trigger_start(esai_priv, tx); |
|---|
| 756 | + if (enabled[rx]) |
|---|
| 757 | + fsl_esai_trigger_start(esai_priv, rx); |
|---|
| 758 | + |
|---|
| 759 | + spin_unlock_irqrestore(&esai_priv->lock, lock_flags); |
|---|
| 586 | 760 | } |
|---|
| 587 | 761 | |
|---|
| 588 | 762 | static int fsl_esai_trigger(struct snd_pcm_substream *substream, int cmd, |
|---|
| .. | .. |
|---|
| 590 | 764 | { |
|---|
| 591 | 765 | struct fsl_esai *esai_priv = snd_soc_dai_get_drvdata(dai); |
|---|
| 592 | 766 | bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; |
|---|
| 593 | | - u8 i, channels = substream->runtime->channels; |
|---|
| 594 | | - u32 pins = DIV_ROUND_UP(channels, esai_priv->slots); |
|---|
| 595 | | - u32 mask; |
|---|
| 767 | + unsigned long lock_flags; |
|---|
| 768 | + |
|---|
| 769 | + esai_priv->channels[tx] = substream->runtime->channels; |
|---|
| 596 | 770 | |
|---|
| 597 | 771 | switch (cmd) { |
|---|
| 598 | 772 | case SNDRV_PCM_TRIGGER_START: |
|---|
| 599 | 773 | case SNDRV_PCM_TRIGGER_RESUME: |
|---|
| 600 | 774 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: |
|---|
| 601 | | - regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx), |
|---|
| 602 | | - ESAI_xFCR_xFEN_MASK, ESAI_xFCR_xFEN); |
|---|
| 603 | | - |
|---|
| 604 | | - /* Write initial words reqiured by ESAI as normal procedure */ |
|---|
| 605 | | - for (i = 0; tx && i < channels; i++) |
|---|
| 606 | | - regmap_write(esai_priv->regmap, REG_ESAI_ETDR, 0x0); |
|---|
| 607 | | - |
|---|
| 608 | | - /* |
|---|
| 609 | | - * When set the TE/RE in the end of enablement flow, there |
|---|
| 610 | | - * will be channel swap issue for multi data line case. |
|---|
| 611 | | - * In order to workaround this issue, we switch the bit |
|---|
| 612 | | - * enablement sequence to below sequence |
|---|
| 613 | | - * 1) clear the xSMB & xSMA: which is done in probe and |
|---|
| 614 | | - * stop state. |
|---|
| 615 | | - * 2) set TE/RE |
|---|
| 616 | | - * 3) set xSMB |
|---|
| 617 | | - * 4) set xSMA: xSMA is the last one in this flow, which |
|---|
| 618 | | - * will trigger esai to start. |
|---|
| 619 | | - */ |
|---|
| 620 | | - regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), |
|---|
| 621 | | - tx ? ESAI_xCR_TE_MASK : ESAI_xCR_RE_MASK, |
|---|
| 622 | | - tx ? ESAI_xCR_TE(pins) : ESAI_xCR_RE(pins)); |
|---|
| 623 | | - mask = tx ? esai_priv->tx_mask : esai_priv->rx_mask; |
|---|
| 624 | | - |
|---|
| 625 | | - regmap_update_bits(esai_priv->regmap, REG_ESAI_xSMB(tx), |
|---|
| 626 | | - ESAI_xSMB_xS_MASK, ESAI_xSMB_xS(mask)); |
|---|
| 627 | | - regmap_update_bits(esai_priv->regmap, REG_ESAI_xSMA(tx), |
|---|
| 628 | | - ESAI_xSMA_xS_MASK, ESAI_xSMA_xS(mask)); |
|---|
| 629 | | - |
|---|
| 775 | + spin_lock_irqsave(&esai_priv->lock, lock_flags); |
|---|
| 776 | + fsl_esai_trigger_start(esai_priv, tx); |
|---|
| 777 | + spin_unlock_irqrestore(&esai_priv->lock, lock_flags); |
|---|
| 630 | 778 | break; |
|---|
| 631 | 779 | case SNDRV_PCM_TRIGGER_SUSPEND: |
|---|
| 632 | 780 | case SNDRV_PCM_TRIGGER_STOP: |
|---|
| 633 | 781 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: |
|---|
| 634 | | - regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), |
|---|
| 635 | | - tx ? ESAI_xCR_TE_MASK : ESAI_xCR_RE_MASK, 0); |
|---|
| 636 | | - regmap_update_bits(esai_priv->regmap, REG_ESAI_xSMA(tx), |
|---|
| 637 | | - ESAI_xSMA_xS_MASK, 0); |
|---|
| 638 | | - regmap_update_bits(esai_priv->regmap, REG_ESAI_xSMB(tx), |
|---|
| 639 | | - ESAI_xSMB_xS_MASK, 0); |
|---|
| 640 | | - |
|---|
| 641 | | - /* Disable and reset FIFO */ |
|---|
| 642 | | - regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx), |
|---|
| 643 | | - ESAI_xFCR_xFR | ESAI_xFCR_xFEN, ESAI_xFCR_xFR); |
|---|
| 644 | | - regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx), |
|---|
| 645 | | - ESAI_xFCR_xFR, 0); |
|---|
| 782 | + spin_lock_irqsave(&esai_priv->lock, lock_flags); |
|---|
| 783 | + fsl_esai_trigger_stop(esai_priv, tx); |
|---|
| 784 | + spin_unlock_irqrestore(&esai_priv->lock, lock_flags); |
|---|
| 646 | 785 | break; |
|---|
| 647 | 786 | default: |
|---|
| 648 | 787 | return -EINVAL; |
|---|
| .. | .. |
|---|
| 653 | 792 | |
|---|
| 654 | 793 | static const struct snd_soc_dai_ops fsl_esai_dai_ops = { |
|---|
| 655 | 794 | .startup = fsl_esai_startup, |
|---|
| 656 | | - .shutdown = fsl_esai_shutdown, |
|---|
| 657 | 795 | .trigger = fsl_esai_trigger, |
|---|
| 658 | 796 | .hw_params = fsl_esai_hw_params, |
|---|
| 659 | 797 | .set_sysclk = fsl_esai_set_dai_sysclk, |
|---|
| .. | .. |
|---|
| 828 | 966 | return -ENOMEM; |
|---|
| 829 | 967 | |
|---|
| 830 | 968 | esai_priv->pdev = pdev; |
|---|
| 831 | | - strncpy(esai_priv->name, np->name, sizeof(esai_priv->name) - 1); |
|---|
| 969 | + snprintf(esai_priv->name, sizeof(esai_priv->name), "%pOFn", np); |
|---|
| 970 | + |
|---|
| 971 | + esai_priv->soc = of_device_get_match_data(&pdev->dev); |
|---|
| 972 | + if (!esai_priv->soc) { |
|---|
| 973 | + dev_err(&pdev->dev, "failed to get soc data\n"); |
|---|
| 974 | + return -ENODEV; |
|---|
| 975 | + } |
|---|
| 832 | 976 | |
|---|
| 833 | 977 | /* Get the addresses and IRQ */ |
|---|
| 834 | 978 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
|---|
| .. | .. |
|---|
| 867 | 1011 | PTR_ERR(esai_priv->spbaclk)); |
|---|
| 868 | 1012 | |
|---|
| 869 | 1013 | irq = platform_get_irq(pdev, 0); |
|---|
| 870 | | - if (irq < 0) { |
|---|
| 871 | | - dev_err(&pdev->dev, "no irq for node %s\n", pdev->name); |
|---|
| 1014 | + if (irq < 0) |
|---|
| 872 | 1015 | return irq; |
|---|
| 873 | | - } |
|---|
| 874 | 1016 | |
|---|
| 875 | | - ret = devm_request_irq(&pdev->dev, irq, esai_isr, 0, |
|---|
| 1017 | + ret = devm_request_irq(&pdev->dev, irq, esai_isr, IRQF_SHARED, |
|---|
| 876 | 1018 | esai_priv->name, esai_priv); |
|---|
| 877 | 1019 | if (ret) { |
|---|
| 878 | 1020 | dev_err(&pdev->dev, "failed to claim irq %u\n", irq); |
|---|
| .. | .. |
|---|
| 909 | 1051 | |
|---|
| 910 | 1052 | dev_set_drvdata(&pdev->dev, esai_priv); |
|---|
| 911 | 1053 | |
|---|
| 912 | | - /* Reset ESAI unit */ |
|---|
| 913 | | - ret = regmap_write(esai_priv->regmap, REG_ESAI_ECR, ESAI_ECR_ERST); |
|---|
| 914 | | - if (ret) { |
|---|
| 915 | | - dev_err(&pdev->dev, "failed to reset ESAI: %d\n", ret); |
|---|
| 1054 | + spin_lock_init(&esai_priv->lock); |
|---|
| 1055 | + ret = fsl_esai_hw_init(esai_priv); |
|---|
| 1056 | + if (ret) |
|---|
| 916 | 1057 | return ret; |
|---|
| 917 | | - } |
|---|
| 918 | | - |
|---|
| 919 | | - /* |
|---|
| 920 | | - * We need to enable ESAI so as to access some of its registers. |
|---|
| 921 | | - * Otherwise, we would fail to dump regmap from user space. |
|---|
| 922 | | - */ |
|---|
| 923 | | - ret = regmap_write(esai_priv->regmap, REG_ESAI_ECR, ESAI_ECR_ESAIEN); |
|---|
| 924 | | - if (ret) { |
|---|
| 925 | | - dev_err(&pdev->dev, "failed to enable ESAI: %d\n", ret); |
|---|
| 926 | | - return ret; |
|---|
| 927 | | - } |
|---|
| 928 | 1058 | |
|---|
| 929 | 1059 | esai_priv->tx_mask = 0xFFFFFFFF; |
|---|
| 930 | 1060 | esai_priv->rx_mask = 0xFFFFFFFF; |
|---|
| .. | .. |
|---|
| 942 | 1072 | return ret; |
|---|
| 943 | 1073 | } |
|---|
| 944 | 1074 | |
|---|
| 1075 | + INIT_WORK(&esai_priv->work, fsl_esai_hw_reset); |
|---|
| 1076 | + |
|---|
| 1077 | + pm_runtime_enable(&pdev->dev); |
|---|
| 1078 | + |
|---|
| 1079 | + regcache_cache_only(esai_priv->regmap, true); |
|---|
| 1080 | + |
|---|
| 945 | 1081 | ret = imx_pcm_dma_init(pdev, IMX_ESAI_DMABUF_SIZE); |
|---|
| 946 | 1082 | if (ret) |
|---|
| 947 | 1083 | dev_err(&pdev->dev, "failed to init imx pcm dma: %d\n", ret); |
|---|
| .. | .. |
|---|
| 949 | 1085 | return ret; |
|---|
| 950 | 1086 | } |
|---|
| 951 | 1087 | |
|---|
| 1088 | +static int fsl_esai_remove(struct platform_device *pdev) |
|---|
| 1089 | +{ |
|---|
| 1090 | + struct fsl_esai *esai_priv = platform_get_drvdata(pdev); |
|---|
| 1091 | + |
|---|
| 1092 | + pm_runtime_disable(&pdev->dev); |
|---|
| 1093 | + cancel_work_sync(&esai_priv->work); |
|---|
| 1094 | + |
|---|
| 1095 | + return 0; |
|---|
| 1096 | +} |
|---|
| 1097 | + |
|---|
| 952 | 1098 | static const struct of_device_id fsl_esai_dt_ids[] = { |
|---|
| 953 | | - { .compatible = "fsl,imx35-esai", }, |
|---|
| 954 | | - { .compatible = "fsl,vf610-esai", }, |
|---|
| 1099 | + { .compatible = "fsl,imx35-esai", .data = &fsl_esai_imx35 }, |
|---|
| 1100 | + { .compatible = "fsl,vf610-esai", .data = &fsl_esai_vf610 }, |
|---|
| 1101 | + { .compatible = "fsl,imx6ull-esai", .data = &fsl_esai_imx6ull }, |
|---|
| 955 | 1102 | {} |
|---|
| 956 | 1103 | }; |
|---|
| 957 | 1104 | MODULE_DEVICE_TABLE(of, fsl_esai_dt_ids); |
|---|
| 958 | 1105 | |
|---|
| 959 | | -#ifdef CONFIG_PM_SLEEP |
|---|
| 960 | | -static int fsl_esai_suspend(struct device *dev) |
|---|
| 961 | | -{ |
|---|
| 962 | | - struct fsl_esai *esai = dev_get_drvdata(dev); |
|---|
| 963 | | - |
|---|
| 964 | | - regcache_cache_only(esai->regmap, true); |
|---|
| 965 | | - regcache_mark_dirty(esai->regmap); |
|---|
| 966 | | - |
|---|
| 967 | | - return 0; |
|---|
| 968 | | -} |
|---|
| 969 | | - |
|---|
| 970 | | -static int fsl_esai_resume(struct device *dev) |
|---|
| 1106 | +#ifdef CONFIG_PM |
|---|
| 1107 | +static int fsl_esai_runtime_resume(struct device *dev) |
|---|
| 971 | 1108 | { |
|---|
| 972 | 1109 | struct fsl_esai *esai = dev_get_drvdata(dev); |
|---|
| 973 | 1110 | int ret; |
|---|
| 974 | 1111 | |
|---|
| 975 | | - regcache_cache_only(esai->regmap, false); |
|---|
| 976 | | - |
|---|
| 977 | | - /* FIFO reset for safety */ |
|---|
| 978 | | - regmap_update_bits(esai->regmap, REG_ESAI_TFCR, |
|---|
| 979 | | - ESAI_xFCR_xFR, ESAI_xFCR_xFR); |
|---|
| 980 | | - regmap_update_bits(esai->regmap, REG_ESAI_RFCR, |
|---|
| 981 | | - ESAI_xFCR_xFR, ESAI_xFCR_xFR); |
|---|
| 982 | | - |
|---|
| 983 | | - ret = regcache_sync(esai->regmap); |
|---|
| 1112 | + /* |
|---|
| 1113 | + * Some platforms might use the same bit to gate all three or two of |
|---|
| 1114 | + * clocks, so keep all clocks open/close at the same time for safety |
|---|
| 1115 | + */ |
|---|
| 1116 | + ret = clk_prepare_enable(esai->coreclk); |
|---|
| 984 | 1117 | if (ret) |
|---|
| 985 | 1118 | return ret; |
|---|
| 1119 | + if (!IS_ERR(esai->spbaclk)) { |
|---|
| 1120 | + ret = clk_prepare_enable(esai->spbaclk); |
|---|
| 1121 | + if (ret) |
|---|
| 1122 | + goto err_spbaclk; |
|---|
| 1123 | + } |
|---|
| 1124 | + if (!IS_ERR(esai->extalclk)) { |
|---|
| 1125 | + ret = clk_prepare_enable(esai->extalclk); |
|---|
| 1126 | + if (ret) |
|---|
| 1127 | + goto err_extalclk; |
|---|
| 1128 | + } |
|---|
| 1129 | + if (!IS_ERR(esai->fsysclk)) { |
|---|
| 1130 | + ret = clk_prepare_enable(esai->fsysclk); |
|---|
| 1131 | + if (ret) |
|---|
| 1132 | + goto err_fsysclk; |
|---|
| 1133 | + } |
|---|
| 986 | 1134 | |
|---|
| 987 | | - /* FIFO reset done */ |
|---|
| 988 | | - regmap_update_bits(esai->regmap, REG_ESAI_TFCR, ESAI_xFCR_xFR, 0); |
|---|
| 989 | | - regmap_update_bits(esai->regmap, REG_ESAI_RFCR, ESAI_xFCR_xFR, 0); |
|---|
| 1135 | + regcache_cache_only(esai->regmap, false); |
|---|
| 1136 | + |
|---|
| 1137 | + ret = fsl_esai_register_restore(esai); |
|---|
| 1138 | + if (ret) |
|---|
| 1139 | + goto err_regcache_sync; |
|---|
| 1140 | + |
|---|
| 1141 | + return 0; |
|---|
| 1142 | + |
|---|
| 1143 | +err_regcache_sync: |
|---|
| 1144 | + if (!IS_ERR(esai->fsysclk)) |
|---|
| 1145 | + clk_disable_unprepare(esai->fsysclk); |
|---|
| 1146 | +err_fsysclk: |
|---|
| 1147 | + if (!IS_ERR(esai->extalclk)) |
|---|
| 1148 | + clk_disable_unprepare(esai->extalclk); |
|---|
| 1149 | +err_extalclk: |
|---|
| 1150 | + if (!IS_ERR(esai->spbaclk)) |
|---|
| 1151 | + clk_disable_unprepare(esai->spbaclk); |
|---|
| 1152 | +err_spbaclk: |
|---|
| 1153 | + clk_disable_unprepare(esai->coreclk); |
|---|
| 1154 | + |
|---|
| 1155 | + return ret; |
|---|
| 1156 | +} |
|---|
| 1157 | + |
|---|
| 1158 | +static int fsl_esai_runtime_suspend(struct device *dev) |
|---|
| 1159 | +{ |
|---|
| 1160 | + struct fsl_esai *esai = dev_get_drvdata(dev); |
|---|
| 1161 | + |
|---|
| 1162 | + regcache_cache_only(esai->regmap, true); |
|---|
| 1163 | + |
|---|
| 1164 | + if (!IS_ERR(esai->fsysclk)) |
|---|
| 1165 | + clk_disable_unprepare(esai->fsysclk); |
|---|
| 1166 | + if (!IS_ERR(esai->extalclk)) |
|---|
| 1167 | + clk_disable_unprepare(esai->extalclk); |
|---|
| 1168 | + if (!IS_ERR(esai->spbaclk)) |
|---|
| 1169 | + clk_disable_unprepare(esai->spbaclk); |
|---|
| 1170 | + clk_disable_unprepare(esai->coreclk); |
|---|
| 990 | 1171 | |
|---|
| 991 | 1172 | return 0; |
|---|
| 992 | 1173 | } |
|---|
| 993 | | -#endif /* CONFIG_PM_SLEEP */ |
|---|
| 1174 | +#endif /* CONFIG_PM */ |
|---|
| 994 | 1175 | |
|---|
| 995 | 1176 | static const struct dev_pm_ops fsl_esai_pm_ops = { |
|---|
| 996 | | - SET_SYSTEM_SLEEP_PM_OPS(fsl_esai_suspend, fsl_esai_resume) |
|---|
| 1177 | + SET_RUNTIME_PM_OPS(fsl_esai_runtime_suspend, |
|---|
| 1178 | + fsl_esai_runtime_resume, |
|---|
| 1179 | + NULL) |
|---|
| 1180 | + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, |
|---|
| 1181 | + pm_runtime_force_resume) |
|---|
| 997 | 1182 | }; |
|---|
| 998 | 1183 | |
|---|
| 999 | 1184 | static struct platform_driver fsl_esai_driver = { |
|---|
| 1000 | 1185 | .probe = fsl_esai_probe, |
|---|
| 1186 | + .remove = fsl_esai_remove, |
|---|
| 1001 | 1187 | .driver = { |
|---|
| 1002 | 1188 | .name = "fsl-esai-dai", |
|---|
| 1003 | 1189 | .pm = &fsl_esai_pm_ops, |
|---|