Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

ASoC: fsl: fsl_qmc_audio: Reduce amount of

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

+87 -82
+33 -11
drivers/soc/fsl/qe/qmc.c
··· 461 461 462 462 ctrl = qmc_read16(&bd->cbd_sc); 463 463 if (ctrl & (QMC_BD_TX_R | QMC_BD_TX_UB)) { 464 - /* We are full ... */ 465 - ret = -EBUSY; 466 - goto end; 464 + if (!(ctrl & (QMC_BD_TX_R | QMC_BD_TX_I)) && bd == chan->txbd_done) { 465 + if (ctrl & QMC_BD_TX_W) 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 476 qmc_write16(&bd->cbd_datlen, length); ··· 482 475 483 476 /* Activate the descriptor */ 484 477 ctrl |= (QMC_BD_TX_R | QMC_BD_TX_UB); 478 + if (complete) 479 + ctrl |= QMC_BD_TX_I; 480 + else 481 + ctrl &= ~QMC_BD_TX_I; 485 482 wmb(); /* Be sure to flush the descriptor before control update */ 486 483 qmc_write16(&bd->cbd_sc, ctrl); 487 484 ··· 580 569 581 570 ctrl = qmc_read16(&bd->cbd_sc); 582 571 if (ctrl & (QMC_BD_RX_E | QMC_BD_RX_UB)) { 583 - /* We are full ... */ 584 - ret = -EBUSY; 585 - goto end; 572 + if (!(ctrl & (QMC_BD_RX_E | QMC_BD_RX_I)) && bd == chan->rxbd_done) { 573 + if (ctrl & QMC_BD_RX_W) 574 + chan->rxbd_done = chan->rxbds; 575 + else 576 + chan->rxbd_done++; 577 + } else { 578 + /* We are full ... */ 579 + ret = -EBUSY; 580 + goto end; 581 + } 586 582 } 587 583 588 584 qmc_write16(&bd->cbd_datlen, 0); /* data length is updated by the QMC */ ··· 605 587 606 588 /* Activate the descriptor */ 607 589 ctrl |= (QMC_BD_RX_E | QMC_BD_RX_UB); 590 + if (complete) 591 + ctrl |= QMC_BD_RX_I; 592 + else 593 + ctrl &= ~QMC_BD_RX_I; 608 594 wmb(); /* Be sure to flush data before descriptor activation */ 609 595 qmc_write16(&bd->cbd_sc, ctrl); 610 596 ··· 1504 1482 1505 1483 /* Init Rx BDs and set Wrap bit on last descriptor */ 1506 1484 BUILD_BUG_ON(QMC_NB_RXBDS == 0); 1507 - val = QMC_BD_RX_I; 1508 1485 for (i = 0; i < QMC_NB_RXBDS; i++) { 1509 1486 bd = chan->rxbds + i; 1510 - qmc_write16(&bd->cbd_sc, val); 1487 + qmc_write16(&bd->cbd_sc, 0); 1511 1488 } 1512 1489 bd = chan->rxbds + QMC_NB_RXBDS - 1; 1513 - qmc_write16(&bd->cbd_sc, val | QMC_BD_RX_W); 1490 + qmc_write16(&bd->cbd_sc, QMC_BD_RX_W); 1514 1491 1515 1492 /* Init Tx BDs and set Wrap bit on last descriptor */ 1516 1493 BUILD_BUG_ON(QMC_NB_TXBDS == 0); 1517 - val = QMC_BD_TX_I; 1518 1494 if (chan->mode == QMC_HDLC) 1519 - val |= QMC_BD_TX_L | QMC_BD_TX_TC; 1495 + val = QMC_BD_TX_L | QMC_BD_TX_TC; 1496 + else 1497 + val = 0; 1520 1498 for (i = 0; i < QMC_NB_TXBDS; i++) { 1521 1499 bd = chan->txbds + i; 1522 1500 qmc_write16(&bd->cbd_sc, val);
+54 -71
sound/soc/fsl/fsl_qmc_audio.c
··· 17 17 #include <sound/pcm_params.h> 18 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 20 struct qmc_dai { 27 21 char *name; 28 22 int id; ··· 27 33 unsigned int nb_chans_avail; 28 34 unsigned int nb_chans_used_tx; 29 35 unsigned int nb_chans_used_rx; 30 - struct qmc_dai_chan *chans; 36 + struct qmc_chan **qmc_chans; 31 37 }; 32 38 33 39 struct qmc_audio { ··· 51 57 size_t ch_dma_offset; 52 58 53 59 unsigned int channels; 54 - DECLARE_BITMAP(chans_pending, 64); 55 60 struct snd_pcm_substream *substream; 56 61 }; 57 62 ··· 119 126 int ret; 120 127 121 128 for (i = 0; i < prtd->channels; i++) { 122 - bitmap_set(prtd->chans_pending, i, 1); 123 - 124 - ret = qmc_chan_write_submit(prtd->qmc_dai->chans[i].qmc_chan, 129 + ret = qmc_chan_write_submit(prtd->qmc_dai->qmc_chans[i], 125 130 prtd->ch_dma_addr_current + i * prtd->ch_dma_offset, 126 131 prtd->ch_dma_size, 127 - qmc_audio_pcm_write_complete, 128 - &prtd->qmc_dai->chans[i]); 132 + i == prtd->channels - 1 ? qmc_audio_pcm_write_complete : 133 + NULL, prtd); 129 134 if (ret) { 130 135 dev_err(prtd->qmc_dai->dev, "write_submit %u failed %d\n", 131 136 i, ret); 132 - bitmap_clear(prtd->chans_pending, i, 1); 133 137 return ret; 134 138 } 135 139 } ··· 136 146 137 147 static void qmc_audio_pcm_write_complete(void *context) 138 148 { 139 - struct qmc_dai_chan *chan = context; 140 - struct qmc_dai_prtd *prtd; 141 - 142 - prtd = chan->prtd_tx; 143 - 144 - /* Mark the current channel as completed */ 145 - bitmap_clear(prtd->chans_pending, chan - prtd->qmc_dai->chans, 1); 146 - 147 - /* 148 - * All QMC channels involved must have completed their transfer before 149 - * submitting a new one. 150 - */ 151 - if (!bitmap_empty(prtd->chans_pending, 64)) 152 - return; 149 + struct qmc_dai_prtd *prtd = context; 153 150 154 151 prtd->buffer_ended += prtd->period_size; 155 152 if (prtd->buffer_ended >= prtd->buffer_size) ··· 159 182 int ret; 160 183 161 184 for (i = 0; i < prtd->channels; i++) { 162 - bitmap_set(prtd->chans_pending, i, 1); 163 - 164 - ret = qmc_chan_read_submit(prtd->qmc_dai->chans[i].qmc_chan, 185 + ret = qmc_chan_read_submit(prtd->qmc_dai->qmc_chans[i], 165 186 prtd->ch_dma_addr_current + i * prtd->ch_dma_offset, 166 187 prtd->ch_dma_size, 167 - qmc_audio_pcm_read_complete, 168 - &prtd->qmc_dai->chans[i]); 188 + i == prtd->channels - 1 ? qmc_audio_pcm_read_complete : 189 + NULL, prtd); 169 190 if (ret) { 170 191 dev_err(prtd->qmc_dai->dev, "read_submit %u failed %d\n", 171 192 i, ret); 172 - bitmap_clear(prtd->chans_pending, i, 1); 173 193 return ret; 174 194 } 175 195 } ··· 176 202 177 203 static void qmc_audio_pcm_read_complete(void *context, size_t length, unsigned int flags) 178 204 { 179 - struct qmc_dai_chan *chan = context; 180 - struct qmc_dai_prtd *prtd; 181 - 182 - prtd = chan->prtd_rx; 183 - 184 - /* Mark the current channel as completed */ 185 - bitmap_clear(prtd->chans_pending, chan - prtd->qmc_dai->chans, 1); 205 + struct qmc_dai_prtd *prtd = context; 186 206 187 207 if (length != prtd->ch_dma_size) { 188 208 dev_err(prtd->qmc_dai->dev, "read complete length = %zu, exp %zu\n", 189 209 length, prtd->ch_dma_size); 190 210 } 191 - 192 - /* 193 - * All QMC channels involved must have completed their transfer before 194 - * submitting a new one. 195 - */ 196 - if (!bitmap_empty(prtd->chans_pending, 64)) 197 - return; 198 211 199 212 prtd->buffer_ended += prtd->period_size; 200 213 if (prtd->buffer_ended >= prtd->buffer_size) ··· 200 239 struct snd_pcm_substream *substream, int cmd) 201 240 { 202 241 struct qmc_dai_prtd *prtd = substream->runtime->private_data; 203 - unsigned int i; 204 242 int ret; 205 243 206 244 if (!prtd->qmc_dai) { ··· 209 249 210 250 switch (cmd) { 211 251 case SNDRV_PCM_TRIGGER_START: 212 - bitmap_zero(prtd->chans_pending, 64); 213 252 prtd->buffer_ended = 0; 214 253 prtd->ch_dma_addr_current = prtd->ch_dma_addr_start; 215 254 216 255 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 217 - for (i = 0; i < prtd->channels; i++) 218 - prtd->qmc_dai->chans[i].prtd_tx = prtd; 219 - 220 256 /* Submit first chunk ... */ 221 257 ret = qmc_audio_pcm_write_submit(prtd); 222 258 if (ret) ··· 228 272 if (ret) 229 273 return ret; 230 274 } else { 231 - for (i = 0; i < prtd->channels; i++) 232 - prtd->qmc_dai->chans[i].prtd_rx = prtd; 233 - 234 275 /* Submit first chunk ... */ 235 276 ret = qmc_audio_pcm_read_submit(prtd); 236 277 if (ret) ··· 597 644 chan_param.mode = QMC_TRANSPARENT; 598 645 chan_param.transp.max_rx_buf_size = params_period_bytes(params) / nb_chans_used; 599 646 for (i = 0; i < nb_chans_used; i++) { 600 - ret = qmc_chan_set_param(qmc_dai->chans[i].qmc_chan, &chan_param); 647 + ret = qmc_chan_set_param(qmc_dai->qmc_chans[i], &chan_param); 601 648 if (ret) { 602 - dev_err(dai->dev, "chans[%u], set param failed %d\n", 649 + dev_err(dai->dev, "qmc_chans[%u], set param failed %d\n", 603 650 i, ret); 604 651 return ret; 605 652 } ··· 641 688 case SNDRV_PCM_TRIGGER_RESUME: 642 689 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 643 690 for (i = 0; i < nb_chans_used; i++) { 644 - ret = qmc_chan_start(qmc_dai->chans[i].qmc_chan, direction); 691 + ret = qmc_chan_start(qmc_dai->qmc_chans[i], direction); 645 692 if (ret) 646 693 goto err_stop; 647 694 } ··· 650 697 case SNDRV_PCM_TRIGGER_STOP: 651 698 /* Stop and reset all QMC channels and return the first error encountered */ 652 699 for (i = 0; i < nb_chans_used; i++) { 653 - ret_tmp = qmc_chan_stop(qmc_dai->chans[i].qmc_chan, direction); 700 + ret_tmp = qmc_chan_stop(qmc_dai->qmc_chans[i], direction); 654 701 if (!ret) 655 702 ret = ret_tmp; 656 703 if (ret_tmp) 657 704 continue; 658 705 659 - ret_tmp = qmc_chan_reset(qmc_dai->chans[i].qmc_chan, direction); 706 + ret_tmp = qmc_chan_reset(qmc_dai->qmc_chans[i], direction); 660 707 if (!ret) 661 708 ret = ret_tmp; 662 709 } ··· 668 715 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 669 716 /* Stop all QMC channels and return the first error encountered */ 670 717 for (i = 0; i < nb_chans_used; i++) { 671 - ret_tmp = qmc_chan_stop(qmc_dai->chans[i].qmc_chan, direction); 718 + ret_tmp = qmc_chan_stop(qmc_dai->qmc_chans[i], direction); 672 719 if (!ret) 673 720 ret = ret_tmp; 674 721 } ··· 684 731 685 732 err_stop: 686 733 while (i--) { 687 - qmc_chan_stop(qmc_dai->chans[i].qmc_chan, direction); 688 - qmc_chan_reset(qmc_dai->chans[i].qmc_chan, direction); 734 + qmc_chan_stop(qmc_dai->qmc_chans[i], direction); 735 + qmc_chan_reset(qmc_dai->qmc_chans[i], direction); 689 736 } 690 737 return ret; 691 738 } ··· 744 791 struct qmc_dai *qmc_dai, 745 792 struct snd_soc_dai_driver *qmc_soc_dai_driver) 746 793 { 794 + struct qmc_chan_ts_info ts_info; 747 795 struct qmc_chan_info info; 748 796 unsigned long rx_fs_rate; 749 797 unsigned long tx_fs_rate; 798 + int prev_last_rx_ts = 0; 799 + int prev_last_tx_ts = 0; 750 800 unsigned int nb_tx_ts; 751 801 unsigned int nb_rx_ts; 752 802 unsigned int i; 803 + int last_rx_ts; 804 + int last_tx_ts; 753 805 int count; 754 806 u32 val; 755 807 int ret; ··· 781 823 return dev_err_probe(qmc_audio->dev, -EINVAL, 782 824 "dai %d no QMC channel defined\n", qmc_dai->id); 783 825 784 - qmc_dai->chans = devm_kcalloc(qmc_audio->dev, count, sizeof(*qmc_dai->chans), GFP_KERNEL); 785 - if (!qmc_dai->chans) 826 + qmc_dai->qmc_chans = devm_kcalloc(qmc_audio->dev, count, sizeof(*qmc_dai->qmc_chans), 827 + GFP_KERNEL); 828 + if (!qmc_dai->qmc_chans) 786 829 return -ENOMEM; 787 830 788 831 for (i = 0; i < count; i++) { 789 - qmc_dai->chans[i].qmc_chan = devm_qmc_chan_get_byphandles_index(qmc_audio->dev, np, 790 - "fsl,qmc-chan", i); 791 - if (IS_ERR(qmc_dai->chans[i].qmc_chan)) { 792 - return dev_err_probe(qmc_audio->dev, PTR_ERR(qmc_dai->chans[i].qmc_chan), 832 + qmc_dai->qmc_chans[i] = devm_qmc_chan_get_byphandles_index(qmc_audio->dev, np, 833 + "fsl,qmc-chan", i); 834 + if (IS_ERR(qmc_dai->qmc_chans[i])) { 835 + return dev_err_probe(qmc_audio->dev, PTR_ERR(qmc_dai->qmc_chans[i]), 793 836 "dai %d get QMC channel %d failed\n", qmc_dai->id, i); 794 837 } 795 838 796 - ret = qmc_chan_get_info(qmc_dai->chans[i].qmc_chan, &info); 839 + ret = qmc_chan_get_info(qmc_dai->qmc_chans[i], &info); 797 840 if (ret) { 798 841 dev_err(qmc_audio->dev, "dai %d get QMC %d channel info failed %d\n", 799 842 qmc_dai->id, i, ret); ··· 838 879 return -EINVAL; 839 880 } 840 881 } 882 + 883 + ret = qmc_chan_get_ts_info(qmc_dai->qmc_chans[i], &ts_info); 884 + if (ret) { 885 + dev_err(qmc_audio->dev, "dai %d get QMC %d channel TS info failed %d\n", 886 + qmc_dai->id, i, ret); 887 + return ret; 888 + } 889 + 890 + last_rx_ts = fls64(ts_info.rx_ts_mask); 891 + last_tx_ts = fls64(ts_info.tx_ts_mask); 892 + 893 + if (prev_last_rx_ts > last_rx_ts) { 894 + dev_err(qmc_audio->dev, "dai %d QMC chan %d unordered channels (RX timeslot %d before %d)\n", 895 + qmc_dai->id, i, prev_last_rx_ts, last_rx_ts); 896 + return -EINVAL; 897 + } 898 + if (prev_last_tx_ts > last_tx_ts) { 899 + dev_err(qmc_audio->dev, "dai %d QMC chan %d unordered channels (TX timeslot %d before %d)\n", 900 + qmc_dai->id, i, prev_last_tx_ts, last_tx_ts); 901 + return -EINVAL; 902 + } 903 + 904 + prev_last_rx_ts = last_rx_ts; 905 + prev_last_tx_ts = last_tx_ts; 841 906 } 842 907 843 908 qmc_dai->nb_chans_avail = count;