Exemple #1
0
int s3c2410_dma_free(unsigned int channel, struct s3c2410_dma_client *client)
{
	struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel);
	unsigned long flags;

	if (chan == NULL)
		return -EINVAL;

	local_irq_save(flags);

	if (chan->client != client) {
		printk(KERN_WARNING "dma%d: possible free from different client (channel %p, passed %p)\n",
		       channel, chan->client, client);
	}

	/* sort out stopping and freeing the channel */


	chan->client = NULL;
	chan->in_use = 0;

	if (!(channel & DMACH_LOW_LEVEL))
		s3c_dma_chan_map[channel] = NULL;

	local_irq_restore(flags);

	return 0;
}
Exemple #2
0
int s3c2410_dma_ctrl(unsigned int channel, enum s3c2410_chan_op op)
{
	struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel);

	WARN_ON(!chan);
	if (!chan)
		return -EINVAL;

	switch (op) {
	case S3C2410_DMAOP_START:
		return s3c64xx_dma_start(chan);

	case S3C2410_DMAOP_STOP:
		return s3c64xx_dma_stop(chan);

	case S3C2410_DMAOP_FLUSH:
		return s3c64xx_dma_flush(chan);

	/* belive PAUSE/RESUME are no-ops */
	case S3C2410_DMAOP_PAUSE:
	case S3C2410_DMAOP_RESUME:
	case S3C2410_DMAOP_STARTED:
	case S3C2410_DMAOP_TIMEOUT:
		return 0;
	}

	return -ENOENT;
}
int
s3c2410_dma_ctrl(enum dma_ch channel, enum s3c2410_chan_op op)
{
	struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel);

	if (chan == NULL)
		return -EINVAL;

	switch (op) {
	case S3C2410_DMAOP_START:
		return s3c2410_dma_start(chan);

	case S3C2410_DMAOP_STOP:
		return s3c2410_dma_dostop(chan);

	case S3C2410_DMAOP_PAUSE:
	case S3C2410_DMAOP_RESUME:
		return -ENOENT;

	case S3C2410_DMAOP_FLUSH:
		return s3c2410_dma_flush(chan);

	case S3C2410_DMAOP_STARTED:
		return s3c2410_dma_started(chan);

	case S3C2410_DMAOP_TIMEOUT:
		return 0;

	}

	return -ENOENT;      /* unknown, don't bother */
}
Exemple #4
0
int s3c2410_dma_setflags(enum dma_ch channel, unsigned int flags)
{
	struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel);

	if (chan == NULL)
		return -EINVAL;

	chan->flags = flags;
	return 0;
}
Exemple #5
0
int s3c2410_dma_set_buffdone_fn(enum dma_ch channel, s3c2410_dma_cbfn_t rtn)
{
	struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel);

	if (chan == NULL)
		return -EINVAL;

	pr_debug("%s: chan=%p, callback rtn=%p\n", __func__, chan, rtn);

	chan->callback_fn = rtn;

	return 0;
}
Exemple #6
0
int s3c2410_dma_set_opfn(enum dma_ch channel, s3c2410_dma_opfn_t rtn)
{
	struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel);

	if (chan == NULL)
		return -EINVAL;

	pr_debug("%s: chan=%p, op rtn=%p\n", __func__, chan, rtn);

	chan->op_fn = rtn;

	return 0;
}
int s3c2410_dma_getposition(enum dma_ch channel, dma_addr_t *src, dma_addr_t *dst)
{
	struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel);

	if (chan == NULL)
		return -EINVAL;

	if (src != NULL)
 		*src = dma_rdreg(chan, S3C2410_DMA_DCSRC);

 	if (dst != NULL)
 		*dst = dma_rdreg(chan, S3C2410_DMA_DCDST);

 	return 0;
}
Exemple #8
0
int s3c2410_dma_getposition(unsigned int channel,
			    dma_addr_t *src, dma_addr_t *dst)
{
	struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel);

	WARN_ON(!chan);
	if (!chan)
		return -EINVAL;

	if (src != NULL)
		*src = readl(chan->regs + PL080_CH_SRC_ADDR);

	if (dst != NULL)
		*dst = readl(chan->regs + PL080_CH_DST_ADDR);

	return 0;
}
Exemple #9
0
int s3c2410_dma_devconfig(int channel,
			  enum s3c2410_dmasrc source,
			  unsigned long devaddr)
{
	struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel);
	u32 peripheral;
	u32 config = 0;

	pr_debug("%s: channel %d, source %d, dev %08lx, chan %p\n",
		 __func__, channel, source, devaddr, chan);

	WARN_ON(!chan);
	if (!chan)
		return -EINVAL;

	peripheral = (chan->peripheral & 0xf);
	chan->source = source;
	chan->dev_addr = devaddr;

	pr_debug("%s: peripheral %d\n", __func__, peripheral);

	switch (source) {
	case S3C2410_DMASRC_HW:
		config = 2 << PL080_CONFIG_FLOW_CONTROL_SHIFT;
		config |= peripheral << PL080_CONFIG_SRC_SEL_SHIFT;
		break;
	case S3C2410_DMASRC_MEM:
		config = 1 << PL080_CONFIG_FLOW_CONTROL_SHIFT;
		config |= peripheral << PL080_CONFIG_DST_SEL_SHIFT;
		break;
	default:
		printk(KERN_ERR "%s: bad source\n", __func__);
		return -EINVAL;
	}

	/* allow TC and ERR interrupts */
	config |= PL080_CONFIG_TC_IRQ_MASK;
	config |= PL080_CONFIG_ERR_IRQ_MASK;

	pr_debug("%s: config %08x\n", __func__, config);

	writel(config, chan->regs + PL080S_CH_CONFIG);

	return 0;
}
int s3c2410_dma_free(enum dma_ch channel, struct s3c2410_dma_client *client)
{
	struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel);
	unsigned long flags;

	if (chan == NULL)
		return -EINVAL;

	local_irq_save(flags);

	if (chan->client != client) {
		printk(KERN_WARNING "dma%d: possible free from different client (channel %p, passed %p)\n",
		       channel, chan->client, client);
	}

	/* sort out stopping and freeing the channel */

	if (chan->state != S3C2410_DMA_IDLE) {
		pr_debug("%s: need to stop dma channel %p\n",
		       __func__, chan);

		/* possibly flush the channel */
		s3c2410_dma_ctrl(channel, S3C2410_DMAOP_STOP);
	}

	chan->client = NULL;
	chan->in_use = 0;

	if (chan->irq_claimed)
		free_irq(chan->irq, (void *)chan);

	chan->irq_claimed = 0;

	if (!(channel & DMACH_LOW_LEVEL))
		s3c_dma_chan_map[channel] = NULL;

	local_irq_restore(flags);

	return 0;
}
Exemple #11
0
int s3c2410_dma_config(unsigned int channel, int xferunit)
{
	struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel);

	if (chan == NULL)
		return -EINVAL;

	switch (xferunit) {
	case 1:
		chan->hw_width = 0;
		break;
	case 2:
		chan->hw_width = 1;
		break;
	case 4:
		chan->hw_width = 2;
		break;
	default:
		printk(KERN_ERR "%s: illegal width %d\n", __func__, xferunit);
		return -EINVAL;
	}

	return 0;
}
Exemple #12
0
int s3c2410_dma_enqueue(unsigned int channel, void *id,
			dma_addr_t data, int size)
{
	struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel);
	struct s3c64xx_dma_buff *next;
	struct s3c64xx_dma_buff *buff;
	struct pl080s_lli *lli;
	int ret;

	WARN_ON(!chan);
	if (!chan)
		return -EINVAL;

	buff = kzalloc(sizeof(struct s3c64xx_dma_buff), GFP_KERNEL);
	if (!buff) {
		printk(KERN_ERR "%s: no memory for buffer\n", __func__);
		return -ENOMEM;
	}

	lli = dma_pool_alloc(dma_pool, GFP_KERNEL, &buff->lli_dma);
	if (!lli) {
		printk(KERN_ERR "%s: no memory for lli\n", __func__);
		ret = -ENOMEM;
		goto err_buff;
	}

	pr_debug("%s: buff %p, dp %08x lli (%p, %08x) %d\n",
		 __func__, buff, data, lli, (u32)buff->lli_dma, size);

	buff->lli = lli;
	buff->pw = id;

	s3c64xx_dma_fill_lli(chan, lli, data, size);

	if ((next = chan->next) != NULL) {
		struct s3c64xx_dma_buff *end = chan->end;
		struct pl080s_lli *endlli = end->lli;

		pr_debug("enquing onto channel\n");

		end->next = buff;
		endlli->next_lli = buff->lli_dma;

		if (chan->flags & S3C2410_DMAF_CIRCULAR) {
			struct s3c64xx_dma_buff *curr = chan->curr;
			lli->next_lli = curr->lli_dma;
		}

		if (next == chan->curr) {
			writel(buff->lli_dma, chan->regs + PL080_CH_LLI);
			chan->next = buff;
		}

		show_lli(endlli);
		chan->end = buff;
	} else {
		pr_debug("enquing onto empty channel\n");

		chan->curr = buff;
		chan->next = buff;
		chan->end = buff;

		s3c64xx_lli_to_regs(chan, lli);
	}

	show_lli(lli);

	dbg_showchan(chan);
	dbg_showbuffs(chan);
	return 0;

