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; }
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 */ }
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; }
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; }
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; }
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; }
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; }
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; }
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; }