|  | @@ -196,3 +196,179 @@ s3c2410_dma_waitforload(struct s3c2410_dma_chan *chan, int line)
 | 
	
		
			
				|  |  |  	return 0;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +/* s3c2410_dma_loadbuffer
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * load a buffer, and update the channel state
 | 
	
		
			
				|  |  | +*/
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static inline int
 | 
	
		
			
				|  |  | +s3c2410_dma_loadbuffer(struct s3c2410_dma_chan *chan,
 | 
	
		
			
				|  |  | +		       struct s3c2410_dma_buf *buf)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +	unsigned long reload;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if (buf == NULL) {
 | 
	
		
			
				|  |  | +		dmawarn("buffer is NULL\n");
 | 
	
		
			
				|  |  | +		return -EINVAL;
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	pr_debug("s3c2410_chan_loadbuffer: loading buff %p (0x%08lx,0x%06x)\n",
 | 
	
		
			
				|  |  | +		 buf, (unsigned long)buf->data, buf->size);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	/* check the state of the channel before we do anything */
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if (chan->load_state == S3C2410_DMALOAD_1LOADED) {
 | 
	
		
			
				|  |  | +		dmawarn("load_state is S3C2410_DMALOAD_1LOADED\n");
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if (chan->load_state == S3C2410_DMALOAD_1LOADED_1RUNNING) {
 | 
	
		
			
				|  |  | +		dmawarn("state is S3C2410_DMALOAD_1LOADED_1RUNNING\n");
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	/* it would seem sensible if we are the last buffer to not bother
 | 
	
		
			
				|  |  | +	 * with the auto-reload bit, so that the DMA engine will not try
 | 
	
		
			
				|  |  | +	 * and load another transfer after this one has finished...
 | 
	
		
			
				|  |  | +	 */
 | 
	
		
			
				|  |  | +	if (chan->load_state == S3C2410_DMALOAD_NONE) {
 | 
	
		
			
				|  |  | +		pr_debug("load_state is none, checking for noreload (next=%p)\n",
 | 
	
		
			
				|  |  | +			 buf->next);
 | 
	
		
			
				|  |  | +		reload = (buf->next == NULL) ? S3C2410_DCON_NORELOAD : 0;
 | 
	
		
			
				|  |  | +	} else {
 | 
	
		
			
				|  |  | +		//pr_debug("load_state is %d => autoreload\n", chan->load_state);
 | 
	
		
			
				|  |  | +		reload = S3C2410_DCON_AUTORELOAD;
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if ((buf->data & 0xf0000000) != 0x30000000) {
 | 
	
		
			
				|  |  | +		dmawarn("dmaload: buffer is %p\n", (void *)buf->data);
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	writel(buf->data, chan->addr_reg);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	dma_wrreg(chan, S3C2410_DMA_DCON,
 | 
	
		
			
				|  |  | +		  chan->dcon | reload | (buf->size/chan->xfer_unit));
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	chan->next = buf->next;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	/* update the state of the channel */
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	switch (chan->load_state) {
 | 
	
		
			
				|  |  | +	case S3C2410_DMALOAD_NONE:
 | 
	
		
			
				|  |  | +		chan->load_state = S3C2410_DMALOAD_1LOADED;
 | 
	
		
			
				|  |  | +		break;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	case S3C2410_DMALOAD_1RUNNING:
 | 
	
		
			
				|  |  | +		chan->load_state = S3C2410_DMALOAD_1LOADED_1RUNNING;
 | 
	
		
			
				|  |  | +		break;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	default:
 | 
	
		
			
				|  |  | +		dmawarn("dmaload: unknown state %d in loadbuffer\n",
 | 
	
		
			
				|  |  | +			chan->load_state);
 | 
	
		
			
				|  |  | +		break;
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	return 0;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/* s3c2410_dma_call_op
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * small routine to call the op routine with the given op if it has been
 | 
	
		
			
				|  |  | + * registered
 | 
	
		
			
				|  |  | +*/
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void
 | 
	
		
			
				|  |  | +s3c2410_dma_call_op(struct s3c2410_dma_chan *chan, enum s3c2410_chan_op op)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +	if (chan->op_fn != NULL) {
 | 
	
		
			
				|  |  | +		(chan->op_fn)(chan, op);
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/* s3c2410_dma_buffdone
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * small wrapper to check if callback routine needs to be called, and
 | 
	
		
			
				|  |  | + * if so, call it
 | 
	
		
			
				|  |  | +*/
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static inline void
 | 
	
		
			
				|  |  | +s3c2410_dma_buffdone(struct s3c2410_dma_chan *chan, struct s3c2410_dma_buf *buf,
 | 
	
		
			
				|  |  | +		     enum s3c2410_dma_buffresult result)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +#if 0
 | 
	
		
			
				|  |  | +	pr_debug("callback_fn=%p, buf=%p, id=%p, size=%d, result=%d\n",
 | 
	
		
			
				|  |  | +		 chan->callback_fn, buf, buf->id, buf->size, result);
 | 
	
		
			
				|  |  | +#endif
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if (chan->callback_fn != NULL) {
 | 
	
		
			
				|  |  | +		(chan->callback_fn)(chan, buf->id, buf->size, result);
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/* s3c2410_dma_start
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * start a dma channel going
 | 
	
		
			
				|  |  | +*/
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static int s3c2410_dma_start(struct s3c2410_dma_chan *chan)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +	unsigned long tmp;
 | 
	
		
			
				|  |  | +	unsigned long flags;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	pr_debug("s3c2410_start_dma: channel=%d\n", chan->number);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	local_irq_save(flags);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if (chan->state == S3C2410_DMA_RUNNING) {
 | 
	
		
			
				|  |  | +		pr_debug("s3c2410_start_dma: already running (%d)\n", chan->state);
 | 
	
		
			
				|  |  | +		local_irq_restore(flags);
 | 
	
		
			
				|  |  | +		return 0;
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	chan->state = S3C2410_DMA_RUNNING;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	/* check whether there is anything to load, and if not, see
 | 
	
		
			
				|  |  | +	 * if we can find anything to load
 | 
	
		
			
				|  |  | +	 */
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if (chan->load_state == S3C2410_DMALOAD_NONE) {
 | 
	
		
			
				|  |  | +		if (chan->next == NULL) {
 | 
	
		
			
				|  |  | +			printk(KERN_ERR "dma%d: channel has nothing loaded\n",
 | 
	
		
			
				|  |  | +			       chan->number);
 | 
	
		
			
				|  |  | +			chan->state = S3C2410_DMA_IDLE;
 | 
	
		
			
				|  |  | +			local_irq_restore(flags);
 | 
	
		
			
				|  |  | +			return -EINVAL;
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		s3c2410_dma_loadbuffer(chan, chan->next);
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	dbg_showchan(chan);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	/* enable the channel */
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if (!chan->irq_enabled) {
 | 
	
		
			
				|  |  | +		enable_irq(chan->irq);
 | 
	
		
			
				|  |  | +		chan->irq_enabled = 1;
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	/* start the channel going */
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	tmp = dma_rdreg(chan, S3C2410_DMA_DMASKTRIG);
 | 
	
		
			
				|  |  | +	tmp &= ~S3C2410_DMASKTRIG_STOP;
 | 
	
		
			
				|  |  | +	tmp |= S3C2410_DMASKTRIG_ON;
 | 
	
		
			
				|  |  | +	dma_wrreg(chan, S3C2410_DMA_DMASKTRIG, tmp);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	pr_debug("dma%d: %08lx to DMASKTRIG\n", chan->number, tmp);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#if 0
 | 
	
		
			
				|  |  | +	/* the dma buffer loads should take care of clearing the AUTO
 | 
	
		
			
				|  |  | +	 * reloading feature */
 | 
	
		
			
				|  |  | +	tmp = dma_rdreg(chan, S3C2410_DMA_DCON);
 | 
	
		
			
				|  |  | +	tmp &= ~S3C2410_DCON_NORELOAD;
 | 
	
		
			
				|  |  | +	dma_wrreg(chan, S3C2410_DMA_DCON, tmp);
 | 
	
		
			
				|  |  | +#endif
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	s3c2410_dma_call_op(chan, S3C2410_DMAOP_START);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	dbg_showchan(chan);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	/* if we've only loaded one buffer onto the channel, then chec
 |