err_buff:
	kfree(buff);
	return ret;
}
int s3c2410_dma_enqueue(unsigned int channel, void *id,
			dma_addr_t data, int size)
{
	struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel);
	struct s3c2410_dma_buf *buf;
	unsigned long flags;

	if (chan == NULL)
		return -EINVAL;

	pr_debug("%s: id=%p, data=%08x, size=%d\n",
		 __func__, id, (unsigned int)data, size);

	buf = kmem_cache_alloc(dma_kmem, GFP_ATOMIC);
	if (buf == NULL) {
		pr_debug("%s: out of memory (%ld alloc)\n",
			 __func__, (long)sizeof(*buf));
		return -ENOMEM;
	}

	//pr_debug("%s: new buffer %p\n", __func__, buf);
	//dbg_showchan(chan);

	buf->next  = NULL;
	buf->data  = buf->ptr = data;
	buf->size  = size;
	buf->id    = id;
	buf->magic = BUF_MAGIC;

	local_irq_save(flags);

	if (chan->curr == NULL) {
		/* we've got nothing loaded... */
		pr_debug("%s: buffer %p queued onto empty channel\n",
			 __func__, buf);

		chan->curr = buf;
		chan->end  = buf;
		chan->next = NULL;
	} else {
		pr_debug("dma%d: %s: buffer %p queued onto non-empty channel\n",
			 chan->number, __func__, buf);

		if (chan->end == NULL)
			pr_debug("dma%d: %s: %p not empty, and chan->end==NULL?\n",
				 chan->number, __func__, chan);

		chan->end->next = buf;
		chan->end = buf;
	}

	/* if necessary, update the next buffer field */
	if (chan->next == NULL)
		chan->next = buf;

	/* check to see if we can load a buffer */
	if (chan->state == S3C2410_DMA_RUNNING) {
		if (chan->load_state == S3C2410_DMALOAD_1LOADED && 1) {
			if (s3c2410_dma_waitforload(chan, __LINE__) == 0) {
				printk(KERN_ERR "dma%d: loadbuffer:"
				       "timeout loading buffer\n",
				       chan->number);
				dbg_showchan(chan);
				local_irq_restore(flags);
				return -EINVAL;
			}
		}

		while (s3c2410_dma_canload(chan) && chan->next != NULL) {
			s3c2410_dma_loadbuffer(chan, chan->next);
		}
	} else if (chan->state == S3C2410_DMA_IDLE) {
		if (chan->flags & S3C2410_DMAF_AUTOSTART) {
			s3c2410_dma_ctrl(chan->number | DMACH_LOW_LEVEL,
					 S3C2410_DMAOP_START);
		}
	}

	local_irq_restore(flags);
	return 0;
}
int s3c2410_dma_devconfig(enum dma_ch channel,
			  enum s3c2410_dmasrc source,
			  unsigned long devaddr)
{
	struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel);
	unsigned int hwcfg;

	if (chan == NULL)
		return -EINVAL;

	pr_debug("%s: source=%d, devaddr=%08lx\n",
		 __func__, (int)source, devaddr);

	chan->source = source;
	chan->dev_addr = devaddr;

	switch (chan->req_ch) {
	case DMACH_XD0:
	case DMACH_XD1:
		hwcfg = 0; /* AHB */
		break;

	default:
		hwcfg = S3C2410_DISRCC_APB;
	}

	/* always assume our peripheral desintation is a fixed
	 * address in memory. */
	 hwcfg |= S3C2410_DISRCC_INC;

	switch (source) {
	case S3C2410_DMASRC_HW:
		/* source is hardware */
		pr_debug("%s: hw source, devaddr=%08lx, hwcfg=%d\n",
			 __func__, devaddr, hwcfg);
		dma_wrreg(chan, S3C2410_DMA_DISRCC, hwcfg & 3);
		dma_wrreg(chan, S3C2410_DMA_DISRC,  devaddr);
		dma_wrreg(chan, S3C2410_DMA_DIDSTC, (0<<1) | (0<<0));

		chan->addr_reg = dma_regaddr(chan, S3C2410_DMA_DIDST);
		break;

	case S3C2410_DMASRC_MEM:
		/* source is memory */
		pr_debug("%s: mem source, devaddr=%08lx, hwcfg=%d\n",
			 __func__, devaddr, hwcfg);
		dma_wrreg(chan, S3C2410_DMA_DISRCC, (0<<1) | (0<<0));
		dma_wrreg(chan, S3C2410_DMA_DIDST,  devaddr);
		dma_wrreg(chan, S3C2410_DMA_DIDSTC, hwcfg & 3);

		chan->addr_reg = dma_regaddr(chan, S3C2410_DMA_DISRC);
		break;

	default:
		printk(KERN_ERR "dma%d: invalid source type (%d)\n",
		       channel, source);

		return -EINVAL;
	}

	if (dma_sel.direction != NULL)
		(dma_sel.direction)(chan, chan->map, source);

	return 0;
}
int s3c2410_dma_config(enum dma_ch channel,
		       int xferunit)
{
	struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel);
	unsigned int dcon;

	pr_debug("%s: chan=%d, xfer_unit=%d\n", __func__, channel, xferunit);

	if (chan == NULL)
		return -EINVAL;

	dcon = chan->dcon & dma_sel.dcon_mask;
	pr_debug("%s: dcon is %08x\n", __func__, dcon);

	switch (chan->req_ch) {
	case DMACH_I2S_IN:
	case DMACH_I2S_OUT:
	case DMACH_PCM_IN:
	case DMACH_PCM_OUT:
	case DMACH_MIC_IN:
	default:
		dcon |= S3C2410_DCON_HANDSHAKE;
		dcon |= S3C2410_DCON_SYNC_PCLK;
		break;

	case DMACH_SDI:
		/* note, ensure if need HANDSHAKE or not */
		dcon |= S3C2410_DCON_SYNC_PCLK;
		break;

	case DMACH_XD0:
	case DMACH_XD1:
		dcon |= S3C2410_DCON_HANDSHAKE;
		dcon |= S3C2410_DCON_SYNC_HCLK;
		break;
	}

	switch (xferunit) {
	case 1:
		dcon |= S3C2410_DCON_BYTE;
		break;

	case 2:
		dcon |= S3C2410_DCON_HALFWORD;
		break;

	case 4:
		dcon |= S3C2410_DCON_WORD;
		break;

	default:
		pr_debug("%s: bad transfer size %d\n", __func__, xferunit);
		return -EINVAL;
	}

	dcon |= S3C2410_DCON_HWTRIG;
	dcon |= S3C2410_DCON_INTREQ;

	pr_debug("%s: dcon now %08x\n", __func__, dcon);

	chan->dcon = dcon;
	chan->xfer_unit = xferunit;

	return 0;
}