Move to request_threaded_irq and use thread for handling
SoundWire DMA interrupts.
Whenever audio data equal to the SoundWire FIFO watermark level
are produced/consumed, interrupt is generated.
Acknowledge the interrupt and wake up the irq thread.
Signed-off-by: Vijendar Mukunda <[email protected]>
---
sound/soc/amd/ps/acp63.h | 18 +++++++++
sound/soc/amd/ps/pci-ps.c | 84 ++++++++++++++++++++++++++++++++++++++-
2 files changed, 100 insertions(+), 2 deletions(-)
diff --git a/sound/soc/amd/ps/acp63.h b/sound/soc/amd/ps/acp63.h
index 5779afdc8f02..c8ba0195846b 100644
--- a/sound/soc/amd/ps/acp63.h
+++ b/sound/soc/amd/ps/acp63.h
@@ -175,6 +175,20 @@ enum acp_pdev_mask {
ACP63_SDW_PDM_DEV_MASK,
};
+enum amd_sdw0_channel {
+ ACP_SDW0_AUDIO0_TX = 0,
+ ACP_SDW0_AUDIO1_TX,
+ ACP_SDW0_AUDIO2_TX,
+ ACP_SDW0_AUDIO0_RX,
+ ACP_SDW0_AUDIO1_RX,
+ ACP_SDW0_AUDIO2_RX,
+};
+
+enum amd_sdw1_channel {
+ ACP_SDW1_AUDIO1_TX,
+ ACP_SDW1_AUDIO1_RX,
+};
+
struct pdm_stream_instance {
u16 num_pages;
u16 channels;
@@ -239,6 +253,8 @@ struct sdw_dma_ring_buf_reg {
* @sdw0_dev_index: SoundWire Manager-0 platform device index
* @sdw1_dev_index: SoundWire Manager-1 platform device index
* @sdw_dma_dev_index: SoundWire DMA controller platform device index
+ * @sdw0-dma_intr_stat: DMA interrupt status array for SoundWire manager-SW0 instance
+ * @sdw_dma_intr_stat: DMA interrupt status array for SoundWire manager-SW1 instance
* @acp_reset: flag set to true when bus reset is applied across all
* the active SoundWire manager instances
*/
@@ -256,6 +272,8 @@ struct acp63_dev_data {
u16 sdw0_dev_index;
u16 sdw1_dev_index;
u16 sdw_dma_dev_index;
+ u16 sdw0_dma_intr_stat[ACP63_SDW0_DMA_MAX_STREAMS];
+ u16 sdw1_dma_intr_stat[ACP63_SDW1_DMA_MAX_STREAMS];
bool acp_reset;
};
diff --git a/sound/soc/amd/ps/pci-ps.c b/sound/soc/amd/ps/pci-ps.c
index 17e29a3e1c21..daf54fe9cafd 100644
--- a/sound/soc/amd/ps/pci-ps.c
+++ b/sound/soc/amd/ps/pci-ps.c
@@ -99,14 +99,44 @@ static int acp63_deinit(void __iomem *acp_base, struct device *dev)
return 0;
}
+static irqreturn_t acp63_irq_thread(int irq, void *context)
+{
+ struct sdw_dma_dev_data *sdw_dma_data;
+ struct acp63_dev_data *adata = context;
+ u32 stream_index;
+ u16 pdev_index;
+
+ pdev_index = adata->sdw_dma_dev_index;
+ sdw_dma_data = dev_get_drvdata(&adata->pdev[pdev_index]->dev);
+
+ for (stream_index = 0; stream_index < ACP63_SDW0_DMA_MAX_STREAMS; stream_index++) {
+ if (adata->sdw0_dma_intr_stat[stream_index]) {
+ if (sdw_dma_data->sdw0_dma_stream[stream_index])
+ snd_pcm_period_elapsed(sdw_dma_data->sdw0_dma_stream[stream_index]);
+ adata->sdw0_dma_intr_stat[stream_index] = 0;
+ }
+ }
+ for (stream_index = 0; stream_index < ACP63_SDW1_DMA_MAX_STREAMS; stream_index++) {
+ if (adata->sdw1_dma_intr_stat[stream_index]) {
+ if (sdw_dma_data->sdw1_dma_stream[stream_index])
+ snd_pcm_period_elapsed(sdw_dma_data->sdw1_dma_stream[stream_index]);
+ adata->sdw1_dma_intr_stat[stream_index] = 0;
+ }
+ }
+ return IRQ_HANDLED;
+}
+
static irqreturn_t acp63_irq_handler(int irq, void *dev_id)
{
struct acp63_dev_data *adata;
struct pdm_dev_data *ps_pdm_data;
struct amd_sdw_manager *amd_manager;
u32 ext_intr_stat, ext_intr_stat1;
+ u32 stream_id = 0;
u16 irq_flag = 0;
+ u16 sdw_dma_irq_flag = 0;
u16 pdev_index;
+ u16 index;
adata = dev_id;
if (!adata)
@@ -153,6 +183,56 @@ static irqreturn_t acp63_irq_handler(int irq, void *dev_id)
snd_pcm_period_elapsed(ps_pdm_data->capture_stream);
irq_flag = 1;
}
+ if (ext_intr_stat & ACP_SDW_DMA_IRQ_MASK) {
+ for (index = ACP_AUDIO2_RX_THRESHOLD; index <= ACP_AUDIO0_TX_THRESHOLD; index++) {
+ if (ext_intr_stat & BIT(index)) {
+ writel(BIT(index), adata->acp63_base + ACP_EXTERNAL_INTR_STAT);
+ switch (index) {
+ case ACP_AUDIO0_TX_THRESHOLD:
+ stream_id = ACP_SDW0_AUDIO0_TX;
+ break;
+ case ACP_AUDIO1_TX_THRESHOLD:
+ stream_id = ACP_SDW0_AUDIO1_TX;
+ break;
+ case ACP_AUDIO2_TX_THRESHOLD:
+ stream_id = ACP_SDW0_AUDIO2_TX;
+ break;
+ case ACP_AUDIO0_RX_THRESHOLD:
+ stream_id = ACP_SDW0_AUDIO0_RX;
+ break;
+ case ACP_AUDIO1_RX_THRESHOLD:
+ stream_id = ACP_SDW0_AUDIO1_RX;
+ break;
+ case ACP_AUDIO2_RX_THRESHOLD:
+ stream_id = ACP_SDW0_AUDIO2_RX;
+ break;
+ }
+
+ adata->sdw0_dma_intr_stat[stream_id] = 1;
+ sdw_dma_irq_flag = 1;
+ }
+ }
+ }
+
+ /* SDW1 BT RX */
+ if (ext_intr_stat1 & ACP_P1_AUDIO1_RX_THRESHOLD) {
+ writel(ACP_P1_AUDIO1_RX_THRESHOLD,
+ adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
+ adata->sdw1_dma_intr_stat[ACP_SDW1_AUDIO1_RX] = 1;
+ sdw_dma_irq_flag = 1;
+ }
+
+ /* SDW1 BT TX*/
+ if (ext_intr_stat1 & ACP_P1_AUDIO1_TX_THRESHOLD) {
+ writel(ACP_P1_AUDIO1_TX_THRESHOLD,
+ adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
+ adata->sdw1_dma_intr_stat[ACP_SDW1_AUDIO1_TX] = 1;
+ sdw_dma_irq_flag = 1;
+ }
+
+ if (sdw_dma_irq_flag)
+ return IRQ_WAKE_THREAD;
+
if (irq_flag)
return IRQ_HANDLED;
else
@@ -544,8 +624,8 @@ static int snd_acp63_probe(struct pci_dev *pci,
ret = acp63_init(adata->acp63_base, &pci->dev);
if (ret)
goto release_regions;
- ret = devm_request_irq(&pci->dev, pci->irq, acp63_irq_handler,
- irqflags, "ACP_PCI_IRQ", adata);
+ ret = devm_request_threaded_irq(&pci->dev, pci->irq, acp63_irq_handler,
+ acp63_irq_thread, irqflags, "ACP_PCI_IRQ", adata);
if (ret) {
dev_err(&pci->dev, "ACP PCI IRQ request failed\n");
goto de_init;
--
2.34.1
> +enum amd_sdw0_channel {
> + ACP_SDW0_AUDIO0_TX = 0,
> + ACP_SDW0_AUDIO1_TX,
> + ACP_SDW0_AUDIO2_TX,
> + ACP_SDW0_AUDIO0_RX,
> + ACP_SDW0_AUDIO1_RX,
> + ACP_SDW0_AUDIO2_RX,
> +};
> +
> +enum amd_sdw1_channel {
> + ACP_SDW1_AUDIO1_TX,
> + ACP_SDW1_AUDIO1_RX,
any specify reason why SDW0 starts with AUDIO0 and SDW1 with AUDIO1?
> +};
> +
> struct pdm_stream_instance {
> u16 num_pages;
> u16 channels;
> @@ -239,6 +253,8 @@ struct sdw_dma_ring_buf_reg {
> * @sdw0_dev_index: SoundWire Manager-0 platform device index
> * @sdw1_dev_index: SoundWire Manager-1 platform device index
> * @sdw_dma_dev_index: SoundWire DMA controller platform device index
> + * @sdw0-dma_intr_stat: DMA interrupt status array for SoundWire manager-SW0 instance
> + * @sdw_dma_intr_stat: DMA interrupt status array for SoundWire manager-SW1 instance
> * @acp_reset: flag set to true when bus reset is applied across all
> * the active SoundWire manager instances
> */
> @@ -256,6 +272,8 @@ struct acp63_dev_data {
> u16 sdw0_dev_index;
> u16 sdw1_dev_index;
> u16 sdw_dma_dev_index;
> + u16 sdw0_dma_intr_stat[ACP63_SDW0_DMA_MAX_STREAMS];
> + u16 sdw1_dma_intr_stat[ACP63_SDW1_DMA_MAX_STREAMS];
> bool acp_reset;
> };
>
> diff --git a/sound/soc/amd/ps/pci-ps.c b/sound/soc/amd/ps/pci-ps.c
> index 17e29a3e1c21..daf54fe9cafd 100644
> --- a/sound/soc/amd/ps/pci-ps.c
> +++ b/sound/soc/amd/ps/pci-ps.c
> @@ -99,14 +99,44 @@ static int acp63_deinit(void __iomem *acp_base, struct device *dev)
> return 0;
> }
>
> +static irqreturn_t acp63_irq_thread(int irq, void *context)
> +{
> + struct sdw_dma_dev_data *sdw_dma_data;
> + struct acp63_dev_data *adata = context;
> + u32 stream_index;
> + u16 pdev_index;
> +
> + pdev_index = adata->sdw_dma_dev_index;
> + sdw_dma_data = dev_get_drvdata(&adata->pdev[pdev_index]->dev);
> +
> + for (stream_index = 0; stream_index < ACP63_SDW0_DMA_MAX_STREAMS; stream_index++) {
> + if (adata->sdw0_dma_intr_stat[stream_index]) {
> + if (sdw_dma_data->sdw0_dma_stream[stream_index])
can this test be false?
> + snd_pcm_period_elapsed(sdw_dma_data->sdw0_dma_stream[stream_index]);
> + adata->sdw0_dma_intr_stat[stream_index] = 0;
> + }
> + }
> + for (stream_index = 0; stream_index < ACP63_SDW1_DMA_MAX_STREAMS; stream_index++) {
> + if (adata->sdw1_dma_intr_stat[stream_index]) {
> + if (sdw_dma_data->sdw1_dma_stream[stream_index])
can this test be false?
> + snd_pcm_period_elapsed(sdw_dma_data->sdw1_dma_stream[stream_index]);
> + adata->sdw1_dma_intr_stat[stream_index] = 0;
> + }
> + }
> + return IRQ_HANDLED;
> +}
> +
> static irqreturn_t acp63_irq_handler(int irq, void *dev_id)
> {
> struct acp63_dev_data *adata;
> struct pdm_dev_data *ps_pdm_data;
> struct amd_sdw_manager *amd_manager;
> u32 ext_intr_stat, ext_intr_stat1;
> + u32 stream_id = 0;
> u16 irq_flag = 0;
> + u16 sdw_dma_irq_flag = 0;
> u16 pdev_index;
> + u16 index;
>
> adata = dev_id;
> if (!adata)
> @@ -153,6 +183,56 @@ static irqreturn_t acp63_irq_handler(int irq, void *dev_id)
> snd_pcm_period_elapsed(ps_pdm_data->capture_stream);
> irq_flag = 1;
> }
> + if (ext_intr_stat & ACP_SDW_DMA_IRQ_MASK) {
> + for (index = ACP_AUDIO2_RX_THRESHOLD; index <= ACP_AUDIO0_TX_THRESHOLD; index++) {
> + if (ext_intr_stat & BIT(index)) {
> + writel(BIT(index), adata->acp63_base + ACP_EXTERNAL_INTR_STAT);
> + switch (index) {
> + case ACP_AUDIO0_TX_THRESHOLD:
> + stream_id = ACP_SDW0_AUDIO0_TX;
> + break;
> + case ACP_AUDIO1_TX_THRESHOLD:
> + stream_id = ACP_SDW0_AUDIO1_TX;
> + break;
> + case ACP_AUDIO2_TX_THRESHOLD:
> + stream_id = ACP_SDW0_AUDIO2_TX;
> + break;
> + case ACP_AUDIO0_RX_THRESHOLD:
> + stream_id = ACP_SDW0_AUDIO0_RX;
> + break;
> + case ACP_AUDIO1_RX_THRESHOLD:
> + stream_id = ACP_SDW0_AUDIO1_RX;
> + break;
> + case ACP_AUDIO2_RX_THRESHOLD:
> + stream_id = ACP_SDW0_AUDIO2_RX;
> + break;
> + }
> +
> + adata->sdw0_dma_intr_stat[stream_id] = 1;
> + sdw_dma_irq_flag = 1;
> + }
> + }
> + }
> +
> + /* SDW1 BT RX */
> + if (ext_intr_stat1 & ACP_P1_AUDIO1_RX_THRESHOLD) {
> + writel(ACP_P1_AUDIO1_RX_THRESHOLD,
> + adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
> + adata->sdw1_dma_intr_stat[ACP_SDW1_AUDIO1_RX] = 1;
> + sdw_dma_irq_flag = 1;
> + }
> +
> + /* SDW1 BT TX*/
keep spaces before */
> + if (ext_intr_stat1 & ACP_P1_AUDIO1_TX_THRESHOLD) {
> + writel(ACP_P1_AUDIO1_TX_THRESHOLD,
> + adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
> + adata->sdw1_dma_intr_stat[ACP_SDW1_AUDIO1_TX] = 1;
> + sdw_dma_irq_flag = 1;
> + }
> +
> + if (sdw_dma_irq_flag)
> + return IRQ_WAKE_THREAD;
> +
> if (irq_flag)
> return IRQ_HANDLED;
> else
> @@ -544,8 +624,8 @@ static int snd_acp63_probe(struct pci_dev *pci,
> ret = acp63_init(adata->acp63_base, &pci->dev);
> if (ret)
> goto release_regions;
> - ret = devm_request_irq(&pci->dev, pci->irq, acp63_irq_handler,
> - irqflags, "ACP_PCI_IRQ", adata);
> + ret = devm_request_threaded_irq(&pci->dev, pci->irq, acp63_irq_handler,
> + acp63_irq_thread, irqflags, "ACP_PCI_IRQ", adata);
> if (ret) {
> dev_err(&pci->dev, "ACP PCI IRQ request failed\n");
> goto de_init;
On 06/06/23 20:29, Pierre-Louis Bossart wrote:
>> +enum amd_sdw0_channel {
>> + ACP_SDW0_AUDIO0_TX = 0,
>> + ACP_SDW0_AUDIO1_TX,
>> + ACP_SDW0_AUDIO2_TX,
>> + ACP_SDW0_AUDIO0_RX,
>> + ACP_SDW0_AUDIO1_RX,
>> + ACP_SDW0_AUDIO2_RX,
>> +};
>> +
>> +enum amd_sdw1_channel {
>> + ACP_SDW1_AUDIO1_TX,
>> + ACP_SDW1_AUDIO1_RX,
> any specify reason why SDW0 starts with AUDIO0 and SDW1 with AUDIO1?
Currently, SDW0 instance uses 3 TX, 3 RX ports whereas SDW1 instance
uses 1 TX, 1 RX ports.
For SDW1 instance, It uses AUDIO1 register set as per our register spec.
We have mantained similar mapping convention here for enums as well.
We have already described SoundWire instance TX/RX ports mapping
in comments in amd_manager.h file.
Please refer comments mentioned in amd_manager.h file.
>
>> +};
>> +
>> struct pdm_stream_instance {
>> u16 num_pages;
>> u16 channels;
>> @@ -239,6 +253,8 @@ struct sdw_dma_ring_buf_reg {
>> * @sdw0_dev_index: SoundWire Manager-0 platform device index
>> * @sdw1_dev_index: SoundWire Manager-1 platform device index
>> * @sdw_dma_dev_index: SoundWire DMA controller platform device index
>> + * @sdw0-dma_intr_stat: DMA interrupt status array for SoundWire manager-SW0 instance
>> + * @sdw_dma_intr_stat: DMA interrupt status array for SoundWire manager-SW1 instance
>> * @acp_reset: flag set to true when bus reset is applied across all
>> * the active SoundWire manager instances
>> */
>> @@ -256,6 +272,8 @@ struct acp63_dev_data {
>> u16 sdw0_dev_index;
>> u16 sdw1_dev_index;
>> u16 sdw_dma_dev_index;
>> + u16 sdw0_dma_intr_stat[ACP63_SDW0_DMA_MAX_STREAMS];
>> + u16 sdw1_dma_intr_stat[ACP63_SDW1_DMA_MAX_STREAMS];
>> bool acp_reset;
>> };
>>
>> diff --git a/sound/soc/amd/ps/pci-ps.c b/sound/soc/amd/ps/pci-ps.c
>> index 17e29a3e1c21..daf54fe9cafd 100644
>> --- a/sound/soc/amd/ps/pci-ps.c
>> +++ b/sound/soc/amd/ps/pci-ps.c
>> @@ -99,14 +99,44 @@ static int acp63_deinit(void __iomem *acp_base, struct device *dev)
>> return 0;
>> }
>>
>> +static irqreturn_t acp63_irq_thread(int irq, void *context)
>> +{
>> + struct sdw_dma_dev_data *sdw_dma_data;
>> + struct acp63_dev_data *adata = context;
>> + u32 stream_index;
>> + u16 pdev_index;
>> +
>> + pdev_index = adata->sdw_dma_dev_index;
>> + sdw_dma_data = dev_get_drvdata(&adata->pdev[pdev_index]->dev);
>> +
>> + for (stream_index = 0; stream_index < ACP63_SDW0_DMA_MAX_STREAMS; stream_index++) {
>> + if (adata->sdw0_dma_intr_stat[stream_index]) {
>> + if (sdw_dma_data->sdw0_dma_stream[stream_index])
> can this test be false?
Our intention is to invoke period elapsed callback for active substreams
based on irq received. Its good to have a NULL check for substream.
>
>> + snd_pcm_period_elapsed(sdw_dma_data->sdw0_dma_stream[stream_index]);
>> + adata->sdw0_dma_intr_stat[stream_index] = 0;
>> + }
>> + }
>> + for (stream_index = 0; stream_index < ACP63_SDW1_DMA_MAX_STREAMS; stream_index++) {
>> + if (adata->sdw1_dma_intr_stat[stream_index]) {
>> + if (sdw_dma_data->sdw1_dma_stream[stream_index])
> can this test be false?
Please refer above comments.
>
>> + snd_pcm_period_elapsed(sdw_dma_data->sdw1_dma_stream[stream_index]);
>> + adata->sdw1_dma_intr_stat[stream_index] = 0;
>> + }
>> + }
>> + return IRQ_HANDLED;
>> +}
>> +
>> static irqreturn_t acp63_irq_handler(int irq, void *dev_id)
>> {
>> struct acp63_dev_data *adata;
>> struct pdm_dev_data *ps_pdm_data;
>> struct amd_sdw_manager *amd_manager;
>> u32 ext_intr_stat, ext_intr_stat1;
>> + u32 stream_id = 0;
>> u16 irq_flag = 0;
>> + u16 sdw_dma_irq_flag = 0;
>> u16 pdev_index;
>> + u16 index;
>>
>> adata = dev_id;
>> if (!adata)
>> @@ -153,6 +183,56 @@ static irqreturn_t acp63_irq_handler(int irq, void *dev_id)
>> snd_pcm_period_elapsed(ps_pdm_data->capture_stream);
>> irq_flag = 1;
>> }
>> + if (ext_intr_stat & ACP_SDW_DMA_IRQ_MASK) {
>> + for (index = ACP_AUDIO2_RX_THRESHOLD; index <= ACP_AUDIO0_TX_THRESHOLD; index++) {
>> + if (ext_intr_stat & BIT(index)) {
>> + writel(BIT(index), adata->acp63_base + ACP_EXTERNAL_INTR_STAT);
>> + switch (index) {
>> + case ACP_AUDIO0_TX_THRESHOLD:
>> + stream_id = ACP_SDW0_AUDIO0_TX;
>> + break;
>> + case ACP_AUDIO1_TX_THRESHOLD:
>> + stream_id = ACP_SDW0_AUDIO1_TX;
>> + break;
>> + case ACP_AUDIO2_TX_THRESHOLD:
>> + stream_id = ACP_SDW0_AUDIO2_TX;
>> + break;
>> + case ACP_AUDIO0_RX_THRESHOLD:
>> + stream_id = ACP_SDW0_AUDIO0_RX;
>> + break;
>> + case ACP_AUDIO1_RX_THRESHOLD:
>> + stream_id = ACP_SDW0_AUDIO1_RX;
>> + break;
>> + case ACP_AUDIO2_RX_THRESHOLD:
>> + stream_id = ACP_SDW0_AUDIO2_RX;
>> + break;
>> + }
>> +
>> + adata->sdw0_dma_intr_stat[stream_id] = 1;
>> + sdw_dma_irq_flag = 1;
>> + }
>> + }
>> + }
>> +
>> + /* SDW1 BT RX */
>> + if (ext_intr_stat1 & ACP_P1_AUDIO1_RX_THRESHOLD) {
>> + writel(ACP_P1_AUDIO1_RX_THRESHOLD,
>> + adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
>> + adata->sdw1_dma_intr_stat[ACP_SDW1_AUDIO1_RX] = 1;
>> + sdw_dma_irq_flag = 1;
>> + }
>> +
>> + /* SDW1 BT TX*/
> keep spaces before */
will fix it.
>
>> + if (ext_intr_stat1 & ACP_P1_AUDIO1_TX_THRESHOLD) {
>> + writel(ACP_P1_AUDIO1_TX_THRESHOLD,
>> + adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
>> + adata->sdw1_dma_intr_stat[ACP_SDW1_AUDIO1_TX] = 1;
>> + sdw_dma_irq_flag = 1;
>> + }
>> +
>> + if (sdw_dma_irq_flag)
>> + return IRQ_WAKE_THREAD;
>> +
>> if (irq_flag)
>> return IRQ_HANDLED;
>> else
>> @@ -544,8 +624,8 @@ static int snd_acp63_probe(struct pci_dev *pci,
>> ret = acp63_init(adata->acp63_base, &pci->dev);
>> if (ret)
>> goto release_regions;
>> - ret = devm_request_irq(&pci->dev, pci->irq, acp63_irq_handler,
>> - irqflags, "ACP_PCI_IRQ", adata);
>> + ret = devm_request_threaded_irq(&pci->dev, pci->irq, acp63_irq_handler,
>> + acp63_irq_thread, irqflags, "ACP_PCI_IRQ", adata);
>> if (ret) {
>> dev_err(&pci->dev, "ACP PCI IRQ request failed\n");
>> goto de_init;