18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * OMAP DMAengine support
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci#include <linux/cpu_pm.h>
68c2ecf20Sopenharmony_ci#include <linux/delay.h>
78c2ecf20Sopenharmony_ci#include <linux/dmaengine.h>
88c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
98c2ecf20Sopenharmony_ci#include <linux/dmapool.h>
108c2ecf20Sopenharmony_ci#include <linux/err.h>
118c2ecf20Sopenharmony_ci#include <linux/init.h>
128c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
138c2ecf20Sopenharmony_ci#include <linux/list.h>
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci#include <linux/omap-dma.h>
168c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
178c2ecf20Sopenharmony_ci#include <linux/slab.h>
188c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
198c2ecf20Sopenharmony_ci#include <linux/of_dma.h>
208c2ecf20Sopenharmony_ci#include <linux/of_device.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#include "../virt-dma.h"
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#define OMAP_SDMA_REQUESTS	127
258c2ecf20Sopenharmony_ci#define OMAP_SDMA_CHANNELS	32
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistruct omap_dma_config {
288c2ecf20Sopenharmony_ci	int lch_end;
298c2ecf20Sopenharmony_ci	unsigned int rw_priority:1;
308c2ecf20Sopenharmony_ci	unsigned int needs_busy_check:1;
318c2ecf20Sopenharmony_ci	unsigned int may_lose_context:1;
328c2ecf20Sopenharmony_ci	unsigned int needs_lch_clear:1;
338c2ecf20Sopenharmony_ci};
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistruct omap_dma_context {
368c2ecf20Sopenharmony_ci	u32 irqenable_l0;
378c2ecf20Sopenharmony_ci	u32 irqenable_l1;
388c2ecf20Sopenharmony_ci	u32 ocp_sysconfig;
398c2ecf20Sopenharmony_ci	u32 gcr;
408c2ecf20Sopenharmony_ci};
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cistruct omap_dmadev {
438c2ecf20Sopenharmony_ci	struct dma_device ddev;
448c2ecf20Sopenharmony_ci	spinlock_t lock;
458c2ecf20Sopenharmony_ci	void __iomem *base;
468c2ecf20Sopenharmony_ci	const struct omap_dma_reg *reg_map;
478c2ecf20Sopenharmony_ci	struct omap_system_dma_plat_info *plat;
488c2ecf20Sopenharmony_ci	const struct omap_dma_config *cfg;
498c2ecf20Sopenharmony_ci	struct notifier_block nb;
508c2ecf20Sopenharmony_ci	struct omap_dma_context context;
518c2ecf20Sopenharmony_ci	int lch_count;
528c2ecf20Sopenharmony_ci	DECLARE_BITMAP(lch_bitmap, OMAP_SDMA_CHANNELS);
538c2ecf20Sopenharmony_ci	struct mutex lch_lock;		/* for assigning logical channels */
548c2ecf20Sopenharmony_ci	bool legacy;
558c2ecf20Sopenharmony_ci	bool ll123_supported;
568c2ecf20Sopenharmony_ci	struct dma_pool *desc_pool;
578c2ecf20Sopenharmony_ci	unsigned dma_requests;
588c2ecf20Sopenharmony_ci	spinlock_t irq_lock;
598c2ecf20Sopenharmony_ci	uint32_t irq_enable_mask;
608c2ecf20Sopenharmony_ci	struct omap_chan **lch_map;
618c2ecf20Sopenharmony_ci};
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_cistruct omap_chan {
648c2ecf20Sopenharmony_ci	struct virt_dma_chan vc;
658c2ecf20Sopenharmony_ci	void __iomem *channel_base;
668c2ecf20Sopenharmony_ci	const struct omap_dma_reg *reg_map;
678c2ecf20Sopenharmony_ci	uint32_t ccr;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	struct dma_slave_config	cfg;
708c2ecf20Sopenharmony_ci	unsigned dma_sig;
718c2ecf20Sopenharmony_ci	bool cyclic;
728c2ecf20Sopenharmony_ci	bool paused;
738c2ecf20Sopenharmony_ci	bool running;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	int dma_ch;
768c2ecf20Sopenharmony_ci	struct omap_desc *desc;
778c2ecf20Sopenharmony_ci	unsigned sgidx;
788c2ecf20Sopenharmony_ci};
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci#define DESC_NXT_SV_REFRESH	(0x1 << 24)
818c2ecf20Sopenharmony_ci#define DESC_NXT_SV_REUSE	(0x2 << 24)
828c2ecf20Sopenharmony_ci#define DESC_NXT_DV_REFRESH	(0x1 << 26)
838c2ecf20Sopenharmony_ci#define DESC_NXT_DV_REUSE	(0x2 << 26)
848c2ecf20Sopenharmony_ci#define DESC_NTYPE_TYPE2	(0x2 << 29)
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci/* Type 2 descriptor with Source or Destination address update */
878c2ecf20Sopenharmony_cistruct omap_type2_desc {
888c2ecf20Sopenharmony_ci	uint32_t next_desc;
898c2ecf20Sopenharmony_ci	uint32_t en;
908c2ecf20Sopenharmony_ci	uint32_t addr; /* src or dst */
918c2ecf20Sopenharmony_ci	uint16_t fn;
928c2ecf20Sopenharmony_ci	uint16_t cicr;
938c2ecf20Sopenharmony_ci	int16_t cdei;
948c2ecf20Sopenharmony_ci	int16_t csei;
958c2ecf20Sopenharmony_ci	int32_t cdfi;
968c2ecf20Sopenharmony_ci	int32_t csfi;
978c2ecf20Sopenharmony_ci} __packed;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_cistruct omap_sg {
1008c2ecf20Sopenharmony_ci	dma_addr_t addr;
1018c2ecf20Sopenharmony_ci	uint32_t en;		/* number of elements (24-bit) */
1028c2ecf20Sopenharmony_ci	uint32_t fn;		/* number of frames (16-bit) */
1038c2ecf20Sopenharmony_ci	int32_t fi;		/* for double indexing */
1048c2ecf20Sopenharmony_ci	int16_t ei;		/* for double indexing */
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	/* Linked list */
1078c2ecf20Sopenharmony_ci	struct omap_type2_desc *t2_desc;
1088c2ecf20Sopenharmony_ci	dma_addr_t t2_desc_paddr;
1098c2ecf20Sopenharmony_ci};
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_cistruct omap_desc {
1128c2ecf20Sopenharmony_ci	struct virt_dma_desc vd;
1138c2ecf20Sopenharmony_ci	bool using_ll;
1148c2ecf20Sopenharmony_ci	enum dma_transfer_direction dir;
1158c2ecf20Sopenharmony_ci	dma_addr_t dev_addr;
1168c2ecf20Sopenharmony_ci	bool polled;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	int32_t fi;		/* for OMAP_DMA_SYNC_PACKET / double indexing */
1198c2ecf20Sopenharmony_ci	int16_t ei;		/* for double indexing */
1208c2ecf20Sopenharmony_ci	uint8_t es;		/* CSDP_DATA_TYPE_xxx */
1218c2ecf20Sopenharmony_ci	uint32_t ccr;		/* CCR value */
1228c2ecf20Sopenharmony_ci	uint16_t clnk_ctrl;	/* CLNK_CTRL value */
1238c2ecf20Sopenharmony_ci	uint16_t cicr;		/* CICR value */
1248c2ecf20Sopenharmony_ci	uint32_t csdp;		/* CSDP value */
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	unsigned sglen;
1278c2ecf20Sopenharmony_ci	struct omap_sg sg[];
1288c2ecf20Sopenharmony_ci};
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_cienum {
1318c2ecf20Sopenharmony_ci	CAPS_0_SUPPORT_LL123	= BIT(20),	/* Linked List type1/2/3 */
1328c2ecf20Sopenharmony_ci	CAPS_0_SUPPORT_LL4	= BIT(21),	/* Linked List type4 */
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	CCR_FS			= BIT(5),
1358c2ecf20Sopenharmony_ci	CCR_READ_PRIORITY	= BIT(6),
1368c2ecf20Sopenharmony_ci	CCR_ENABLE		= BIT(7),
1378c2ecf20Sopenharmony_ci	CCR_AUTO_INIT		= BIT(8),	/* OMAP1 only */
1388c2ecf20Sopenharmony_ci	CCR_REPEAT		= BIT(9),	/* OMAP1 only */
1398c2ecf20Sopenharmony_ci	CCR_OMAP31_DISABLE	= BIT(10),	/* OMAP1 only */
1408c2ecf20Sopenharmony_ci	CCR_SUSPEND_SENSITIVE	= BIT(8),	/* OMAP2+ only */
1418c2ecf20Sopenharmony_ci	CCR_RD_ACTIVE		= BIT(9),	/* OMAP2+ only */
1428c2ecf20Sopenharmony_ci	CCR_WR_ACTIVE		= BIT(10),	/* OMAP2+ only */
1438c2ecf20Sopenharmony_ci	CCR_SRC_AMODE_CONSTANT	= 0 << 12,
1448c2ecf20Sopenharmony_ci	CCR_SRC_AMODE_POSTINC	= 1 << 12,
1458c2ecf20Sopenharmony_ci	CCR_SRC_AMODE_SGLIDX	= 2 << 12,
1468c2ecf20Sopenharmony_ci	CCR_SRC_AMODE_DBLIDX	= 3 << 12,
1478c2ecf20Sopenharmony_ci	CCR_DST_AMODE_CONSTANT	= 0 << 14,
1488c2ecf20Sopenharmony_ci	CCR_DST_AMODE_POSTINC	= 1 << 14,
1498c2ecf20Sopenharmony_ci	CCR_DST_AMODE_SGLIDX	= 2 << 14,
1508c2ecf20Sopenharmony_ci	CCR_DST_AMODE_DBLIDX	= 3 << 14,
1518c2ecf20Sopenharmony_ci	CCR_CONSTANT_FILL	= BIT(16),
1528c2ecf20Sopenharmony_ci	CCR_TRANSPARENT_COPY	= BIT(17),
1538c2ecf20Sopenharmony_ci	CCR_BS			= BIT(18),
1548c2ecf20Sopenharmony_ci	CCR_SUPERVISOR		= BIT(22),
1558c2ecf20Sopenharmony_ci	CCR_PREFETCH		= BIT(23),
1568c2ecf20Sopenharmony_ci	CCR_TRIGGER_SRC		= BIT(24),
1578c2ecf20Sopenharmony_ci	CCR_BUFFERING_DISABLE	= BIT(25),
1588c2ecf20Sopenharmony_ci	CCR_WRITE_PRIORITY	= BIT(26),
1598c2ecf20Sopenharmony_ci	CCR_SYNC_ELEMENT	= 0,
1608c2ecf20Sopenharmony_ci	CCR_SYNC_FRAME		= CCR_FS,
1618c2ecf20Sopenharmony_ci	CCR_SYNC_BLOCK		= CCR_BS,
1628c2ecf20Sopenharmony_ci	CCR_SYNC_PACKET		= CCR_BS | CCR_FS,
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	CSDP_DATA_TYPE_8	= 0,
1658c2ecf20Sopenharmony_ci	CSDP_DATA_TYPE_16	= 1,
1668c2ecf20Sopenharmony_ci	CSDP_DATA_TYPE_32	= 2,
1678c2ecf20Sopenharmony_ci	CSDP_SRC_PORT_EMIFF	= 0 << 2, /* OMAP1 only */
1688c2ecf20Sopenharmony_ci	CSDP_SRC_PORT_EMIFS	= 1 << 2, /* OMAP1 only */
1698c2ecf20Sopenharmony_ci	CSDP_SRC_PORT_OCP_T1	= 2 << 2, /* OMAP1 only */
1708c2ecf20Sopenharmony_ci	CSDP_SRC_PORT_TIPB	= 3 << 2, /* OMAP1 only */
1718c2ecf20Sopenharmony_ci	CSDP_SRC_PORT_OCP_T2	= 4 << 2, /* OMAP1 only */
1728c2ecf20Sopenharmony_ci	CSDP_SRC_PORT_MPUI	= 5 << 2, /* OMAP1 only */
1738c2ecf20Sopenharmony_ci	CSDP_SRC_PACKED		= BIT(6),
1748c2ecf20Sopenharmony_ci	CSDP_SRC_BURST_1	= 0 << 7,
1758c2ecf20Sopenharmony_ci	CSDP_SRC_BURST_16	= 1 << 7,
1768c2ecf20Sopenharmony_ci	CSDP_SRC_BURST_32	= 2 << 7,
1778c2ecf20Sopenharmony_ci	CSDP_SRC_BURST_64	= 3 << 7,
1788c2ecf20Sopenharmony_ci	CSDP_DST_PORT_EMIFF	= 0 << 9, /* OMAP1 only */
1798c2ecf20Sopenharmony_ci	CSDP_DST_PORT_EMIFS	= 1 << 9, /* OMAP1 only */
1808c2ecf20Sopenharmony_ci	CSDP_DST_PORT_OCP_T1	= 2 << 9, /* OMAP1 only */
1818c2ecf20Sopenharmony_ci	CSDP_DST_PORT_TIPB	= 3 << 9, /* OMAP1 only */
1828c2ecf20Sopenharmony_ci	CSDP_DST_PORT_OCP_T2	= 4 << 9, /* OMAP1 only */
1838c2ecf20Sopenharmony_ci	CSDP_DST_PORT_MPUI	= 5 << 9, /* OMAP1 only */
1848c2ecf20Sopenharmony_ci	CSDP_DST_PACKED		= BIT(13),
1858c2ecf20Sopenharmony_ci	CSDP_DST_BURST_1	= 0 << 14,
1868c2ecf20Sopenharmony_ci	CSDP_DST_BURST_16	= 1 << 14,
1878c2ecf20Sopenharmony_ci	CSDP_DST_BURST_32	= 2 << 14,
1888c2ecf20Sopenharmony_ci	CSDP_DST_BURST_64	= 3 << 14,
1898c2ecf20Sopenharmony_ci	CSDP_WRITE_NON_POSTED	= 0 << 16,
1908c2ecf20Sopenharmony_ci	CSDP_WRITE_POSTED	= 1 << 16,
1918c2ecf20Sopenharmony_ci	CSDP_WRITE_LAST_NON_POSTED = 2 << 16,
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	CICR_TOUT_IE		= BIT(0),	/* OMAP1 only */
1948c2ecf20Sopenharmony_ci	CICR_DROP_IE		= BIT(1),
1958c2ecf20Sopenharmony_ci	CICR_HALF_IE		= BIT(2),
1968c2ecf20Sopenharmony_ci	CICR_FRAME_IE		= BIT(3),
1978c2ecf20Sopenharmony_ci	CICR_LAST_IE		= BIT(4),
1988c2ecf20Sopenharmony_ci	CICR_BLOCK_IE		= BIT(5),
1998c2ecf20Sopenharmony_ci	CICR_PKT_IE		= BIT(7),	/* OMAP2+ only */
2008c2ecf20Sopenharmony_ci	CICR_TRANS_ERR_IE	= BIT(8),	/* OMAP2+ only */
2018c2ecf20Sopenharmony_ci	CICR_SUPERVISOR_ERR_IE	= BIT(10),	/* OMAP2+ only */
2028c2ecf20Sopenharmony_ci	CICR_MISALIGNED_ERR_IE	= BIT(11),	/* OMAP2+ only */
2038c2ecf20Sopenharmony_ci	CICR_DRAIN_IE		= BIT(12),	/* OMAP2+ only */
2048c2ecf20Sopenharmony_ci	CICR_SUPER_BLOCK_IE	= BIT(14),	/* OMAP2+ only */
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	CLNK_CTRL_ENABLE_LNK	= BIT(15),
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	CDP_DST_VALID_INC	= 0 << 0,
2098c2ecf20Sopenharmony_ci	CDP_DST_VALID_RELOAD	= 1 << 0,
2108c2ecf20Sopenharmony_ci	CDP_DST_VALID_REUSE	= 2 << 0,
2118c2ecf20Sopenharmony_ci	CDP_SRC_VALID_INC	= 0 << 2,
2128c2ecf20Sopenharmony_ci	CDP_SRC_VALID_RELOAD	= 1 << 2,
2138c2ecf20Sopenharmony_ci	CDP_SRC_VALID_REUSE	= 2 << 2,
2148c2ecf20Sopenharmony_ci	CDP_NTYPE_TYPE1		= 1 << 4,
2158c2ecf20Sopenharmony_ci	CDP_NTYPE_TYPE2		= 2 << 4,
2168c2ecf20Sopenharmony_ci	CDP_NTYPE_TYPE3		= 3 << 4,
2178c2ecf20Sopenharmony_ci	CDP_TMODE_NORMAL	= 0 << 8,
2188c2ecf20Sopenharmony_ci	CDP_TMODE_LLIST		= 1 << 8,
2198c2ecf20Sopenharmony_ci	CDP_FAST		= BIT(10),
2208c2ecf20Sopenharmony_ci};
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_cistatic const unsigned es_bytes[] = {
2238c2ecf20Sopenharmony_ci	[CSDP_DATA_TYPE_8] = 1,
2248c2ecf20Sopenharmony_ci	[CSDP_DATA_TYPE_16] = 2,
2258c2ecf20Sopenharmony_ci	[CSDP_DATA_TYPE_32] = 4,
2268c2ecf20Sopenharmony_ci};
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_cistatic bool omap_dma_filter_fn(struct dma_chan *chan, void *param);
2298c2ecf20Sopenharmony_cistatic struct of_dma_filter_info omap_dma_info = {
2308c2ecf20Sopenharmony_ci	.filter_fn = omap_dma_filter_fn,
2318c2ecf20Sopenharmony_ci};
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_cistatic inline struct omap_dmadev *to_omap_dma_dev(struct dma_device *d)
2348c2ecf20Sopenharmony_ci{
2358c2ecf20Sopenharmony_ci	return container_of(d, struct omap_dmadev, ddev);
2368c2ecf20Sopenharmony_ci}
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_cistatic inline struct omap_chan *to_omap_dma_chan(struct dma_chan *c)
2398c2ecf20Sopenharmony_ci{
2408c2ecf20Sopenharmony_ci	return container_of(c, struct omap_chan, vc.chan);
2418c2ecf20Sopenharmony_ci}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_cistatic inline struct omap_desc *to_omap_dma_desc(struct dma_async_tx_descriptor *t)
2448c2ecf20Sopenharmony_ci{
2458c2ecf20Sopenharmony_ci	return container_of(t, struct omap_desc, vd.tx);
2468c2ecf20Sopenharmony_ci}
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_cistatic void omap_dma_desc_free(struct virt_dma_desc *vd)
2498c2ecf20Sopenharmony_ci{
2508c2ecf20Sopenharmony_ci	struct omap_desc *d = to_omap_dma_desc(&vd->tx);
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	if (d->using_ll) {
2538c2ecf20Sopenharmony_ci		struct omap_dmadev *od = to_omap_dma_dev(vd->tx.chan->device);
2548c2ecf20Sopenharmony_ci		int i;
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci		for (i = 0; i < d->sglen; i++) {
2578c2ecf20Sopenharmony_ci			if (d->sg[i].t2_desc)
2588c2ecf20Sopenharmony_ci				dma_pool_free(od->desc_pool, d->sg[i].t2_desc,
2598c2ecf20Sopenharmony_ci					      d->sg[i].t2_desc_paddr);
2608c2ecf20Sopenharmony_ci		}
2618c2ecf20Sopenharmony_ci	}
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	kfree(d);
2648c2ecf20Sopenharmony_ci}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_cistatic void omap_dma_fill_type2_desc(struct omap_desc *d, int idx,
2678c2ecf20Sopenharmony_ci				     enum dma_transfer_direction dir, bool last)
2688c2ecf20Sopenharmony_ci{
2698c2ecf20Sopenharmony_ci	struct omap_sg *sg = &d->sg[idx];
2708c2ecf20Sopenharmony_ci	struct omap_type2_desc *t2_desc = sg->t2_desc;
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	if (idx)
2738c2ecf20Sopenharmony_ci		d->sg[idx - 1].t2_desc->next_desc = sg->t2_desc_paddr;
2748c2ecf20Sopenharmony_ci	if (last)
2758c2ecf20Sopenharmony_ci		t2_desc->next_desc = 0xfffffffc;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	t2_desc->en = sg->en;
2788c2ecf20Sopenharmony_ci	t2_desc->addr = sg->addr;
2798c2ecf20Sopenharmony_ci	t2_desc->fn = sg->fn & 0xffff;
2808c2ecf20Sopenharmony_ci	t2_desc->cicr = d->cicr;
2818c2ecf20Sopenharmony_ci	if (!last)
2828c2ecf20Sopenharmony_ci		t2_desc->cicr &= ~CICR_BLOCK_IE;
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	switch (dir) {
2858c2ecf20Sopenharmony_ci	case DMA_DEV_TO_MEM:
2868c2ecf20Sopenharmony_ci		t2_desc->cdei = sg->ei;
2878c2ecf20Sopenharmony_ci		t2_desc->csei = d->ei;
2888c2ecf20Sopenharmony_ci		t2_desc->cdfi = sg->fi;
2898c2ecf20Sopenharmony_ci		t2_desc->csfi = d->fi;
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci		t2_desc->en |= DESC_NXT_DV_REFRESH;
2928c2ecf20Sopenharmony_ci		t2_desc->en |= DESC_NXT_SV_REUSE;
2938c2ecf20Sopenharmony_ci		break;
2948c2ecf20Sopenharmony_ci	case DMA_MEM_TO_DEV:
2958c2ecf20Sopenharmony_ci		t2_desc->cdei = d->ei;
2968c2ecf20Sopenharmony_ci		t2_desc->csei = sg->ei;
2978c2ecf20Sopenharmony_ci		t2_desc->cdfi = d->fi;
2988c2ecf20Sopenharmony_ci		t2_desc->csfi = sg->fi;
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci		t2_desc->en |= DESC_NXT_SV_REFRESH;
3018c2ecf20Sopenharmony_ci		t2_desc->en |= DESC_NXT_DV_REUSE;
3028c2ecf20Sopenharmony_ci		break;
3038c2ecf20Sopenharmony_ci	default:
3048c2ecf20Sopenharmony_ci		return;
3058c2ecf20Sopenharmony_ci	}
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	t2_desc->en |= DESC_NTYPE_TYPE2;
3088c2ecf20Sopenharmony_ci}
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_cistatic void omap_dma_write(uint32_t val, unsigned type, void __iomem *addr)
3118c2ecf20Sopenharmony_ci{
3128c2ecf20Sopenharmony_ci	switch (type) {
3138c2ecf20Sopenharmony_ci	case OMAP_DMA_REG_16BIT:
3148c2ecf20Sopenharmony_ci		writew_relaxed(val, addr);
3158c2ecf20Sopenharmony_ci		break;
3168c2ecf20Sopenharmony_ci	case OMAP_DMA_REG_2X16BIT:
3178c2ecf20Sopenharmony_ci		writew_relaxed(val, addr);
3188c2ecf20Sopenharmony_ci		writew_relaxed(val >> 16, addr + 2);
3198c2ecf20Sopenharmony_ci		break;
3208c2ecf20Sopenharmony_ci	case OMAP_DMA_REG_32BIT:
3218c2ecf20Sopenharmony_ci		writel_relaxed(val, addr);
3228c2ecf20Sopenharmony_ci		break;
3238c2ecf20Sopenharmony_ci	default:
3248c2ecf20Sopenharmony_ci		WARN_ON(1);
3258c2ecf20Sopenharmony_ci	}
3268c2ecf20Sopenharmony_ci}
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_cistatic unsigned omap_dma_read(unsigned type, void __iomem *addr)
3298c2ecf20Sopenharmony_ci{
3308c2ecf20Sopenharmony_ci	unsigned val;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	switch (type) {
3338c2ecf20Sopenharmony_ci	case OMAP_DMA_REG_16BIT:
3348c2ecf20Sopenharmony_ci		val = readw_relaxed(addr);
3358c2ecf20Sopenharmony_ci		break;
3368c2ecf20Sopenharmony_ci	case OMAP_DMA_REG_2X16BIT:
3378c2ecf20Sopenharmony_ci		val = readw_relaxed(addr);
3388c2ecf20Sopenharmony_ci		val |= readw_relaxed(addr + 2) << 16;
3398c2ecf20Sopenharmony_ci		break;
3408c2ecf20Sopenharmony_ci	case OMAP_DMA_REG_32BIT:
3418c2ecf20Sopenharmony_ci		val = readl_relaxed(addr);
3428c2ecf20Sopenharmony_ci		break;
3438c2ecf20Sopenharmony_ci	default:
3448c2ecf20Sopenharmony_ci		WARN_ON(1);
3458c2ecf20Sopenharmony_ci		val = 0;
3468c2ecf20Sopenharmony_ci	}
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	return val;
3498c2ecf20Sopenharmony_ci}
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_cistatic void omap_dma_glbl_write(struct omap_dmadev *od, unsigned reg, unsigned val)
3528c2ecf20Sopenharmony_ci{
3538c2ecf20Sopenharmony_ci	const struct omap_dma_reg *r = od->reg_map + reg;
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	WARN_ON(r->stride);
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	omap_dma_write(val, r->type, od->base + r->offset);
3588c2ecf20Sopenharmony_ci}
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_cistatic unsigned omap_dma_glbl_read(struct omap_dmadev *od, unsigned reg)
3618c2ecf20Sopenharmony_ci{
3628c2ecf20Sopenharmony_ci	const struct omap_dma_reg *r = od->reg_map + reg;
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	WARN_ON(r->stride);
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	return omap_dma_read(r->type, od->base + r->offset);
3678c2ecf20Sopenharmony_ci}
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_cistatic void omap_dma_chan_write(struct omap_chan *c, unsigned reg, unsigned val)
3708c2ecf20Sopenharmony_ci{
3718c2ecf20Sopenharmony_ci	const struct omap_dma_reg *r = c->reg_map + reg;
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	omap_dma_write(val, r->type, c->channel_base + r->offset);
3748c2ecf20Sopenharmony_ci}
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_cistatic unsigned omap_dma_chan_read(struct omap_chan *c, unsigned reg)
3778c2ecf20Sopenharmony_ci{
3788c2ecf20Sopenharmony_ci	const struct omap_dma_reg *r = c->reg_map + reg;
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	return omap_dma_read(r->type, c->channel_base + r->offset);
3818c2ecf20Sopenharmony_ci}
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_cistatic void omap_dma_clear_csr(struct omap_chan *c)
3848c2ecf20Sopenharmony_ci{
3858c2ecf20Sopenharmony_ci	if (dma_omap1())
3868c2ecf20Sopenharmony_ci		omap_dma_chan_read(c, CSR);
3878c2ecf20Sopenharmony_ci	else
3888c2ecf20Sopenharmony_ci		omap_dma_chan_write(c, CSR, ~0);
3898c2ecf20Sopenharmony_ci}
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_cistatic unsigned omap_dma_get_csr(struct omap_chan *c)
3928c2ecf20Sopenharmony_ci{
3938c2ecf20Sopenharmony_ci	unsigned val = omap_dma_chan_read(c, CSR);
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	if (!dma_omap1())
3968c2ecf20Sopenharmony_ci		omap_dma_chan_write(c, CSR, val);
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	return val;
3998c2ecf20Sopenharmony_ci}
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_cistatic void omap_dma_clear_lch(struct omap_dmadev *od, int lch)
4028c2ecf20Sopenharmony_ci{
4038c2ecf20Sopenharmony_ci	struct omap_chan *c;
4048c2ecf20Sopenharmony_ci	int i;
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	c = od->lch_map[lch];
4078c2ecf20Sopenharmony_ci	if (!c)
4088c2ecf20Sopenharmony_ci		return;
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	for (i = CSDP; i <= od->cfg->lch_end; i++)
4118c2ecf20Sopenharmony_ci		omap_dma_chan_write(c, i, 0);
4128c2ecf20Sopenharmony_ci}
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_cistatic void omap_dma_assign(struct omap_dmadev *od, struct omap_chan *c,
4158c2ecf20Sopenharmony_ci	unsigned lch)
4168c2ecf20Sopenharmony_ci{
4178c2ecf20Sopenharmony_ci	c->channel_base = od->base + od->plat->channel_stride * lch;
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	od->lch_map[lch] = c;
4208c2ecf20Sopenharmony_ci}
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_cistatic void omap_dma_start(struct omap_chan *c, struct omap_desc *d)
4238c2ecf20Sopenharmony_ci{
4248c2ecf20Sopenharmony_ci	struct omap_dmadev *od = to_omap_dma_dev(c->vc.chan.device);
4258c2ecf20Sopenharmony_ci	uint16_t cicr = d->cicr;
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	if (__dma_omap15xx(od->plat->dma_attr))
4288c2ecf20Sopenharmony_ci		omap_dma_chan_write(c, CPC, 0);
4298c2ecf20Sopenharmony_ci	else
4308c2ecf20Sopenharmony_ci		omap_dma_chan_write(c, CDAC, 0);
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	omap_dma_clear_csr(c);
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	if (d->using_ll) {
4358c2ecf20Sopenharmony_ci		uint32_t cdp = CDP_TMODE_LLIST | CDP_NTYPE_TYPE2 | CDP_FAST;
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci		if (d->dir == DMA_DEV_TO_MEM)
4388c2ecf20Sopenharmony_ci			cdp |= (CDP_DST_VALID_RELOAD | CDP_SRC_VALID_REUSE);
4398c2ecf20Sopenharmony_ci		else
4408c2ecf20Sopenharmony_ci			cdp |= (CDP_DST_VALID_REUSE | CDP_SRC_VALID_RELOAD);
4418c2ecf20Sopenharmony_ci		omap_dma_chan_write(c, CDP, cdp);
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci		omap_dma_chan_write(c, CNDP, d->sg[0].t2_desc_paddr);
4448c2ecf20Sopenharmony_ci		omap_dma_chan_write(c, CCDN, 0);
4458c2ecf20Sopenharmony_ci		omap_dma_chan_write(c, CCFN, 0xffff);
4468c2ecf20Sopenharmony_ci		omap_dma_chan_write(c, CCEN, 0xffffff);
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci		cicr &= ~CICR_BLOCK_IE;
4498c2ecf20Sopenharmony_ci	} else if (od->ll123_supported) {
4508c2ecf20Sopenharmony_ci		omap_dma_chan_write(c, CDP, 0);
4518c2ecf20Sopenharmony_ci	}
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	/* Enable interrupts */
4548c2ecf20Sopenharmony_ci	omap_dma_chan_write(c, CICR, cicr);
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	/* Enable channel */
4578c2ecf20Sopenharmony_ci	omap_dma_chan_write(c, CCR, d->ccr | CCR_ENABLE);
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	c->running = true;
4608c2ecf20Sopenharmony_ci}
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_cistatic void omap_dma_drain_chan(struct omap_chan *c)
4638c2ecf20Sopenharmony_ci{
4648c2ecf20Sopenharmony_ci	int i;
4658c2ecf20Sopenharmony_ci	u32 val;
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	/* Wait for sDMA FIFO to drain */
4688c2ecf20Sopenharmony_ci	for (i = 0; ; i++) {
4698c2ecf20Sopenharmony_ci		val = omap_dma_chan_read(c, CCR);
4708c2ecf20Sopenharmony_ci		if (!(val & (CCR_RD_ACTIVE | CCR_WR_ACTIVE)))
4718c2ecf20Sopenharmony_ci			break;
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci		if (i > 100)
4748c2ecf20Sopenharmony_ci			break;
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci		udelay(5);
4778c2ecf20Sopenharmony_ci	}
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci	if (val & (CCR_RD_ACTIVE | CCR_WR_ACTIVE))
4808c2ecf20Sopenharmony_ci		dev_err(c->vc.chan.device->dev,
4818c2ecf20Sopenharmony_ci			"DMA drain did not complete on lch %d\n",
4828c2ecf20Sopenharmony_ci			c->dma_ch);
4838c2ecf20Sopenharmony_ci}
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_cistatic int omap_dma_stop(struct omap_chan *c)
4868c2ecf20Sopenharmony_ci{
4878c2ecf20Sopenharmony_ci	struct omap_dmadev *od = to_omap_dma_dev(c->vc.chan.device);
4888c2ecf20Sopenharmony_ci	uint32_t val;
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	/* disable irq */
4918c2ecf20Sopenharmony_ci	omap_dma_chan_write(c, CICR, 0);
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	omap_dma_clear_csr(c);
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	val = omap_dma_chan_read(c, CCR);
4968c2ecf20Sopenharmony_ci	if (od->plat->errata & DMA_ERRATA_i541 && val & CCR_TRIGGER_SRC) {
4978c2ecf20Sopenharmony_ci		uint32_t sysconfig;
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci		sysconfig = omap_dma_glbl_read(od, OCP_SYSCONFIG);
5008c2ecf20Sopenharmony_ci		val = sysconfig & ~DMA_SYSCONFIG_MIDLEMODE_MASK;
5018c2ecf20Sopenharmony_ci		val |= DMA_SYSCONFIG_MIDLEMODE(DMA_IDLEMODE_NO_IDLE);
5028c2ecf20Sopenharmony_ci		omap_dma_glbl_write(od, OCP_SYSCONFIG, val);
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci		val = omap_dma_chan_read(c, CCR);
5058c2ecf20Sopenharmony_ci		val &= ~CCR_ENABLE;
5068c2ecf20Sopenharmony_ci		omap_dma_chan_write(c, CCR, val);
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci		if (!(c->ccr & CCR_BUFFERING_DISABLE))
5098c2ecf20Sopenharmony_ci			omap_dma_drain_chan(c);
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci		omap_dma_glbl_write(od, OCP_SYSCONFIG, sysconfig);
5128c2ecf20Sopenharmony_ci	} else {
5138c2ecf20Sopenharmony_ci		if (!(val & CCR_ENABLE))
5148c2ecf20Sopenharmony_ci			return -EINVAL;
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci		val &= ~CCR_ENABLE;
5178c2ecf20Sopenharmony_ci		omap_dma_chan_write(c, CCR, val);
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci		if (!(c->ccr & CCR_BUFFERING_DISABLE))
5208c2ecf20Sopenharmony_ci			omap_dma_drain_chan(c);
5218c2ecf20Sopenharmony_ci	}
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	mb();
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	if (!__dma_omap15xx(od->plat->dma_attr) && c->cyclic) {
5268c2ecf20Sopenharmony_ci		val = omap_dma_chan_read(c, CLNK_CTRL);
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci		if (dma_omap1())
5298c2ecf20Sopenharmony_ci			val |= 1 << 14; /* set the STOP_LNK bit */
5308c2ecf20Sopenharmony_ci		else
5318c2ecf20Sopenharmony_ci			val &= ~CLNK_CTRL_ENABLE_LNK;
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci		omap_dma_chan_write(c, CLNK_CTRL, val);
5348c2ecf20Sopenharmony_ci	}
5358c2ecf20Sopenharmony_ci	c->running = false;
5368c2ecf20Sopenharmony_ci	return 0;
5378c2ecf20Sopenharmony_ci}
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_cistatic void omap_dma_start_sg(struct omap_chan *c, struct omap_desc *d)
5408c2ecf20Sopenharmony_ci{
5418c2ecf20Sopenharmony_ci	struct omap_sg *sg = d->sg + c->sgidx;
5428c2ecf20Sopenharmony_ci	unsigned cxsa, cxei, cxfi;
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	if (d->dir == DMA_DEV_TO_MEM || d->dir == DMA_MEM_TO_MEM) {
5458c2ecf20Sopenharmony_ci		cxsa = CDSA;
5468c2ecf20Sopenharmony_ci		cxei = CDEI;
5478c2ecf20Sopenharmony_ci		cxfi = CDFI;
5488c2ecf20Sopenharmony_ci	} else {
5498c2ecf20Sopenharmony_ci		cxsa = CSSA;
5508c2ecf20Sopenharmony_ci		cxei = CSEI;
5518c2ecf20Sopenharmony_ci		cxfi = CSFI;
5528c2ecf20Sopenharmony_ci	}
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci	omap_dma_chan_write(c, cxsa, sg->addr);
5558c2ecf20Sopenharmony_ci	omap_dma_chan_write(c, cxei, sg->ei);
5568c2ecf20Sopenharmony_ci	omap_dma_chan_write(c, cxfi, sg->fi);
5578c2ecf20Sopenharmony_ci	omap_dma_chan_write(c, CEN, sg->en);
5588c2ecf20Sopenharmony_ci	omap_dma_chan_write(c, CFN, sg->fn);
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci	omap_dma_start(c, d);
5618c2ecf20Sopenharmony_ci	c->sgidx++;
5628c2ecf20Sopenharmony_ci}
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_cistatic void omap_dma_start_desc(struct omap_chan *c)
5658c2ecf20Sopenharmony_ci{
5668c2ecf20Sopenharmony_ci	struct virt_dma_desc *vd = vchan_next_desc(&c->vc);
5678c2ecf20Sopenharmony_ci	struct omap_desc *d;
5688c2ecf20Sopenharmony_ci	unsigned cxsa, cxei, cxfi;
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	if (!vd) {
5718c2ecf20Sopenharmony_ci		c->desc = NULL;
5728c2ecf20Sopenharmony_ci		return;
5738c2ecf20Sopenharmony_ci	}
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	list_del(&vd->node);
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci	c->desc = d = to_omap_dma_desc(&vd->tx);
5788c2ecf20Sopenharmony_ci	c->sgidx = 0;
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci	/*
5818c2ecf20Sopenharmony_ci	 * This provides the necessary barrier to ensure data held in
5828c2ecf20Sopenharmony_ci	 * DMA coherent memory is visible to the DMA engine prior to
5838c2ecf20Sopenharmony_ci	 * the transfer starting.
5848c2ecf20Sopenharmony_ci	 */
5858c2ecf20Sopenharmony_ci	mb();
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci	omap_dma_chan_write(c, CCR, d->ccr);
5888c2ecf20Sopenharmony_ci	if (dma_omap1())
5898c2ecf20Sopenharmony_ci		omap_dma_chan_write(c, CCR2, d->ccr >> 16);
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	if (d->dir == DMA_DEV_TO_MEM || d->dir == DMA_MEM_TO_MEM) {
5928c2ecf20Sopenharmony_ci		cxsa = CSSA;
5938c2ecf20Sopenharmony_ci		cxei = CSEI;
5948c2ecf20Sopenharmony_ci		cxfi = CSFI;
5958c2ecf20Sopenharmony_ci	} else {
5968c2ecf20Sopenharmony_ci		cxsa = CDSA;
5978c2ecf20Sopenharmony_ci		cxei = CDEI;
5988c2ecf20Sopenharmony_ci		cxfi = CDFI;
5998c2ecf20Sopenharmony_ci	}
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci	omap_dma_chan_write(c, cxsa, d->dev_addr);
6028c2ecf20Sopenharmony_ci	omap_dma_chan_write(c, cxei, d->ei);
6038c2ecf20Sopenharmony_ci	omap_dma_chan_write(c, cxfi, d->fi);
6048c2ecf20Sopenharmony_ci	omap_dma_chan_write(c, CSDP, d->csdp);
6058c2ecf20Sopenharmony_ci	omap_dma_chan_write(c, CLNK_CTRL, d->clnk_ctrl);
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci	omap_dma_start_sg(c, d);
6088c2ecf20Sopenharmony_ci}
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_cistatic void omap_dma_callback(int ch, u16 status, void *data)
6118c2ecf20Sopenharmony_ci{
6128c2ecf20Sopenharmony_ci	struct omap_chan *c = data;
6138c2ecf20Sopenharmony_ci	struct omap_desc *d;
6148c2ecf20Sopenharmony_ci	unsigned long flags;
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci	spin_lock_irqsave(&c->vc.lock, flags);
6178c2ecf20Sopenharmony_ci	d = c->desc;
6188c2ecf20Sopenharmony_ci	if (d) {
6198c2ecf20Sopenharmony_ci		if (c->cyclic) {
6208c2ecf20Sopenharmony_ci			vchan_cyclic_callback(&d->vd);
6218c2ecf20Sopenharmony_ci		} else if (d->using_ll || c->sgidx == d->sglen) {
6228c2ecf20Sopenharmony_ci			omap_dma_start_desc(c);
6238c2ecf20Sopenharmony_ci			vchan_cookie_complete(&d->vd);
6248c2ecf20Sopenharmony_ci		} else {
6258c2ecf20Sopenharmony_ci			omap_dma_start_sg(c, d);
6268c2ecf20Sopenharmony_ci		}
6278c2ecf20Sopenharmony_ci	}
6288c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&c->vc.lock, flags);
6298c2ecf20Sopenharmony_ci}
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_cistatic irqreturn_t omap_dma_irq(int irq, void *devid)
6328c2ecf20Sopenharmony_ci{
6338c2ecf20Sopenharmony_ci	struct omap_dmadev *od = devid;
6348c2ecf20Sopenharmony_ci	unsigned status, channel;
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci	spin_lock(&od->irq_lock);
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci	status = omap_dma_glbl_read(od, IRQSTATUS_L1);
6398c2ecf20Sopenharmony_ci	status &= od->irq_enable_mask;
6408c2ecf20Sopenharmony_ci	if (status == 0) {
6418c2ecf20Sopenharmony_ci		spin_unlock(&od->irq_lock);
6428c2ecf20Sopenharmony_ci		return IRQ_NONE;
6438c2ecf20Sopenharmony_ci	}
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_ci	while ((channel = ffs(status)) != 0) {
6468c2ecf20Sopenharmony_ci		unsigned mask, csr;
6478c2ecf20Sopenharmony_ci		struct omap_chan *c;
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ci		channel -= 1;
6508c2ecf20Sopenharmony_ci		mask = BIT(channel);
6518c2ecf20Sopenharmony_ci		status &= ~mask;
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci		c = od->lch_map[channel];
6548c2ecf20Sopenharmony_ci		if (c == NULL) {
6558c2ecf20Sopenharmony_ci			/* This should never happen */
6568c2ecf20Sopenharmony_ci			dev_err(od->ddev.dev, "invalid channel %u\n", channel);
6578c2ecf20Sopenharmony_ci			continue;
6588c2ecf20Sopenharmony_ci		}
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci		csr = omap_dma_get_csr(c);
6618c2ecf20Sopenharmony_ci		omap_dma_glbl_write(od, IRQSTATUS_L1, mask);
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_ci		omap_dma_callback(channel, csr, c);
6648c2ecf20Sopenharmony_ci	}
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci	spin_unlock(&od->irq_lock);
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
6698c2ecf20Sopenharmony_ci}
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_cistatic int omap_dma_get_lch(struct omap_dmadev *od, int *lch)
6728c2ecf20Sopenharmony_ci{
6738c2ecf20Sopenharmony_ci	int channel;
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_ci	mutex_lock(&od->lch_lock);
6768c2ecf20Sopenharmony_ci	channel = find_first_zero_bit(od->lch_bitmap, od->lch_count);
6778c2ecf20Sopenharmony_ci	if (channel >= od->lch_count)
6788c2ecf20Sopenharmony_ci		goto out_busy;
6798c2ecf20Sopenharmony_ci	set_bit(channel, od->lch_bitmap);
6808c2ecf20Sopenharmony_ci	mutex_unlock(&od->lch_lock);
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci	omap_dma_clear_lch(od, channel);
6838c2ecf20Sopenharmony_ci	*lch = channel;
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci	return 0;
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_ciout_busy:
6888c2ecf20Sopenharmony_ci	mutex_unlock(&od->lch_lock);
6898c2ecf20Sopenharmony_ci	*lch = -EINVAL;
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci	return -EBUSY;
6928c2ecf20Sopenharmony_ci}
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_cistatic void omap_dma_put_lch(struct omap_dmadev *od, int lch)
6958c2ecf20Sopenharmony_ci{
6968c2ecf20Sopenharmony_ci	omap_dma_clear_lch(od, lch);
6978c2ecf20Sopenharmony_ci	mutex_lock(&od->lch_lock);
6988c2ecf20Sopenharmony_ci	clear_bit(lch, od->lch_bitmap);
6998c2ecf20Sopenharmony_ci	mutex_unlock(&od->lch_lock);
7008c2ecf20Sopenharmony_ci}
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_cistatic int omap_dma_alloc_chan_resources(struct dma_chan *chan)
7038c2ecf20Sopenharmony_ci{
7048c2ecf20Sopenharmony_ci	struct omap_dmadev *od = to_omap_dma_dev(chan->device);
7058c2ecf20Sopenharmony_ci	struct omap_chan *c = to_omap_dma_chan(chan);
7068c2ecf20Sopenharmony_ci	struct device *dev = od->ddev.dev;
7078c2ecf20Sopenharmony_ci	int ret;
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_ci	if (od->legacy) {
7108c2ecf20Sopenharmony_ci		ret = omap_request_dma(c->dma_sig, "DMA engine",
7118c2ecf20Sopenharmony_ci				       omap_dma_callback, c, &c->dma_ch);
7128c2ecf20Sopenharmony_ci	} else {
7138c2ecf20Sopenharmony_ci		ret = omap_dma_get_lch(od, &c->dma_ch);
7148c2ecf20Sopenharmony_ci	}
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_ci	dev_dbg(dev, "allocating channel %u for %u\n", c->dma_ch, c->dma_sig);
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci	if (ret >= 0) {
7198c2ecf20Sopenharmony_ci		omap_dma_assign(od, c, c->dma_ch);
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci		if (!od->legacy) {
7228c2ecf20Sopenharmony_ci			unsigned val;
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci			spin_lock_irq(&od->irq_lock);
7258c2ecf20Sopenharmony_ci			val = BIT(c->dma_ch);
7268c2ecf20Sopenharmony_ci			omap_dma_glbl_write(od, IRQSTATUS_L1, val);
7278c2ecf20Sopenharmony_ci			od->irq_enable_mask |= val;
7288c2ecf20Sopenharmony_ci			omap_dma_glbl_write(od, IRQENABLE_L1, od->irq_enable_mask);
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_ci			val = omap_dma_glbl_read(od, IRQENABLE_L0);
7318c2ecf20Sopenharmony_ci			val &= ~BIT(c->dma_ch);
7328c2ecf20Sopenharmony_ci			omap_dma_glbl_write(od, IRQENABLE_L0, val);
7338c2ecf20Sopenharmony_ci			spin_unlock_irq(&od->irq_lock);
7348c2ecf20Sopenharmony_ci		}
7358c2ecf20Sopenharmony_ci	}
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_ci	if (dma_omap1()) {
7388c2ecf20Sopenharmony_ci		if (__dma_omap16xx(od->plat->dma_attr)) {
7398c2ecf20Sopenharmony_ci			c->ccr = CCR_OMAP31_DISABLE;
7408c2ecf20Sopenharmony_ci			/* Duplicate what plat-omap/dma.c does */
7418c2ecf20Sopenharmony_ci			c->ccr |= c->dma_ch + 1;
7428c2ecf20Sopenharmony_ci		} else {
7438c2ecf20Sopenharmony_ci			c->ccr = c->dma_sig & 0x1f;
7448c2ecf20Sopenharmony_ci		}
7458c2ecf20Sopenharmony_ci	} else {
7468c2ecf20Sopenharmony_ci		c->ccr = c->dma_sig & 0x1f;
7478c2ecf20Sopenharmony_ci		c->ccr |= (c->dma_sig & ~0x1f) << 14;
7488c2ecf20Sopenharmony_ci	}
7498c2ecf20Sopenharmony_ci	if (od->plat->errata & DMA_ERRATA_IFRAME_BUFFERING)
7508c2ecf20Sopenharmony_ci		c->ccr |= CCR_BUFFERING_DISABLE;
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_ci	return ret;
7538c2ecf20Sopenharmony_ci}
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_cistatic void omap_dma_free_chan_resources(struct dma_chan *chan)
7568c2ecf20Sopenharmony_ci{
7578c2ecf20Sopenharmony_ci	struct omap_dmadev *od = to_omap_dma_dev(chan->device);
7588c2ecf20Sopenharmony_ci	struct omap_chan *c = to_omap_dma_chan(chan);
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_ci	if (!od->legacy) {
7618c2ecf20Sopenharmony_ci		spin_lock_irq(&od->irq_lock);
7628c2ecf20Sopenharmony_ci		od->irq_enable_mask &= ~BIT(c->dma_ch);
7638c2ecf20Sopenharmony_ci		omap_dma_glbl_write(od, IRQENABLE_L1, od->irq_enable_mask);
7648c2ecf20Sopenharmony_ci		spin_unlock_irq(&od->irq_lock);
7658c2ecf20Sopenharmony_ci	}
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci	c->channel_base = NULL;
7688c2ecf20Sopenharmony_ci	od->lch_map[c->dma_ch] = NULL;
7698c2ecf20Sopenharmony_ci	vchan_free_chan_resources(&c->vc);
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_ci	if (od->legacy)
7728c2ecf20Sopenharmony_ci		omap_free_dma(c->dma_ch);
7738c2ecf20Sopenharmony_ci	else
7748c2ecf20Sopenharmony_ci		omap_dma_put_lch(od, c->dma_ch);
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci	dev_dbg(od->ddev.dev, "freeing channel %u used for %u\n", c->dma_ch,
7778c2ecf20Sopenharmony_ci		c->dma_sig);
7788c2ecf20Sopenharmony_ci	c->dma_sig = 0;
7798c2ecf20Sopenharmony_ci}
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_cistatic size_t omap_dma_sg_size(struct omap_sg *sg)
7828c2ecf20Sopenharmony_ci{
7838c2ecf20Sopenharmony_ci	return sg->en * sg->fn;
7848c2ecf20Sopenharmony_ci}
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_cistatic size_t omap_dma_desc_size(struct omap_desc *d)
7878c2ecf20Sopenharmony_ci{
7888c2ecf20Sopenharmony_ci	unsigned i;
7898c2ecf20Sopenharmony_ci	size_t size;
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_ci	for (size = i = 0; i < d->sglen; i++)
7928c2ecf20Sopenharmony_ci		size += omap_dma_sg_size(&d->sg[i]);
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_ci	return size * es_bytes[d->es];
7958c2ecf20Sopenharmony_ci}
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_cistatic size_t omap_dma_desc_size_pos(struct omap_desc *d, dma_addr_t addr)
7988c2ecf20Sopenharmony_ci{
7998c2ecf20Sopenharmony_ci	unsigned i;
8008c2ecf20Sopenharmony_ci	size_t size, es_size = es_bytes[d->es];
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_ci	for (size = i = 0; i < d->sglen; i++) {
8038c2ecf20Sopenharmony_ci		size_t this_size = omap_dma_sg_size(&d->sg[i]) * es_size;
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci		if (size)
8068c2ecf20Sopenharmony_ci			size += this_size;
8078c2ecf20Sopenharmony_ci		else if (addr >= d->sg[i].addr &&
8088c2ecf20Sopenharmony_ci			 addr < d->sg[i].addr + this_size)
8098c2ecf20Sopenharmony_ci			size += d->sg[i].addr + this_size - addr;
8108c2ecf20Sopenharmony_ci	}
8118c2ecf20Sopenharmony_ci	return size;
8128c2ecf20Sopenharmony_ci}
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_ci/*
8158c2ecf20Sopenharmony_ci * OMAP 3.2/3.3 erratum: sometimes 0 is returned if CSAC/CDAC is
8168c2ecf20Sopenharmony_ci * read before the DMA controller finished disabling the channel.
8178c2ecf20Sopenharmony_ci */
8188c2ecf20Sopenharmony_cistatic uint32_t omap_dma_chan_read_3_3(struct omap_chan *c, unsigned reg)
8198c2ecf20Sopenharmony_ci{
8208c2ecf20Sopenharmony_ci	struct omap_dmadev *od = to_omap_dma_dev(c->vc.chan.device);
8218c2ecf20Sopenharmony_ci	uint32_t val;
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_ci	val = omap_dma_chan_read(c, reg);
8248c2ecf20Sopenharmony_ci	if (val == 0 && od->plat->errata & DMA_ERRATA_3_3)
8258c2ecf20Sopenharmony_ci		val = omap_dma_chan_read(c, reg);
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci	return val;
8288c2ecf20Sopenharmony_ci}
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_cistatic dma_addr_t omap_dma_get_src_pos(struct omap_chan *c)
8318c2ecf20Sopenharmony_ci{
8328c2ecf20Sopenharmony_ci	struct omap_dmadev *od = to_omap_dma_dev(c->vc.chan.device);
8338c2ecf20Sopenharmony_ci	dma_addr_t addr, cdac;
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_ci	if (__dma_omap15xx(od->plat->dma_attr)) {
8368c2ecf20Sopenharmony_ci		addr = omap_dma_chan_read(c, CPC);
8378c2ecf20Sopenharmony_ci	} else {
8388c2ecf20Sopenharmony_ci		addr = omap_dma_chan_read_3_3(c, CSAC);
8398c2ecf20Sopenharmony_ci		cdac = omap_dma_chan_read_3_3(c, CDAC);
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_ci		/*
8428c2ecf20Sopenharmony_ci		 * CDAC == 0 indicates that the DMA transfer on the channel has
8438c2ecf20Sopenharmony_ci		 * not been started (no data has been transferred so far).
8448c2ecf20Sopenharmony_ci		 * Return the programmed source start address in this case.
8458c2ecf20Sopenharmony_ci		 */
8468c2ecf20Sopenharmony_ci		if (cdac == 0)
8478c2ecf20Sopenharmony_ci			addr = omap_dma_chan_read(c, CSSA);
8488c2ecf20Sopenharmony_ci	}
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_ci	if (dma_omap1())
8518c2ecf20Sopenharmony_ci		addr |= omap_dma_chan_read(c, CSSA) & 0xffff0000;
8528c2ecf20Sopenharmony_ci
8538c2ecf20Sopenharmony_ci	return addr;
8548c2ecf20Sopenharmony_ci}
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_cistatic dma_addr_t omap_dma_get_dst_pos(struct omap_chan *c)
8578c2ecf20Sopenharmony_ci{
8588c2ecf20Sopenharmony_ci	struct omap_dmadev *od = to_omap_dma_dev(c->vc.chan.device);
8598c2ecf20Sopenharmony_ci	dma_addr_t addr;
8608c2ecf20Sopenharmony_ci
8618c2ecf20Sopenharmony_ci	if (__dma_omap15xx(od->plat->dma_attr)) {
8628c2ecf20Sopenharmony_ci		addr = omap_dma_chan_read(c, CPC);
8638c2ecf20Sopenharmony_ci	} else {
8648c2ecf20Sopenharmony_ci		addr = omap_dma_chan_read_3_3(c, CDAC);
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_ci		/*
8678c2ecf20Sopenharmony_ci		 * CDAC == 0 indicates that the DMA transfer on the channel
8688c2ecf20Sopenharmony_ci		 * has not been started (no data has been transferred so
8698c2ecf20Sopenharmony_ci		 * far).  Return the programmed destination start address in
8708c2ecf20Sopenharmony_ci		 * this case.
8718c2ecf20Sopenharmony_ci		 */
8728c2ecf20Sopenharmony_ci		if (addr == 0)
8738c2ecf20Sopenharmony_ci			addr = omap_dma_chan_read(c, CDSA);
8748c2ecf20Sopenharmony_ci	}
8758c2ecf20Sopenharmony_ci
8768c2ecf20Sopenharmony_ci	if (dma_omap1())
8778c2ecf20Sopenharmony_ci		addr |= omap_dma_chan_read(c, CDSA) & 0xffff0000;
8788c2ecf20Sopenharmony_ci
8798c2ecf20Sopenharmony_ci	return addr;
8808c2ecf20Sopenharmony_ci}
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_cistatic enum dma_status omap_dma_tx_status(struct dma_chan *chan,
8838c2ecf20Sopenharmony_ci	dma_cookie_t cookie, struct dma_tx_state *txstate)
8848c2ecf20Sopenharmony_ci{
8858c2ecf20Sopenharmony_ci	struct omap_chan *c = to_omap_dma_chan(chan);
8868c2ecf20Sopenharmony_ci	enum dma_status ret;
8878c2ecf20Sopenharmony_ci	unsigned long flags;
8888c2ecf20Sopenharmony_ci	struct omap_desc *d = NULL;
8898c2ecf20Sopenharmony_ci
8908c2ecf20Sopenharmony_ci	ret = dma_cookie_status(chan, cookie, txstate);
8918c2ecf20Sopenharmony_ci	if (ret == DMA_COMPLETE)
8928c2ecf20Sopenharmony_ci		return ret;
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_ci	spin_lock_irqsave(&c->vc.lock, flags);
8958c2ecf20Sopenharmony_ci	if (c->desc && c->desc->vd.tx.cookie == cookie)
8968c2ecf20Sopenharmony_ci		d = c->desc;
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_ci	if (!txstate)
8998c2ecf20Sopenharmony_ci		goto out;
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_ci	if (d) {
9028c2ecf20Sopenharmony_ci		dma_addr_t pos;
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_ci		if (d->dir == DMA_MEM_TO_DEV)
9058c2ecf20Sopenharmony_ci			pos = omap_dma_get_src_pos(c);
9068c2ecf20Sopenharmony_ci		else if (d->dir == DMA_DEV_TO_MEM  || d->dir == DMA_MEM_TO_MEM)
9078c2ecf20Sopenharmony_ci			pos = omap_dma_get_dst_pos(c);
9088c2ecf20Sopenharmony_ci		else
9098c2ecf20Sopenharmony_ci			pos = 0;
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_ci		txstate->residue = omap_dma_desc_size_pos(d, pos);
9128c2ecf20Sopenharmony_ci	} else {
9138c2ecf20Sopenharmony_ci		struct virt_dma_desc *vd = vchan_find_desc(&c->vc, cookie);
9148c2ecf20Sopenharmony_ci
9158c2ecf20Sopenharmony_ci		if (vd)
9168c2ecf20Sopenharmony_ci			txstate->residue = omap_dma_desc_size(
9178c2ecf20Sopenharmony_ci						to_omap_dma_desc(&vd->tx));
9188c2ecf20Sopenharmony_ci		else
9198c2ecf20Sopenharmony_ci			txstate->residue = 0;
9208c2ecf20Sopenharmony_ci	}
9218c2ecf20Sopenharmony_ci
9228c2ecf20Sopenharmony_ciout:
9238c2ecf20Sopenharmony_ci	if (ret == DMA_IN_PROGRESS && c->paused) {
9248c2ecf20Sopenharmony_ci		ret = DMA_PAUSED;
9258c2ecf20Sopenharmony_ci	} else if (d && d->polled && c->running) {
9268c2ecf20Sopenharmony_ci		uint32_t ccr = omap_dma_chan_read(c, CCR);
9278c2ecf20Sopenharmony_ci		/*
9288c2ecf20Sopenharmony_ci		 * The channel is no longer active, set the return value
9298c2ecf20Sopenharmony_ci		 * accordingly and mark it as completed
9308c2ecf20Sopenharmony_ci		 */
9318c2ecf20Sopenharmony_ci		if (!(ccr & CCR_ENABLE)) {
9328c2ecf20Sopenharmony_ci			ret = DMA_COMPLETE;
9338c2ecf20Sopenharmony_ci			omap_dma_start_desc(c);
9348c2ecf20Sopenharmony_ci			vchan_cookie_complete(&d->vd);
9358c2ecf20Sopenharmony_ci		}
9368c2ecf20Sopenharmony_ci	}
9378c2ecf20Sopenharmony_ci
9388c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&c->vc.lock, flags);
9398c2ecf20Sopenharmony_ci
9408c2ecf20Sopenharmony_ci	return ret;
9418c2ecf20Sopenharmony_ci}
9428c2ecf20Sopenharmony_ci
9438c2ecf20Sopenharmony_cistatic void omap_dma_issue_pending(struct dma_chan *chan)
9448c2ecf20Sopenharmony_ci{
9458c2ecf20Sopenharmony_ci	struct omap_chan *c = to_omap_dma_chan(chan);
9468c2ecf20Sopenharmony_ci	unsigned long flags;
9478c2ecf20Sopenharmony_ci
9488c2ecf20Sopenharmony_ci	spin_lock_irqsave(&c->vc.lock, flags);
9498c2ecf20Sopenharmony_ci	if (vchan_issue_pending(&c->vc) && !c->desc)
9508c2ecf20Sopenharmony_ci		omap_dma_start_desc(c);
9518c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&c->vc.lock, flags);
9528c2ecf20Sopenharmony_ci}
9538c2ecf20Sopenharmony_ci
9548c2ecf20Sopenharmony_cistatic struct dma_async_tx_descriptor *omap_dma_prep_slave_sg(
9558c2ecf20Sopenharmony_ci	struct dma_chan *chan, struct scatterlist *sgl, unsigned sglen,
9568c2ecf20Sopenharmony_ci	enum dma_transfer_direction dir, unsigned long tx_flags, void *context)
9578c2ecf20Sopenharmony_ci{
9588c2ecf20Sopenharmony_ci	struct omap_dmadev *od = to_omap_dma_dev(chan->device);
9598c2ecf20Sopenharmony_ci	struct omap_chan *c = to_omap_dma_chan(chan);
9608c2ecf20Sopenharmony_ci	enum dma_slave_buswidth dev_width;
9618c2ecf20Sopenharmony_ci	struct scatterlist *sgent;
9628c2ecf20Sopenharmony_ci	struct omap_desc *d;
9638c2ecf20Sopenharmony_ci	dma_addr_t dev_addr;
9648c2ecf20Sopenharmony_ci	unsigned i, es, en, frame_bytes;
9658c2ecf20Sopenharmony_ci	bool ll_failed = false;
9668c2ecf20Sopenharmony_ci	u32 burst;
9678c2ecf20Sopenharmony_ci	u32 port_window, port_window_bytes;
9688c2ecf20Sopenharmony_ci
9698c2ecf20Sopenharmony_ci	if (dir == DMA_DEV_TO_MEM) {
9708c2ecf20Sopenharmony_ci		dev_addr = c->cfg.src_addr;
9718c2ecf20Sopenharmony_ci		dev_width = c->cfg.src_addr_width;
9728c2ecf20Sopenharmony_ci		burst = c->cfg.src_maxburst;
9738c2ecf20Sopenharmony_ci		port_window = c->cfg.src_port_window_size;
9748c2ecf20Sopenharmony_ci	} else if (dir == DMA_MEM_TO_DEV) {
9758c2ecf20Sopenharmony_ci		dev_addr = c->cfg.dst_addr;
9768c2ecf20Sopenharmony_ci		dev_width = c->cfg.dst_addr_width;
9778c2ecf20Sopenharmony_ci		burst = c->cfg.dst_maxburst;
9788c2ecf20Sopenharmony_ci		port_window = c->cfg.dst_port_window_size;
9798c2ecf20Sopenharmony_ci	} else {
9808c2ecf20Sopenharmony_ci		dev_err(chan->device->dev, "%s: bad direction?\n", __func__);
9818c2ecf20Sopenharmony_ci		return NULL;
9828c2ecf20Sopenharmony_ci	}
9838c2ecf20Sopenharmony_ci
9848c2ecf20Sopenharmony_ci	/* Bus width translates to the element size (ES) */
9858c2ecf20Sopenharmony_ci	switch (dev_width) {
9868c2ecf20Sopenharmony_ci	case DMA_SLAVE_BUSWIDTH_1_BYTE:
9878c2ecf20Sopenharmony_ci		es = CSDP_DATA_TYPE_8;
9888c2ecf20Sopenharmony_ci		break;
9898c2ecf20Sopenharmony_ci	case DMA_SLAVE_BUSWIDTH_2_BYTES:
9908c2ecf20Sopenharmony_ci		es = CSDP_DATA_TYPE_16;
9918c2ecf20Sopenharmony_ci		break;
9928c2ecf20Sopenharmony_ci	case DMA_SLAVE_BUSWIDTH_4_BYTES:
9938c2ecf20Sopenharmony_ci		es = CSDP_DATA_TYPE_32;
9948c2ecf20Sopenharmony_ci		break;
9958c2ecf20Sopenharmony_ci	default: /* not reached */
9968c2ecf20Sopenharmony_ci		return NULL;
9978c2ecf20Sopenharmony_ci	}
9988c2ecf20Sopenharmony_ci
9998c2ecf20Sopenharmony_ci	/* Now allocate and setup the descriptor. */
10008c2ecf20Sopenharmony_ci	d = kzalloc(struct_size(d, sg, sglen), GFP_ATOMIC);
10018c2ecf20Sopenharmony_ci	if (!d)
10028c2ecf20Sopenharmony_ci		return NULL;
10038c2ecf20Sopenharmony_ci
10048c2ecf20Sopenharmony_ci	d->dir = dir;
10058c2ecf20Sopenharmony_ci	d->dev_addr = dev_addr;
10068c2ecf20Sopenharmony_ci	d->es = es;
10078c2ecf20Sopenharmony_ci
10088c2ecf20Sopenharmony_ci	/* When the port_window is used, one frame must cover the window */
10098c2ecf20Sopenharmony_ci	if (port_window) {
10108c2ecf20Sopenharmony_ci		burst = port_window;
10118c2ecf20Sopenharmony_ci		port_window_bytes = port_window * es_bytes[es];
10128c2ecf20Sopenharmony_ci
10138c2ecf20Sopenharmony_ci		d->ei = 1;
10148c2ecf20Sopenharmony_ci		/*
10158c2ecf20Sopenharmony_ci		 * One frame covers the port_window and by  configure
10168c2ecf20Sopenharmony_ci		 * the source frame index to be -1 * (port_window - 1)
10178c2ecf20Sopenharmony_ci		 * we instruct the sDMA that after a frame is processed
10188c2ecf20Sopenharmony_ci		 * it should move back to the start of the window.
10198c2ecf20Sopenharmony_ci		 */
10208c2ecf20Sopenharmony_ci		d->fi = -(port_window_bytes - 1);
10218c2ecf20Sopenharmony_ci	}
10228c2ecf20Sopenharmony_ci
10238c2ecf20Sopenharmony_ci	d->ccr = c->ccr | CCR_SYNC_FRAME;
10248c2ecf20Sopenharmony_ci	if (dir == DMA_DEV_TO_MEM) {
10258c2ecf20Sopenharmony_ci		d->csdp = CSDP_DST_BURST_64 | CSDP_DST_PACKED;
10268c2ecf20Sopenharmony_ci
10278c2ecf20Sopenharmony_ci		d->ccr |= CCR_DST_AMODE_POSTINC;
10288c2ecf20Sopenharmony_ci		if (port_window) {
10298c2ecf20Sopenharmony_ci			d->ccr |= CCR_SRC_AMODE_DBLIDX;
10308c2ecf20Sopenharmony_ci
10318c2ecf20Sopenharmony_ci			if (port_window_bytes >= 64)
10328c2ecf20Sopenharmony_ci				d->csdp |= CSDP_SRC_BURST_64;
10338c2ecf20Sopenharmony_ci			else if (port_window_bytes >= 32)
10348c2ecf20Sopenharmony_ci				d->csdp |= CSDP_SRC_BURST_32;
10358c2ecf20Sopenharmony_ci			else if (port_window_bytes >= 16)
10368c2ecf20Sopenharmony_ci				d->csdp |= CSDP_SRC_BURST_16;
10378c2ecf20Sopenharmony_ci
10388c2ecf20Sopenharmony_ci		} else {
10398c2ecf20Sopenharmony_ci			d->ccr |= CCR_SRC_AMODE_CONSTANT;
10408c2ecf20Sopenharmony_ci		}
10418c2ecf20Sopenharmony_ci	} else {
10428c2ecf20Sopenharmony_ci		d->csdp = CSDP_SRC_BURST_64 | CSDP_SRC_PACKED;
10438c2ecf20Sopenharmony_ci
10448c2ecf20Sopenharmony_ci		d->ccr |= CCR_SRC_AMODE_POSTINC;
10458c2ecf20Sopenharmony_ci		if (port_window) {
10468c2ecf20Sopenharmony_ci			d->ccr |= CCR_DST_AMODE_DBLIDX;
10478c2ecf20Sopenharmony_ci
10488c2ecf20Sopenharmony_ci			if (port_window_bytes >= 64)
10498c2ecf20Sopenharmony_ci				d->csdp |= CSDP_DST_BURST_64;
10508c2ecf20Sopenharmony_ci			else if (port_window_bytes >= 32)
10518c2ecf20Sopenharmony_ci				d->csdp |= CSDP_DST_BURST_32;
10528c2ecf20Sopenharmony_ci			else if (port_window_bytes >= 16)
10538c2ecf20Sopenharmony_ci				d->csdp |= CSDP_DST_BURST_16;
10548c2ecf20Sopenharmony_ci		} else {
10558c2ecf20Sopenharmony_ci			d->ccr |= CCR_DST_AMODE_CONSTANT;
10568c2ecf20Sopenharmony_ci		}
10578c2ecf20Sopenharmony_ci	}
10588c2ecf20Sopenharmony_ci
10598c2ecf20Sopenharmony_ci	d->cicr = CICR_DROP_IE | CICR_BLOCK_IE;
10608c2ecf20Sopenharmony_ci	d->csdp |= es;
10618c2ecf20Sopenharmony_ci
10628c2ecf20Sopenharmony_ci	if (dma_omap1()) {
10638c2ecf20Sopenharmony_ci		d->cicr |= CICR_TOUT_IE;
10648c2ecf20Sopenharmony_ci
10658c2ecf20Sopenharmony_ci		if (dir == DMA_DEV_TO_MEM)
10668c2ecf20Sopenharmony_ci			d->csdp |= CSDP_DST_PORT_EMIFF | CSDP_SRC_PORT_TIPB;
10678c2ecf20Sopenharmony_ci		else
10688c2ecf20Sopenharmony_ci			d->csdp |= CSDP_DST_PORT_TIPB | CSDP_SRC_PORT_EMIFF;
10698c2ecf20Sopenharmony_ci	} else {
10708c2ecf20Sopenharmony_ci		if (dir == DMA_DEV_TO_MEM)
10718c2ecf20Sopenharmony_ci			d->ccr |= CCR_TRIGGER_SRC;
10728c2ecf20Sopenharmony_ci
10738c2ecf20Sopenharmony_ci		d->cicr |= CICR_MISALIGNED_ERR_IE | CICR_TRANS_ERR_IE;
10748c2ecf20Sopenharmony_ci
10758c2ecf20Sopenharmony_ci		if (port_window)
10768c2ecf20Sopenharmony_ci			d->csdp |= CSDP_WRITE_LAST_NON_POSTED;
10778c2ecf20Sopenharmony_ci	}
10788c2ecf20Sopenharmony_ci	if (od->plat->errata & DMA_ERRATA_PARALLEL_CHANNELS)
10798c2ecf20Sopenharmony_ci		d->clnk_ctrl = c->dma_ch;
10808c2ecf20Sopenharmony_ci
10818c2ecf20Sopenharmony_ci	/*
10828c2ecf20Sopenharmony_ci	 * Build our scatterlist entries: each contains the address,
10838c2ecf20Sopenharmony_ci	 * the number of elements (EN) in each frame, and the number of
10848c2ecf20Sopenharmony_ci	 * frames (FN).  Number of bytes for this entry = ES * EN * FN.
10858c2ecf20Sopenharmony_ci	 *
10868c2ecf20Sopenharmony_ci	 * Burst size translates to number of elements with frame sync.
10878c2ecf20Sopenharmony_ci	 * Note: DMA engine defines burst to be the number of dev-width
10888c2ecf20Sopenharmony_ci	 * transfers.
10898c2ecf20Sopenharmony_ci	 */
10908c2ecf20Sopenharmony_ci	en = burst;
10918c2ecf20Sopenharmony_ci	frame_bytes = es_bytes[es] * en;
10928c2ecf20Sopenharmony_ci
10938c2ecf20Sopenharmony_ci	if (sglen >= 2)
10948c2ecf20Sopenharmony_ci		d->using_ll = od->ll123_supported;
10958c2ecf20Sopenharmony_ci
10968c2ecf20Sopenharmony_ci	for_each_sg(sgl, sgent, sglen, i) {
10978c2ecf20Sopenharmony_ci		struct omap_sg *osg = &d->sg[i];
10988c2ecf20Sopenharmony_ci
10998c2ecf20Sopenharmony_ci		osg->addr = sg_dma_address(sgent);
11008c2ecf20Sopenharmony_ci		osg->en = en;
11018c2ecf20Sopenharmony_ci		osg->fn = sg_dma_len(sgent) / frame_bytes;
11028c2ecf20Sopenharmony_ci
11038c2ecf20Sopenharmony_ci		if (d->using_ll) {
11048c2ecf20Sopenharmony_ci			osg->t2_desc = dma_pool_alloc(od->desc_pool, GFP_ATOMIC,
11058c2ecf20Sopenharmony_ci						      &osg->t2_desc_paddr);
11068c2ecf20Sopenharmony_ci			if (!osg->t2_desc) {
11078c2ecf20Sopenharmony_ci				dev_err(chan->device->dev,
11088c2ecf20Sopenharmony_ci					"t2_desc[%d] allocation failed\n", i);
11098c2ecf20Sopenharmony_ci				ll_failed = true;
11108c2ecf20Sopenharmony_ci				d->using_ll = false;
11118c2ecf20Sopenharmony_ci				continue;
11128c2ecf20Sopenharmony_ci			}
11138c2ecf20Sopenharmony_ci
11148c2ecf20Sopenharmony_ci			omap_dma_fill_type2_desc(d, i, dir, (i == sglen - 1));
11158c2ecf20Sopenharmony_ci		}
11168c2ecf20Sopenharmony_ci	}
11178c2ecf20Sopenharmony_ci
11188c2ecf20Sopenharmony_ci	d->sglen = sglen;
11198c2ecf20Sopenharmony_ci
11208c2ecf20Sopenharmony_ci	/* Release the dma_pool entries if one allocation failed */
11218c2ecf20Sopenharmony_ci	if (ll_failed) {
11228c2ecf20Sopenharmony_ci		for (i = 0; i < d->sglen; i++) {
11238c2ecf20Sopenharmony_ci			struct omap_sg *osg = &d->sg[i];
11248c2ecf20Sopenharmony_ci
11258c2ecf20Sopenharmony_ci			if (osg->t2_desc) {
11268c2ecf20Sopenharmony_ci				dma_pool_free(od->desc_pool, osg->t2_desc,
11278c2ecf20Sopenharmony_ci					      osg->t2_desc_paddr);
11288c2ecf20Sopenharmony_ci				osg->t2_desc = NULL;
11298c2ecf20Sopenharmony_ci			}
11308c2ecf20Sopenharmony_ci		}
11318c2ecf20Sopenharmony_ci	}
11328c2ecf20Sopenharmony_ci
11338c2ecf20Sopenharmony_ci	return vchan_tx_prep(&c->vc, &d->vd, tx_flags);
11348c2ecf20Sopenharmony_ci}
11358c2ecf20Sopenharmony_ci
11368c2ecf20Sopenharmony_cistatic struct dma_async_tx_descriptor *omap_dma_prep_dma_cyclic(
11378c2ecf20Sopenharmony_ci	struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len,
11388c2ecf20Sopenharmony_ci	size_t period_len, enum dma_transfer_direction dir, unsigned long flags)
11398c2ecf20Sopenharmony_ci{
11408c2ecf20Sopenharmony_ci	struct omap_dmadev *od = to_omap_dma_dev(chan->device);
11418c2ecf20Sopenharmony_ci	struct omap_chan *c = to_omap_dma_chan(chan);
11428c2ecf20Sopenharmony_ci	enum dma_slave_buswidth dev_width;
11438c2ecf20Sopenharmony_ci	struct omap_desc *d;
11448c2ecf20Sopenharmony_ci	dma_addr_t dev_addr;
11458c2ecf20Sopenharmony_ci	unsigned es;
11468c2ecf20Sopenharmony_ci	u32 burst;
11478c2ecf20Sopenharmony_ci
11488c2ecf20Sopenharmony_ci	if (dir == DMA_DEV_TO_MEM) {
11498c2ecf20Sopenharmony_ci		dev_addr = c->cfg.src_addr;
11508c2ecf20Sopenharmony_ci		dev_width = c->cfg.src_addr_width;
11518c2ecf20Sopenharmony_ci		burst = c->cfg.src_maxburst;
11528c2ecf20Sopenharmony_ci	} else if (dir == DMA_MEM_TO_DEV) {
11538c2ecf20Sopenharmony_ci		dev_addr = c->cfg.dst_addr;
11548c2ecf20Sopenharmony_ci		dev_width = c->cfg.dst_addr_width;
11558c2ecf20Sopenharmony_ci		burst = c->cfg.dst_maxburst;
11568c2ecf20Sopenharmony_ci	} else {
11578c2ecf20Sopenharmony_ci		dev_err(chan->device->dev, "%s: bad direction?\n", __func__);
11588c2ecf20Sopenharmony_ci		return NULL;
11598c2ecf20Sopenharmony_ci	}
11608c2ecf20Sopenharmony_ci
11618c2ecf20Sopenharmony_ci	/* Bus width translates to the element size (ES) */
11628c2ecf20Sopenharmony_ci	switch (dev_width) {
11638c2ecf20Sopenharmony_ci	case DMA_SLAVE_BUSWIDTH_1_BYTE:
11648c2ecf20Sopenharmony_ci		es = CSDP_DATA_TYPE_8;
11658c2ecf20Sopenharmony_ci		break;
11668c2ecf20Sopenharmony_ci	case DMA_SLAVE_BUSWIDTH_2_BYTES:
11678c2ecf20Sopenharmony_ci		es = CSDP_DATA_TYPE_16;
11688c2ecf20Sopenharmony_ci		break;
11698c2ecf20Sopenharmony_ci	case DMA_SLAVE_BUSWIDTH_4_BYTES:
11708c2ecf20Sopenharmony_ci		es = CSDP_DATA_TYPE_32;
11718c2ecf20Sopenharmony_ci		break;
11728c2ecf20Sopenharmony_ci	default: /* not reached */
11738c2ecf20Sopenharmony_ci		return NULL;
11748c2ecf20Sopenharmony_ci	}
11758c2ecf20Sopenharmony_ci
11768c2ecf20Sopenharmony_ci	/* Now allocate and setup the descriptor. */
11778c2ecf20Sopenharmony_ci	d = kzalloc(sizeof(*d) + sizeof(d->sg[0]), GFP_ATOMIC);
11788c2ecf20Sopenharmony_ci	if (!d)
11798c2ecf20Sopenharmony_ci		return NULL;
11808c2ecf20Sopenharmony_ci
11818c2ecf20Sopenharmony_ci	d->dir = dir;
11828c2ecf20Sopenharmony_ci	d->dev_addr = dev_addr;
11838c2ecf20Sopenharmony_ci	d->fi = burst;
11848c2ecf20Sopenharmony_ci	d->es = es;
11858c2ecf20Sopenharmony_ci	d->sg[0].addr = buf_addr;
11868c2ecf20Sopenharmony_ci	d->sg[0].en = period_len / es_bytes[es];
11878c2ecf20Sopenharmony_ci	d->sg[0].fn = buf_len / period_len;
11888c2ecf20Sopenharmony_ci	d->sglen = 1;
11898c2ecf20Sopenharmony_ci
11908c2ecf20Sopenharmony_ci	d->ccr = c->ccr;
11918c2ecf20Sopenharmony_ci	if (dir == DMA_DEV_TO_MEM)
11928c2ecf20Sopenharmony_ci		d->ccr |= CCR_DST_AMODE_POSTINC | CCR_SRC_AMODE_CONSTANT;
11938c2ecf20Sopenharmony_ci	else
11948c2ecf20Sopenharmony_ci		d->ccr |= CCR_DST_AMODE_CONSTANT | CCR_SRC_AMODE_POSTINC;
11958c2ecf20Sopenharmony_ci
11968c2ecf20Sopenharmony_ci	d->cicr = CICR_DROP_IE;
11978c2ecf20Sopenharmony_ci	if (flags & DMA_PREP_INTERRUPT)
11988c2ecf20Sopenharmony_ci		d->cicr |= CICR_FRAME_IE;
11998c2ecf20Sopenharmony_ci
12008c2ecf20Sopenharmony_ci	d->csdp = es;
12018c2ecf20Sopenharmony_ci
12028c2ecf20Sopenharmony_ci	if (dma_omap1()) {
12038c2ecf20Sopenharmony_ci		d->cicr |= CICR_TOUT_IE;
12048c2ecf20Sopenharmony_ci
12058c2ecf20Sopenharmony_ci		if (dir == DMA_DEV_TO_MEM)
12068c2ecf20Sopenharmony_ci			d->csdp |= CSDP_DST_PORT_EMIFF | CSDP_SRC_PORT_MPUI;
12078c2ecf20Sopenharmony_ci		else
12088c2ecf20Sopenharmony_ci			d->csdp |= CSDP_DST_PORT_MPUI | CSDP_SRC_PORT_EMIFF;
12098c2ecf20Sopenharmony_ci	} else {
12108c2ecf20Sopenharmony_ci		if (burst)
12118c2ecf20Sopenharmony_ci			d->ccr |= CCR_SYNC_PACKET;
12128c2ecf20Sopenharmony_ci		else
12138c2ecf20Sopenharmony_ci			d->ccr |= CCR_SYNC_ELEMENT;
12148c2ecf20Sopenharmony_ci
12158c2ecf20Sopenharmony_ci		if (dir == DMA_DEV_TO_MEM) {
12168c2ecf20Sopenharmony_ci			d->ccr |= CCR_TRIGGER_SRC;
12178c2ecf20Sopenharmony_ci			d->csdp |= CSDP_DST_PACKED;
12188c2ecf20Sopenharmony_ci		} else {
12198c2ecf20Sopenharmony_ci			d->csdp |= CSDP_SRC_PACKED;
12208c2ecf20Sopenharmony_ci		}
12218c2ecf20Sopenharmony_ci
12228c2ecf20Sopenharmony_ci		d->cicr |= CICR_MISALIGNED_ERR_IE | CICR_TRANS_ERR_IE;
12238c2ecf20Sopenharmony_ci
12248c2ecf20Sopenharmony_ci		d->csdp |= CSDP_DST_BURST_64 | CSDP_SRC_BURST_64;
12258c2ecf20Sopenharmony_ci	}
12268c2ecf20Sopenharmony_ci
12278c2ecf20Sopenharmony_ci	if (__dma_omap15xx(od->plat->dma_attr))
12288c2ecf20Sopenharmony_ci		d->ccr |= CCR_AUTO_INIT | CCR_REPEAT;
12298c2ecf20Sopenharmony_ci	else
12308c2ecf20Sopenharmony_ci		d->clnk_ctrl = c->dma_ch | CLNK_CTRL_ENABLE_LNK;
12318c2ecf20Sopenharmony_ci
12328c2ecf20Sopenharmony_ci	c->cyclic = true;
12338c2ecf20Sopenharmony_ci
12348c2ecf20Sopenharmony_ci	return vchan_tx_prep(&c->vc, &d->vd, flags);
12358c2ecf20Sopenharmony_ci}
12368c2ecf20Sopenharmony_ci
12378c2ecf20Sopenharmony_cistatic struct dma_async_tx_descriptor *omap_dma_prep_dma_memcpy(
12388c2ecf20Sopenharmony_ci	struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
12398c2ecf20Sopenharmony_ci	size_t len, unsigned long tx_flags)
12408c2ecf20Sopenharmony_ci{
12418c2ecf20Sopenharmony_ci	struct omap_chan *c = to_omap_dma_chan(chan);
12428c2ecf20Sopenharmony_ci	struct omap_desc *d;
12438c2ecf20Sopenharmony_ci	uint8_t data_type;
12448c2ecf20Sopenharmony_ci
12458c2ecf20Sopenharmony_ci	d = kzalloc(sizeof(*d) + sizeof(d->sg[0]), GFP_ATOMIC);
12468c2ecf20Sopenharmony_ci	if (!d)
12478c2ecf20Sopenharmony_ci		return NULL;
12488c2ecf20Sopenharmony_ci
12498c2ecf20Sopenharmony_ci	data_type = __ffs((src | dest | len));
12508c2ecf20Sopenharmony_ci	if (data_type > CSDP_DATA_TYPE_32)
12518c2ecf20Sopenharmony_ci		data_type = CSDP_DATA_TYPE_32;
12528c2ecf20Sopenharmony_ci
12538c2ecf20Sopenharmony_ci	d->dir = DMA_MEM_TO_MEM;
12548c2ecf20Sopenharmony_ci	d->dev_addr = src;
12558c2ecf20Sopenharmony_ci	d->fi = 0;
12568c2ecf20Sopenharmony_ci	d->es = data_type;
12578c2ecf20Sopenharmony_ci	d->sg[0].en = len / BIT(data_type);
12588c2ecf20Sopenharmony_ci	d->sg[0].fn = 1;
12598c2ecf20Sopenharmony_ci	d->sg[0].addr = dest;
12608c2ecf20Sopenharmony_ci	d->sglen = 1;
12618c2ecf20Sopenharmony_ci	d->ccr = c->ccr;
12628c2ecf20Sopenharmony_ci	d->ccr |= CCR_DST_AMODE_POSTINC | CCR_SRC_AMODE_POSTINC;
12638c2ecf20Sopenharmony_ci
12648c2ecf20Sopenharmony_ci	if (tx_flags & DMA_PREP_INTERRUPT)
12658c2ecf20Sopenharmony_ci		d->cicr |= CICR_FRAME_IE;
12668c2ecf20Sopenharmony_ci	else
12678c2ecf20Sopenharmony_ci		d->polled = true;
12688c2ecf20Sopenharmony_ci
12698c2ecf20Sopenharmony_ci	d->csdp = data_type;
12708c2ecf20Sopenharmony_ci
12718c2ecf20Sopenharmony_ci	if (dma_omap1()) {
12728c2ecf20Sopenharmony_ci		d->cicr |= CICR_TOUT_IE;
12738c2ecf20Sopenharmony_ci		d->csdp |= CSDP_DST_PORT_EMIFF | CSDP_SRC_PORT_EMIFF;
12748c2ecf20Sopenharmony_ci	} else {
12758c2ecf20Sopenharmony_ci		d->csdp |= CSDP_DST_PACKED | CSDP_SRC_PACKED;
12768c2ecf20Sopenharmony_ci		d->cicr |= CICR_MISALIGNED_ERR_IE | CICR_TRANS_ERR_IE;
12778c2ecf20Sopenharmony_ci		d->csdp |= CSDP_DST_BURST_64 | CSDP_SRC_BURST_64;
12788c2ecf20Sopenharmony_ci	}
12798c2ecf20Sopenharmony_ci
12808c2ecf20Sopenharmony_ci	return vchan_tx_prep(&c->vc, &d->vd, tx_flags);
12818c2ecf20Sopenharmony_ci}
12828c2ecf20Sopenharmony_ci
12838c2ecf20Sopenharmony_cistatic struct dma_async_tx_descriptor *omap_dma_prep_dma_interleaved(
12848c2ecf20Sopenharmony_ci	struct dma_chan *chan, struct dma_interleaved_template *xt,
12858c2ecf20Sopenharmony_ci	unsigned long flags)
12868c2ecf20Sopenharmony_ci{
12878c2ecf20Sopenharmony_ci	struct omap_chan *c = to_omap_dma_chan(chan);
12888c2ecf20Sopenharmony_ci	struct omap_desc *d;
12898c2ecf20Sopenharmony_ci	struct omap_sg *sg;
12908c2ecf20Sopenharmony_ci	uint8_t data_type;
12918c2ecf20Sopenharmony_ci	size_t src_icg, dst_icg;
12928c2ecf20Sopenharmony_ci
12938c2ecf20Sopenharmony_ci	/* Slave mode is not supported */
12948c2ecf20Sopenharmony_ci	if (is_slave_direction(xt->dir))
12958c2ecf20Sopenharmony_ci		return NULL;
12968c2ecf20Sopenharmony_ci
12978c2ecf20Sopenharmony_ci	if (xt->frame_size != 1 || xt->numf == 0)
12988c2ecf20Sopenharmony_ci		return NULL;
12998c2ecf20Sopenharmony_ci
13008c2ecf20Sopenharmony_ci	d = kzalloc(sizeof(*d) + sizeof(d->sg[0]), GFP_ATOMIC);
13018c2ecf20Sopenharmony_ci	if (!d)
13028c2ecf20Sopenharmony_ci		return NULL;
13038c2ecf20Sopenharmony_ci
13048c2ecf20Sopenharmony_ci	data_type = __ffs((xt->src_start | xt->dst_start | xt->sgl[0].size));
13058c2ecf20Sopenharmony_ci	if (data_type > CSDP_DATA_TYPE_32)
13068c2ecf20Sopenharmony_ci		data_type = CSDP_DATA_TYPE_32;
13078c2ecf20Sopenharmony_ci
13088c2ecf20Sopenharmony_ci	sg = &d->sg[0];
13098c2ecf20Sopenharmony_ci	d->dir = DMA_MEM_TO_MEM;
13108c2ecf20Sopenharmony_ci	d->dev_addr = xt->src_start;
13118c2ecf20Sopenharmony_ci	d->es = data_type;
13128c2ecf20Sopenharmony_ci	sg->en = xt->sgl[0].size / BIT(data_type);
13138c2ecf20Sopenharmony_ci	sg->fn = xt->numf;
13148c2ecf20Sopenharmony_ci	sg->addr = xt->dst_start;
13158c2ecf20Sopenharmony_ci	d->sglen = 1;
13168c2ecf20Sopenharmony_ci	d->ccr = c->ccr;
13178c2ecf20Sopenharmony_ci
13188c2ecf20Sopenharmony_ci	src_icg = dmaengine_get_src_icg(xt, &xt->sgl[0]);
13198c2ecf20Sopenharmony_ci	dst_icg = dmaengine_get_dst_icg(xt, &xt->sgl[0]);
13208c2ecf20Sopenharmony_ci	if (src_icg) {
13218c2ecf20Sopenharmony_ci		d->ccr |= CCR_SRC_AMODE_DBLIDX;
13228c2ecf20Sopenharmony_ci		d->ei = 1;
13238c2ecf20Sopenharmony_ci		d->fi = src_icg + 1;
13248c2ecf20Sopenharmony_ci	} else if (xt->src_inc) {
13258c2ecf20Sopenharmony_ci		d->ccr |= CCR_SRC_AMODE_POSTINC;
13268c2ecf20Sopenharmony_ci		d->fi = 0;
13278c2ecf20Sopenharmony_ci	} else {
13288c2ecf20Sopenharmony_ci		dev_err(chan->device->dev,
13298c2ecf20Sopenharmony_ci			"%s: SRC constant addressing is not supported\n",
13308c2ecf20Sopenharmony_ci			__func__);
13318c2ecf20Sopenharmony_ci		kfree(d);
13328c2ecf20Sopenharmony_ci		return NULL;
13338c2ecf20Sopenharmony_ci	}
13348c2ecf20Sopenharmony_ci
13358c2ecf20Sopenharmony_ci	if (dst_icg) {
13368c2ecf20Sopenharmony_ci		d->ccr |= CCR_DST_AMODE_DBLIDX;
13378c2ecf20Sopenharmony_ci		sg->ei = 1;
13388c2ecf20Sopenharmony_ci		sg->fi = dst_icg + 1;
13398c2ecf20Sopenharmony_ci	} else if (xt->dst_inc) {
13408c2ecf20Sopenharmony_ci		d->ccr |= CCR_DST_AMODE_POSTINC;
13418c2ecf20Sopenharmony_ci		sg->fi = 0;
13428c2ecf20Sopenharmony_ci	} else {
13438c2ecf20Sopenharmony_ci		dev_err(chan->device->dev,
13448c2ecf20Sopenharmony_ci			"%s: DST constant addressing is not supported\n",
13458c2ecf20Sopenharmony_ci			__func__);
13468c2ecf20Sopenharmony_ci		kfree(d);
13478c2ecf20Sopenharmony_ci		return NULL;
13488c2ecf20Sopenharmony_ci	}
13498c2ecf20Sopenharmony_ci
13508c2ecf20Sopenharmony_ci	d->cicr = CICR_DROP_IE | CICR_FRAME_IE;
13518c2ecf20Sopenharmony_ci
13528c2ecf20Sopenharmony_ci	d->csdp = data_type;
13538c2ecf20Sopenharmony_ci
13548c2ecf20Sopenharmony_ci	if (dma_omap1()) {
13558c2ecf20Sopenharmony_ci		d->cicr |= CICR_TOUT_IE;
13568c2ecf20Sopenharmony_ci		d->csdp |= CSDP_DST_PORT_EMIFF | CSDP_SRC_PORT_EMIFF;
13578c2ecf20Sopenharmony_ci	} else {
13588c2ecf20Sopenharmony_ci		d->csdp |= CSDP_DST_PACKED | CSDP_SRC_PACKED;
13598c2ecf20Sopenharmony_ci		d->cicr |= CICR_MISALIGNED_ERR_IE | CICR_TRANS_ERR_IE;
13608c2ecf20Sopenharmony_ci		d->csdp |= CSDP_DST_BURST_64 | CSDP_SRC_BURST_64;
13618c2ecf20Sopenharmony_ci	}
13628c2ecf20Sopenharmony_ci
13638c2ecf20Sopenharmony_ci	return vchan_tx_prep(&c->vc, &d->vd, flags);
13648c2ecf20Sopenharmony_ci}
13658c2ecf20Sopenharmony_ci
13668c2ecf20Sopenharmony_cistatic int omap_dma_slave_config(struct dma_chan *chan, struct dma_slave_config *cfg)
13678c2ecf20Sopenharmony_ci{
13688c2ecf20Sopenharmony_ci	struct omap_chan *c = to_omap_dma_chan(chan);
13698c2ecf20Sopenharmony_ci
13708c2ecf20Sopenharmony_ci	if (cfg->src_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES ||
13718c2ecf20Sopenharmony_ci	    cfg->dst_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES)
13728c2ecf20Sopenharmony_ci		return -EINVAL;
13738c2ecf20Sopenharmony_ci
13748c2ecf20Sopenharmony_ci	if (cfg->src_maxburst > chan->device->max_burst ||
13758c2ecf20Sopenharmony_ci	    cfg->dst_maxburst > chan->device->max_burst)
13768c2ecf20Sopenharmony_ci		return -EINVAL;
13778c2ecf20Sopenharmony_ci
13788c2ecf20Sopenharmony_ci	memcpy(&c->cfg, cfg, sizeof(c->cfg));
13798c2ecf20Sopenharmony_ci
13808c2ecf20Sopenharmony_ci	return 0;
13818c2ecf20Sopenharmony_ci}
13828c2ecf20Sopenharmony_ci
13838c2ecf20Sopenharmony_cistatic int omap_dma_terminate_all(struct dma_chan *chan)
13848c2ecf20Sopenharmony_ci{
13858c2ecf20Sopenharmony_ci	struct omap_chan *c = to_omap_dma_chan(chan);
13868c2ecf20Sopenharmony_ci	unsigned long flags;
13878c2ecf20Sopenharmony_ci	LIST_HEAD(head);
13888c2ecf20Sopenharmony_ci
13898c2ecf20Sopenharmony_ci	spin_lock_irqsave(&c->vc.lock, flags);
13908c2ecf20Sopenharmony_ci
13918c2ecf20Sopenharmony_ci	/*
13928c2ecf20Sopenharmony_ci	 * Stop DMA activity: we assume the callback will not be called
13938c2ecf20Sopenharmony_ci	 * after omap_dma_stop() returns (even if it does, it will see
13948c2ecf20Sopenharmony_ci	 * c->desc is NULL and exit.)
13958c2ecf20Sopenharmony_ci	 */
13968c2ecf20Sopenharmony_ci	if (c->desc) {
13978c2ecf20Sopenharmony_ci		vchan_terminate_vdesc(&c->desc->vd);
13988c2ecf20Sopenharmony_ci		c->desc = NULL;
13998c2ecf20Sopenharmony_ci		/* Avoid stopping the dma twice */
14008c2ecf20Sopenharmony_ci		if (!c->paused)
14018c2ecf20Sopenharmony_ci			omap_dma_stop(c);
14028c2ecf20Sopenharmony_ci	}
14038c2ecf20Sopenharmony_ci
14048c2ecf20Sopenharmony_ci	c->cyclic = false;
14058c2ecf20Sopenharmony_ci	c->paused = false;
14068c2ecf20Sopenharmony_ci
14078c2ecf20Sopenharmony_ci	vchan_get_all_descriptors(&c->vc, &head);
14088c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&c->vc.lock, flags);
14098c2ecf20Sopenharmony_ci	vchan_dma_desc_free_list(&c->vc, &head);
14108c2ecf20Sopenharmony_ci
14118c2ecf20Sopenharmony_ci	return 0;
14128c2ecf20Sopenharmony_ci}
14138c2ecf20Sopenharmony_ci
14148c2ecf20Sopenharmony_cistatic void omap_dma_synchronize(struct dma_chan *chan)
14158c2ecf20Sopenharmony_ci{
14168c2ecf20Sopenharmony_ci	struct omap_chan *c = to_omap_dma_chan(chan);
14178c2ecf20Sopenharmony_ci
14188c2ecf20Sopenharmony_ci	vchan_synchronize(&c->vc);
14198c2ecf20Sopenharmony_ci}
14208c2ecf20Sopenharmony_ci
14218c2ecf20Sopenharmony_cistatic int omap_dma_pause(struct dma_chan *chan)
14228c2ecf20Sopenharmony_ci{
14238c2ecf20Sopenharmony_ci	struct omap_chan *c = to_omap_dma_chan(chan);
14248c2ecf20Sopenharmony_ci	struct omap_dmadev *od = to_omap_dma_dev(chan->device);
14258c2ecf20Sopenharmony_ci	unsigned long flags;
14268c2ecf20Sopenharmony_ci	int ret = -EINVAL;
14278c2ecf20Sopenharmony_ci	bool can_pause = false;
14288c2ecf20Sopenharmony_ci
14298c2ecf20Sopenharmony_ci	spin_lock_irqsave(&od->irq_lock, flags);
14308c2ecf20Sopenharmony_ci
14318c2ecf20Sopenharmony_ci	if (!c->desc)
14328c2ecf20Sopenharmony_ci		goto out;
14338c2ecf20Sopenharmony_ci
14348c2ecf20Sopenharmony_ci	if (c->cyclic)
14358c2ecf20Sopenharmony_ci		can_pause = true;
14368c2ecf20Sopenharmony_ci
14378c2ecf20Sopenharmony_ci	/*
14388c2ecf20Sopenharmony_ci	 * We do not allow DMA_MEM_TO_DEV transfers to be paused.
14398c2ecf20Sopenharmony_ci	 * From the AM572x TRM, 16.1.4.18 Disabling a Channel During Transfer:
14408c2ecf20Sopenharmony_ci	 * "When a channel is disabled during a transfer, the channel undergoes
14418c2ecf20Sopenharmony_ci	 * an abort, unless it is hardware-source-synchronized …".
14428c2ecf20Sopenharmony_ci	 * A source-synchronised channel is one where the fetching of data is
14438c2ecf20Sopenharmony_ci	 * under control of the device. In other words, a device-to-memory
14448c2ecf20Sopenharmony_ci	 * transfer. So, a destination-synchronised channel (which would be a
14458c2ecf20Sopenharmony_ci	 * memory-to-device transfer) undergoes an abort if the the CCR_ENABLE
14468c2ecf20Sopenharmony_ci	 * bit is cleared.
14478c2ecf20Sopenharmony_ci	 * From 16.1.4.20.4.6.2 Abort: "If an abort trigger occurs, the channel
14488c2ecf20Sopenharmony_ci	 * aborts immediately after completion of current read/write
14498c2ecf20Sopenharmony_ci	 * transactions and then the FIFO is cleaned up." The term "cleaned up"
14508c2ecf20Sopenharmony_ci	 * is not defined. TI recommends to check that RD_ACTIVE and WR_ACTIVE
14518c2ecf20Sopenharmony_ci	 * are both clear _before_ disabling the channel, otherwise data loss
14528c2ecf20Sopenharmony_ci	 * will occur.
14538c2ecf20Sopenharmony_ci	 * The problem is that if the channel is active, then device activity
14548c2ecf20Sopenharmony_ci	 * can result in DMA activity starting between reading those as both
14558c2ecf20Sopenharmony_ci	 * clear and the write to DMA_CCR to clear the enable bit hitting the
14568c2ecf20Sopenharmony_ci	 * hardware. If the DMA hardware can't drain the data in its FIFO to the
14578c2ecf20Sopenharmony_ci	 * destination, then data loss "might" occur (say if we write to an UART
14588c2ecf20Sopenharmony_ci	 * and the UART is not accepting any further data).
14598c2ecf20Sopenharmony_ci	 */
14608c2ecf20Sopenharmony_ci	else if (c->desc->dir == DMA_DEV_TO_MEM)
14618c2ecf20Sopenharmony_ci		can_pause = true;
14628c2ecf20Sopenharmony_ci
14638c2ecf20Sopenharmony_ci	if (can_pause && !c->paused) {
14648c2ecf20Sopenharmony_ci		ret = omap_dma_stop(c);
14658c2ecf20Sopenharmony_ci		if (!ret)
14668c2ecf20Sopenharmony_ci			c->paused = true;
14678c2ecf20Sopenharmony_ci	}
14688c2ecf20Sopenharmony_ciout:
14698c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&od->irq_lock, flags);
14708c2ecf20Sopenharmony_ci
14718c2ecf20Sopenharmony_ci	return ret;
14728c2ecf20Sopenharmony_ci}
14738c2ecf20Sopenharmony_ci
14748c2ecf20Sopenharmony_cistatic int omap_dma_resume(struct dma_chan *chan)
14758c2ecf20Sopenharmony_ci{
14768c2ecf20Sopenharmony_ci	struct omap_chan *c = to_omap_dma_chan(chan);
14778c2ecf20Sopenharmony_ci	struct omap_dmadev *od = to_omap_dma_dev(chan->device);
14788c2ecf20Sopenharmony_ci	unsigned long flags;
14798c2ecf20Sopenharmony_ci	int ret = -EINVAL;
14808c2ecf20Sopenharmony_ci
14818c2ecf20Sopenharmony_ci	spin_lock_irqsave(&od->irq_lock, flags);
14828c2ecf20Sopenharmony_ci
14838c2ecf20Sopenharmony_ci	if (c->paused && c->desc) {
14848c2ecf20Sopenharmony_ci		mb();
14858c2ecf20Sopenharmony_ci
14868c2ecf20Sopenharmony_ci		/* Restore channel link register */
14878c2ecf20Sopenharmony_ci		omap_dma_chan_write(c, CLNK_CTRL, c->desc->clnk_ctrl);
14888c2ecf20Sopenharmony_ci
14898c2ecf20Sopenharmony_ci		omap_dma_start(c, c->desc);
14908c2ecf20Sopenharmony_ci		c->paused = false;
14918c2ecf20Sopenharmony_ci		ret = 0;
14928c2ecf20Sopenharmony_ci	}
14938c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&od->irq_lock, flags);
14948c2ecf20Sopenharmony_ci
14958c2ecf20Sopenharmony_ci	return ret;
14968c2ecf20Sopenharmony_ci}
14978c2ecf20Sopenharmony_ci
14988c2ecf20Sopenharmony_cistatic int omap_dma_chan_init(struct omap_dmadev *od)
14998c2ecf20Sopenharmony_ci{
15008c2ecf20Sopenharmony_ci	struct omap_chan *c;
15018c2ecf20Sopenharmony_ci
15028c2ecf20Sopenharmony_ci	c = kzalloc(sizeof(*c), GFP_KERNEL);
15038c2ecf20Sopenharmony_ci	if (!c)
15048c2ecf20Sopenharmony_ci		return -ENOMEM;
15058c2ecf20Sopenharmony_ci
15068c2ecf20Sopenharmony_ci	c->reg_map = od->reg_map;
15078c2ecf20Sopenharmony_ci	c->vc.desc_free = omap_dma_desc_free;
15088c2ecf20Sopenharmony_ci	vchan_init(&c->vc, &od->ddev);
15098c2ecf20Sopenharmony_ci
15108c2ecf20Sopenharmony_ci	return 0;
15118c2ecf20Sopenharmony_ci}
15128c2ecf20Sopenharmony_ci
15138c2ecf20Sopenharmony_cistatic void omap_dma_free(struct omap_dmadev *od)
15148c2ecf20Sopenharmony_ci{
15158c2ecf20Sopenharmony_ci	while (!list_empty(&od->ddev.channels)) {
15168c2ecf20Sopenharmony_ci		struct omap_chan *c = list_first_entry(&od->ddev.channels,
15178c2ecf20Sopenharmony_ci			struct omap_chan, vc.chan.device_node);
15188c2ecf20Sopenharmony_ci
15198c2ecf20Sopenharmony_ci		list_del(&c->vc.chan.device_node);
15208c2ecf20Sopenharmony_ci		tasklet_kill(&c->vc.task);
15218c2ecf20Sopenharmony_ci		kfree(c);
15228c2ecf20Sopenharmony_ci	}
15238c2ecf20Sopenharmony_ci}
15248c2ecf20Sopenharmony_ci
15258c2ecf20Sopenharmony_ci/* Currently used by omap2 & 3 to block deeper SoC idle states */
15268c2ecf20Sopenharmony_cistatic bool omap_dma_busy(struct omap_dmadev *od)
15278c2ecf20Sopenharmony_ci{
15288c2ecf20Sopenharmony_ci	struct omap_chan *c;
15298c2ecf20Sopenharmony_ci	int lch = -1;
15308c2ecf20Sopenharmony_ci
15318c2ecf20Sopenharmony_ci	while (1) {
15328c2ecf20Sopenharmony_ci		lch = find_next_bit(od->lch_bitmap, od->lch_count, lch + 1);
15338c2ecf20Sopenharmony_ci		if (lch >= od->lch_count)
15348c2ecf20Sopenharmony_ci			break;
15358c2ecf20Sopenharmony_ci		c = od->lch_map[lch];
15368c2ecf20Sopenharmony_ci		if (!c)
15378c2ecf20Sopenharmony_ci			continue;
15388c2ecf20Sopenharmony_ci		if (omap_dma_chan_read(c, CCR) & CCR_ENABLE)
15398c2ecf20Sopenharmony_ci			return true;
15408c2ecf20Sopenharmony_ci	}
15418c2ecf20Sopenharmony_ci
15428c2ecf20Sopenharmony_ci	return false;
15438c2ecf20Sopenharmony_ci}
15448c2ecf20Sopenharmony_ci
15458c2ecf20Sopenharmony_ci/* Currently only used for omap2. For omap1, also a check for lcd_dma is needed */
15468c2ecf20Sopenharmony_cistatic int omap_dma_busy_notifier(struct notifier_block *nb,
15478c2ecf20Sopenharmony_ci				  unsigned long cmd, void *v)
15488c2ecf20Sopenharmony_ci{
15498c2ecf20Sopenharmony_ci	struct omap_dmadev *od;
15508c2ecf20Sopenharmony_ci
15518c2ecf20Sopenharmony_ci	od = container_of(nb, struct omap_dmadev, nb);
15528c2ecf20Sopenharmony_ci
15538c2ecf20Sopenharmony_ci	switch (cmd) {
15548c2ecf20Sopenharmony_ci	case CPU_CLUSTER_PM_ENTER:
15558c2ecf20Sopenharmony_ci		if (omap_dma_busy(od))
15568c2ecf20Sopenharmony_ci			return NOTIFY_BAD;
15578c2ecf20Sopenharmony_ci		break;
15588c2ecf20Sopenharmony_ci	case CPU_CLUSTER_PM_ENTER_FAILED:
15598c2ecf20Sopenharmony_ci	case CPU_CLUSTER_PM_EXIT:
15608c2ecf20Sopenharmony_ci		break;
15618c2ecf20Sopenharmony_ci	}
15628c2ecf20Sopenharmony_ci
15638c2ecf20Sopenharmony_ci	return NOTIFY_OK;
15648c2ecf20Sopenharmony_ci}
15658c2ecf20Sopenharmony_ci
15668c2ecf20Sopenharmony_ci/*
15678c2ecf20Sopenharmony_ci * We are using IRQENABLE_L1, and legacy DMA code was using IRQENABLE_L0.
15688c2ecf20Sopenharmony_ci * As the DSP may be using IRQENABLE_L2 and L3, let's not touch those for
15698c2ecf20Sopenharmony_ci * now. Context save seems to be only currently needed on omap3.
15708c2ecf20Sopenharmony_ci */
15718c2ecf20Sopenharmony_cistatic void omap_dma_context_save(struct omap_dmadev *od)
15728c2ecf20Sopenharmony_ci{
15738c2ecf20Sopenharmony_ci	od->context.irqenable_l0 = omap_dma_glbl_read(od, IRQENABLE_L0);
15748c2ecf20Sopenharmony_ci	od->context.irqenable_l1 = omap_dma_glbl_read(od, IRQENABLE_L1);
15758c2ecf20Sopenharmony_ci	od->context.ocp_sysconfig = omap_dma_glbl_read(od, OCP_SYSCONFIG);
15768c2ecf20Sopenharmony_ci	od->context.gcr = omap_dma_glbl_read(od, GCR);
15778c2ecf20Sopenharmony_ci}
15788c2ecf20Sopenharmony_ci
15798c2ecf20Sopenharmony_cistatic void omap_dma_context_restore(struct omap_dmadev *od)
15808c2ecf20Sopenharmony_ci{
15818c2ecf20Sopenharmony_ci	int i;
15828c2ecf20Sopenharmony_ci
15838c2ecf20Sopenharmony_ci	omap_dma_glbl_write(od, GCR, od->context.gcr);
15848c2ecf20Sopenharmony_ci	omap_dma_glbl_write(od, OCP_SYSCONFIG, od->context.ocp_sysconfig);
15858c2ecf20Sopenharmony_ci	omap_dma_glbl_write(od, IRQENABLE_L0, od->context.irqenable_l0);
15868c2ecf20Sopenharmony_ci	omap_dma_glbl_write(od, IRQENABLE_L1, od->context.irqenable_l1);
15878c2ecf20Sopenharmony_ci
15888c2ecf20Sopenharmony_ci	/* Clear IRQSTATUS_L0 as legacy DMA code is no longer doing it */
15898c2ecf20Sopenharmony_ci	if (od->plat->errata & DMA_ROMCODE_BUG)
15908c2ecf20Sopenharmony_ci		omap_dma_glbl_write(od, IRQSTATUS_L0, 0);
15918c2ecf20Sopenharmony_ci
15928c2ecf20Sopenharmony_ci	/* Clear dma channels */
15938c2ecf20Sopenharmony_ci	for (i = 0; i < od->lch_count; i++)
15948c2ecf20Sopenharmony_ci		omap_dma_clear_lch(od, i);
15958c2ecf20Sopenharmony_ci}
15968c2ecf20Sopenharmony_ci
15978c2ecf20Sopenharmony_ci/* Currently only used for omap3 */
15988c2ecf20Sopenharmony_cistatic int omap_dma_context_notifier(struct notifier_block *nb,
15998c2ecf20Sopenharmony_ci				     unsigned long cmd, void *v)
16008c2ecf20Sopenharmony_ci{
16018c2ecf20Sopenharmony_ci	struct omap_dmadev *od;
16028c2ecf20Sopenharmony_ci
16038c2ecf20Sopenharmony_ci	od = container_of(nb, struct omap_dmadev, nb);
16048c2ecf20Sopenharmony_ci
16058c2ecf20Sopenharmony_ci	switch (cmd) {
16068c2ecf20Sopenharmony_ci	case CPU_CLUSTER_PM_ENTER:
16078c2ecf20Sopenharmony_ci		if (omap_dma_busy(od))
16088c2ecf20Sopenharmony_ci			return NOTIFY_BAD;
16098c2ecf20Sopenharmony_ci		omap_dma_context_save(od);
16108c2ecf20Sopenharmony_ci		break;
16118c2ecf20Sopenharmony_ci	case CPU_CLUSTER_PM_ENTER_FAILED:
16128c2ecf20Sopenharmony_ci	case CPU_CLUSTER_PM_EXIT:
16138c2ecf20Sopenharmony_ci		omap_dma_context_restore(od);
16148c2ecf20Sopenharmony_ci		break;
16158c2ecf20Sopenharmony_ci	}
16168c2ecf20Sopenharmony_ci
16178c2ecf20Sopenharmony_ci	return NOTIFY_OK;
16188c2ecf20Sopenharmony_ci}
16198c2ecf20Sopenharmony_ci
16208c2ecf20Sopenharmony_cistatic void omap_dma_init_gcr(struct omap_dmadev *od, int arb_rate,
16218c2ecf20Sopenharmony_ci			      int max_fifo_depth, int tparams)
16228c2ecf20Sopenharmony_ci{
16238c2ecf20Sopenharmony_ci	u32 val;
16248c2ecf20Sopenharmony_ci
16258c2ecf20Sopenharmony_ci	/* Set only for omap2430 and later */
16268c2ecf20Sopenharmony_ci	if (!od->cfg->rw_priority)
16278c2ecf20Sopenharmony_ci		return;
16288c2ecf20Sopenharmony_ci
16298c2ecf20Sopenharmony_ci	if (max_fifo_depth == 0)
16308c2ecf20Sopenharmony_ci		max_fifo_depth = 1;
16318c2ecf20Sopenharmony_ci	if (arb_rate == 0)
16328c2ecf20Sopenharmony_ci		arb_rate = 1;
16338c2ecf20Sopenharmony_ci
16348c2ecf20Sopenharmony_ci	val = 0xff & max_fifo_depth;
16358c2ecf20Sopenharmony_ci	val |= (0x3 & tparams) << 12;
16368c2ecf20Sopenharmony_ci	val |= (arb_rate & 0xff) << 16;
16378c2ecf20Sopenharmony_ci
16388c2ecf20Sopenharmony_ci	omap_dma_glbl_write(od, GCR, val);
16398c2ecf20Sopenharmony_ci}
16408c2ecf20Sopenharmony_ci
16418c2ecf20Sopenharmony_ci#define OMAP_DMA_BUSWIDTHS	(BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | \
16428c2ecf20Sopenharmony_ci				 BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | \
16438c2ecf20Sopenharmony_ci				 BIT(DMA_SLAVE_BUSWIDTH_4_BYTES))
16448c2ecf20Sopenharmony_ci
16458c2ecf20Sopenharmony_ci/*
16468c2ecf20Sopenharmony_ci * No flags currently set for default configuration as omap1 is still
16478c2ecf20Sopenharmony_ci * using platform data.
16488c2ecf20Sopenharmony_ci */
16498c2ecf20Sopenharmony_cistatic const struct omap_dma_config default_cfg;
16508c2ecf20Sopenharmony_ci
16518c2ecf20Sopenharmony_cistatic int omap_dma_probe(struct platform_device *pdev)
16528c2ecf20Sopenharmony_ci{
16538c2ecf20Sopenharmony_ci	const struct omap_dma_config *conf;
16548c2ecf20Sopenharmony_ci	struct omap_dmadev *od;
16558c2ecf20Sopenharmony_ci	struct resource *res;
16568c2ecf20Sopenharmony_ci	int rc, i, irq;
16578c2ecf20Sopenharmony_ci	u32 val;
16588c2ecf20Sopenharmony_ci
16598c2ecf20Sopenharmony_ci	od = devm_kzalloc(&pdev->dev, sizeof(*od), GFP_KERNEL);
16608c2ecf20Sopenharmony_ci	if (!od)
16618c2ecf20Sopenharmony_ci		return -ENOMEM;
16628c2ecf20Sopenharmony_ci
16638c2ecf20Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
16648c2ecf20Sopenharmony_ci	od->base = devm_ioremap_resource(&pdev->dev, res);
16658c2ecf20Sopenharmony_ci	if (IS_ERR(od->base))
16668c2ecf20Sopenharmony_ci		return PTR_ERR(od->base);
16678c2ecf20Sopenharmony_ci
16688c2ecf20Sopenharmony_ci	conf = of_device_get_match_data(&pdev->dev);
16698c2ecf20Sopenharmony_ci	if (conf) {
16708c2ecf20Sopenharmony_ci		od->cfg = conf;
16718c2ecf20Sopenharmony_ci		od->plat = dev_get_platdata(&pdev->dev);
16728c2ecf20Sopenharmony_ci		if (!od->plat) {
16738c2ecf20Sopenharmony_ci			dev_err(&pdev->dev, "omap_system_dma_plat_info is missing");
16748c2ecf20Sopenharmony_ci			return -ENODEV;
16758c2ecf20Sopenharmony_ci		}
16768c2ecf20Sopenharmony_ci	} else {
16778c2ecf20Sopenharmony_ci		od->cfg = &default_cfg;
16788c2ecf20Sopenharmony_ci
16798c2ecf20Sopenharmony_ci		od->plat = omap_get_plat_info();
16808c2ecf20Sopenharmony_ci		if (!od->plat)
16818c2ecf20Sopenharmony_ci			return -EPROBE_DEFER;
16828c2ecf20Sopenharmony_ci	}
16838c2ecf20Sopenharmony_ci
16848c2ecf20Sopenharmony_ci	od->reg_map = od->plat->reg_map;
16858c2ecf20Sopenharmony_ci
16868c2ecf20Sopenharmony_ci	dma_cap_set(DMA_SLAVE, od->ddev.cap_mask);
16878c2ecf20Sopenharmony_ci	dma_cap_set(DMA_CYCLIC, od->ddev.cap_mask);
16888c2ecf20Sopenharmony_ci	dma_cap_set(DMA_MEMCPY, od->ddev.cap_mask);
16898c2ecf20Sopenharmony_ci	dma_cap_set(DMA_INTERLEAVE, od->ddev.cap_mask);
16908c2ecf20Sopenharmony_ci	od->ddev.device_alloc_chan_resources = omap_dma_alloc_chan_resources;
16918c2ecf20Sopenharmony_ci	od->ddev.device_free_chan_resources = omap_dma_free_chan_resources;
16928c2ecf20Sopenharmony_ci	od->ddev.device_tx_status = omap_dma_tx_status;
16938c2ecf20Sopenharmony_ci	od->ddev.device_issue_pending = omap_dma_issue_pending;
16948c2ecf20Sopenharmony_ci	od->ddev.device_prep_slave_sg = omap_dma_prep_slave_sg;
16958c2ecf20Sopenharmony_ci	od->ddev.device_prep_dma_cyclic = omap_dma_prep_dma_cyclic;
16968c2ecf20Sopenharmony_ci	od->ddev.device_prep_dma_memcpy = omap_dma_prep_dma_memcpy;
16978c2ecf20Sopenharmony_ci	od->ddev.device_prep_interleaved_dma = omap_dma_prep_dma_interleaved;
16988c2ecf20Sopenharmony_ci	od->ddev.device_config = omap_dma_slave_config;
16998c2ecf20Sopenharmony_ci	od->ddev.device_pause = omap_dma_pause;
17008c2ecf20Sopenharmony_ci	od->ddev.device_resume = omap_dma_resume;
17018c2ecf20Sopenharmony_ci	od->ddev.device_terminate_all = omap_dma_terminate_all;
17028c2ecf20Sopenharmony_ci	od->ddev.device_synchronize = omap_dma_synchronize;
17038c2ecf20Sopenharmony_ci	od->ddev.src_addr_widths = OMAP_DMA_BUSWIDTHS;
17048c2ecf20Sopenharmony_ci	od->ddev.dst_addr_widths = OMAP_DMA_BUSWIDTHS;
17058c2ecf20Sopenharmony_ci	od->ddev.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV);
17068c2ecf20Sopenharmony_ci	if (__dma_omap15xx(od->plat->dma_attr))
17078c2ecf20Sopenharmony_ci		od->ddev.residue_granularity =
17088c2ecf20Sopenharmony_ci				DMA_RESIDUE_GRANULARITY_DESCRIPTOR;
17098c2ecf20Sopenharmony_ci	else
17108c2ecf20Sopenharmony_ci		od->ddev.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST;
17118c2ecf20Sopenharmony_ci	od->ddev.max_burst = SZ_16M - 1; /* CCEN: 24bit unsigned */
17128c2ecf20Sopenharmony_ci	od->ddev.dev = &pdev->dev;
17138c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&od->ddev.channels);
17148c2ecf20Sopenharmony_ci	mutex_init(&od->lch_lock);
17158c2ecf20Sopenharmony_ci	spin_lock_init(&od->lock);
17168c2ecf20Sopenharmony_ci	spin_lock_init(&od->irq_lock);
17178c2ecf20Sopenharmony_ci
17188c2ecf20Sopenharmony_ci	/* Number of DMA requests */
17198c2ecf20Sopenharmony_ci	od->dma_requests = OMAP_SDMA_REQUESTS;
17208c2ecf20Sopenharmony_ci	if (pdev->dev.of_node && of_property_read_u32(pdev->dev.of_node,
17218c2ecf20Sopenharmony_ci						      "dma-requests",
17228c2ecf20Sopenharmony_ci						      &od->dma_requests)) {
17238c2ecf20Sopenharmony_ci		dev_info(&pdev->dev,
17248c2ecf20Sopenharmony_ci			 "Missing dma-requests property, using %u.\n",
17258c2ecf20Sopenharmony_ci			 OMAP_SDMA_REQUESTS);
17268c2ecf20Sopenharmony_ci	}
17278c2ecf20Sopenharmony_ci
17288c2ecf20Sopenharmony_ci	/* Number of available logical channels */
17298c2ecf20Sopenharmony_ci	if (!pdev->dev.of_node) {
17308c2ecf20Sopenharmony_ci		od->lch_count = od->plat->dma_attr->lch_count;
17318c2ecf20Sopenharmony_ci		if (unlikely(!od->lch_count))
17328c2ecf20Sopenharmony_ci			od->lch_count = OMAP_SDMA_CHANNELS;
17338c2ecf20Sopenharmony_ci	} else if (of_property_read_u32(pdev->dev.of_node, "dma-channels",
17348c2ecf20Sopenharmony_ci					&od->lch_count)) {
17358c2ecf20Sopenharmony_ci		dev_info(&pdev->dev,
17368c2ecf20Sopenharmony_ci			 "Missing dma-channels property, using %u.\n",
17378c2ecf20Sopenharmony_ci			 OMAP_SDMA_CHANNELS);
17388c2ecf20Sopenharmony_ci		od->lch_count = OMAP_SDMA_CHANNELS;
17398c2ecf20Sopenharmony_ci	}
17408c2ecf20Sopenharmony_ci
17418c2ecf20Sopenharmony_ci	/* Mask of allowed logical channels */
17428c2ecf20Sopenharmony_ci	if (pdev->dev.of_node && !of_property_read_u32(pdev->dev.of_node,
17438c2ecf20Sopenharmony_ci						       "dma-channel-mask",
17448c2ecf20Sopenharmony_ci						       &val)) {
17458c2ecf20Sopenharmony_ci		/* Tag channels not in mask as reserved */
17468c2ecf20Sopenharmony_ci		val = ~val;
17478c2ecf20Sopenharmony_ci		bitmap_from_arr32(od->lch_bitmap, &val, od->lch_count);
17488c2ecf20Sopenharmony_ci	}
17498c2ecf20Sopenharmony_ci	if (od->plat->dma_attr->dev_caps & HS_CHANNELS_RESERVED)
17508c2ecf20Sopenharmony_ci		bitmap_set(od->lch_bitmap, 0, 2);
17518c2ecf20Sopenharmony_ci
17528c2ecf20Sopenharmony_ci	od->lch_map = devm_kcalloc(&pdev->dev, od->lch_count,
17538c2ecf20Sopenharmony_ci				   sizeof(*od->lch_map),
17548c2ecf20Sopenharmony_ci				   GFP_KERNEL);
17558c2ecf20Sopenharmony_ci	if (!od->lch_map)
17568c2ecf20Sopenharmony_ci		return -ENOMEM;
17578c2ecf20Sopenharmony_ci
17588c2ecf20Sopenharmony_ci	for (i = 0; i < od->dma_requests; i++) {
17598c2ecf20Sopenharmony_ci		rc = omap_dma_chan_init(od);
17608c2ecf20Sopenharmony_ci		if (rc) {
17618c2ecf20Sopenharmony_ci			omap_dma_free(od);
17628c2ecf20Sopenharmony_ci			return rc;
17638c2ecf20Sopenharmony_ci		}
17648c2ecf20Sopenharmony_ci	}
17658c2ecf20Sopenharmony_ci
17668c2ecf20Sopenharmony_ci	irq = platform_get_irq(pdev, 1);
17678c2ecf20Sopenharmony_ci	if (irq <= 0) {
17688c2ecf20Sopenharmony_ci		dev_info(&pdev->dev, "failed to get L1 IRQ: %d\n", irq);
17698c2ecf20Sopenharmony_ci		od->legacy = true;
17708c2ecf20Sopenharmony_ci	} else {
17718c2ecf20Sopenharmony_ci		/* Disable all interrupts */
17728c2ecf20Sopenharmony_ci		od->irq_enable_mask = 0;
17738c2ecf20Sopenharmony_ci		omap_dma_glbl_write(od, IRQENABLE_L1, 0);
17748c2ecf20Sopenharmony_ci
17758c2ecf20Sopenharmony_ci		rc = devm_request_irq(&pdev->dev, irq, omap_dma_irq,
17768c2ecf20Sopenharmony_ci				      IRQF_SHARED, "omap-dma-engine", od);
17778c2ecf20Sopenharmony_ci		if (rc) {
17788c2ecf20Sopenharmony_ci			omap_dma_free(od);
17798c2ecf20Sopenharmony_ci			return rc;
17808c2ecf20Sopenharmony_ci		}
17818c2ecf20Sopenharmony_ci	}
17828c2ecf20Sopenharmony_ci
17838c2ecf20Sopenharmony_ci	if (omap_dma_glbl_read(od, CAPS_0) & CAPS_0_SUPPORT_LL123)
17848c2ecf20Sopenharmony_ci		od->ll123_supported = true;
17858c2ecf20Sopenharmony_ci
17868c2ecf20Sopenharmony_ci	od->ddev.filter.map = od->plat->slave_map;
17878c2ecf20Sopenharmony_ci	od->ddev.filter.mapcnt = od->plat->slavecnt;
17888c2ecf20Sopenharmony_ci	od->ddev.filter.fn = omap_dma_filter_fn;
17898c2ecf20Sopenharmony_ci
17908c2ecf20Sopenharmony_ci	if (od->ll123_supported) {
17918c2ecf20Sopenharmony_ci		od->desc_pool = dma_pool_create(dev_name(&pdev->dev),
17928c2ecf20Sopenharmony_ci						&pdev->dev,
17938c2ecf20Sopenharmony_ci						sizeof(struct omap_type2_desc),
17948c2ecf20Sopenharmony_ci						4, 0);
17958c2ecf20Sopenharmony_ci		if (!od->desc_pool) {
17968c2ecf20Sopenharmony_ci			dev_err(&pdev->dev,
17978c2ecf20Sopenharmony_ci				"unable to allocate descriptor pool\n");
17988c2ecf20Sopenharmony_ci			od->ll123_supported = false;
17998c2ecf20Sopenharmony_ci		}
18008c2ecf20Sopenharmony_ci	}
18018c2ecf20Sopenharmony_ci
18028c2ecf20Sopenharmony_ci	rc = dma_async_device_register(&od->ddev);
18038c2ecf20Sopenharmony_ci	if (rc) {
18048c2ecf20Sopenharmony_ci		pr_warn("OMAP-DMA: failed to register slave DMA engine device: %d\n",
18058c2ecf20Sopenharmony_ci			rc);
18068c2ecf20Sopenharmony_ci		omap_dma_free(od);
18078c2ecf20Sopenharmony_ci		return rc;
18088c2ecf20Sopenharmony_ci	}
18098c2ecf20Sopenharmony_ci
18108c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, od);
18118c2ecf20Sopenharmony_ci
18128c2ecf20Sopenharmony_ci	if (pdev->dev.of_node) {
18138c2ecf20Sopenharmony_ci		omap_dma_info.dma_cap = od->ddev.cap_mask;
18148c2ecf20Sopenharmony_ci
18158c2ecf20Sopenharmony_ci		/* Device-tree DMA controller registration */
18168c2ecf20Sopenharmony_ci		rc = of_dma_controller_register(pdev->dev.of_node,
18178c2ecf20Sopenharmony_ci				of_dma_simple_xlate, &omap_dma_info);
18188c2ecf20Sopenharmony_ci		if (rc) {
18198c2ecf20Sopenharmony_ci			pr_warn("OMAP-DMA: failed to register DMA controller\n");
18208c2ecf20Sopenharmony_ci			dma_async_device_unregister(&od->ddev);
18218c2ecf20Sopenharmony_ci			omap_dma_free(od);
18228c2ecf20Sopenharmony_ci		}
18238c2ecf20Sopenharmony_ci	}
18248c2ecf20Sopenharmony_ci
18258c2ecf20Sopenharmony_ci	omap_dma_init_gcr(od, DMA_DEFAULT_ARB_RATE, DMA_DEFAULT_FIFO_DEPTH, 0);
18268c2ecf20Sopenharmony_ci
18278c2ecf20Sopenharmony_ci	if (od->cfg->needs_busy_check) {
18288c2ecf20Sopenharmony_ci		od->nb.notifier_call = omap_dma_busy_notifier;
18298c2ecf20Sopenharmony_ci		cpu_pm_register_notifier(&od->nb);
18308c2ecf20Sopenharmony_ci	} else if (od->cfg->may_lose_context) {
18318c2ecf20Sopenharmony_ci		od->nb.notifier_call = omap_dma_context_notifier;
18328c2ecf20Sopenharmony_ci		cpu_pm_register_notifier(&od->nb);
18338c2ecf20Sopenharmony_ci	}
18348c2ecf20Sopenharmony_ci
18358c2ecf20Sopenharmony_ci	dev_info(&pdev->dev, "OMAP DMA engine driver%s\n",
18368c2ecf20Sopenharmony_ci		 od->ll123_supported ? " (LinkedList1/2/3 supported)" : "");
18378c2ecf20Sopenharmony_ci
18388c2ecf20Sopenharmony_ci	return rc;
18398c2ecf20Sopenharmony_ci}
18408c2ecf20Sopenharmony_ci
18418c2ecf20Sopenharmony_cistatic int omap_dma_remove(struct platform_device *pdev)
18428c2ecf20Sopenharmony_ci{
18438c2ecf20Sopenharmony_ci	struct omap_dmadev *od = platform_get_drvdata(pdev);
18448c2ecf20Sopenharmony_ci	int irq;
18458c2ecf20Sopenharmony_ci
18468c2ecf20Sopenharmony_ci	if (od->cfg->may_lose_context)
18478c2ecf20Sopenharmony_ci		cpu_pm_unregister_notifier(&od->nb);
18488c2ecf20Sopenharmony_ci
18498c2ecf20Sopenharmony_ci	if (pdev->dev.of_node)
18508c2ecf20Sopenharmony_ci		of_dma_controller_free(pdev->dev.of_node);
18518c2ecf20Sopenharmony_ci
18528c2ecf20Sopenharmony_ci	irq = platform_get_irq(pdev, 1);
18538c2ecf20Sopenharmony_ci	devm_free_irq(&pdev->dev, irq, od);
18548c2ecf20Sopenharmony_ci
18558c2ecf20Sopenharmony_ci	dma_async_device_unregister(&od->ddev);
18568c2ecf20Sopenharmony_ci
18578c2ecf20Sopenharmony_ci	if (!od->legacy) {
18588c2ecf20Sopenharmony_ci		/* Disable all interrupts */
18598c2ecf20Sopenharmony_ci		omap_dma_glbl_write(od, IRQENABLE_L0, 0);
18608c2ecf20Sopenharmony_ci	}
18618c2ecf20Sopenharmony_ci
18628c2ecf20Sopenharmony_ci	if (od->ll123_supported)
18638c2ecf20Sopenharmony_ci		dma_pool_destroy(od->desc_pool);
18648c2ecf20Sopenharmony_ci
18658c2ecf20Sopenharmony_ci	omap_dma_free(od);
18668c2ecf20Sopenharmony_ci
18678c2ecf20Sopenharmony_ci	return 0;
18688c2ecf20Sopenharmony_ci}
18698c2ecf20Sopenharmony_ci
18708c2ecf20Sopenharmony_cistatic const struct omap_dma_config omap2420_data = {
18718c2ecf20Sopenharmony_ci	.lch_end = CCFN,
18728c2ecf20Sopenharmony_ci	.rw_priority = true,
18738c2ecf20Sopenharmony_ci	.needs_lch_clear = true,
18748c2ecf20Sopenharmony_ci	.needs_busy_check = true,
18758c2ecf20Sopenharmony_ci};
18768c2ecf20Sopenharmony_ci
18778c2ecf20Sopenharmony_cistatic const struct omap_dma_config omap2430_data = {
18788c2ecf20Sopenharmony_ci	.lch_end = CCFN,
18798c2ecf20Sopenharmony_ci	.rw_priority = true,
18808c2ecf20Sopenharmony_ci	.needs_lch_clear = true,
18818c2ecf20Sopenharmony_ci};
18828c2ecf20Sopenharmony_ci
18838c2ecf20Sopenharmony_cistatic const struct omap_dma_config omap3430_data = {
18848c2ecf20Sopenharmony_ci	.lch_end = CCFN,
18858c2ecf20Sopenharmony_ci	.rw_priority = true,
18868c2ecf20Sopenharmony_ci	.needs_lch_clear = true,
18878c2ecf20Sopenharmony_ci	.may_lose_context = true,
18888c2ecf20Sopenharmony_ci};
18898c2ecf20Sopenharmony_ci
18908c2ecf20Sopenharmony_cistatic const struct omap_dma_config omap3630_data = {
18918c2ecf20Sopenharmony_ci	.lch_end = CCDN,
18928c2ecf20Sopenharmony_ci	.rw_priority = true,
18938c2ecf20Sopenharmony_ci	.needs_lch_clear = true,
18948c2ecf20Sopenharmony_ci	.may_lose_context = true,
18958c2ecf20Sopenharmony_ci};
18968c2ecf20Sopenharmony_ci
18978c2ecf20Sopenharmony_cistatic const struct omap_dma_config omap4_data = {
18988c2ecf20Sopenharmony_ci	.lch_end = CCDN,
18998c2ecf20Sopenharmony_ci	.rw_priority = true,
19008c2ecf20Sopenharmony_ci	.needs_lch_clear = true,
19018c2ecf20Sopenharmony_ci};
19028c2ecf20Sopenharmony_ci
19038c2ecf20Sopenharmony_cistatic const struct of_device_id omap_dma_match[] = {
19048c2ecf20Sopenharmony_ci	{ .compatible = "ti,omap2420-sdma", .data = &omap2420_data, },
19058c2ecf20Sopenharmony_ci	{ .compatible = "ti,omap2430-sdma", .data = &omap2430_data, },
19068c2ecf20Sopenharmony_ci	{ .compatible = "ti,omap3430-sdma", .data = &omap3430_data, },
19078c2ecf20Sopenharmony_ci	{ .compatible = "ti,omap3630-sdma", .data = &omap3630_data, },
19088c2ecf20Sopenharmony_ci	{ .compatible = "ti,omap4430-sdma", .data = &omap4_data, },
19098c2ecf20Sopenharmony_ci	{},
19108c2ecf20Sopenharmony_ci};
19118c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, omap_dma_match);
19128c2ecf20Sopenharmony_ci
19138c2ecf20Sopenharmony_cistatic struct platform_driver omap_dma_driver = {
19148c2ecf20Sopenharmony_ci	.probe	= omap_dma_probe,
19158c2ecf20Sopenharmony_ci	.remove	= omap_dma_remove,
19168c2ecf20Sopenharmony_ci	.driver = {
19178c2ecf20Sopenharmony_ci		.name = "omap-dma-engine",
19188c2ecf20Sopenharmony_ci		.of_match_table = omap_dma_match,
19198c2ecf20Sopenharmony_ci	},
19208c2ecf20Sopenharmony_ci};
19218c2ecf20Sopenharmony_ci
19228c2ecf20Sopenharmony_cistatic bool omap_dma_filter_fn(struct dma_chan *chan, void *param)
19238c2ecf20Sopenharmony_ci{
19248c2ecf20Sopenharmony_ci	if (chan->device->dev->driver == &omap_dma_driver.driver) {
19258c2ecf20Sopenharmony_ci		struct omap_dmadev *od = to_omap_dma_dev(chan->device);
19268c2ecf20Sopenharmony_ci		struct omap_chan *c = to_omap_dma_chan(chan);
19278c2ecf20Sopenharmony_ci		unsigned req = *(unsigned *)param;
19288c2ecf20Sopenharmony_ci
19298c2ecf20Sopenharmony_ci		if (req <= od->dma_requests) {
19308c2ecf20Sopenharmony_ci			c->dma_sig = req;
19318c2ecf20Sopenharmony_ci			return true;
19328c2ecf20Sopenharmony_ci		}
19338c2ecf20Sopenharmony_ci	}
19348c2ecf20Sopenharmony_ci	return false;
19358c2ecf20Sopenharmony_ci}
19368c2ecf20Sopenharmony_ci
19378c2ecf20Sopenharmony_cistatic int omap_dma_init(void)
19388c2ecf20Sopenharmony_ci{
19398c2ecf20Sopenharmony_ci	return platform_driver_register(&omap_dma_driver);
19408c2ecf20Sopenharmony_ci}
19418c2ecf20Sopenharmony_cisubsys_initcall(omap_dma_init);
19428c2ecf20Sopenharmony_ci
19438c2ecf20Sopenharmony_cistatic void __exit omap_dma_exit(void)
19448c2ecf20Sopenharmony_ci{
19458c2ecf20Sopenharmony_ci	platform_driver_unregister(&omap_dma_driver);
19468c2ecf20Sopenharmony_ci}
19478c2ecf20Sopenharmony_cimodule_exit(omap_dma_exit);
19488c2ecf20Sopenharmony_ci
19498c2ecf20Sopenharmony_ciMODULE_AUTHOR("Russell King");
19508c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
1951