Merge series from Christophe Leroy <christophe.leroy@csgroup.eu>: This is a RESEND of v3 sent one month ago, see: https://lore.kernel.org/all/cover.1754993232.git.christophe.leroy@csgroup.eu/ This series reduces significantly the amount of interrupts on fsl_qmc_audio device. Patches 1 and 2 are preparatory patches. Patch 3 is the main change Patch 4 is a cleanup which is enabled by previous patch
Unified · Split
| @@ -461,9 +461,16 @@ int qmc_chan_write_submit(struct qmc_chan *chan, dma_addr_t addr, size_t length, · 460 unmodified lines | |||
| 461 | 461 | ||
| 462 | ctrl = qmc_read16(&bd->cbd_sc); | 462 | ctrl = qmc_read16(&bd->cbd_sc); |
| 463 | if (ctrl & (QMC_BD_TX_R | QMC_BD_TX_UB)) { | 463 | if (ctrl & (QMC_BD_TX_R | QMC_BD_TX_UB)) { |
| 464 | /* We are full ... */ | 464 | if (!(ctrl & (QMC_BD_TX_R | QMC_BD_TX_I)) && bd == chan->txbd_done) { |
| 465 | ret = -EBUSY; | 465 | if (ctrl & QMC_BD_TX_W) |
| 466 | goto end; | 466 | chan->txbd_done = chan->txbds; |
| 467 | else | ||
| 468 | chan->txbd_done++; | ||
| 469 | } else { | ||
| 470 | /* We are full ... */ | ||
| 471 | ret = -EBUSY; | ||
| 472 | goto end; | ||
| 473 | } | ||
| 467 | } | 474 | } |
| 468 | 475 | ||
| 469 | qmc_write16(&bd->cbd_datlen, length); | 476 | qmc_write16(&bd->cbd_datlen, length); |
| @@ -475,6 +482,10 @@ int qmc_chan_write_submit(struct qmc_chan *chan, dma_addr_t addr, size_t length, · 5 unmodified lines | |||
| 475 | 482 | ||
| 476 | /* Activate the descriptor */ | 483 | /* Activate the descriptor */ |
| 477 | ctrl |= (QMC_BD_TX_R | QMC_BD_TX_UB); | 484 | ctrl |= (QMC_BD_TX_R | QMC_BD_TX_UB); |
| 485 | if (complete) | ||
| 486 | ctrl |= QMC_BD_TX_I; | ||
| 487 | else | ||
| 488 | ctrl &= ~QMC_BD_TX_I; | ||
| 478 | wmb(); /* Be sure to flush the descriptor before control update */ | 489 | wmb(); /* Be sure to flush the descriptor before control update */ |
| 479 | qmc_write16(&bd->cbd_sc, ctrl); | 490 | qmc_write16(&bd->cbd_sc, ctrl); |
| 480 | 491 | ||
| @@ -569,9 +580,16 @@ int qmc_chan_read_submit(struct qmc_chan *chan, dma_addr_t addr, size_t length, · 88 unmodified lines | |||
| 569 | 580 | ||
| 570 | ctrl = qmc_read16(&bd->cbd_sc); | 581 | ctrl = qmc_read16(&bd->cbd_sc); |
| 571 | if (ctrl & (QMC_BD_RX_E | QMC_BD_RX_UB)) { | 582 | if (ctrl & (QMC_BD_RX_E | QMC_BD_RX_UB)) { |
| 572 | /* We are full ... */ | 583 | if (!(ctrl & (QMC_BD_RX_E | QMC_BD_RX_I)) && bd == chan->rxbd_done) { |
| 573 | ret = -EBUSY; | 584 | if (ctrl & QMC_BD_RX_W) |
| 574 | goto end; | 585 | chan->rxbd_done = chan->rxbds; |
| 586 | else | ||
| 587 | chan->rxbd_done++; | ||
| 588 | } else { | ||
| 589 | /* We are full ... */ | ||
| 590 | ret = -EBUSY; | ||
| 591 | goto end; | ||
| 592 | } | ||
| 575 | } | 593 | } |
| 576 | 594 | ||
| 577 | qmc_write16(&bd->cbd_datlen, 0); /* data length is updated by the QMC */ | 595 | qmc_write16(&bd->cbd_datlen, 0); /* data length is updated by the QMC */ |
| @@ -587,6 +605,10 @@ int qmc_chan_read_submit(struct qmc_chan *chan, dma_addr_t addr, size_t length, · 9 unmodified lines | |||
| 587 | 605 | ||
| 588 | /* Activate the descriptor */ | 606 | /* Activate the descriptor */ |
| 589 | ctrl |= (QMC_BD_RX_E | QMC_BD_RX_UB); | 607 | ctrl |= (QMC_BD_RX_E | QMC_BD_RX_UB); |
| 608 | if (complete) | ||
| 609 | ctrl |= QMC_BD_RX_I; | ||
| 610 | else | ||
| 611 | ctrl &= ~QMC_BD_RX_I; | ||
| 590 | wmb(); /* Be sure to flush data before descriptor activation */ | 612 | wmb(); /* Be sure to flush data before descriptor activation */ |
| 591 | qmc_write16(&bd->cbd_sc, ctrl); | 613 | qmc_write16(&bd->cbd_sc, ctrl); |
| 592 | 614 | ||
| @@ -1482,19 +1504,19 @@ static int qmc_setup_chan(struct qmc *qmc, struct qmc_chan *chan) · 889 unmodified lines | |||
| 1482 | 1504 | ||
| 1483 | /* Init Rx BDs and set Wrap bit on last descriptor */ | 1505 | /* Init Rx BDs and set Wrap bit on last descriptor */ |
| 1484 | BUILD_BUG_ON(QMC_NB_RXBDS == 0); | 1506 | BUILD_BUG_ON(QMC_NB_RXBDS == 0); |
| 1485 | val = QMC_BD_RX_I; | ||
| 1486 | for (i = 0; i < QMC_NB_RXBDS; i++) { | 1507 | for (i = 0; i < QMC_NB_RXBDS; i++) { |
| 1487 | bd = chan->rxbds + i; | 1508 | bd = chan->rxbds + i; |
| 1488 | qmc_write16(&bd->cbd_sc, val); | 1509 | qmc_write16(&bd->cbd_sc, 0); |
| 1489 | } | 1510 | } |
| 1490 | bd = chan->rxbds + QMC_NB_RXBDS - 1; | 1511 | bd = chan->rxbds + QMC_NB_RXBDS - 1; |
| 1491 | qmc_write16(&bd->cbd_sc, val | QMC_BD_RX_W); | 1512 | qmc_write16(&bd->cbd_sc, QMC_BD_RX_W); |
| 1492 | 1513 | ||
| 1493 | /* Init Tx BDs and set Wrap bit on last descriptor */ | 1514 | /* Init Tx BDs and set Wrap bit on last descriptor */ |
| 1494 | BUILD_BUG_ON(QMC_NB_TXBDS == 0); | 1515 | BUILD_BUG_ON(QMC_NB_TXBDS == 0); |
| 1495 | val = QMC_BD_TX_I; | ||
| 1496 | if (chan->mode == QMC_HDLC) | 1516 | if (chan->mode == QMC_HDLC) |
| 1497 | val |= QMC_BD_TX_L | QMC_BD_TX_TC; | 1517 | val = QMC_BD_TX_L | QMC_BD_TX_TC; |
| 1518 | else | ||
| 1519 | val = 0; | ||
| 1498 | for (i = 0; i < QMC_NB_TXBDS; i++) { | 1520 | for (i = 0; i < QMC_NB_TXBDS; i++) { |
| 1499 | bd = chan->txbds + i; | 1521 | bd = chan->txbds + i; |
| 1500 | qmc_write16(&bd->cbd_sc, val); | 1522 | qmc_write16(&bd->cbd_sc, val); |
| @@ -17,12 +17,6 @@ · 16 unmodified lines | |||
| 17 | #include <sound/pcm_params.h> | 17 | #include <sound/pcm_params.h> |
| 18 | #include <sound/soc.h> | 18 | #include <sound/soc.h> |
| 19 | 19 | ||
| 20 | struct qmc_dai_chan { | ||
| 21 | struct qmc_dai_prtd *prtd_tx; | ||
| 22 | struct qmc_dai_prtd *prtd_rx; | ||
| 23 | struct qmc_chan *qmc_chan; | ||
| 24 | }; | ||
| 25 | |||
| 26 | struct qmc_dai { | 20 | struct qmc_dai { |
| 27 | char *name; | 21 | char *name; |
| 28 | int id; | 22 | int id; |
| @@ -33,7 +27,7 @@ struct qmc_dai { · 4 unmodified lines | |||
| 33 | unsigned int nb_chans_avail; | 27 | unsigned int nb_chans_avail; |
| 34 | unsigned int nb_chans_used_tx; | 28 | unsigned int nb_chans_used_tx; |
| 35 | unsigned int nb_chans_used_rx; | 29 | unsigned int nb_chans_used_rx; |
| 36 | struct qmc_dai_chan *chans; | 30 | struct qmc_chan **qmc_chans; |
| 37 | }; | 31 | }; |
| 38 | 32 | ||
| 39 | struct qmc_audio { | 33 | struct qmc_audio { |
| @@ -57,7 +51,6 @@ struct qmc_dai_prtd { · 17 unmodified lines | |||
| 57 | size_t ch_dma_offset; | 51 | size_t ch_dma_offset; |
| 58 | 52 | ||
| 59 | unsigned int channels; | 53 | unsigned int channels; |
| 60 | DECLARE_BITMAP(chans_pending, 64); | ||
| 61 | struct snd_pcm_substream *substream; | 54 | struct snd_pcm_substream *substream; |
| 62 | }; | 55 | }; |
| 63 | 56 | ||
| @@ -126,17 +119,14 @@ static int qmc_audio_pcm_write_submit(struct qmc_dai_prtd *prtd) · 62 unmodified lines | |||
| 126 | int ret; | 119 | int ret; |
| 127 | 120 | ||
| 128 | for (i = 0; i < prtd->channels; i++) { | 121 | for (i = 0; i < prtd->channels; i++) { |
| 129 | bitmap_set(prtd->chans_pending, i, 1); | 122 | ret = qmc_chan_write_submit(prtd->qmc_dai->qmc_chans[i], |
| 130 | |||
| 131 | ret = qmc_chan_write_submit(prtd->qmc_dai->chans[i].qmc_chan, | ||
| 132 | prtd->ch_dma_addr_current + i * prtd->ch_dma_offset, | 123 | prtd->ch_dma_addr_current + i * prtd->ch_dma_offset, |
| 133 | prtd->ch_dma_size, | 124 | prtd->ch_dma_size, |
| 134 | qmc_audio_pcm_write_complete, | 125 | i == prtd->channels - 1 ? qmc_audio_pcm_write_complete : |
| 135 | &prtd->qmc_dai->chans[i]); | 126 | NULL, prtd); |
| 136 | if (ret) { | 127 | if (ret) { |
| 137 | dev_err(prtd->qmc_dai->dev, "write_submit %u failed %d\n", | 128 | dev_err(prtd->qmc_dai->dev, "write_submit %u failed %d\n", |
| 138 | i, ret); | 129 | i, ret); |
| 139 | bitmap_clear(prtd->chans_pending, i, 1); | ||
| 140 | return ret; | 130 | return ret; |
| 141 | } | 131 | } |
| 142 | } | 132 | } |
| @@ -146,20 +136,7 @@ static int qmc_audio_pcm_write_submit(struct qmc_dai_prtd *prtd) · 3 unmodified lines | |||
| 146 | 136 | ||
| 147 | static void qmc_audio_pcm_write_complete(void *context) | 137 | static void qmc_audio_pcm_write_complete(void *context) |
| 148 | { | 138 | { |
| 149 | struct qmc_dai_chan *chan = context; | 139 | struct qmc_dai_prtd *prtd = context; |
| 150 | struct qmc_dai_prtd *prtd; | ||
| 151 | |||
| 152 | prtd = chan->prtd_tx; | ||
| 153 | |||
| 154 | /* Mark the current channel as completed */ | ||
| 155 | bitmap_clear(prtd->chans_pending, chan - prtd->qmc_dai->chans, 1); | ||
| 156 | |||
| 157 | /* | ||
| 158 | * All QMC channels involved must have completed their transfer before | ||
| 159 | * submitting a new one. | ||
| 160 | */ | ||
| 161 | if (!bitmap_empty(prtd->chans_pending, 64)) | ||
| 162 | return; | ||
| 163 | 140 | ||
| 164 | prtd->buffer_ended += prtd->period_size; | 141 | prtd->buffer_ended += prtd->period_size; |
| 165 | if (prtd->buffer_ended >= prtd->buffer_size) | 142 | if (prtd->buffer_ended >= prtd->buffer_size) |
| @@ -182,17 +159,14 @@ static int qmc_audio_pcm_read_submit(struct qmc_dai_prtd *prtd) · 16 unmodified lines | |||
| 182 | int ret; | 159 | int ret; |
| 183 | 160 | ||
| 184 | for (i = 0; i < prtd->channels; i++) { | 161 | for (i = 0; i < prtd->channels; i++) { |
| 185 | bitmap_set(prtd->chans_pending, i, 1); | 162 | ret = qmc_chan_read_submit(prtd->qmc_dai->qmc_chans[i], |
| 186 | |||
| 187 | ret = qmc_chan_read_submit(prtd->qmc_dai->chans[i].qmc_chan, | ||
| 188 | prtd->ch_dma_addr_current + i * prtd->ch_dma_offset, | 163 | prtd->ch_dma_addr_current + i * prtd->ch_dma_offset, |
| 189 | prtd->ch_dma_size, | 164 | prtd->ch_dma_size, |
| 190 | qmc_audio_pcm_read_complete, | 165 | i == prtd->channels - 1 ? qmc_audio_pcm_read_complete : |
| 191 | &prtd->qmc_dai->chans[i]); | 166 | NULL, prtd); |
| 192 | if (ret) { | 167 | if (ret) { |
| 193 | dev_err(prtd->qmc_dai->dev, "read_submit %u failed %d\n", | 168 | dev_err(prtd->qmc_dai->dev, "read_submit %u failed %d\n", |
| 194 | i, ret); | 169 | i, ret); |
| 195 | bitmap_clear(prtd->chans_pending, i, 1); | ||
| 196 | return ret; | 170 | return ret; |
| 197 | } | 171 | } |
| 198 | } | 172 | } |
| @@ -202,26 +176,13 @@ static int qmc_audio_pcm_read_submit(struct qmc_dai_prtd *prtd) · 3 unmodified lines | |||
| 202 | 176 | ||
| 203 | static void qmc_audio_pcm_read_complete(void *context, size_t length, unsigned int flags) | 177 | static void qmc_audio_pcm_read_complete(void *context, size_t length, unsigned int flags) |
| 204 | { | 178 | { |
| 205 | struct qmc_dai_chan *chan = context; | 179 | struct qmc_dai_prtd *prtd = context; |
| 206 | struct qmc_dai_prtd *prtd; | ||
| 207 | |||
| 208 | prtd = chan->prtd_rx; | ||
| 209 | |||
| 210 | /* Mark the current channel as completed */ | ||
| 211 | bitmap_clear(prtd->chans_pending, chan - prtd->qmc_dai->chans, 1); | ||
| 212 | 180 | ||
| 213 | if (length != prtd->ch_dma_size) { | 181 | if (length != prtd->ch_dma_size) { |
| 214 | dev_err(prtd->qmc_dai->dev, "read complete length = %zu, exp %zu\n", | 182 | dev_err(prtd->qmc_dai->dev, "read complete length = %zu, exp %zu\n", |
| 215 | length, prtd->ch_dma_size); | 183 | length, prtd->ch_dma_size); |
| 216 | } | 184 | } |
| 217 | 185 | ||
| 218 | /* | ||
| 219 | * All QMC channels involved must have completed their transfer before | ||
| 220 | * submitting a new one. | ||
| 221 | */ | ||
| 222 | if (!bitmap_empty(prtd->chans_pending, 64)) | ||
| 223 | return; | ||
| 224 | |||
| 225 | prtd->buffer_ended += prtd->period_size; | 186 | prtd->buffer_ended += prtd->period_size; |
| 226 | if (prtd->buffer_ended >= prtd->buffer_size) | 187 | if (prtd->buffer_ended >= prtd->buffer_size) |
| 227 | prtd->buffer_ended = 0; | 188 | prtd->buffer_ended = 0; |
| @@ -239,7 +200,6 @@ static int qmc_audio_pcm_trigger(struct snd_soc_component *component, · 11 unmodified lines | |||
| 239 | struct snd_pcm_substream *substream, int cmd) | 200 | struct snd_pcm_substream *substream, int cmd) |
| 240 | { | 201 | { |
| 241 | struct qmc_dai_prtd *prtd = substream->runtime->private_data; | 202 | struct qmc_dai_prtd *prtd = substream->runtime->private_data; |
| 242 | unsigned int i; | ||
| 243 | int ret; | 203 | int ret; |
| 244 | 204 | ||
| 245 | if (!prtd->qmc_dai) { | 205 | if (!prtd->qmc_dai) { |
| @@ -249,14 +209,10 @@ static int qmc_audio_pcm_trigger(struct snd_soc_component *component, · 3 unmodified lines | |||
| 249 | 209 | ||
| 250 | switch (cmd) { | 210 | switch (cmd) { |
| 251 | case SNDRV_PCM_TRIGGER_START: | 211 | case SNDRV_PCM_TRIGGER_START: |
| 252 | bitmap_zero(prtd->chans_pending, 64); | ||
| 253 | prtd->buffer_ended = 0; | 212 | prtd->buffer_ended = 0; |
| 254 | prtd->ch_dma_addr_current = prtd->ch_dma_addr_start; | 213 | prtd->ch_dma_addr_current = prtd->ch_dma_addr_start; |
| 255 | 214 | ||
| 256 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | 215 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { |
| 257 | for (i = 0; i < prtd->channels; i++) | ||
| 258 | prtd->qmc_dai->chans[i].prtd_tx = prtd; | ||
| 259 | |||
| 260 | /* Submit first chunk ... */ | 216 | /* Submit first chunk ... */ |
| 261 | ret = qmc_audio_pcm_write_submit(prtd); | 217 | ret = qmc_audio_pcm_write_submit(prtd); |
| 262 | if (ret) | 218 | if (ret) |
| @@ -272,9 +228,6 @@ static int qmc_audio_pcm_trigger(struct snd_soc_component *component, · 9 unmodified lines | |||
| 272 | if (ret) | 228 | if (ret) |
| 273 | return ret; | 229 | return ret; |
| 274 | } else { | 230 | } else { |
| 275 | for (i = 0; i < prtd->channels; i++) | ||
| 276 | prtd->qmc_dai->chans[i].prtd_rx = prtd; | ||
| 277 | |||
| 278 | /* Submit first chunk ... */ | 231 | /* Submit first chunk ... */ |
| 279 | ret = qmc_audio_pcm_read_submit(prtd); | 232 | ret = qmc_audio_pcm_read_submit(prtd); |
| 280 | if (ret) | 233 | if (ret) |
| @@ -644,9 +597,9 @@ static int qmc_dai_hw_params(struct snd_pcm_substream *substream, · 363 unmodified lines | |||
| 644 | chan_param.mode = QMC_TRANSPARENT; | 597 | chan_param.mode = QMC_TRANSPARENT; |
| 645 | chan_param.transp.max_rx_buf_size = params_period_bytes(params) / nb_chans_used; | 598 | chan_param.transp.max_rx_buf_size = params_period_bytes(params) / nb_chans_used; |
| 646 | for (i = 0; i < nb_chans_used; i++) { | 599 | for (i = 0; i < nb_chans_used; i++) { |
| 647 | ret = qmc_chan_set_param(qmc_dai->chans[i].qmc_chan, &chan_param); | 600 | ret = qmc_chan_set_param(qmc_dai->qmc_chans[i], &chan_param); |
| 648 | if (ret) { | 601 | if (ret) { |
| 649 | dev_err(dai->dev, "chans[%u], set param failed %d\n", | 602 | dev_err(dai->dev, "qmc_chans[%u], set param failed %d\n", |
| 650 | i, ret); | 603 | i, ret); |
| 651 | return ret; | 604 | return ret; |
| 652 | } | 605 | } |
| @@ -688,7 +641,7 @@ static int qmc_dai_trigger(struct snd_pcm_substream *substream, int cmd, · 35 unmodified lines | |||
| 688 | case SNDRV_PCM_TRIGGER_RESUME: | 641 | case SNDRV_PCM_TRIGGER_RESUME: |
| 689 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | 642 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: |
| 690 | for (i = 0; i < nb_chans_used; i++) { | 643 | for (i = 0; i < nb_chans_used; i++) { |
| 691 | ret = qmc_chan_start(qmc_dai->chans[i].qmc_chan, direction); | 644 | ret = qmc_chan_start(qmc_dai->qmc_chans[i], direction); |
| 692 | if (ret) | 645 | if (ret) |
| 693 | goto err_stop; | 646 | goto err_stop; |
| 694 | } | 647 | } |
| @@ -697,13 +650,13 @@ static int qmc_dai_trigger(struct snd_pcm_substream *substream, int cmd, · 2 unmodified lines | |||
| 697 | case SNDRV_PCM_TRIGGER_STOP: | 650 | case SNDRV_PCM_TRIGGER_STOP: |
| 698 | /* Stop and reset all QMC channels and return the first error encountered */ | 651 | /* Stop and reset all QMC channels and return the first error encountered */ |
| 699 | for (i = 0; i < nb_chans_used; i++) { | 652 | for (i = 0; i < nb_chans_used; i++) { |
| 700 | ret_tmp = qmc_chan_stop(qmc_dai->chans[i].qmc_chan, direction); | 653 | ret_tmp = qmc_chan_stop(qmc_dai->qmc_chans[i], direction); |
| 701 | if (!ret) | 654 | if (!ret) |
| 702 | ret = ret_tmp; | 655 | ret = ret_tmp; |
| 703 | if (ret_tmp) | 656 | if (ret_tmp) |
| 704 | continue; | 657 | continue; |
| 705 | 658 | ||
| 706 | ret_tmp = qmc_chan_reset(qmc_dai->chans[i].qmc_chan, direction); | 659 | ret_tmp = qmc_chan_reset(qmc_dai->qmc_chans[i], direction); |
| 707 | if (!ret) | 660 | if (!ret) |
| 708 | ret = ret_tmp; | 661 | ret = ret_tmp; |
| 709 | } | 662 | } |
| @@ -715,7 +668,7 @@ static int qmc_dai_trigger(struct snd_pcm_substream *substream, int cmd, · 5 unmodified lines | |||
| 715 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | 668 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: |
| 716 | /* Stop all QMC channels and return the first error encountered */ | 669 | /* Stop all QMC channels and return the first error encountered */ |
| 717 | for (i = 0; i < nb_chans_used; i++) { | 670 | for (i = 0; i < nb_chans_used; i++) { |
| 718 | ret_tmp = qmc_chan_stop(qmc_dai->chans[i].qmc_chan, direction); | 671 | ret_tmp = qmc_chan_stop(qmc_dai->qmc_chans[i], direction); |
| 719 | if (!ret) | 672 | if (!ret) |
| 720 | ret = ret_tmp; | 673 | ret = ret_tmp; |
| 721 | } | 674 | } |
| @@ -731,8 +684,8 @@ static int qmc_dai_trigger(struct snd_pcm_substream *substream, int cmd, · 9 unmodified lines | |||
| 731 | 684 | ||
| 732 | err_stop: | 685 | err_stop: |
| 733 | while (i--) { | 686 | while (i--) { |
| 734 | qmc_chan_stop(qmc_dai->chans[i].qmc_chan, direction); | 687 | qmc_chan_stop(qmc_dai->qmc_chans[i], direction); |
| 735 | qmc_chan_reset(qmc_dai->chans[i].qmc_chan, direction); | 688 | qmc_chan_reset(qmc_dai->qmc_chans[i], direction); |
| 736 | } | 689 | } |
| 737 | return ret; | 690 | return ret; |
| 738 | } | 691 | } |
| @@ -791,12 +744,17 @@ static int qmc_audio_dai_parse(struct qmc_audio *qmc_audio, struct device_node * · 52 unmodified lines | |||
| 791 | struct qmc_dai *qmc_dai, | 744 | struct qmc_dai *qmc_dai, |
| 792 | struct snd_soc_dai_driver *qmc_soc_dai_driver) | 745 | struct snd_soc_dai_driver *qmc_soc_dai_driver) |
| 793 | { | 746 | { |
| 747 | struct qmc_chan_ts_info ts_info; | ||
| 794 | struct qmc_chan_info info; | 748 | struct qmc_chan_info info; |
| 795 | unsigned long rx_fs_rate; | 749 | unsigned long rx_fs_rate; |
| 796 | unsigned long tx_fs_rate; | 750 | unsigned long tx_fs_rate; |
| 751 | int prev_last_rx_ts = 0; | ||
| 752 | int prev_last_tx_ts = 0; | ||
| 797 | unsigned int nb_tx_ts; | 753 | unsigned int nb_tx_ts; |
| 798 | unsigned int nb_rx_ts; | 754 | unsigned int nb_rx_ts; |
| 799 | unsigned int i; | 755 | unsigned int i; |
| 756 | int last_rx_ts; | ||
| 757 | int last_tx_ts; | ||
| 800 | int count; | 758 | int count; |
| 801 | u32 val; | 759 | u32 val; |
| 802 | int ret; | 760 | int ret; |
| @@ -823,19 +781,20 @@ static int qmc_audio_dai_parse(struct qmc_audio *qmc_audio, struct device_node * · 20 unmodified lines | |||
| 823 | return dev_err_probe(qmc_audio->dev, -EINVAL, | 781 | return dev_err_probe(qmc_audio->dev, -EINVAL, |
| 824 | "dai %d no QMC channel defined\n", qmc_dai->id); | 782 | "dai %d no QMC channel defined\n", qmc_dai->id); |
| 825 | 783 | ||
| 826 | qmc_dai->chans = devm_kcalloc(qmc_audio->dev, count, sizeof(*qmc_dai->chans), GFP_KERNEL); | 784 | qmc_dai->qmc_chans = devm_kcalloc(qmc_audio->dev, count, sizeof(*qmc_dai->qmc_chans), |
| 827 | if (!qmc_dai->chans) | 785 | GFP_KERNEL); |
| 786 | if (!qmc_dai->qmc_chans) | ||
| 828 | return -ENOMEM; | 787 | return -ENOMEM; |
| 829 | 788 | ||
| 830 | for (i = 0; i < count; i++) { | 789 | for (i = 0; i < count; i++) { |
| 831 | qmc_dai->chans[i].qmc_chan = devm_qmc_chan_get_byphandles_index(qmc_audio->dev, np, | 790 | qmc_dai->qmc_chans[i] = devm_qmc_chan_get_byphandles_index(qmc_audio->dev, np, |
| 832 | "fsl,qmc-chan", i); | 791 | "fsl,qmc-chan", i); |
| 833 | if (IS_ERR(qmc_dai->chans[i].qmc_chan)) { | 792 | if (IS_ERR(qmc_dai->qmc_chans[i])) { |
| 834 | return dev_err_probe(qmc_audio->dev, PTR_ERR(qmc_dai->chans[i].qmc_chan), | 793 | return dev_err_probe(qmc_audio->dev, PTR_ERR(qmc_dai->qmc_chans[i]), |
| 835 | "dai %d get QMC channel %d failed\n", qmc_dai->id, i); | 794 | "dai %d get QMC channel %d failed\n", qmc_dai->id, i); |
| 836 | } | 795 | } |
| 837 | 796 | ||
| 838 | ret = qmc_chan_get_info(qmc_dai->chans[i].qmc_chan, &info); | 797 | ret = qmc_chan_get_info(qmc_dai->qmc_chans[i], &info); |
| 839 | if (ret) { | 798 | if (ret) { |
| 840 | dev_err(qmc_audio->dev, "dai %d get QMC %d channel info failed %d\n", | 799 | dev_err(qmc_audio->dev, "dai %d get QMC %d channel info failed %d\n", |
| 841 | qmc_dai->id, i, ret); | 800 | qmc_dai->id, i, ret); |
| @@ -879,6 +838,30 @@ static int qmc_audio_dai_parse(struct qmc_audio *qmc_audio, struct device_node * · 37 unmodified lines | |||
| 879 | return -EINVAL; | 838 | return -EINVAL; |
| 880 | } | 839 | } |
| 881 | } | 840 | } |
| 841 | |||
| 842 | ret = qmc_chan_get_ts_info(qmc_dai->qmc_chans[i], &ts_info); | ||
| 843 | if (ret) { | ||
| 844 | dev_err(qmc_audio->dev, "dai %d get QMC %d channel TS info failed %d\n", | ||
| 845 | qmc_dai->id, i, ret); | ||
| 846 | return ret; | ||
| 847 | } | ||
| 848 | |||
| 849 | last_rx_ts = fls64(ts_info.rx_ts_mask); | ||
| 850 | last_tx_ts = fls64(ts_info.tx_ts_mask); | ||
| 851 | |||
| 852 | if (prev_last_rx_ts > last_rx_ts) { | ||
| 853 | dev_err(qmc_audio->dev, "dai %d QMC chan %d unordered channels (RX timeslot %d before %d)\n", | ||
| 854 | qmc_dai->id, i, prev_last_rx_ts, last_rx_ts); | ||
| 855 | return -EINVAL; | ||
| 856 | } | ||
| 857 | if (prev_last_tx_ts > last_tx_ts) { | ||
| 858 | dev_err(qmc_audio->dev, "dai %d QMC chan %d unordered channels (TX timeslot %d before %d)\n", | ||
| 859 | qmc_dai->id, i, prev_last_tx_ts, last_tx_ts); | ||
| 860 | return -EINVAL; | ||
| 861 | } | ||
| 862 | |||
| 863 | prev_last_rx_ts = last_rx_ts; | ||
| 864 | prev_last_tx_ts = last_tx_ts; | ||
| 882 | } | 865 | } |
| 883 | 866 | ||
| 884 | qmc_dai->nb_chans_avail = count; | 867 | qmc_dai->nb_chans_avail = count; |