.. | .. |
---|
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, |
---|