[Sound-open-firmware] [PATCH] dw-dma: add support for interrupt per channel

Pierre-Louis Bossart pierre-louis.bossart at linux.intel.com
Thu Jan 25 19:13:54 CET 2018


CHECK: Alignment should match open parenthesis
#72: FILE: src/drivers/dw-dma.c:863:
+        dw_write(dma, DW_CTRL_HIGH(i),
+                    DW_CTLH_CLASS(dp->chan[i].class));

CHECK: Alignment should match open parenthesis
#75: FILE: src/drivers/dw-dma.c:866:
+        dw_write(dma, DW_CFG_LOW(i),
+                    DW_CFG_CLASS(dp->chan[i].class));

CHECK: Alignment should match open parenthesis
#125: FILE: src/drivers/dw-dma.c:916:
+        if (status_block & mask &&
+            p->chan[i].cb_type & DMA_IRQ_TYPE_BLOCK) {

CHECK: multiple assignments should be avoided
#126: FILE: src/drivers/dw-dma.c:917:
+            next.src = next.dest = DMA_RELOAD_LLI;

CHECK: Alignment should match open parenthesis
#135: FILE: src/drivers/dw-dma.c:926:
+    if ((status_tfr & mask) &&
+        (p->chan[i].cb_type & DMA_IRQ_TYPE_LLIST)) {

CHECK: multiple assignments should be avoided
#138: FILE: src/drivers/dw-dma.c:929:
+        next.src = next.dest = DMA_RELOAD_LLI;

CHECK: Alignment should match open parenthesis
#188: FILE: src/drivers/dw-dma.c:979:
+        dma_int[i] = rzalloc(RZONE_SYS, RFLAGS_NONE,
+                        sizeof(struct dma_int));

CHECK: Alignment should match open parenthesis
#197: FILE: src/drivers/dw-dma.c:988:
+        interrupt_register(dma_int[i]->irq,
+                dw_dma_irq_handler, dma_int[i]);

On 01/25/2018 03:46 AM, Keyon Jie wrote:
> On Apollolake, the interrupt number for different channels of
> the same controller are different, here add implementation of
> it: register interrupt handler for each channel, and don't
> need check channel in its specific handler anymore.
>
> Signed-off-by: Keyon Jie <yang.jie at linux.intel.com>
> ---
> Tested on GP-MRB,
> SOF Master: f7beb51118e6e8463a864b9416c773a508930e06,
> SOF Tool Master: 59d81995f682876bd34f939332e8838c76f714ec,
> https://github.com/plbossart/sound/tree/topic/sof-v4.14:
> 5a91e6776d41b0e97828882294cdc00b5c0bafd6
>
> src/drivers/dw-dma.c   | 244 +++++++++++++++++++++++++++++++++++++------------
>   src/include/reef/dma.h |   6 ++
>   2 files changed, 192 insertions(+), 58 deletions(-)
>
> diff --git a/src/drivers/dw-dma.c b/src/drivers/dw-dma.c
> index 09e441c..a0782cb 100644
> --- a/src/drivers/dw-dma.c
> +++ b/src/drivers/dw-dma.c
> @@ -809,6 +809,191 @@ static inline void dw_dma_chan_reload_next(struct dma *dma, int channel,
>   	dw_write(dma, DW_DMA_CHAN_EN, CHAN_ENABLE(channel));
>   }
>   
> +static void dw_dma_setup(struct dma *dma)
> +{
> +	struct dw_drv_plat_data *dp = dma->plat_data.drv_plat_data;
> +	int i;
> +
> +	/* we cannot config DMAC if DMAC has been already enabled by host */
> +	if (dw_read(dma, DW_DMA_CFG) != 0)
> +		dw_write(dma, DW_DMA_CFG, 0x0);
> +
> +	/* now check that it's 0 */
> +	for (i = DW_DMA_CFG_TRIES; i > 0; i--) {
> +		if (dw_read(dma, DW_DMA_CFG) == 0)
> +			goto found;
> +	}
> +	trace_dma_error("eDs");
> +	return;
> +
> +found:
> +	for (i = 0; i <  DW_MAX_CHAN; i++)
> +		dw_read(dma, DW_DMA_CHAN_EN);
> +
> +#ifdef HAVE_HDDA
> +	/* enable HDDA before DMAC */
> +	shim_write(SHIM_HMDC, SHIM_HMDC_HDDA_ALLCH);
> +#endif
> +
> +	/* enable the DMA controller */
> +	dw_write(dma, DW_DMA_CFG, 1);
> +
> +	/* mask all interrupts for all 8 channels */
> +	dw_write(dma, DW_MASK_TFR, INT_MASK_ALL);
> +	dw_write(dma, DW_MASK_BLOCK, INT_MASK_ALL);
> +	dw_write(dma, DW_MASK_SRC_TRAN, INT_MASK_ALL);
> +	dw_write(dma, DW_MASK_DST_TRAN, INT_MASK_ALL);
> +	dw_write(dma, DW_MASK_ERR, INT_MASK_ALL);
> +
> +#ifdef DW_FIFO_PARTITION
> +	/* TODO: we cannot config DMA FIFOs if DMAC has been already */
> +	/* allocate FIFO partitions, 128 bytes for each ch */
> +	dw_write(dma, DW_FIFO_PART1_LO, 0x100080);
> +	dw_write(dma, DW_FIFO_PART1_HI, 0x100080);
> +	dw_write(dma, DW_FIFO_PART0_HI, 0x100080);
> +	dw_write(dma, DW_FIFO_PART0_LO, 0x100080 | (1 << 26));
> +	dw_write(dma, DW_FIFO_PART0_LO, 0x100080);
> +#endif
> +
> +	/* set channel priorities */
> +	for (i = 0; i <  DW_MAX_CHAN; i++) {
> +#if defined CONFIG_BAYTRAIL || defined CONFIG_CHERRYTRAIL ||\
> +	defined CONFIG_APOLLOLAKE || defined CONFIG_CANNONLAKE
> +		dw_write(dma, DW_CTRL_HIGH(i),
> +					DW_CTLH_CLASS(dp->chan[i].class));
> +#elif defined CONFIG_BROADWELL || defined CONFIG_HASWELL
> +		dw_write(dma, DW_CFG_LOW(i),
> +					DW_CFG_CLASS(dp->chan[i].class));
> +#endif
> +	}
> +}
> +
> +#ifdef CONFIG_APOLLOLAKE
> +/* external layer 2 interrupt for dmac */
> +static void dw_dma_irq_handler(void *data)
> +{
> +	struct dma_int *dma_int = (struct dma_int *)data;
> +	struct dma *dma = dma_int->dma;
> +	struct dma_pdata *p = dma_get_drvdata(dma);
> +	struct dma_sg_elem next;
> +	uint32_t status_tfr = 0, status_block = 0, status_err = 0, status_intr;
> +	uint32_t mask;
> +	int i = dma_int->channel;
> +
> +	status_intr = dw_read(dma, DW_INTR_STATUS);
> +	if (!status_intr)
> +		trace_dma_error("eDI");
> +
> +	trace_dma("irq");
> +	trace_value(status_intr);
> +
> +	/* get the source of our IRQ. */
> +	status_block = dw_read(dma, DW_STATUS_BLOCK);
> +	status_tfr = dw_read(dma, DW_STATUS_TFR);
> +
> +	/* TODO: handle errors, just clear them atm */
> +	status_err = dw_read(dma, DW_STATUS_ERR);
> +	if (status_err) {
> +		trace_dma_error("eDi");
> +		dw_write(dma, DW_CLEAR_ERR, status_err & i);
> +	}
> +
> +	/* clear interrupts for channel*/
> +	dw_write(dma, DW_CLEAR_BLOCK, status_block);
> +	dw_write(dma, DW_CLEAR_TFR, status_tfr);
> +
> +	/* skip if channel is not running */
> +	if (p->chan[i].status != COMP_STATE_ACTIVE) {
> +		trace_dma_error("eDs");
> +		return;
> +	}
> +
> +	mask = 0x1 << i;
> +
> +#if DW_USE_HW_LLI
> +		/* end of a LLI block */
> +		if (status_block & mask &&
> +			p->chan[i].cb_type & DMA_IRQ_TYPE_BLOCK) {
> +			next.src = next.dest = DMA_RELOAD_LLI;
> +			/* will reload lli by default */
> +			next.size = DMA_RELOAD_LLI;
> +			p->chan[i].cb(p->chan[i].cb_data,
> +					DMA_IRQ_TYPE_BLOCK, &next);
> +		}
> +#endif
> +	/* end of a transfer */
> +	if ((status_tfr & mask) &&
> +		(p->chan[i].cb_type & DMA_IRQ_TYPE_LLIST)) {
> +		trace_value(status_tfr);
> +
> +		next.src = next.dest = DMA_RELOAD_LLI;
> +		next.size = DMA_RELOAD_LLI; /* will reload lli by default */
> +		if (p->chan[i].cb)
> +			p->chan[i].cb(p->chan[i].cb_data,
> +				DMA_IRQ_TYPE_LLIST, &next);
> +
> +		/* check for reload channel:
> +		 * next.size is DMA_RELOAD_END, stop this dma copy;
> +		 * next.size > 0 but not DMA_RELOAD_LLI, use next
> +		 * element for next copy;
> +		 * if we are waiting for pause, pause it;
> +		 * otherwise, reload lli
> +		 */
> +		switch (next.size) {
> +		case DMA_RELOAD_END:
> +			p->chan[i].status = COMP_STATE_PREPARE;
> +			break;
> +		case DMA_RELOAD_LLI:
> +			/* reload lli, but let's check if it is paused */
> +			if (p->chan[i].status != COMP_STATE_PAUSED)
> +				dw_dma_chan_reload_lli(dma, i);
> +			break;
> +		default:
> +			dw_dma_chan_reload_next(dma, i, &next);
> +			break;
> +		}
> +	}
> +}
> +
> +static int dw_dma_probe(struct dma *dma)
> +{
> +	struct dma_int *dma_int[DW_MAX_CHAN];
> +	struct dma_pdata *dw_pdata;
> +	int i;
> +
> +	/* allocate private data */
> +	dw_pdata = rzalloc(RZONE_SYS, RFLAGS_NONE, sizeof(*dw_pdata));
> +	dma_set_drvdata(dma, dw_pdata);
> +
> +	spinlock_init(&dma->lock);
> +
> +	dw_dma_setup(dma);
> +
> +	/* init work */
> +	for (i = 0; i < dma->plat_data.channels; i++) {
> +		dw_pdata->chan[i].dma = dma;
> +		dw_pdata->chan[i].channel = i;
> +		dw_pdata->chan[i].status = COMP_STATE_INIT;
> +
> +		dma_int[i] = rzalloc(RZONE_SYS, RFLAGS_NONE,
> +						sizeof(struct dma_int));
> +
> +		dma_int[i]->dma = dma;
> +		dma_int[i]->channel = i;
> +		dma_int[i]->irq = dma->plat_data.irq +
> +						(i << REEF_IRQ_BIT_SHIFT);
> +
> +		/* register our IRQ handler */
> +		interrupt_register(dma_int[i]->irq,
> +				dw_dma_irq_handler, dma_int[i]);
> +		interrupt_enable(dma_int[i]->irq);
> +	}
> +
> +	return 0;
> +}
> +
> +#else
> +
>   /* this will probably be called at the end of every period copied */
>   static void dw_dma_irq_handler(void *data)
>   {
> @@ -906,64 +1091,6 @@ static void dw_dma_irq_handler(void *data)
>   	}
>   }
>   
> -static void dw_dma_setup(struct dma *dma)
> -{
> -	struct dw_drv_plat_data *dp = dma->plat_data.drv_plat_data;
> -	int i;
> -
> -	/* we cannot config DMAC if DMAC has been already enabled by host */
> -	if (dw_read(dma, DW_DMA_CFG) != 0)
> -		dw_write(dma, DW_DMA_CFG, 0x0);
> -
> -	/* now check that it's 0 */
> -	for (i = DW_DMA_CFG_TRIES; i > 0; i--) {
> -		if (dw_read(dma, DW_DMA_CFG) == 0)
> -			goto found;
> -	}
> -	trace_dma_error("eDs");
> -	return;
> -
> -found:
> -	for (i = 0; i <  DW_MAX_CHAN; i++)
> -		dw_read(dma, DW_DMA_CHAN_EN);
> -
> -#ifdef HAVE_HDDA
> -	/* enable HDDA before DMAC */
> -	shim_write(SHIM_HMDC, SHIM_HMDC_HDDA_ALLCH);
> -#endif
> -
> -	/* enable the DMA controller */
> -	dw_write(dma, DW_DMA_CFG, 1);
> -
> -	/* mask all interrupts for all 8 channels */
> -	dw_write(dma, DW_MASK_TFR, INT_MASK_ALL);
> -	dw_write(dma, DW_MASK_BLOCK, INT_MASK_ALL);
> -	dw_write(dma, DW_MASK_SRC_TRAN, INT_MASK_ALL);
> -	dw_write(dma, DW_MASK_DST_TRAN, INT_MASK_ALL);
> -	dw_write(dma, DW_MASK_ERR, INT_MASK_ALL);
> -
> -#ifdef DW_FIFO_PARTITION
> -	/* TODO: we cannot config DMA FIFOs if DMAC has been already */
> -	/* allocate FIFO partitions, 128 bytes for each ch */
> -	dw_write(dma, DW_FIFO_PART1_LO, 0x100080);
> -	dw_write(dma, DW_FIFO_PART1_HI, 0x100080);
> -	dw_write(dma, DW_FIFO_PART0_HI, 0x100080);
> -	dw_write(dma, DW_FIFO_PART0_LO, 0x100080 | (1 << 26));
> -	dw_write(dma, DW_FIFO_PART0_LO, 0x100080);
> -#endif
> -
> -	/* set channel priorities */
> -	for (i = 0; i <  DW_MAX_CHAN; i++) {
> -#if defined CONFIG_BAYTRAIL || defined CONFIG_CHERRYTRAIL \
> -	|| defined CONFIG_APOLLOLAKE || defined CONFIG_CANNONLAKE
> -		dw_write(dma, DW_CTRL_HIGH(i), DW_CTLH_CLASS(dp->chan[i].class));
> -#else
> -		dw_write(dma, DW_CFG_LOW(i), DW_CFG_CLASS(dp->chan[i].class));
> -#endif
> -	}
> -
> -}
> -
>   static int dw_dma_probe(struct dma *dma)
>   {
>   	struct dma_pdata *dw_pdata;
> @@ -990,6 +1117,7 @@ static int dw_dma_probe(struct dma *dma)
>   
>   	return 0;
>   }
> +#endif
>   
>   const struct dma_ops dw_dma_ops = {
>   	.channel_get	= dw_dma_channel_get,
> diff --git a/src/include/reef/dma.h b/src/include/reef/dma.h
> index e33adaa..77f8f71 100644
> --- a/src/include/reef/dma.h
> +++ b/src/include/reef/dma.h
> @@ -128,6 +128,12 @@ struct dma {
>   	void *private;
>   };
>   
> +struct dma_int {
> +	struct dma *dma;
> +	uint32_t channel;
> +	uint32_t irq;
> +};
> +
>   struct dma *dma_get(int dmac_id);
>   
>   #define dma_set_drvdata(dma, data) \



More information about the Sound-open-firmware mailing list