void dma_resume_channel(int channel) { /* Resume - must reinit to where it left off (so the docs say) */ unsigned long control = DMAC_CH_CONTROL(channel); if ((control & 0x7ff) == 0) return; /* empty */ DMAC_INT_TC_CLEAR = (1<<channel); DMAC_INT_ERR_CLEAR = (1<<channel); DMAC_CH_SRC_ADDR(channel) = DMAC_CH_SRC_ADDR(channel); DMAC_CH_DST_ADDR(channel) = DMAC_CH_DST_ADDR(channel); DMAC_CH_LLI(channel) = DMAC_CH_LLI(channel); DMAC_CH_CONTROL(channel) = control; bitset32(&DMAC_CH_CONFIGURATION(channel), (1<<0)); }
void dma_enable_channel(int channel, void *src, void *dst, int peri, int flow_controller, bool src_inc, bool dst_inc, size_t size, int nwords, void (*callback)(void)) { dma_callback[channel] = callback; /* Clear any pending interrupts leftover from previous operation */ DMAC_INT_TC_CLEAR = (1<<channel); DMAC_INT_ERR_CLEAR = (1<<channel); DMAC_CH_SRC_ADDR(channel) = (int)src; DMAC_CH_DST_ADDR(channel) = (int)dst; /* When LLI is 0 channel is disabled upon transfer completion */ DMAC_CH_LLI(channel) = 0; /* Channel Control Register */ DMAC_CH_CONTROL(channel) = ((1<<31) /* LLI triggers terminal count interrupt */ /* | (1<<30) */ /* cacheable = 1, non = 0 */ /* | (1<<29) */ /* bufferable = 1, non = 0 */ /* | (1<<28) */ /* privileged = 1, user = 0 */ | (dst_inc? (1<<27): 0) /* specify address increment */ | (src_inc? (1<<26): 0) /* specify address increment */ /* [25:24] */ /* undefined */ | (2<<21) /* dst width = word, 32bit */ | (2<<18) /* src width = word, 32bit */ /* OF uses transfers of 4 * 32 bits words on memory, i2sin, i2sout */ /* OF uses transfers of 8 * 32 bits words on SD */ | (nwords<<15) /* dst size */ | (nwords<<12) /* src size */ | ((size & 0x7ff)<<0)); /* transfer size */ /* Channel Config Register */ DMAC_CH_CONFIGURATION(channel) = /* [31:19] */ /* Read undefined. Write as zero */ /* (0<<18) */ /* Halt Bit */ /* (0<<17) */ /* Active Bit */ /* (0<<16) */ /* Lock Bit */ (1<<15) /* terminal count interrupt mask */ | (1<<14) /* interrupt error mask */ | (flow_controller<<11) /* flow controller is peripheral or SDMAC */ /* we set the same peripheral as source and destination because we * always use memory-to-peripheral or peripheral-to-memory transfers */ | (peri<<6) /* dst peripheral */ | (peri<<1) /* src peripheral */ | (1<<0); /* enable channel */ }
static irqreturn_t str8100_dma_tc_irq_handler(int this_irq, void *dev_id, struct pt_regs *regs) { u32 dma_tc_status,tot_size; u32 len; //printk("%s: this_irq=%d\n",__FUNCTION__,this_irq); HAL_INTC_DISABLE_INTERRUPT_SOURCE(this_irq); //todo: HAL_DMAC_READ_TERMINAL_COUNT_INTERRUPT_STATUS(dma_tc_status); printk("%s: this_irq=%d, dma_tc_status=%.8x\n",__FUNCTION__,this_irq,dma_tc_status); #ifdef I2S_WM8772_DMAC_LLP_RING_TEST u32 i; /* * For LLP ring test, the TC interrupt shoule not happen!! */ for(i=0;i<8;i++) if (dma_tc_status & DMAC_CH_ID(i)){ HAL_DMAC_CLEAR_TERMINAL_COUNT_INTERRUPT_STATUS(DMAC_CH_ID(i)); printk("%s: channel %d: Error!! there should be no tc irq happened!!\n",__FUNCTION__,i); } #else /* * For this case, it's recommended to set I2S_WM8772_DMAC_LLP_NUM = 1 */ /* * For DMA's Tx for I2S Left Channel */ if (dma_tc_status & DMAC_CH_ID(i2s_wm8772_dma_left_tx_channel)) { HAL_DMAC_DISABLE_CHANNEL(i2s_wm8772_dma_left_tx_channel); HAL_DMAC_CLEAR_TERMINAL_COUNT_INTERRUPT_STATUS(DMAC_CH_ID(i2s_wm8772_dma_left_tx_channel)); /* * Re-initialize DMA's channel for Left_Tx */ DMAC_CH_SRC_ADDR(i2s_wm8772_dma_left_tx_channel) = i2s_left_tx_channel_dma_llp_desc[0].SrcAddr; DMAC_CH_DST_ADDR(i2s_wm8772_dma_left_tx_channel) = i2s_left_tx_channel_dma_llp_desc[0].DstAddr; /* * Note this macro DMAC_CH_SIZE is to configure TOT_SIZE field which is the total transfer * number of source transfer width! */ tot_size = Hal_Dmac_Get_Channel_Transfer_Unit_Number(I2S_WM8772_BUFFER_SIZE * 4, DMAC_CH_SRC_WIDTH_32_BITS); DMAC_CH_SIZE(i2s_wm8772_dma_left_tx_channel) = tot_size & 0x0FFF; HAL_DMAC_ENABLE_CHANNEL(i2s_wm8772_dma_left_tx_channel); } /* * For DMA's Tx for I2S Right Channel */ if (dma_tc_status & DMAC_CH_ID(i2s_wm8772_dma_right_tx_channel)) { HAL_DMAC_DISABLE_CHANNEL(i2s_wm8772_dma_right_tx_channel); HAL_DMAC_CLEAR_TERMINAL_COUNT_INTERRUPT_STATUS(DMAC_CH_ID(i2s_wm8772_dma_right_tx_channel)); /* * Re-initialize DMA's channel for Right_Tx */ DMAC_CH_SRC_ADDR(i2s_wm8772_dma_right_tx_channel) = i2s_right_tx_channel_dma_llp_desc[0].SrcAddr; DMAC_CH_DST_ADDR(i2s_wm8772_dma_right_tx_channel) = i2s_right_tx_channel_dma_llp_desc[0].DstAddr; /* * Note this macro DMAC_CH_SIZE is to configure TOT_SIZE field which is the total transfer * number of source transfer width! */ tot_size = Hal_Dmac_Get_Channel_Transfer_Unit_Number(I2S_WM8772_BUFFER_SIZE * 4, DMAC_CH_SRC_WIDTH_32_BITS); DMAC_CH_SIZE(i2s_wm8772_dma_right_tx_channel) = tot_size & 0x0FFF; HAL_DMAC_ENABLE_CHANNEL(i2s_wm8772_dma_right_tx_channel); } /* * For DMA's Rx for I2S Left Channel */ if (dma_tc_status & DMAC_CH_ID(i2s_wm8772_dma_left_rx_channel)) { HAL_DMAC_DISABLE_CHANNEL(i2s_wm8772_dma_left_rx_channel); HAL_DMAC_CLEAR_TERMINAL_COUNT_INTERRUPT_STATUS(DMAC_CH_ID(i2s_wm8772_dma_left_rx_channel)); /* * Re-initialize DMA's channel for Left_Rx */ DMAC_CH_SRC_ADDR(i2s_wm8772_dma_left_rx_channel) = i2s_left_rx_channel_dma_llp_desc[0].SrcAddr; DMAC_CH_DST_ADDR(i2s_wm8772_dma_left_rx_channel) = i2s_left_rx_channel_dma_llp_desc[0].DstAddr; /* * Note this macro DMAC_CH_SIZE is to configure TOT_SIZE field which is the total transfer * number of source transfer width! */ tot_size = Hal_Dmac_Get_Channel_Transfer_Unit_Number(I2S_WM8772_BUFFER_SIZE * 4, DMAC_CH_SRC_WIDTH_32_BITS); DMAC_CH_SIZE(i2s_wm8772_dma_left_rx_channel) = tot_size & 0x0FFF; HAL_DMAC_ENABLE_CHANNEL(i2s_wm8772_dma_left_rx_channel); } /* * For DMA's Rx for I2S Right Channel */ if (dma_tc_status & DMAC_CH_ID(i2s_wm8772_dma_right_rx_channel)) { HAL_DMAC_DISABLE_CHANNEL(i2s_wm8772_dma_right_rx_channel); HAL_DMAC_CLEAR_TERMINAL_COUNT_INTERRUPT_STATUS(DMAC_CH_ID(i2s_wm8772_dma_right_rx_channel)); /* * Re-initialize DMA's channel for Right_Rx */ DMAC_CH_SRC_ADDR(i2s_wm8772_dma_right_rx_channel) = i2s_right_rx_channel_dma_llp_desc[0].SrcAddr; DMAC_CH_DST_ADDR(i2s_wm8772_dma_right_rx_channel) = i2s_right_rx_channel_dma_llp_desc[0].DstAddr; /* * Note this macro DMAC_CH_SIZE is to configure TOT_SIZE field which is the total transfer * number of source transfer width! */ tot_size = Hal_Dmac_Get_Channel_Transfer_Unit_Number(I2S_WM8772_BUFFER_SIZE * 4, DMAC_CH_SRC_WIDTH_32_BITS); DMAC_CH_SIZE(i2s_wm8772_dma_right_rx_channel) = tot_size & 0x0FFF; HAL_DMAC_ENABLE_CHANNEL(i2s_wm8772_dma_right_rx_channel); } #endif HAL_INTC_CLEAR_EDGE_TRIGGER_INTERRUPT(this_irq); HAL_INTC_ENABLE_INTERRUPT_SOURCE(this_irq); return IRQ_HANDLED; }