/*
* OMAP DMAengine support
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/delay.h>
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/omap-dma.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/of_dma.h>
#include <linux/of_device.h>
#include "virt-dma.h"
struct omap_dmadev {
struct dma_device ddev;
spinlock_t lock;
struct tasklet_struct task;
struct list_head pending;
struct omap_system_dma_plat_info *plat;
};
struct omap_chan {
struct virt_dma_chan vc;
struct list_head node;
struct omap_system_dma_plat_info *plat;
struct dma_slave_config cfg;
unsigned dma_sig;
bool cyclic;
bool paused;
int dma_ch;
struct omap_desc *desc;
unsigned sgidx;
};
struct omap_sg {
dma_addr_t addr;
uint32_t en; /* number of elements (24-bit) */
uint32_t fn; /* number of frames (16-bit) */
};
struct omap_desc {
struct virt_dma_desc vd;
enum dma_transfer_direction dir;
dma_addr_t dev_addr;
int16_t fi; /* for OMAP_DMA_SYNC_PACKET */
uint8_t es; /* OMAP_DMA_DATA_TYPE_xxx */
uint8_t sync_mode; /* OMAP_DMA_SYNC_xxx */
uint8_t sync_type; /* OMAP_DMA_xxx_SYNC* */
uint16_t cicr; /* CICR value */
uint32_t csdp; /* CSDP value */
unsigned sglen;
struct omap_sg sg[0];
};
static const unsigned es_bytes[] = {
[OMAP_DMA_DATA_TYPE_S8] = 1,
[OMAP_DMA_DATA_TYPE_S16] = 2,
[OMAP_DMA_DATA_TYPE_S32] = 4,
};
static struct of_dma_filter_info omap_dma_info = {
.filter_fn = omap_dma_filter_fn,
};
static inline struct omap_dmadev *to_omap_dma_dev(struct dma_device *d)
{
return container_of(d, struct omap_dmadev, ddev);
}
static inline struct omap_chan *to_omap_dma_chan(struct dma_chan *c)
{
return container_of(c, struct omap_chan, vc.chan);
}
static inline struct omap_desc *to_omap_dma_desc(struct dma_async_tx_descriptor *t)
{
return container_of(t, struct omap_desc, vd.tx);
}
static void omap_dma_desc_free(struct virt_dma_desc *vd)
{
kfree(container_of(vd, struct omap_desc, vd));
}
static void omap_dma_start(struct omap_chan *c, struct omap_desc *d)
{
struct omap_dmadev *od = to_omap_dma_dev(c->vc.chan.device);
uint32_t val;
if (__dma_omap15xx(od->plat->dma_attr))
c->plat->dma_write(0, CPC, c->dma_ch);
else
c->plat->dma_write(0, CDAC, c->dma_ch);
if (!__dma_omap15xx(od->plat->dma_attr) && c->cyclic) {
val = c->plat->dma_read(CLNK_CTRL, c->dma_ch);
if (dma_omap1())
val &= ~(1 << 14);
val |= c->dma_ch | 1 << 15;
c->plat->dma_write(val, CLNK_CTRL, c->dma_ch);
} else if (od->plat->errata & DMA_ERRATA_PARALLEL_CHANNELS)
c->plat->dma_write(c->dma_ch, CLNK_CTRL, c->dma_ch);
/* Clear CSR */
if (dma_omap1())
c->plat->dma_read(CSR, c->dma_ch);
else
c->plat->dma_write(~0, CSR, c->dma_ch);
/* Enable interrupts */
c->plat->dma_write(d->cicr, CICR, c->dma_ch);
val = c->plat->dma_read(CCR, c->dma_ch);
if (od->plat->errata & DMA_ERRATA_IFRAME_BUFFERING)
val |= OMAP_DMA_CCR_BUFFERING_DISABLE;
val |= OMAP_DMA_CCR_EN;
mb();
c->plat->dma_write(val, CCR, c->dma_ch);
}
static void omap_dma_stop(struct omap_chan *c)
{
struct omap_dmadev *od = to_omap_dma_dev(c->