📄 dma_ep93xx.c
字号:
/****************************************************************************** * arch/arm/mach-ep9312/dma_ep93xx.c * * Support functions for the ep93xx internal DMA channels. * (see also Documentation/arm/ep93xx/dma.txt) * * Copyright (C) 2003 Cirrus Logic * * A large portion of this file is based on the dma api implemented by * Nicolas Pitre, dma-sa1100.c, copyrighted 2000. * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * ****************************************************************************/#include <linux/module.h>#include <linux/init.h>#include <linux/sched.h>#include <linux/spinlock.h>#include <linux/slab.h>#include <linux/errno.h>#include <linux/delay.h>#include <asm/system.h>#include <asm/irq.h>#include <asm/hardware.h>#include <asm/io.h>#include <asm/dma.h>#include <asm/mach/dma.h>#include "dma_ep93xx.h"/***************************************************************************** * * Debugging macros * ****************************************************************************/#undef DEBUG//#define DEBUG 1#ifdef DEBUG#define DPRINTK( fmt, arg... ) printk( fmt, ##arg )#else#define DPRINTK( fmt, arg... )#endif/***************************************************************************** * * static global variables * ****************************************************************************/ep93xx_dma_t dma_chan[MAX_EP93XX_DMA_CHANNELS];/* * lock used to protect the list of dma channels while searching for a free * channel during dma_request. */static spinlock_t dma_list_lock;/***************************************************************************** * * Internal DMA processing functions. * ****************************************************************************//***************************************************************************** * * get_dma_channel_from_handle() * * If Handle is valid, returns the DMA channel # (0 to 9 for channels 1-10) * If Handle is not valid, returns -1. * ****************************************************************************/static intdma_get_channel_from_handle(int handle){ int channel; /* * Get the DMA channel # from the handle. */ channel = ((int)handle & DMA_HANDLE_SPECIFIER_MASK) >> 28; /* * See if this is a valid handle. */ if (dma_chan[channel].last_valid_handle != (int)handle) { DPRINTK("DMA ERROR - invalid handle 0x%x \n", handle); return(-1); } /* * See if this instance is still open */ if (!dma_chan[channel].ref_count ) return(-1); return(channel);}static void dma_m2m_transfer_done(ep93xx_dma_t *dma){ unsigned int uiCONTROL; unsigned int M2M_reg_base = dma->reg_base; unsigned int read_back; DPRINTK("1 "); outl( 0, M2M_reg_base+M2M_OFFSET_INTERRUPT ); if (dma->total_buffers) { /* * The current_buffer has already been tranfered, so add the * byte count to the total_bytes field. */ dma->total_bytes = dma->total_bytes + dma->buffer_queue[dma->current_buffer].size; /* * Mark the current_buffer as used. */ dma->buffer_queue[dma->current_buffer].used = TRUE; /* * Increment the used buffer counter */ dma->used_buffers++; DPRINTK("#%d", dma->current_buffer); /* * Increment the current_buffer */ dma->current_buffer = (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS; /* * check if there's a new buffer to transfer. */ if (dma->new_buffers && dma->xfer_enable) { /* * We have a new buffer to transfer so program in the * buffer values. Since a STALL interrupt was * triggered, we program the buffer descriptor 0 * * Set the SAR_BASE/DAR_BASE/BCR registers with values * from the next buffer in the queue. */ outl( dma->buffer_queue[dma->current_buffer].source, M2M_reg_base + M2M_OFFSET_SAR_BASE0 ); outl( dma->buffer_queue[dma->current_buffer].dest, M2M_reg_base + M2M_OFFSET_DAR_BASE0 ); outl( dma->buffer_queue[dma->current_buffer].size, M2M_reg_base + M2M_OFFSET_BCR0 ); DPRINTK("SAR_BASE0 - 0x%x\n", dma->buffer_queue[dma->current_buffer].source); DPRINTK("DAR_BASE0 - 0x%x\n", dma->buffer_queue[dma->current_buffer].dest); DPRINTK("BCR0 - 0x%x\n", dma->buffer_queue[dma->current_buffer].size); /* * Decrement the new buffer counter */ dma->new_buffers--; /* * If there's a second new buffer, we program the * second buffer descriptor. */ if (dma->new_buffers) { outl( dma->buffer_queue[(dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].source, M2M_reg_base+M2M_OFFSET_SAR_BASE1 ); outl( dma->buffer_queue[(dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].dest, M2M_reg_base+M2M_OFFSET_DAR_BASE1 ); outl( dma->buffer_queue[(dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].size, M2M_reg_base+M2M_OFFSET_BCR1 ); uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL); uiCONTROL |= CONTROL_M2M_NFBINTEN; outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL ); dma->new_buffers--; } } else { DPRINTK("2 \n"); /* * There's a chance we setup both buffer descriptors, * but didn't service the NFB quickly enough, causing * the channel to transfer both buffers, then enter the * stall state. So, we need to be able to process the * second buffer. */ if ((dma->used_buffers + dma->new_buffers) < dma->total_buffers) { DPRINTK("3 "); /* * The current_buffer has already been * tranferred, so add the byte count to the * total_bytes field. */ dma->total_bytes = dma->total_bytes + dma->buffer_queue[dma->current_buffer].size; /* * Mark the current_buffer as used. */ dma->buffer_queue[dma->current_buffer].used = TRUE; /* * Increment the used buffer counter */ dma->used_buffers++; DPRINTK("#%d", dma->current_buffer); /* * Increment the current buffer pointer. */ dma->current_buffer = (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS; } /* * No new buffers to transfer, so disable the channel. */ uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL); uiCONTROL &= ~CONTROL_M2M_ENABLE; outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL ); /* * Indicate that this channel is in the pause by * starvation state by setting the pause bit to true. */ dma->pause = TRUE; } } else { /* * No buffers to transfer, or old buffers to mark as used, * so disable the channel */ uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL); uiCONTROL &= ~CONTROL_M2M_ENABLE; outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL ); /* * Must read the control register back after a write. */ read_back = inl(M2M_reg_base+M2M_OFFSET_CONTROL); /* * Indicate that this channel is in the pause by * starvation state by setting the pause bit to true. */ dma->pause = TRUE; }}static void dma_m2m_next_frame_buffer(ep93xx_dma_t *dma){ int loop; unsigned int uiCONTROL; unsigned int M2M_reg_base = dma->reg_base; DPRINTK("5 "); if (dma->total_buffers) { DPRINTK("6 "); /* * The iCurrentBuffer has already been transfered. so add the * byte count from the current buffer to the total byte count. */ dma->total_bytes = dma->total_bytes + dma->buffer_queue[dma->current_buffer].size; /* * Mark the Current Buffer as used. */ dma->buffer_queue[dma->current_buffer].used = TRUE; /* * Increment the used buffer counter */ dma->used_buffers++; DPRINTK("#%d", dma->current_buffer); if ((dma->buffer_queue[ (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].last) || (dma->new_buffers == 0) || (dma->xfer_enable == FALSE)) { DPRINTK("7 "); /* * This is the last Buffer in this transaction, so * disable the NFB interrupt. We shouldn't get an NFB * int when the FSM moves to the ON state where it * would typically get the NFB int indicating a new * buffer can be programmed. Instead, once in the ON * state, the DMA will just proceed to complete the * transfer of the current buffer, move the FSB * directly to the STALL state where a STALL interrupt * will be generated. */ uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL); uiCONTROL &= ~CONTROL_M2M_NFBINTEN ; outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL ); /* * The current buffer has been transferred, so * increment the current buffer counter to reflect * this. */ dma->current_buffer = (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS; DPRINTK("End of NFB handling. \n"); DPRINTK("CONTROL - 0x%x \n", inl(M2M_reg_base+M2M_OFFSET_CONTROL) ); DPRINTK("STATUS - 0x%x \n", inl(M2M_reg_base+M2M_OFFSET_STATUS) ); DPRINTK("SAR_BASE0 - 0x%x \n", inl(M2M_reg_base+M2M_OFFSET_SAR_BASE0) ); DPRINTK("SAR_CUR0 - 0x%x \n", inl(M2M_reg_base+M2M_OFFSET_SAR_CURRENT0) ); DPRINTK("DAR_BASE0 - 0x%x \n", inl(M2M_reg_base+M2M_OFFSET_DAR_BASE0) ); DPRINTK("DAR_CUR0 - 0x%x \n", inl(M2M_reg_base+M2M_OFFSET_DAR_CURRENT0) ); DPRINTK("Buffer buf_id source size last used \n"); for (loop = 0; loop < 32; loop ++) DPRINTK("%d 0x%x 0x%x 0x%x %d %d \n", loop, dma->buffer_queue[loop].buf_id, dma->buffer_queue[loop].source, dma->buffer_queue[loop].size, dma->buffer_queue[loop].last, dma->buffer_queue[loop].used); DPRINTK("pause 0x%x 0x%x 0x%x %d %d \n", dma->pause_buf.buf_id, dma->pause_buf.source, dma->pause_buf.size, dma->pause_buf.last, dma->pause_buf.used); DPRINTK("Pause - %d \n", dma->pause); DPRINTK("xfer_enable - %d \n", dma->xfer_enable); DPRINTK("total bytes - 0x%x \n", dma->total_bytes); DPRINTK("total buffer - %d \n", dma->total_buffers); DPRINTK("new buffers - %d \n", dma->new_buffers); DPRINTK("current buffer - %d \n", dma->current_buffer); DPRINTK("last buffer - %d \n", dma->last_buffer); DPRINTK("used buffers - %d \n", dma->used_buffers); DPRINTK("callback addr - 0x%p \n", dma->callback); } else if (dma->new_buffers) { DPRINTK("8 "); /* * We have a new buffer, so increment the current * buffer to point to the next buffer, which is already * programmed into the DMA. Next time around, it'll be * pointing to the current buffer. */ dma->current_buffer = (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS; /* * We know we have a new buffer to program as the next * buffer, so check which set of SAR_BASE/DAR_BASE/BCR * registers to program. */ if ( inl(M2M_reg_base+M2M_OFFSET_STATUS) & STATUS_M2M_NB ) { /* * Set the SAR_BASE1/DAR_BASE1/BCR1 registers * with values from the next buffer in the * queue. */ outl( dma->buffer_queue[(dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].source, M2M_reg_base+M2M_OFFSET_SAR_BASE1 ); outl( dma->buffer_queue[(dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].dest, M2M_reg_base+M2M_OFFSET_DAR_BASE1 ); outl( dma->buffer_queue[(dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].size, M2M_reg_base+M2M_OFFSET_BCR1 ); } else { /* * Set the SAR_BASE0/DAR_BASE0/BCR0 registers * with values from the next buffer in the * queue. */ outl( dma->buffer_queue[(dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].source, M2M_reg_base+M2M_OFFSET_SAR_BASE0 ); outl( dma->buffer_queue[(dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].dest, M2M_reg_base+M2M_OFFSET_DAR_BASE0 ); outl( dma->buffer_queue[(dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].size, M2M_reg_base+M2M_OFFSET_BCR0 ); } /* * Decrement the new buffers counter */ dma->new_buffers--; } } else { /* * Total number of buffers is 0 - really we should never get * here, but just in case. */ DPRINTK("9 \n"); /* * No new buffers to transfer, so Disable the channel */ uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL); uiCONTROL &= ~CONTROL_M2M_ENABLE; outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL ); /* * Indicate that the channel is paused by starvation. */ dma->pause = 1; }}/***************************************************************************** * * dma_m2m_irq_handler * ****************************************************************************/static voiddma_m2m_irq_handler(int irq, void *dev_id, struct pt_regs *regs){ ep93xx_dma_t *dma = (ep93xx_dma_t *)dev_id; unsigned int M2M_reg_base = dma->reg_base; ep93xx_dma_dev_t dma_int = UNDEF_INT; int status;// printk("+m2m irq=%d\n", irq); /* * Determine what kind of dma interrupt this is. */ status = inl(M2M_reg_base + M2M_OFFSET_INTERRUPT); if ( status & INTERRUPT_M2M_DONEINT ) dma_int = DONE; // we're done with a requested dma else if ( status & INTERRUPT_M2M_NFBINT ) dma_int = NFB; // we're done with one dma buffer DPRINTK("IRQ: b=%#x st=%#x\n", (int)dma->current_buffer, dma_int); switch (dma_int) { /* * Next Frame Buffer Interrupt. If there's a new buffer program it * Check if this is the last buffer in the transfer, * and if it is, disable the NFB int to prevent being * interrupted for another buffer when we know there won't be * another. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -