18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2008
48c2ecf20Sopenharmony_ci * Guennadi Liakhovetski, DENX Software Engineering, <lg@denx.de>
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Copyright (C) 2005-2007 Freescale Semiconductor, Inc. All Rights Reserved.
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
108c2ecf20Sopenharmony_ci#include <linux/init.h>
118c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
128c2ecf20Sopenharmony_ci#include <linux/err.h>
138c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
148c2ecf20Sopenharmony_ci#include <linux/delay.h>
158c2ecf20Sopenharmony_ci#include <linux/list.h>
168c2ecf20Sopenharmony_ci#include <linux/clk.h>
178c2ecf20Sopenharmony_ci#include <linux/vmalloc.h>
188c2ecf20Sopenharmony_ci#include <linux/string.h>
198c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
208c2ecf20Sopenharmony_ci#include <linux/io.h>
218c2ecf20Sopenharmony_ci#include <linux/module.h>
228c2ecf20Sopenharmony_ci#include <linux/dma/ipu-dma.h>
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#include "../dmaengine.h"
258c2ecf20Sopenharmony_ci#include "ipu_intern.h"
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#define FS_VF_IN_VALID	0x00000002
288c2ecf20Sopenharmony_ci#define FS_ENC_IN_VALID	0x00000001
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_cistatic int ipu_disable_channel(struct idmac *idmac, struct idmac_channel *ichan,
318c2ecf20Sopenharmony_ci			       bool wait_for_stop);
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci/*
348c2ecf20Sopenharmony_ci * There can be only one, we could allocate it dynamically, but then we'd have
358c2ecf20Sopenharmony_ci * to add an extra parameter to some functions, and use something as ugly as
368c2ecf20Sopenharmony_ci *	struct ipu *ipu = to_ipu(to_idmac(ichan->dma_chan.device));
378c2ecf20Sopenharmony_ci * in the ISR
388c2ecf20Sopenharmony_ci */
398c2ecf20Sopenharmony_cistatic struct ipu ipu_data;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci#define to_ipu(id) container_of(id, struct ipu, idmac)
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistatic u32 __idmac_read_icreg(struct ipu *ipu, unsigned long reg)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	return __raw_readl(ipu->reg_ic + reg);
468c2ecf20Sopenharmony_ci}
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci#define idmac_read_icreg(ipu, reg) __idmac_read_icreg(ipu, reg - IC_CONF)
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistatic void __idmac_write_icreg(struct ipu *ipu, u32 value, unsigned long reg)
518c2ecf20Sopenharmony_ci{
528c2ecf20Sopenharmony_ci	__raw_writel(value, ipu->reg_ic + reg);
538c2ecf20Sopenharmony_ci}
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci#define idmac_write_icreg(ipu, v, reg) __idmac_write_icreg(ipu, v, reg - IC_CONF)
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistatic u32 idmac_read_ipureg(struct ipu *ipu, unsigned long reg)
588c2ecf20Sopenharmony_ci{
598c2ecf20Sopenharmony_ci	return __raw_readl(ipu->reg_ipu + reg);
608c2ecf20Sopenharmony_ci}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_cistatic void idmac_write_ipureg(struct ipu *ipu, u32 value, unsigned long reg)
638c2ecf20Sopenharmony_ci{
648c2ecf20Sopenharmony_ci	__raw_writel(value, ipu->reg_ipu + reg);
658c2ecf20Sopenharmony_ci}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci/*****************************************************************************
688c2ecf20Sopenharmony_ci * IPU / IC common functions
698c2ecf20Sopenharmony_ci */
708c2ecf20Sopenharmony_cistatic void dump_idmac_reg(struct ipu *ipu)
718c2ecf20Sopenharmony_ci{
728c2ecf20Sopenharmony_ci	dev_dbg(ipu->dev, "IDMAC_CONF 0x%x, IC_CONF 0x%x, IDMAC_CHA_EN 0x%x, "
738c2ecf20Sopenharmony_ci		"IDMAC_CHA_PRI 0x%x, IDMAC_CHA_BUSY 0x%x\n",
748c2ecf20Sopenharmony_ci		idmac_read_icreg(ipu, IDMAC_CONF),
758c2ecf20Sopenharmony_ci		idmac_read_icreg(ipu, IC_CONF),
768c2ecf20Sopenharmony_ci		idmac_read_icreg(ipu, IDMAC_CHA_EN),
778c2ecf20Sopenharmony_ci		idmac_read_icreg(ipu, IDMAC_CHA_PRI),
788c2ecf20Sopenharmony_ci		idmac_read_icreg(ipu, IDMAC_CHA_BUSY));
798c2ecf20Sopenharmony_ci	dev_dbg(ipu->dev, "BUF0_RDY 0x%x, BUF1_RDY 0x%x, CUR_BUF 0x%x, "
808c2ecf20Sopenharmony_ci		"DB_MODE 0x%x, TASKS_STAT 0x%x\n",
818c2ecf20Sopenharmony_ci		idmac_read_ipureg(ipu, IPU_CHA_BUF0_RDY),
828c2ecf20Sopenharmony_ci		idmac_read_ipureg(ipu, IPU_CHA_BUF1_RDY),
838c2ecf20Sopenharmony_ci		idmac_read_ipureg(ipu, IPU_CHA_CUR_BUF),
848c2ecf20Sopenharmony_ci		idmac_read_ipureg(ipu, IPU_CHA_DB_MODE_SEL),
858c2ecf20Sopenharmony_ci		idmac_read_ipureg(ipu, IPU_TASKS_STAT));
868c2ecf20Sopenharmony_ci}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_cistatic uint32_t bytes_per_pixel(enum pixel_fmt fmt)
898c2ecf20Sopenharmony_ci{
908c2ecf20Sopenharmony_ci	switch (fmt) {
918c2ecf20Sopenharmony_ci	case IPU_PIX_FMT_GENERIC:	/* generic data */
928c2ecf20Sopenharmony_ci	case IPU_PIX_FMT_RGB332:
938c2ecf20Sopenharmony_ci	case IPU_PIX_FMT_YUV420P:
948c2ecf20Sopenharmony_ci	case IPU_PIX_FMT_YUV422P:
958c2ecf20Sopenharmony_ci	default:
968c2ecf20Sopenharmony_ci		return 1;
978c2ecf20Sopenharmony_ci	case IPU_PIX_FMT_RGB565:
988c2ecf20Sopenharmony_ci	case IPU_PIX_FMT_YUYV:
998c2ecf20Sopenharmony_ci	case IPU_PIX_FMT_UYVY:
1008c2ecf20Sopenharmony_ci		return 2;
1018c2ecf20Sopenharmony_ci	case IPU_PIX_FMT_BGR24:
1028c2ecf20Sopenharmony_ci	case IPU_PIX_FMT_RGB24:
1038c2ecf20Sopenharmony_ci		return 3;
1048c2ecf20Sopenharmony_ci	case IPU_PIX_FMT_GENERIC_32:	/* generic data */
1058c2ecf20Sopenharmony_ci	case IPU_PIX_FMT_BGR32:
1068c2ecf20Sopenharmony_ci	case IPU_PIX_FMT_RGB32:
1078c2ecf20Sopenharmony_ci	case IPU_PIX_FMT_ABGR32:
1088c2ecf20Sopenharmony_ci		return 4;
1098c2ecf20Sopenharmony_ci	}
1108c2ecf20Sopenharmony_ci}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci/* Enable direct write to memory by the Camera Sensor Interface */
1138c2ecf20Sopenharmony_cistatic void ipu_ic_enable_task(struct ipu *ipu, enum ipu_channel channel)
1148c2ecf20Sopenharmony_ci{
1158c2ecf20Sopenharmony_ci	uint32_t ic_conf, mask;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	switch (channel) {
1188c2ecf20Sopenharmony_ci	case IDMAC_IC_0:
1198c2ecf20Sopenharmony_ci		mask = IC_CONF_PRPENC_EN;
1208c2ecf20Sopenharmony_ci		break;
1218c2ecf20Sopenharmony_ci	case IDMAC_IC_7:
1228c2ecf20Sopenharmony_ci		mask = IC_CONF_RWS_EN | IC_CONF_PRPENC_EN;
1238c2ecf20Sopenharmony_ci		break;
1248c2ecf20Sopenharmony_ci	default:
1258c2ecf20Sopenharmony_ci		return;
1268c2ecf20Sopenharmony_ci	}
1278c2ecf20Sopenharmony_ci	ic_conf = idmac_read_icreg(ipu, IC_CONF) | mask;
1288c2ecf20Sopenharmony_ci	idmac_write_icreg(ipu, ic_conf, IC_CONF);
1298c2ecf20Sopenharmony_ci}
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci/* Called under spin_lock_irqsave(&ipu_data.lock) */
1328c2ecf20Sopenharmony_cistatic void ipu_ic_disable_task(struct ipu *ipu, enum ipu_channel channel)
1338c2ecf20Sopenharmony_ci{
1348c2ecf20Sopenharmony_ci	uint32_t ic_conf, mask;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	switch (channel) {
1378c2ecf20Sopenharmony_ci	case IDMAC_IC_0:
1388c2ecf20Sopenharmony_ci		mask = IC_CONF_PRPENC_EN;
1398c2ecf20Sopenharmony_ci		break;
1408c2ecf20Sopenharmony_ci	case IDMAC_IC_7:
1418c2ecf20Sopenharmony_ci		mask = IC_CONF_RWS_EN | IC_CONF_PRPENC_EN;
1428c2ecf20Sopenharmony_ci		break;
1438c2ecf20Sopenharmony_ci	default:
1448c2ecf20Sopenharmony_ci		return;
1458c2ecf20Sopenharmony_ci	}
1468c2ecf20Sopenharmony_ci	ic_conf = idmac_read_icreg(ipu, IC_CONF) & ~mask;
1478c2ecf20Sopenharmony_ci	idmac_write_icreg(ipu, ic_conf, IC_CONF);
1488c2ecf20Sopenharmony_ci}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_cistatic uint32_t ipu_channel_status(struct ipu *ipu, enum ipu_channel channel)
1518c2ecf20Sopenharmony_ci{
1528c2ecf20Sopenharmony_ci	uint32_t stat = TASK_STAT_IDLE;
1538c2ecf20Sopenharmony_ci	uint32_t task_stat_reg = idmac_read_ipureg(ipu, IPU_TASKS_STAT);
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	switch (channel) {
1568c2ecf20Sopenharmony_ci	case IDMAC_IC_7:
1578c2ecf20Sopenharmony_ci		stat = (task_stat_reg & TSTAT_CSI2MEM_MASK) >>
1588c2ecf20Sopenharmony_ci			TSTAT_CSI2MEM_OFFSET;
1598c2ecf20Sopenharmony_ci		break;
1608c2ecf20Sopenharmony_ci	case IDMAC_IC_0:
1618c2ecf20Sopenharmony_ci	case IDMAC_SDC_0:
1628c2ecf20Sopenharmony_ci	case IDMAC_SDC_1:
1638c2ecf20Sopenharmony_ci	default:
1648c2ecf20Sopenharmony_ci		break;
1658c2ecf20Sopenharmony_ci	}
1668c2ecf20Sopenharmony_ci	return stat;
1678c2ecf20Sopenharmony_ci}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_cistruct chan_param_mem_planar {
1708c2ecf20Sopenharmony_ci	/* Word 0 */
1718c2ecf20Sopenharmony_ci	u32	xv:10;
1728c2ecf20Sopenharmony_ci	u32	yv:10;
1738c2ecf20Sopenharmony_ci	u32	xb:12;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	u32	yb:12;
1768c2ecf20Sopenharmony_ci	u32	res1:2;
1778c2ecf20Sopenharmony_ci	u32	nsb:1;
1788c2ecf20Sopenharmony_ci	u32	lnpb:6;
1798c2ecf20Sopenharmony_ci	u32	ubo_l:11;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	u32	ubo_h:15;
1828c2ecf20Sopenharmony_ci	u32	vbo_l:17;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	u32	vbo_h:9;
1858c2ecf20Sopenharmony_ci	u32	res2:3;
1868c2ecf20Sopenharmony_ci	u32	fw:12;
1878c2ecf20Sopenharmony_ci	u32	fh_l:8;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	u32	fh_h:4;
1908c2ecf20Sopenharmony_ci	u32	res3:28;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	/* Word 1 */
1938c2ecf20Sopenharmony_ci	u32	eba0;
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	u32	eba1;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	u32	bpp:3;
1988c2ecf20Sopenharmony_ci	u32	sl:14;
1998c2ecf20Sopenharmony_ci	u32	pfs:3;
2008c2ecf20Sopenharmony_ci	u32	bam:3;
2018c2ecf20Sopenharmony_ci	u32	res4:2;
2028c2ecf20Sopenharmony_ci	u32	npb:6;
2038c2ecf20Sopenharmony_ci	u32	res5:1;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	u32	sat:2;
2068c2ecf20Sopenharmony_ci	u32	res6:30;
2078c2ecf20Sopenharmony_ci} __attribute__ ((packed));
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_cistruct chan_param_mem_interleaved {
2108c2ecf20Sopenharmony_ci	/* Word 0 */
2118c2ecf20Sopenharmony_ci	u32	xv:10;
2128c2ecf20Sopenharmony_ci	u32	yv:10;
2138c2ecf20Sopenharmony_ci	u32	xb:12;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	u32	yb:12;
2168c2ecf20Sopenharmony_ci	u32	sce:1;
2178c2ecf20Sopenharmony_ci	u32	res1:1;
2188c2ecf20Sopenharmony_ci	u32	nsb:1;
2198c2ecf20Sopenharmony_ci	u32	lnpb:6;
2208c2ecf20Sopenharmony_ci	u32	sx:10;
2218c2ecf20Sopenharmony_ci	u32	sy_l:1;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	u32	sy_h:9;
2248c2ecf20Sopenharmony_ci	u32	ns:10;
2258c2ecf20Sopenharmony_ci	u32	sm:10;
2268c2ecf20Sopenharmony_ci	u32	sdx_l:3;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	u32	sdx_h:2;
2298c2ecf20Sopenharmony_ci	u32	sdy:5;
2308c2ecf20Sopenharmony_ci	u32	sdrx:1;
2318c2ecf20Sopenharmony_ci	u32	sdry:1;
2328c2ecf20Sopenharmony_ci	u32	sdr1:1;
2338c2ecf20Sopenharmony_ci	u32	res2:2;
2348c2ecf20Sopenharmony_ci	u32	fw:12;
2358c2ecf20Sopenharmony_ci	u32	fh_l:8;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	u32	fh_h:4;
2388c2ecf20Sopenharmony_ci	u32	res3:28;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	/* Word 1 */
2418c2ecf20Sopenharmony_ci	u32	eba0;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	u32	eba1;
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	u32	bpp:3;
2468c2ecf20Sopenharmony_ci	u32	sl:14;
2478c2ecf20Sopenharmony_ci	u32	pfs:3;
2488c2ecf20Sopenharmony_ci	u32	bam:3;
2498c2ecf20Sopenharmony_ci	u32	res4:2;
2508c2ecf20Sopenharmony_ci	u32	npb:6;
2518c2ecf20Sopenharmony_ci	u32	res5:1;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	u32	sat:2;
2548c2ecf20Sopenharmony_ci	u32	scc:1;
2558c2ecf20Sopenharmony_ci	u32	ofs0:5;
2568c2ecf20Sopenharmony_ci	u32	ofs1:5;
2578c2ecf20Sopenharmony_ci	u32	ofs2:5;
2588c2ecf20Sopenharmony_ci	u32	ofs3:5;
2598c2ecf20Sopenharmony_ci	u32	wid0:3;
2608c2ecf20Sopenharmony_ci	u32	wid1:3;
2618c2ecf20Sopenharmony_ci	u32	wid2:3;
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	u32	wid3:3;
2648c2ecf20Sopenharmony_ci	u32	dec_sel:1;
2658c2ecf20Sopenharmony_ci	u32	res6:28;
2668c2ecf20Sopenharmony_ci} __attribute__ ((packed));
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ciunion chan_param_mem {
2698c2ecf20Sopenharmony_ci	struct chan_param_mem_planar		pp;
2708c2ecf20Sopenharmony_ci	struct chan_param_mem_interleaved	ip;
2718c2ecf20Sopenharmony_ci};
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_cistatic void ipu_ch_param_set_plane_offset(union chan_param_mem *params,
2748c2ecf20Sopenharmony_ci					  u32 u_offset, u32 v_offset)
2758c2ecf20Sopenharmony_ci{
2768c2ecf20Sopenharmony_ci	params->pp.ubo_l = u_offset & 0x7ff;
2778c2ecf20Sopenharmony_ci	params->pp.ubo_h = u_offset >> 11;
2788c2ecf20Sopenharmony_ci	params->pp.vbo_l = v_offset & 0x1ffff;
2798c2ecf20Sopenharmony_ci	params->pp.vbo_h = v_offset >> 17;
2808c2ecf20Sopenharmony_ci}
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_cistatic void ipu_ch_param_set_size(union chan_param_mem *params,
2838c2ecf20Sopenharmony_ci				  uint32_t pixel_fmt, uint16_t width,
2848c2ecf20Sopenharmony_ci				  uint16_t height, uint16_t stride)
2858c2ecf20Sopenharmony_ci{
2868c2ecf20Sopenharmony_ci	u32 u_offset;
2878c2ecf20Sopenharmony_ci	u32 v_offset;
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	params->pp.fw		= width - 1;
2908c2ecf20Sopenharmony_ci	params->pp.fh_l		= height - 1;
2918c2ecf20Sopenharmony_ci	params->pp.fh_h		= (height - 1) >> 8;
2928c2ecf20Sopenharmony_ci	params->pp.sl		= stride - 1;
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	switch (pixel_fmt) {
2958c2ecf20Sopenharmony_ci	case IPU_PIX_FMT_GENERIC:
2968c2ecf20Sopenharmony_ci		/*Represents 8-bit Generic data */
2978c2ecf20Sopenharmony_ci		params->pp.bpp	= 3;
2988c2ecf20Sopenharmony_ci		params->pp.pfs	= 7;
2998c2ecf20Sopenharmony_ci		params->pp.npb	= 31;
3008c2ecf20Sopenharmony_ci		params->pp.sat	= 2;		/* SAT = use 32-bit access */
3018c2ecf20Sopenharmony_ci		break;
3028c2ecf20Sopenharmony_ci	case IPU_PIX_FMT_GENERIC_32:
3038c2ecf20Sopenharmony_ci		/*Represents 32-bit Generic data */
3048c2ecf20Sopenharmony_ci		params->pp.bpp	= 0;
3058c2ecf20Sopenharmony_ci		params->pp.pfs	= 7;
3068c2ecf20Sopenharmony_ci		params->pp.npb	= 7;
3078c2ecf20Sopenharmony_ci		params->pp.sat	= 2;		/* SAT = use 32-bit access */
3088c2ecf20Sopenharmony_ci		break;
3098c2ecf20Sopenharmony_ci	case IPU_PIX_FMT_RGB565:
3108c2ecf20Sopenharmony_ci		params->ip.bpp	= 2;
3118c2ecf20Sopenharmony_ci		params->ip.pfs	= 4;
3128c2ecf20Sopenharmony_ci		params->ip.npb	= 15;
3138c2ecf20Sopenharmony_ci		params->ip.sat	= 2;		/* SAT = 32-bit access */
3148c2ecf20Sopenharmony_ci		params->ip.ofs0	= 0;		/* Red bit offset */
3158c2ecf20Sopenharmony_ci		params->ip.ofs1	= 5;		/* Green bit offset */
3168c2ecf20Sopenharmony_ci		params->ip.ofs2	= 11;		/* Blue bit offset */
3178c2ecf20Sopenharmony_ci		params->ip.ofs3	= 16;		/* Alpha bit offset */
3188c2ecf20Sopenharmony_ci		params->ip.wid0	= 4;		/* Red bit width - 1 */
3198c2ecf20Sopenharmony_ci		params->ip.wid1	= 5;		/* Green bit width - 1 */
3208c2ecf20Sopenharmony_ci		params->ip.wid2	= 4;		/* Blue bit width - 1 */
3218c2ecf20Sopenharmony_ci		break;
3228c2ecf20Sopenharmony_ci	case IPU_PIX_FMT_BGR24:
3238c2ecf20Sopenharmony_ci		params->ip.bpp	= 1;		/* 24 BPP & RGB PFS */
3248c2ecf20Sopenharmony_ci		params->ip.pfs	= 4;
3258c2ecf20Sopenharmony_ci		params->ip.npb	= 7;
3268c2ecf20Sopenharmony_ci		params->ip.sat	= 2;		/* SAT = 32-bit access */
3278c2ecf20Sopenharmony_ci		params->ip.ofs0	= 0;		/* Red bit offset */
3288c2ecf20Sopenharmony_ci		params->ip.ofs1	= 8;		/* Green bit offset */
3298c2ecf20Sopenharmony_ci		params->ip.ofs2	= 16;		/* Blue bit offset */
3308c2ecf20Sopenharmony_ci		params->ip.ofs3	= 24;		/* Alpha bit offset */
3318c2ecf20Sopenharmony_ci		params->ip.wid0	= 7;		/* Red bit width - 1 */
3328c2ecf20Sopenharmony_ci		params->ip.wid1	= 7;		/* Green bit width - 1 */
3338c2ecf20Sopenharmony_ci		params->ip.wid2	= 7;		/* Blue bit width - 1 */
3348c2ecf20Sopenharmony_ci		break;
3358c2ecf20Sopenharmony_ci	case IPU_PIX_FMT_RGB24:
3368c2ecf20Sopenharmony_ci		params->ip.bpp	= 1;		/* 24 BPP & RGB PFS */
3378c2ecf20Sopenharmony_ci		params->ip.pfs	= 4;
3388c2ecf20Sopenharmony_ci		params->ip.npb	= 7;
3398c2ecf20Sopenharmony_ci		params->ip.sat	= 2;		/* SAT = 32-bit access */
3408c2ecf20Sopenharmony_ci		params->ip.ofs0	= 16;		/* Red bit offset */
3418c2ecf20Sopenharmony_ci		params->ip.ofs1	= 8;		/* Green bit offset */
3428c2ecf20Sopenharmony_ci		params->ip.ofs2	= 0;		/* Blue bit offset */
3438c2ecf20Sopenharmony_ci		params->ip.ofs3	= 24;		/* Alpha bit offset */
3448c2ecf20Sopenharmony_ci		params->ip.wid0	= 7;		/* Red bit width - 1 */
3458c2ecf20Sopenharmony_ci		params->ip.wid1	= 7;		/* Green bit width - 1 */
3468c2ecf20Sopenharmony_ci		params->ip.wid2	= 7;		/* Blue bit width - 1 */
3478c2ecf20Sopenharmony_ci		break;
3488c2ecf20Sopenharmony_ci	case IPU_PIX_FMT_BGRA32:
3498c2ecf20Sopenharmony_ci	case IPU_PIX_FMT_BGR32:
3508c2ecf20Sopenharmony_ci	case IPU_PIX_FMT_ABGR32:
3518c2ecf20Sopenharmony_ci		params->ip.bpp	= 0;
3528c2ecf20Sopenharmony_ci		params->ip.pfs	= 4;
3538c2ecf20Sopenharmony_ci		params->ip.npb	= 7;
3548c2ecf20Sopenharmony_ci		params->ip.sat	= 2;		/* SAT = 32-bit access */
3558c2ecf20Sopenharmony_ci		params->ip.ofs0	= 8;		/* Red bit offset */
3568c2ecf20Sopenharmony_ci		params->ip.ofs1	= 16;		/* Green bit offset */
3578c2ecf20Sopenharmony_ci		params->ip.ofs2	= 24;		/* Blue bit offset */
3588c2ecf20Sopenharmony_ci		params->ip.ofs3	= 0;		/* Alpha bit offset */
3598c2ecf20Sopenharmony_ci		params->ip.wid0	= 7;		/* Red bit width - 1 */
3608c2ecf20Sopenharmony_ci		params->ip.wid1	= 7;		/* Green bit width - 1 */
3618c2ecf20Sopenharmony_ci		params->ip.wid2	= 7;		/* Blue bit width - 1 */
3628c2ecf20Sopenharmony_ci		params->ip.wid3	= 7;		/* Alpha bit width - 1 */
3638c2ecf20Sopenharmony_ci		break;
3648c2ecf20Sopenharmony_ci	case IPU_PIX_FMT_RGBA32:
3658c2ecf20Sopenharmony_ci	case IPU_PIX_FMT_RGB32:
3668c2ecf20Sopenharmony_ci		params->ip.bpp	= 0;
3678c2ecf20Sopenharmony_ci		params->ip.pfs	= 4;
3688c2ecf20Sopenharmony_ci		params->ip.npb	= 7;
3698c2ecf20Sopenharmony_ci		params->ip.sat	= 2;		/* SAT = 32-bit access */
3708c2ecf20Sopenharmony_ci		params->ip.ofs0	= 24;		/* Red bit offset */
3718c2ecf20Sopenharmony_ci		params->ip.ofs1	= 16;		/* Green bit offset */
3728c2ecf20Sopenharmony_ci		params->ip.ofs2	= 8;		/* Blue bit offset */
3738c2ecf20Sopenharmony_ci		params->ip.ofs3	= 0;		/* Alpha bit offset */
3748c2ecf20Sopenharmony_ci		params->ip.wid0	= 7;		/* Red bit width - 1 */
3758c2ecf20Sopenharmony_ci		params->ip.wid1	= 7;		/* Green bit width - 1 */
3768c2ecf20Sopenharmony_ci		params->ip.wid2	= 7;		/* Blue bit width - 1 */
3778c2ecf20Sopenharmony_ci		params->ip.wid3	= 7;		/* Alpha bit width - 1 */
3788c2ecf20Sopenharmony_ci		break;
3798c2ecf20Sopenharmony_ci	case IPU_PIX_FMT_UYVY:
3808c2ecf20Sopenharmony_ci		params->ip.bpp	= 2;
3818c2ecf20Sopenharmony_ci		params->ip.pfs	= 6;
3828c2ecf20Sopenharmony_ci		params->ip.npb	= 7;
3838c2ecf20Sopenharmony_ci		params->ip.sat	= 2;		/* SAT = 32-bit access */
3848c2ecf20Sopenharmony_ci		break;
3858c2ecf20Sopenharmony_ci	case IPU_PIX_FMT_YUV420P2:
3868c2ecf20Sopenharmony_ci	case IPU_PIX_FMT_YUV420P:
3878c2ecf20Sopenharmony_ci		params->ip.bpp	= 3;
3888c2ecf20Sopenharmony_ci		params->ip.pfs	= 3;
3898c2ecf20Sopenharmony_ci		params->ip.npb	= 7;
3908c2ecf20Sopenharmony_ci		params->ip.sat	= 2;		/* SAT = 32-bit access */
3918c2ecf20Sopenharmony_ci		u_offset = stride * height;
3928c2ecf20Sopenharmony_ci		v_offset = u_offset + u_offset / 4;
3938c2ecf20Sopenharmony_ci		ipu_ch_param_set_plane_offset(params, u_offset, v_offset);
3948c2ecf20Sopenharmony_ci		break;
3958c2ecf20Sopenharmony_ci	case IPU_PIX_FMT_YVU422P:
3968c2ecf20Sopenharmony_ci		params->ip.bpp	= 3;
3978c2ecf20Sopenharmony_ci		params->ip.pfs	= 2;
3988c2ecf20Sopenharmony_ci		params->ip.npb	= 7;
3998c2ecf20Sopenharmony_ci		params->ip.sat	= 2;		/* SAT = 32-bit access */
4008c2ecf20Sopenharmony_ci		v_offset = stride * height;
4018c2ecf20Sopenharmony_ci		u_offset = v_offset + v_offset / 2;
4028c2ecf20Sopenharmony_ci		ipu_ch_param_set_plane_offset(params, u_offset, v_offset);
4038c2ecf20Sopenharmony_ci		break;
4048c2ecf20Sopenharmony_ci	case IPU_PIX_FMT_YUV422P:
4058c2ecf20Sopenharmony_ci		params->ip.bpp	= 3;
4068c2ecf20Sopenharmony_ci		params->ip.pfs	= 2;
4078c2ecf20Sopenharmony_ci		params->ip.npb	= 7;
4088c2ecf20Sopenharmony_ci		params->ip.sat	= 2;		/* SAT = 32-bit access */
4098c2ecf20Sopenharmony_ci		u_offset = stride * height;
4108c2ecf20Sopenharmony_ci		v_offset = u_offset + u_offset / 2;
4118c2ecf20Sopenharmony_ci		ipu_ch_param_set_plane_offset(params, u_offset, v_offset);
4128c2ecf20Sopenharmony_ci		break;
4138c2ecf20Sopenharmony_ci	default:
4148c2ecf20Sopenharmony_ci		dev_err(ipu_data.dev,
4158c2ecf20Sopenharmony_ci			"mx3 ipu: unimplemented pixel format %d\n", pixel_fmt);
4168c2ecf20Sopenharmony_ci		break;
4178c2ecf20Sopenharmony_ci	}
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	params->pp.nsb = 1;
4208c2ecf20Sopenharmony_ci}
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_cistatic void ipu_ch_param_set_buffer(union chan_param_mem *params,
4238c2ecf20Sopenharmony_ci				    dma_addr_t buf0, dma_addr_t buf1)
4248c2ecf20Sopenharmony_ci{
4258c2ecf20Sopenharmony_ci	params->pp.eba0 = buf0;
4268c2ecf20Sopenharmony_ci	params->pp.eba1 = buf1;
4278c2ecf20Sopenharmony_ci}
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_cistatic void ipu_ch_param_set_rotation(union chan_param_mem *params,
4308c2ecf20Sopenharmony_ci				      enum ipu_rotate_mode rotate)
4318c2ecf20Sopenharmony_ci{
4328c2ecf20Sopenharmony_ci	params->pp.bam = rotate;
4338c2ecf20Sopenharmony_ci}
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_cistatic void ipu_write_param_mem(uint32_t addr, uint32_t *data,
4368c2ecf20Sopenharmony_ci				uint32_t num_words)
4378c2ecf20Sopenharmony_ci{
4388c2ecf20Sopenharmony_ci	for (; num_words > 0; num_words--) {
4398c2ecf20Sopenharmony_ci		dev_dbg(ipu_data.dev,
4408c2ecf20Sopenharmony_ci			"write param mem - addr = 0x%08X, data = 0x%08X\n",
4418c2ecf20Sopenharmony_ci			addr, *data);
4428c2ecf20Sopenharmony_ci		idmac_write_ipureg(&ipu_data, addr, IPU_IMA_ADDR);
4438c2ecf20Sopenharmony_ci		idmac_write_ipureg(&ipu_data, *data++, IPU_IMA_DATA);
4448c2ecf20Sopenharmony_ci		addr++;
4458c2ecf20Sopenharmony_ci		if ((addr & 0x7) == 5) {
4468c2ecf20Sopenharmony_ci			addr &= ~0x7;	/* set to word 0 */
4478c2ecf20Sopenharmony_ci			addr += 8;	/* increment to next row */
4488c2ecf20Sopenharmony_ci		}
4498c2ecf20Sopenharmony_ci	}
4508c2ecf20Sopenharmony_ci}
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_cistatic int calc_resize_coeffs(uint32_t in_size, uint32_t out_size,
4538c2ecf20Sopenharmony_ci			      uint32_t *resize_coeff,
4548c2ecf20Sopenharmony_ci			      uint32_t *downsize_coeff)
4558c2ecf20Sopenharmony_ci{
4568c2ecf20Sopenharmony_ci	uint32_t temp_size;
4578c2ecf20Sopenharmony_ci	uint32_t temp_downsize;
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	*resize_coeff	= 1 << 13;
4608c2ecf20Sopenharmony_ci	*downsize_coeff	= 1 << 13;
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	/* Cannot downsize more than 8:1 */
4638c2ecf20Sopenharmony_ci	if (out_size << 3 < in_size)
4648c2ecf20Sopenharmony_ci		return -EINVAL;
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	/* compute downsizing coefficient */
4678c2ecf20Sopenharmony_ci	temp_downsize = 0;
4688c2ecf20Sopenharmony_ci	temp_size = in_size;
4698c2ecf20Sopenharmony_ci	while (temp_size >= out_size * 2 && temp_downsize < 2) {
4708c2ecf20Sopenharmony_ci		temp_size >>= 1;
4718c2ecf20Sopenharmony_ci		temp_downsize++;
4728c2ecf20Sopenharmony_ci	}
4738c2ecf20Sopenharmony_ci	*downsize_coeff = temp_downsize;
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	/*
4768c2ecf20Sopenharmony_ci	 * compute resizing coefficient using the following formula:
4778c2ecf20Sopenharmony_ci	 * resize_coeff = M*(SI -1)/(SO - 1)
4788c2ecf20Sopenharmony_ci	 * where M = 2^13, SI - input size, SO - output size
4798c2ecf20Sopenharmony_ci	 */
4808c2ecf20Sopenharmony_ci	*resize_coeff = (8192L * (temp_size - 1)) / (out_size - 1);
4818c2ecf20Sopenharmony_ci	if (*resize_coeff >= 16384L) {
4828c2ecf20Sopenharmony_ci		dev_err(ipu_data.dev, "Warning! Overflow on resize coeff.\n");
4838c2ecf20Sopenharmony_ci		*resize_coeff = 0x3FFF;
4848c2ecf20Sopenharmony_ci	}
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	dev_dbg(ipu_data.dev, "resizing from %u -> %u pixels, "
4878c2ecf20Sopenharmony_ci		"downsize=%u, resize=%u.%lu (reg=%u)\n", in_size, out_size,
4888c2ecf20Sopenharmony_ci		*downsize_coeff, *resize_coeff >= 8192L ? 1 : 0,
4898c2ecf20Sopenharmony_ci		((*resize_coeff & 0x1FFF) * 10000L) / 8192L, *resize_coeff);
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	return 0;
4928c2ecf20Sopenharmony_ci}
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_cistatic enum ipu_color_space format_to_colorspace(enum pixel_fmt fmt)
4958c2ecf20Sopenharmony_ci{
4968c2ecf20Sopenharmony_ci	switch (fmt) {
4978c2ecf20Sopenharmony_ci	case IPU_PIX_FMT_RGB565:
4988c2ecf20Sopenharmony_ci	case IPU_PIX_FMT_BGR24:
4998c2ecf20Sopenharmony_ci	case IPU_PIX_FMT_RGB24:
5008c2ecf20Sopenharmony_ci	case IPU_PIX_FMT_BGR32:
5018c2ecf20Sopenharmony_ci	case IPU_PIX_FMT_RGB32:
5028c2ecf20Sopenharmony_ci		return IPU_COLORSPACE_RGB;
5038c2ecf20Sopenharmony_ci	default:
5048c2ecf20Sopenharmony_ci		return IPU_COLORSPACE_YCBCR;
5058c2ecf20Sopenharmony_ci	}
5068c2ecf20Sopenharmony_ci}
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_cistatic int ipu_ic_init_prpenc(struct ipu *ipu,
5098c2ecf20Sopenharmony_ci			      union ipu_channel_param *params, bool src_is_csi)
5108c2ecf20Sopenharmony_ci{
5118c2ecf20Sopenharmony_ci	uint32_t reg, ic_conf;
5128c2ecf20Sopenharmony_ci	uint32_t downsize_coeff, resize_coeff;
5138c2ecf20Sopenharmony_ci	enum ipu_color_space in_fmt, out_fmt;
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci	/* Setup vertical resizing */
5168c2ecf20Sopenharmony_ci	calc_resize_coeffs(params->video.in_height,
5178c2ecf20Sopenharmony_ci			    params->video.out_height,
5188c2ecf20Sopenharmony_ci			    &resize_coeff, &downsize_coeff);
5198c2ecf20Sopenharmony_ci	reg = (downsize_coeff << 30) | (resize_coeff << 16);
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	/* Setup horizontal resizing */
5228c2ecf20Sopenharmony_ci	calc_resize_coeffs(params->video.in_width,
5238c2ecf20Sopenharmony_ci			    params->video.out_width,
5248c2ecf20Sopenharmony_ci			    &resize_coeff, &downsize_coeff);
5258c2ecf20Sopenharmony_ci	reg |= (downsize_coeff << 14) | resize_coeff;
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci	/* Setup color space conversion */
5288c2ecf20Sopenharmony_ci	in_fmt = format_to_colorspace(params->video.in_pixel_fmt);
5298c2ecf20Sopenharmony_ci	out_fmt = format_to_colorspace(params->video.out_pixel_fmt);
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	/*
5328c2ecf20Sopenharmony_ci	 * Colourspace conversion unsupported yet - see _init_csc() in
5338c2ecf20Sopenharmony_ci	 * Freescale sources
5348c2ecf20Sopenharmony_ci	 */
5358c2ecf20Sopenharmony_ci	if (in_fmt != out_fmt) {
5368c2ecf20Sopenharmony_ci		dev_err(ipu->dev, "Colourspace conversion unsupported!\n");
5378c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
5388c2ecf20Sopenharmony_ci	}
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	idmac_write_icreg(ipu, reg, IC_PRP_ENC_RSC);
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	ic_conf = idmac_read_icreg(ipu, IC_CONF);
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	if (src_is_csi)
5458c2ecf20Sopenharmony_ci		ic_conf &= ~IC_CONF_RWS_EN;
5468c2ecf20Sopenharmony_ci	else
5478c2ecf20Sopenharmony_ci		ic_conf |= IC_CONF_RWS_EN;
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci	idmac_write_icreg(ipu, ic_conf, IC_CONF);
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci	return 0;
5528c2ecf20Sopenharmony_ci}
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_cistatic uint32_t dma_param_addr(uint32_t dma_ch)
5558c2ecf20Sopenharmony_ci{
5568c2ecf20Sopenharmony_ci	/* Channel Parameter Memory */
5578c2ecf20Sopenharmony_ci	return 0x10000 | (dma_ch << 4);
5588c2ecf20Sopenharmony_ci}
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_cistatic void ipu_channel_set_priority(struct ipu *ipu, enum ipu_channel channel,
5618c2ecf20Sopenharmony_ci				     bool prio)
5628c2ecf20Sopenharmony_ci{
5638c2ecf20Sopenharmony_ci	u32 reg = idmac_read_icreg(ipu, IDMAC_CHA_PRI);
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	if (prio)
5668c2ecf20Sopenharmony_ci		reg |= 1UL << channel;
5678c2ecf20Sopenharmony_ci	else
5688c2ecf20Sopenharmony_ci		reg &= ~(1UL << channel);
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	idmac_write_icreg(ipu, reg, IDMAC_CHA_PRI);
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci	dump_idmac_reg(ipu);
5738c2ecf20Sopenharmony_ci}
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_cistatic uint32_t ipu_channel_conf_mask(enum ipu_channel channel)
5768c2ecf20Sopenharmony_ci{
5778c2ecf20Sopenharmony_ci	uint32_t mask;
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci	switch (channel) {
5808c2ecf20Sopenharmony_ci	case IDMAC_IC_0:
5818c2ecf20Sopenharmony_ci	case IDMAC_IC_7:
5828c2ecf20Sopenharmony_ci		mask = IPU_CONF_CSI_EN | IPU_CONF_IC_EN;
5838c2ecf20Sopenharmony_ci		break;
5848c2ecf20Sopenharmony_ci	case IDMAC_SDC_0:
5858c2ecf20Sopenharmony_ci	case IDMAC_SDC_1:
5868c2ecf20Sopenharmony_ci		mask = IPU_CONF_SDC_EN | IPU_CONF_DI_EN;
5878c2ecf20Sopenharmony_ci		break;
5888c2ecf20Sopenharmony_ci	default:
5898c2ecf20Sopenharmony_ci		mask = 0;
5908c2ecf20Sopenharmony_ci		break;
5918c2ecf20Sopenharmony_ci	}
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci	return mask;
5948c2ecf20Sopenharmony_ci}
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci/**
5978c2ecf20Sopenharmony_ci * ipu_enable_channel() - enable an IPU channel.
5988c2ecf20Sopenharmony_ci * @idmac:	IPU DMAC context.
5998c2ecf20Sopenharmony_ci * @ichan:	IDMAC channel.
6008c2ecf20Sopenharmony_ci * @return:	0 on success or negative error code on failure.
6018c2ecf20Sopenharmony_ci */
6028c2ecf20Sopenharmony_cistatic int ipu_enable_channel(struct idmac *idmac, struct idmac_channel *ichan)
6038c2ecf20Sopenharmony_ci{
6048c2ecf20Sopenharmony_ci	struct ipu *ipu = to_ipu(idmac);
6058c2ecf20Sopenharmony_ci	enum ipu_channel channel = ichan->dma_chan.chan_id;
6068c2ecf20Sopenharmony_ci	uint32_t reg;
6078c2ecf20Sopenharmony_ci	unsigned long flags;
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ipu->lock, flags);
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci	/* Reset to buffer 0 */
6128c2ecf20Sopenharmony_ci	idmac_write_ipureg(ipu, 1UL << channel, IPU_CHA_CUR_BUF);
6138c2ecf20Sopenharmony_ci	ichan->active_buffer = 0;
6148c2ecf20Sopenharmony_ci	ichan->status = IPU_CHANNEL_ENABLED;
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci	switch (channel) {
6178c2ecf20Sopenharmony_ci	case IDMAC_SDC_0:
6188c2ecf20Sopenharmony_ci	case IDMAC_SDC_1:
6198c2ecf20Sopenharmony_ci	case IDMAC_IC_7:
6208c2ecf20Sopenharmony_ci		ipu_channel_set_priority(ipu, channel, true);
6218c2ecf20Sopenharmony_ci	default:
6228c2ecf20Sopenharmony_ci		break;
6238c2ecf20Sopenharmony_ci	}
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci	reg = idmac_read_icreg(ipu, IDMAC_CHA_EN);
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci	idmac_write_icreg(ipu, reg | (1UL << channel), IDMAC_CHA_EN);
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci	ipu_ic_enable_task(ipu, channel);
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ipu->lock, flags);
6328c2ecf20Sopenharmony_ci	return 0;
6338c2ecf20Sopenharmony_ci}
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci/**
6368c2ecf20Sopenharmony_ci * ipu_init_channel_buffer() - initialize a buffer for logical IPU channel.
6378c2ecf20Sopenharmony_ci * @ichan:	IDMAC channel.
6388c2ecf20Sopenharmony_ci * @pixel_fmt:	pixel format of buffer. Pixel format is a FOURCC ASCII code.
6398c2ecf20Sopenharmony_ci * @width:	width of buffer in pixels.
6408c2ecf20Sopenharmony_ci * @height:	height of buffer in pixels.
6418c2ecf20Sopenharmony_ci * @stride:	stride length of buffer in pixels.
6428c2ecf20Sopenharmony_ci * @rot_mode:	rotation mode of buffer. A rotation setting other than
6438c2ecf20Sopenharmony_ci *		IPU_ROTATE_VERT_FLIP should only be used for input buffers of
6448c2ecf20Sopenharmony_ci *		rotation channels.
6458c2ecf20Sopenharmony_ci * @phyaddr_0:	buffer 0 physical address.
6468c2ecf20Sopenharmony_ci * @phyaddr_1:	buffer 1 physical address. Setting this to a value other than
6478c2ecf20Sopenharmony_ci *		NULL enables double buffering mode.
6488c2ecf20Sopenharmony_ci * @return:	0 on success or negative error code on failure.
6498c2ecf20Sopenharmony_ci */
6508c2ecf20Sopenharmony_cistatic int ipu_init_channel_buffer(struct idmac_channel *ichan,
6518c2ecf20Sopenharmony_ci				   enum pixel_fmt pixel_fmt,
6528c2ecf20Sopenharmony_ci				   uint16_t width, uint16_t height,
6538c2ecf20Sopenharmony_ci				   uint32_t stride,
6548c2ecf20Sopenharmony_ci				   enum ipu_rotate_mode rot_mode,
6558c2ecf20Sopenharmony_ci				   dma_addr_t phyaddr_0, dma_addr_t phyaddr_1)
6568c2ecf20Sopenharmony_ci{
6578c2ecf20Sopenharmony_ci	enum ipu_channel channel = ichan->dma_chan.chan_id;
6588c2ecf20Sopenharmony_ci	struct idmac *idmac = to_idmac(ichan->dma_chan.device);
6598c2ecf20Sopenharmony_ci	struct ipu *ipu = to_ipu(idmac);
6608c2ecf20Sopenharmony_ci	union chan_param_mem params = {};
6618c2ecf20Sopenharmony_ci	unsigned long flags;
6628c2ecf20Sopenharmony_ci	uint32_t reg;
6638c2ecf20Sopenharmony_ci	uint32_t stride_bytes;
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci	stride_bytes = stride * bytes_per_pixel(pixel_fmt);
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_ci	if (stride_bytes % 4) {
6688c2ecf20Sopenharmony_ci		dev_err(ipu->dev,
6698c2ecf20Sopenharmony_ci			"Stride length must be 32-bit aligned, stride = %d, bytes = %d\n",
6708c2ecf20Sopenharmony_ci			stride, stride_bytes);
6718c2ecf20Sopenharmony_ci		return -EINVAL;
6728c2ecf20Sopenharmony_ci	}
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci	/* IC channel's stride must be a multiple of 8 pixels */
6758c2ecf20Sopenharmony_ci	if ((channel <= IDMAC_IC_13) && (stride % 8)) {
6768c2ecf20Sopenharmony_ci		dev_err(ipu->dev, "Stride must be 8 pixel multiple\n");
6778c2ecf20Sopenharmony_ci		return -EINVAL;
6788c2ecf20Sopenharmony_ci	}
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci	/* Build parameter memory data for DMA channel */
6818c2ecf20Sopenharmony_ci	ipu_ch_param_set_size(&params, pixel_fmt, width, height, stride_bytes);
6828c2ecf20Sopenharmony_ci	ipu_ch_param_set_buffer(&params, phyaddr_0, phyaddr_1);
6838c2ecf20Sopenharmony_ci	ipu_ch_param_set_rotation(&params, rot_mode);
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ipu->lock, flags);
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_ci	ipu_write_param_mem(dma_param_addr(channel), (uint32_t *)&params, 10);
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci	reg = idmac_read_ipureg(ipu, IPU_CHA_DB_MODE_SEL);
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci	if (phyaddr_1)
6928c2ecf20Sopenharmony_ci		reg |= 1UL << channel;
6938c2ecf20Sopenharmony_ci	else
6948c2ecf20Sopenharmony_ci		reg &= ~(1UL << channel);
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_ci	idmac_write_ipureg(ipu, reg, IPU_CHA_DB_MODE_SEL);
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci	ichan->status = IPU_CHANNEL_READY;
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ipu->lock, flags);
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci	return 0;
7038c2ecf20Sopenharmony_ci}
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci/**
7068c2ecf20Sopenharmony_ci * ipu_select_buffer() - mark a channel's buffer as ready.
7078c2ecf20Sopenharmony_ci * @channel:	channel ID.
7088c2ecf20Sopenharmony_ci * @buffer_n:	buffer number to mark ready.
7098c2ecf20Sopenharmony_ci */
7108c2ecf20Sopenharmony_cistatic void ipu_select_buffer(enum ipu_channel channel, int buffer_n)
7118c2ecf20Sopenharmony_ci{
7128c2ecf20Sopenharmony_ci	/* No locking - this is a write-one-to-set register, cleared by IPU */
7138c2ecf20Sopenharmony_ci	if (buffer_n == 0)
7148c2ecf20Sopenharmony_ci		/* Mark buffer 0 as ready. */
7158c2ecf20Sopenharmony_ci		idmac_write_ipureg(&ipu_data, 1UL << channel, IPU_CHA_BUF0_RDY);
7168c2ecf20Sopenharmony_ci	else
7178c2ecf20Sopenharmony_ci		/* Mark buffer 1 as ready. */
7188c2ecf20Sopenharmony_ci		idmac_write_ipureg(&ipu_data, 1UL << channel, IPU_CHA_BUF1_RDY);
7198c2ecf20Sopenharmony_ci}
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci/**
7228c2ecf20Sopenharmony_ci * ipu_update_channel_buffer() - update physical address of a channel buffer.
7238c2ecf20Sopenharmony_ci * @ichan:	IDMAC channel.
7248c2ecf20Sopenharmony_ci * @buffer_n:	buffer number to update.
7258c2ecf20Sopenharmony_ci *		0 or 1 are the only valid values.
7268c2ecf20Sopenharmony_ci * @phyaddr:	buffer physical address.
7278c2ecf20Sopenharmony_ci */
7288c2ecf20Sopenharmony_ci/* Called under spin_lock(_irqsave)(&ichan->lock) */
7298c2ecf20Sopenharmony_cistatic void ipu_update_channel_buffer(struct idmac_channel *ichan,
7308c2ecf20Sopenharmony_ci				      int buffer_n, dma_addr_t phyaddr)
7318c2ecf20Sopenharmony_ci{
7328c2ecf20Sopenharmony_ci	enum ipu_channel channel = ichan->dma_chan.chan_id;
7338c2ecf20Sopenharmony_ci	uint32_t reg;
7348c2ecf20Sopenharmony_ci	unsigned long flags;
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ipu_data.lock, flags);
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_ci	if (buffer_n == 0) {
7398c2ecf20Sopenharmony_ci		reg = idmac_read_ipureg(&ipu_data, IPU_CHA_BUF0_RDY);
7408c2ecf20Sopenharmony_ci		if (reg & (1UL << channel)) {
7418c2ecf20Sopenharmony_ci			ipu_ic_disable_task(&ipu_data, channel);
7428c2ecf20Sopenharmony_ci			ichan->status = IPU_CHANNEL_READY;
7438c2ecf20Sopenharmony_ci		}
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_ci		/* 44.3.3.1.9 - Row Number 1 (WORD1, offset 0) */
7468c2ecf20Sopenharmony_ci		idmac_write_ipureg(&ipu_data, dma_param_addr(channel) +
7478c2ecf20Sopenharmony_ci				   0x0008UL, IPU_IMA_ADDR);
7488c2ecf20Sopenharmony_ci		idmac_write_ipureg(&ipu_data, phyaddr, IPU_IMA_DATA);
7498c2ecf20Sopenharmony_ci	} else {
7508c2ecf20Sopenharmony_ci		reg = idmac_read_ipureg(&ipu_data, IPU_CHA_BUF1_RDY);
7518c2ecf20Sopenharmony_ci		if (reg & (1UL << channel)) {
7528c2ecf20Sopenharmony_ci			ipu_ic_disable_task(&ipu_data, channel);
7538c2ecf20Sopenharmony_ci			ichan->status = IPU_CHANNEL_READY;
7548c2ecf20Sopenharmony_ci		}
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_ci		/* Check if double-buffering is already enabled */
7578c2ecf20Sopenharmony_ci		reg = idmac_read_ipureg(&ipu_data, IPU_CHA_DB_MODE_SEL);
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_ci		if (!(reg & (1UL << channel)))
7608c2ecf20Sopenharmony_ci			idmac_write_ipureg(&ipu_data, reg | (1UL << channel),
7618c2ecf20Sopenharmony_ci					   IPU_CHA_DB_MODE_SEL);
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_ci		/* 44.3.3.1.9 - Row Number 1 (WORD1, offset 1) */
7648c2ecf20Sopenharmony_ci		idmac_write_ipureg(&ipu_data, dma_param_addr(channel) +
7658c2ecf20Sopenharmony_ci				   0x0009UL, IPU_IMA_ADDR);
7668c2ecf20Sopenharmony_ci		idmac_write_ipureg(&ipu_data, phyaddr, IPU_IMA_DATA);
7678c2ecf20Sopenharmony_ci	}
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ipu_data.lock, flags);
7708c2ecf20Sopenharmony_ci}
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_ci/* Called under spin_lock_irqsave(&ichan->lock) */
7738c2ecf20Sopenharmony_cistatic int ipu_submit_buffer(struct idmac_channel *ichan,
7748c2ecf20Sopenharmony_ci	struct idmac_tx_desc *desc, struct scatterlist *sg, int buf_idx)
7758c2ecf20Sopenharmony_ci{
7768c2ecf20Sopenharmony_ci	unsigned int chan_id = ichan->dma_chan.chan_id;
7778c2ecf20Sopenharmony_ci	struct device *dev = &ichan->dma_chan.dev->device;
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_ci	if (async_tx_test_ack(&desc->txd))
7808c2ecf20Sopenharmony_ci		return -EINTR;
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci	/*
7838c2ecf20Sopenharmony_ci	 * On first invocation this shouldn't be necessary, the call to
7848c2ecf20Sopenharmony_ci	 * ipu_init_channel_buffer() above will set addresses for us, so we
7858c2ecf20Sopenharmony_ci	 * could make it conditional on status >= IPU_CHANNEL_ENABLED, but
7868c2ecf20Sopenharmony_ci	 * doing it again shouldn't hurt either.
7878c2ecf20Sopenharmony_ci	 */
7888c2ecf20Sopenharmony_ci	ipu_update_channel_buffer(ichan, buf_idx, sg_dma_address(sg));
7898c2ecf20Sopenharmony_ci
7908c2ecf20Sopenharmony_ci	ipu_select_buffer(chan_id, buf_idx);
7918c2ecf20Sopenharmony_ci	dev_dbg(dev, "Updated sg %p on channel 0x%x buffer %d\n",
7928c2ecf20Sopenharmony_ci		sg, chan_id, buf_idx);
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_ci	return 0;
7958c2ecf20Sopenharmony_ci}
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ci/* Called under spin_lock_irqsave(&ichan->lock) */
7988c2ecf20Sopenharmony_cistatic int ipu_submit_channel_buffers(struct idmac_channel *ichan,
7998c2ecf20Sopenharmony_ci				      struct idmac_tx_desc *desc)
8008c2ecf20Sopenharmony_ci{
8018c2ecf20Sopenharmony_ci	struct scatterlist *sg;
8028c2ecf20Sopenharmony_ci	int i, ret = 0;
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_ci	for (i = 0, sg = desc->sg; i < 2 && sg; i++) {
8058c2ecf20Sopenharmony_ci		if (!ichan->sg[i]) {
8068c2ecf20Sopenharmony_ci			ichan->sg[i] = sg;
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_ci			ret = ipu_submit_buffer(ichan, desc, sg, i);
8098c2ecf20Sopenharmony_ci			if (ret < 0)
8108c2ecf20Sopenharmony_ci				return ret;
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci			sg = sg_next(sg);
8138c2ecf20Sopenharmony_ci		}
8148c2ecf20Sopenharmony_ci	}
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci	return ret;
8178c2ecf20Sopenharmony_ci}
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_cistatic dma_cookie_t idmac_tx_submit(struct dma_async_tx_descriptor *tx)
8208c2ecf20Sopenharmony_ci{
8218c2ecf20Sopenharmony_ci	struct idmac_tx_desc *desc = to_tx_desc(tx);
8228c2ecf20Sopenharmony_ci	struct idmac_channel *ichan = to_idmac_chan(tx->chan);
8238c2ecf20Sopenharmony_ci	struct idmac *idmac = to_idmac(tx->chan->device);
8248c2ecf20Sopenharmony_ci	struct ipu *ipu = to_ipu(idmac);
8258c2ecf20Sopenharmony_ci	struct device *dev = &ichan->dma_chan.dev->device;
8268c2ecf20Sopenharmony_ci	dma_cookie_t cookie;
8278c2ecf20Sopenharmony_ci	unsigned long flags;
8288c2ecf20Sopenharmony_ci	int ret;
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci	/* Sanity check */
8318c2ecf20Sopenharmony_ci	if (!list_empty(&desc->list)) {
8328c2ecf20Sopenharmony_ci		/* The descriptor doesn't belong to client */
8338c2ecf20Sopenharmony_ci		dev_err(dev, "Descriptor %p not prepared!\n", tx);
8348c2ecf20Sopenharmony_ci		return -EBUSY;
8358c2ecf20Sopenharmony_ci	}
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_ci	mutex_lock(&ichan->chan_mutex);
8388c2ecf20Sopenharmony_ci
8398c2ecf20Sopenharmony_ci	async_tx_clear_ack(tx);
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_ci	if (ichan->status < IPU_CHANNEL_READY) {
8428c2ecf20Sopenharmony_ci		struct idmac_video_param *video = &ichan->params.video;
8438c2ecf20Sopenharmony_ci		/*
8448c2ecf20Sopenharmony_ci		 * Initial buffer assignment - the first two sg-entries from
8458c2ecf20Sopenharmony_ci		 * the descriptor will end up in the IDMAC buffers
8468c2ecf20Sopenharmony_ci		 */
8478c2ecf20Sopenharmony_ci		dma_addr_t dma_1 = sg_is_last(desc->sg) ? 0 :
8488c2ecf20Sopenharmony_ci			sg_dma_address(&desc->sg[1]);
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_ci		WARN_ON(ichan->sg[0] || ichan->sg[1]);
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_ci		cookie = ipu_init_channel_buffer(ichan,
8538c2ecf20Sopenharmony_ci						 video->out_pixel_fmt,
8548c2ecf20Sopenharmony_ci						 video->out_width,
8558c2ecf20Sopenharmony_ci						 video->out_height,
8568c2ecf20Sopenharmony_ci						 video->out_stride,
8578c2ecf20Sopenharmony_ci						 IPU_ROTATE_NONE,
8588c2ecf20Sopenharmony_ci						 sg_dma_address(&desc->sg[0]),
8598c2ecf20Sopenharmony_ci						 dma_1);
8608c2ecf20Sopenharmony_ci		if (cookie < 0)
8618c2ecf20Sopenharmony_ci			goto out;
8628c2ecf20Sopenharmony_ci	}
8638c2ecf20Sopenharmony_ci
8648c2ecf20Sopenharmony_ci	dev_dbg(dev, "Submitting sg %p\n", &desc->sg[0]);
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_ci	cookie = dma_cookie_assign(tx);
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_ci	/* ipu->lock can be taken under ichan->lock, but not v.v. */
8698c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ichan->lock, flags);
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_ci	list_add_tail(&desc->list, &ichan->queue);
8728c2ecf20Sopenharmony_ci	/* submit_buffers() atomically verifies and fills empty sg slots */
8738c2ecf20Sopenharmony_ci	ret = ipu_submit_channel_buffers(ichan, desc);
8748c2ecf20Sopenharmony_ci
8758c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ichan->lock, flags);
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_ci	if (ret < 0) {
8788c2ecf20Sopenharmony_ci		cookie = ret;
8798c2ecf20Sopenharmony_ci		goto dequeue;
8808c2ecf20Sopenharmony_ci	}
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_ci	if (ichan->status < IPU_CHANNEL_ENABLED) {
8838c2ecf20Sopenharmony_ci		ret = ipu_enable_channel(idmac, ichan);
8848c2ecf20Sopenharmony_ci		if (ret < 0) {
8858c2ecf20Sopenharmony_ci			cookie = ret;
8868c2ecf20Sopenharmony_ci			goto dequeue;
8878c2ecf20Sopenharmony_ci		}
8888c2ecf20Sopenharmony_ci	}
8898c2ecf20Sopenharmony_ci
8908c2ecf20Sopenharmony_ci	dump_idmac_reg(ipu);
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_cidequeue:
8938c2ecf20Sopenharmony_ci	if (cookie < 0) {
8948c2ecf20Sopenharmony_ci		spin_lock_irqsave(&ichan->lock, flags);
8958c2ecf20Sopenharmony_ci		list_del_init(&desc->list);
8968c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&ichan->lock, flags);
8978c2ecf20Sopenharmony_ci		tx->cookie = cookie;
8988c2ecf20Sopenharmony_ci		ichan->dma_chan.cookie = cookie;
8998c2ecf20Sopenharmony_ci	}
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_ciout:
9028c2ecf20Sopenharmony_ci	mutex_unlock(&ichan->chan_mutex);
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_ci	return cookie;
9058c2ecf20Sopenharmony_ci}
9068c2ecf20Sopenharmony_ci
9078c2ecf20Sopenharmony_ci/* Called with ichan->chan_mutex held */
9088c2ecf20Sopenharmony_cistatic int idmac_desc_alloc(struct idmac_channel *ichan, int n)
9098c2ecf20Sopenharmony_ci{
9108c2ecf20Sopenharmony_ci	struct idmac_tx_desc *desc =
9118c2ecf20Sopenharmony_ci		vmalloc(array_size(n, sizeof(struct idmac_tx_desc)));
9128c2ecf20Sopenharmony_ci	struct idmac *idmac = to_idmac(ichan->dma_chan.device);
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_ci	if (!desc)
9158c2ecf20Sopenharmony_ci		return -ENOMEM;
9168c2ecf20Sopenharmony_ci
9178c2ecf20Sopenharmony_ci	/* No interrupts, just disable the tasklet for a moment */
9188c2ecf20Sopenharmony_ci	tasklet_disable(&to_ipu(idmac)->tasklet);
9198c2ecf20Sopenharmony_ci
9208c2ecf20Sopenharmony_ci	ichan->n_tx_desc = n;
9218c2ecf20Sopenharmony_ci	ichan->desc = desc;
9228c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&ichan->queue);
9238c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&ichan->free_list);
9248c2ecf20Sopenharmony_ci
9258c2ecf20Sopenharmony_ci	while (n--) {
9268c2ecf20Sopenharmony_ci		struct dma_async_tx_descriptor *txd = &desc->txd;
9278c2ecf20Sopenharmony_ci
9288c2ecf20Sopenharmony_ci		memset(txd, 0, sizeof(*txd));
9298c2ecf20Sopenharmony_ci		dma_async_tx_descriptor_init(txd, &ichan->dma_chan);
9308c2ecf20Sopenharmony_ci		txd->tx_submit		= idmac_tx_submit;
9318c2ecf20Sopenharmony_ci
9328c2ecf20Sopenharmony_ci		list_add(&desc->list, &ichan->free_list);
9338c2ecf20Sopenharmony_ci
9348c2ecf20Sopenharmony_ci		desc++;
9358c2ecf20Sopenharmony_ci	}
9368c2ecf20Sopenharmony_ci
9378c2ecf20Sopenharmony_ci	tasklet_enable(&to_ipu(idmac)->tasklet);
9388c2ecf20Sopenharmony_ci
9398c2ecf20Sopenharmony_ci	return 0;
9408c2ecf20Sopenharmony_ci}
9418c2ecf20Sopenharmony_ci
9428c2ecf20Sopenharmony_ci/**
9438c2ecf20Sopenharmony_ci * ipu_init_channel() - initialize an IPU channel.
9448c2ecf20Sopenharmony_ci * @idmac:	IPU DMAC context.
9458c2ecf20Sopenharmony_ci * @ichan:	pointer to the channel object.
9468c2ecf20Sopenharmony_ci * @return      0 on success or negative error code on failure.
9478c2ecf20Sopenharmony_ci */
9488c2ecf20Sopenharmony_cistatic int ipu_init_channel(struct idmac *idmac, struct idmac_channel *ichan)
9498c2ecf20Sopenharmony_ci{
9508c2ecf20Sopenharmony_ci	union ipu_channel_param *params = &ichan->params;
9518c2ecf20Sopenharmony_ci	uint32_t ipu_conf;
9528c2ecf20Sopenharmony_ci	enum ipu_channel channel = ichan->dma_chan.chan_id;
9538c2ecf20Sopenharmony_ci	unsigned long flags;
9548c2ecf20Sopenharmony_ci	uint32_t reg;
9558c2ecf20Sopenharmony_ci	struct ipu *ipu = to_ipu(idmac);
9568c2ecf20Sopenharmony_ci	int ret = 0, n_desc = 0;
9578c2ecf20Sopenharmony_ci
9588c2ecf20Sopenharmony_ci	dev_dbg(ipu->dev, "init channel = %d\n", channel);
9598c2ecf20Sopenharmony_ci
9608c2ecf20Sopenharmony_ci	if (channel != IDMAC_SDC_0 && channel != IDMAC_SDC_1 &&
9618c2ecf20Sopenharmony_ci	    channel != IDMAC_IC_7)
9628c2ecf20Sopenharmony_ci		return -EINVAL;
9638c2ecf20Sopenharmony_ci
9648c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ipu->lock, flags);
9658c2ecf20Sopenharmony_ci
9668c2ecf20Sopenharmony_ci	switch (channel) {
9678c2ecf20Sopenharmony_ci	case IDMAC_IC_7:
9688c2ecf20Sopenharmony_ci		n_desc = 16;
9698c2ecf20Sopenharmony_ci		reg = idmac_read_icreg(ipu, IC_CONF);
9708c2ecf20Sopenharmony_ci		idmac_write_icreg(ipu, reg & ~IC_CONF_CSI_MEM_WR_EN, IC_CONF);
9718c2ecf20Sopenharmony_ci		break;
9728c2ecf20Sopenharmony_ci	case IDMAC_IC_0:
9738c2ecf20Sopenharmony_ci		n_desc = 16;
9748c2ecf20Sopenharmony_ci		reg = idmac_read_ipureg(ipu, IPU_FS_PROC_FLOW);
9758c2ecf20Sopenharmony_ci		idmac_write_ipureg(ipu, reg & ~FS_ENC_IN_VALID, IPU_FS_PROC_FLOW);
9768c2ecf20Sopenharmony_ci		ret = ipu_ic_init_prpenc(ipu, params, true);
9778c2ecf20Sopenharmony_ci		break;
9788c2ecf20Sopenharmony_ci	case IDMAC_SDC_0:
9798c2ecf20Sopenharmony_ci	case IDMAC_SDC_1:
9808c2ecf20Sopenharmony_ci		n_desc = 4;
9818c2ecf20Sopenharmony_ci	default:
9828c2ecf20Sopenharmony_ci		break;
9838c2ecf20Sopenharmony_ci	}
9848c2ecf20Sopenharmony_ci
9858c2ecf20Sopenharmony_ci	ipu->channel_init_mask |= 1L << channel;
9868c2ecf20Sopenharmony_ci
9878c2ecf20Sopenharmony_ci	/* Enable IPU sub module */
9888c2ecf20Sopenharmony_ci	ipu_conf = idmac_read_ipureg(ipu, IPU_CONF) |
9898c2ecf20Sopenharmony_ci		ipu_channel_conf_mask(channel);
9908c2ecf20Sopenharmony_ci	idmac_write_ipureg(ipu, ipu_conf, IPU_CONF);
9918c2ecf20Sopenharmony_ci
9928c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ipu->lock, flags);
9938c2ecf20Sopenharmony_ci
9948c2ecf20Sopenharmony_ci	if (n_desc && !ichan->desc)
9958c2ecf20Sopenharmony_ci		ret = idmac_desc_alloc(ichan, n_desc);
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_ci	dump_idmac_reg(ipu);
9988c2ecf20Sopenharmony_ci
9998c2ecf20Sopenharmony_ci	return ret;
10008c2ecf20Sopenharmony_ci}
10018c2ecf20Sopenharmony_ci
10028c2ecf20Sopenharmony_ci/**
10038c2ecf20Sopenharmony_ci * ipu_uninit_channel() - uninitialize an IPU channel.
10048c2ecf20Sopenharmony_ci * @idmac:	IPU DMAC context.
10058c2ecf20Sopenharmony_ci * @ichan:	pointer to the channel object.
10068c2ecf20Sopenharmony_ci */
10078c2ecf20Sopenharmony_cistatic void ipu_uninit_channel(struct idmac *idmac, struct idmac_channel *ichan)
10088c2ecf20Sopenharmony_ci{
10098c2ecf20Sopenharmony_ci	enum ipu_channel channel = ichan->dma_chan.chan_id;
10108c2ecf20Sopenharmony_ci	unsigned long flags;
10118c2ecf20Sopenharmony_ci	uint32_t reg;
10128c2ecf20Sopenharmony_ci	unsigned long chan_mask = 1UL << channel;
10138c2ecf20Sopenharmony_ci	uint32_t ipu_conf;
10148c2ecf20Sopenharmony_ci	struct ipu *ipu = to_ipu(idmac);
10158c2ecf20Sopenharmony_ci
10168c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ipu->lock, flags);
10178c2ecf20Sopenharmony_ci
10188c2ecf20Sopenharmony_ci	if (!(ipu->channel_init_mask & chan_mask)) {
10198c2ecf20Sopenharmony_ci		dev_err(ipu->dev, "Channel already uninitialized %d\n",
10208c2ecf20Sopenharmony_ci			channel);
10218c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&ipu->lock, flags);
10228c2ecf20Sopenharmony_ci		return;
10238c2ecf20Sopenharmony_ci	}
10248c2ecf20Sopenharmony_ci
10258c2ecf20Sopenharmony_ci	/* Reset the double buffer */
10268c2ecf20Sopenharmony_ci	reg = idmac_read_ipureg(ipu, IPU_CHA_DB_MODE_SEL);
10278c2ecf20Sopenharmony_ci	idmac_write_ipureg(ipu, reg & ~chan_mask, IPU_CHA_DB_MODE_SEL);
10288c2ecf20Sopenharmony_ci
10298c2ecf20Sopenharmony_ci	ichan->sec_chan_en = false;
10308c2ecf20Sopenharmony_ci
10318c2ecf20Sopenharmony_ci	switch (channel) {
10328c2ecf20Sopenharmony_ci	case IDMAC_IC_7:
10338c2ecf20Sopenharmony_ci		reg = idmac_read_icreg(ipu, IC_CONF);
10348c2ecf20Sopenharmony_ci		idmac_write_icreg(ipu, reg & ~(IC_CONF_RWS_EN | IC_CONF_PRPENC_EN),
10358c2ecf20Sopenharmony_ci			     IC_CONF);
10368c2ecf20Sopenharmony_ci		break;
10378c2ecf20Sopenharmony_ci	case IDMAC_IC_0:
10388c2ecf20Sopenharmony_ci		reg = idmac_read_icreg(ipu, IC_CONF);
10398c2ecf20Sopenharmony_ci		idmac_write_icreg(ipu, reg & ~(IC_CONF_PRPENC_EN | IC_CONF_PRPENC_CSC1),
10408c2ecf20Sopenharmony_ci				  IC_CONF);
10418c2ecf20Sopenharmony_ci		break;
10428c2ecf20Sopenharmony_ci	case IDMAC_SDC_0:
10438c2ecf20Sopenharmony_ci	case IDMAC_SDC_1:
10448c2ecf20Sopenharmony_ci	default:
10458c2ecf20Sopenharmony_ci		break;
10468c2ecf20Sopenharmony_ci	}
10478c2ecf20Sopenharmony_ci
10488c2ecf20Sopenharmony_ci	ipu->channel_init_mask &= ~(1L << channel);
10498c2ecf20Sopenharmony_ci
10508c2ecf20Sopenharmony_ci	ipu_conf = idmac_read_ipureg(ipu, IPU_CONF) &
10518c2ecf20Sopenharmony_ci		~ipu_channel_conf_mask(channel);
10528c2ecf20Sopenharmony_ci	idmac_write_ipureg(ipu, ipu_conf, IPU_CONF);
10538c2ecf20Sopenharmony_ci
10548c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ipu->lock, flags);
10558c2ecf20Sopenharmony_ci
10568c2ecf20Sopenharmony_ci	ichan->n_tx_desc = 0;
10578c2ecf20Sopenharmony_ci	vfree(ichan->desc);
10588c2ecf20Sopenharmony_ci	ichan->desc = NULL;
10598c2ecf20Sopenharmony_ci}
10608c2ecf20Sopenharmony_ci
10618c2ecf20Sopenharmony_ci/**
10628c2ecf20Sopenharmony_ci * ipu_disable_channel() - disable an IPU channel.
10638c2ecf20Sopenharmony_ci * @idmac:		IPU DMAC context.
10648c2ecf20Sopenharmony_ci * @ichan:		channel object pointer.
10658c2ecf20Sopenharmony_ci * @wait_for_stop:	flag to set whether to wait for channel end of frame or
10668c2ecf20Sopenharmony_ci *			return immediately.
10678c2ecf20Sopenharmony_ci * @return:		0 on success or negative error code on failure.
10688c2ecf20Sopenharmony_ci */
10698c2ecf20Sopenharmony_cistatic int ipu_disable_channel(struct idmac *idmac, struct idmac_channel *ichan,
10708c2ecf20Sopenharmony_ci			       bool wait_for_stop)
10718c2ecf20Sopenharmony_ci{
10728c2ecf20Sopenharmony_ci	enum ipu_channel channel = ichan->dma_chan.chan_id;
10738c2ecf20Sopenharmony_ci	struct ipu *ipu = to_ipu(idmac);
10748c2ecf20Sopenharmony_ci	uint32_t reg;
10758c2ecf20Sopenharmony_ci	unsigned long flags;
10768c2ecf20Sopenharmony_ci	unsigned long chan_mask = 1UL << channel;
10778c2ecf20Sopenharmony_ci	unsigned int timeout;
10788c2ecf20Sopenharmony_ci
10798c2ecf20Sopenharmony_ci	if (wait_for_stop && channel != IDMAC_SDC_1 && channel != IDMAC_SDC_0) {
10808c2ecf20Sopenharmony_ci		timeout = 40;
10818c2ecf20Sopenharmony_ci		/* This waiting always fails. Related to spurious irq problem */
10828c2ecf20Sopenharmony_ci		while ((idmac_read_icreg(ipu, IDMAC_CHA_BUSY) & chan_mask) ||
10838c2ecf20Sopenharmony_ci		       (ipu_channel_status(ipu, channel) == TASK_STAT_ACTIVE)) {
10848c2ecf20Sopenharmony_ci			timeout--;
10858c2ecf20Sopenharmony_ci			msleep(10);
10868c2ecf20Sopenharmony_ci
10878c2ecf20Sopenharmony_ci			if (!timeout) {
10888c2ecf20Sopenharmony_ci				dev_dbg(ipu->dev,
10898c2ecf20Sopenharmony_ci					"Warning: timeout waiting for channel %u to "
10908c2ecf20Sopenharmony_ci					"stop: buf0_rdy = 0x%08X, buf1_rdy = 0x%08X, "
10918c2ecf20Sopenharmony_ci					"busy = 0x%08X, tstat = 0x%08X\n", channel,
10928c2ecf20Sopenharmony_ci					idmac_read_ipureg(ipu, IPU_CHA_BUF0_RDY),
10938c2ecf20Sopenharmony_ci					idmac_read_ipureg(ipu, IPU_CHA_BUF1_RDY),
10948c2ecf20Sopenharmony_ci					idmac_read_icreg(ipu, IDMAC_CHA_BUSY),
10958c2ecf20Sopenharmony_ci					idmac_read_ipureg(ipu, IPU_TASKS_STAT));
10968c2ecf20Sopenharmony_ci				break;
10978c2ecf20Sopenharmony_ci			}
10988c2ecf20Sopenharmony_ci		}
10998c2ecf20Sopenharmony_ci		dev_dbg(ipu->dev, "timeout = %d * 10ms\n", 40 - timeout);
11008c2ecf20Sopenharmony_ci	}
11018c2ecf20Sopenharmony_ci	/* SDC BG and FG must be disabled before DMA is disabled */
11028c2ecf20Sopenharmony_ci	if (wait_for_stop && (channel == IDMAC_SDC_0 ||
11038c2ecf20Sopenharmony_ci			      channel == IDMAC_SDC_1)) {
11048c2ecf20Sopenharmony_ci		for (timeout = 5;
11058c2ecf20Sopenharmony_ci		     timeout && !ipu_irq_status(ichan->eof_irq); timeout--)
11068c2ecf20Sopenharmony_ci			msleep(5);
11078c2ecf20Sopenharmony_ci	}
11088c2ecf20Sopenharmony_ci
11098c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ipu->lock, flags);
11108c2ecf20Sopenharmony_ci
11118c2ecf20Sopenharmony_ci	/* Disable IC task */
11128c2ecf20Sopenharmony_ci	ipu_ic_disable_task(ipu, channel);
11138c2ecf20Sopenharmony_ci
11148c2ecf20Sopenharmony_ci	/* Disable DMA channel(s) */
11158c2ecf20Sopenharmony_ci	reg = idmac_read_icreg(ipu, IDMAC_CHA_EN);
11168c2ecf20Sopenharmony_ci	idmac_write_icreg(ipu, reg & ~chan_mask, IDMAC_CHA_EN);
11178c2ecf20Sopenharmony_ci
11188c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ipu->lock, flags);
11198c2ecf20Sopenharmony_ci
11208c2ecf20Sopenharmony_ci	return 0;
11218c2ecf20Sopenharmony_ci}
11228c2ecf20Sopenharmony_ci
11238c2ecf20Sopenharmony_cistatic struct scatterlist *idmac_sg_next(struct idmac_channel *ichan,
11248c2ecf20Sopenharmony_ci	struct idmac_tx_desc **desc, struct scatterlist *sg)
11258c2ecf20Sopenharmony_ci{
11268c2ecf20Sopenharmony_ci	struct scatterlist *sgnew = sg ? sg_next(sg) : NULL;
11278c2ecf20Sopenharmony_ci
11288c2ecf20Sopenharmony_ci	if (sgnew)
11298c2ecf20Sopenharmony_ci		/* next sg-element in this list */
11308c2ecf20Sopenharmony_ci		return sgnew;
11318c2ecf20Sopenharmony_ci
11328c2ecf20Sopenharmony_ci	if ((*desc)->list.next == &ichan->queue)
11338c2ecf20Sopenharmony_ci		/* No more descriptors on the queue */
11348c2ecf20Sopenharmony_ci		return NULL;
11358c2ecf20Sopenharmony_ci
11368c2ecf20Sopenharmony_ci	/* Fetch next descriptor */
11378c2ecf20Sopenharmony_ci	*desc = list_entry((*desc)->list.next, struct idmac_tx_desc, list);
11388c2ecf20Sopenharmony_ci	return (*desc)->sg;
11398c2ecf20Sopenharmony_ci}
11408c2ecf20Sopenharmony_ci
11418c2ecf20Sopenharmony_ci/*
11428c2ecf20Sopenharmony_ci * We have several possibilities here:
11438c2ecf20Sopenharmony_ci * current BUF		next BUF
11448c2ecf20Sopenharmony_ci *
11458c2ecf20Sopenharmony_ci * not last sg		next not last sg
11468c2ecf20Sopenharmony_ci * not last sg		next last sg
11478c2ecf20Sopenharmony_ci * last sg		first sg from next descriptor
11488c2ecf20Sopenharmony_ci * last sg		NULL
11498c2ecf20Sopenharmony_ci *
11508c2ecf20Sopenharmony_ci * Besides, the descriptor queue might be empty or not. We process all these
11518c2ecf20Sopenharmony_ci * cases carefully.
11528c2ecf20Sopenharmony_ci */
11538c2ecf20Sopenharmony_cistatic irqreturn_t idmac_interrupt(int irq, void *dev_id)
11548c2ecf20Sopenharmony_ci{
11558c2ecf20Sopenharmony_ci	struct idmac_channel *ichan = dev_id;
11568c2ecf20Sopenharmony_ci	struct device *dev = &ichan->dma_chan.dev->device;
11578c2ecf20Sopenharmony_ci	unsigned int chan_id = ichan->dma_chan.chan_id;
11588c2ecf20Sopenharmony_ci	struct scatterlist **sg, *sgnext, *sgnew = NULL;
11598c2ecf20Sopenharmony_ci	/* Next transfer descriptor */
11608c2ecf20Sopenharmony_ci	struct idmac_tx_desc *desc, *descnew;
11618c2ecf20Sopenharmony_ci	bool done = false;
11628c2ecf20Sopenharmony_ci	u32 ready0, ready1, curbuf, err;
11638c2ecf20Sopenharmony_ci	unsigned long flags;
11648c2ecf20Sopenharmony_ci	struct dmaengine_desc_callback cb;
11658c2ecf20Sopenharmony_ci
11668c2ecf20Sopenharmony_ci	/* IDMAC has cleared the respective BUFx_RDY bit, we manage the buffer */
11678c2ecf20Sopenharmony_ci
11688c2ecf20Sopenharmony_ci	dev_dbg(dev, "IDMAC irq %d, buf %d\n", irq, ichan->active_buffer);
11698c2ecf20Sopenharmony_ci
11708c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ipu_data.lock, flags);
11718c2ecf20Sopenharmony_ci
11728c2ecf20Sopenharmony_ci	ready0	= idmac_read_ipureg(&ipu_data, IPU_CHA_BUF0_RDY);
11738c2ecf20Sopenharmony_ci	ready1	= idmac_read_ipureg(&ipu_data, IPU_CHA_BUF1_RDY);
11748c2ecf20Sopenharmony_ci	curbuf	= idmac_read_ipureg(&ipu_data, IPU_CHA_CUR_BUF);
11758c2ecf20Sopenharmony_ci	err	= idmac_read_ipureg(&ipu_data, IPU_INT_STAT_4);
11768c2ecf20Sopenharmony_ci
11778c2ecf20Sopenharmony_ci	if (err & (1 << chan_id)) {
11788c2ecf20Sopenharmony_ci		idmac_write_ipureg(&ipu_data, 1 << chan_id, IPU_INT_STAT_4);
11798c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&ipu_data.lock, flags);
11808c2ecf20Sopenharmony_ci		/*
11818c2ecf20Sopenharmony_ci		 * Doing this
11828c2ecf20Sopenharmony_ci		 * ichan->sg[0] = ichan->sg[1] = NULL;
11838c2ecf20Sopenharmony_ci		 * you can force channel re-enable on the next tx_submit(), but
11848c2ecf20Sopenharmony_ci		 * this is dirty - think about descriptors with multiple
11858c2ecf20Sopenharmony_ci		 * sg elements.
11868c2ecf20Sopenharmony_ci		 */
11878c2ecf20Sopenharmony_ci		dev_warn(dev, "NFB4EOF on channel %d, ready %x, %x, cur %x\n",
11888c2ecf20Sopenharmony_ci			 chan_id, ready0, ready1, curbuf);
11898c2ecf20Sopenharmony_ci		return IRQ_HANDLED;
11908c2ecf20Sopenharmony_ci	}
11918c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ipu_data.lock, flags);
11928c2ecf20Sopenharmony_ci
11938c2ecf20Sopenharmony_ci	/* Other interrupts do not interfere with this channel */
11948c2ecf20Sopenharmony_ci	spin_lock(&ichan->lock);
11958c2ecf20Sopenharmony_ci	if (unlikely((ichan->active_buffer && (ready1 >> chan_id) & 1) ||
11968c2ecf20Sopenharmony_ci		     (!ichan->active_buffer && (ready0 >> chan_id) & 1)
11978c2ecf20Sopenharmony_ci		     )) {
11988c2ecf20Sopenharmony_ci		spin_unlock(&ichan->lock);
11998c2ecf20Sopenharmony_ci		dev_dbg(dev,
12008c2ecf20Sopenharmony_ci			"IRQ with active buffer still ready on channel %x, "
12018c2ecf20Sopenharmony_ci			"active %d, ready %x, %x!\n", chan_id,
12028c2ecf20Sopenharmony_ci			ichan->active_buffer, ready0, ready1);
12038c2ecf20Sopenharmony_ci		return IRQ_NONE;
12048c2ecf20Sopenharmony_ci	}
12058c2ecf20Sopenharmony_ci
12068c2ecf20Sopenharmony_ci	if (unlikely(list_empty(&ichan->queue))) {
12078c2ecf20Sopenharmony_ci		ichan->sg[ichan->active_buffer] = NULL;
12088c2ecf20Sopenharmony_ci		spin_unlock(&ichan->lock);
12098c2ecf20Sopenharmony_ci		dev_err(dev,
12108c2ecf20Sopenharmony_ci			"IRQ without queued buffers on channel %x, active %d, "
12118c2ecf20Sopenharmony_ci			"ready %x, %x!\n", chan_id,
12128c2ecf20Sopenharmony_ci			ichan->active_buffer, ready0, ready1);
12138c2ecf20Sopenharmony_ci		return IRQ_NONE;
12148c2ecf20Sopenharmony_ci	}
12158c2ecf20Sopenharmony_ci
12168c2ecf20Sopenharmony_ci	/*
12178c2ecf20Sopenharmony_ci	 * active_buffer is a software flag, it shows which buffer we are
12188c2ecf20Sopenharmony_ci	 * currently expecting back from the hardware, IDMAC should be
12198c2ecf20Sopenharmony_ci	 * processing the other buffer already
12208c2ecf20Sopenharmony_ci	 */
12218c2ecf20Sopenharmony_ci	sg = &ichan->sg[ichan->active_buffer];
12228c2ecf20Sopenharmony_ci	sgnext = ichan->sg[!ichan->active_buffer];
12238c2ecf20Sopenharmony_ci
12248c2ecf20Sopenharmony_ci	if (!*sg) {
12258c2ecf20Sopenharmony_ci		spin_unlock(&ichan->lock);
12268c2ecf20Sopenharmony_ci		return IRQ_HANDLED;
12278c2ecf20Sopenharmony_ci	}
12288c2ecf20Sopenharmony_ci
12298c2ecf20Sopenharmony_ci	desc = list_entry(ichan->queue.next, struct idmac_tx_desc, list);
12308c2ecf20Sopenharmony_ci	descnew = desc;
12318c2ecf20Sopenharmony_ci
12328c2ecf20Sopenharmony_ci	dev_dbg(dev, "IDMAC irq %d, dma %#llx, next dma %#llx, current %d, curbuf %#x\n",
12338c2ecf20Sopenharmony_ci		irq, (u64)sg_dma_address(*sg),
12348c2ecf20Sopenharmony_ci		sgnext ? (u64)sg_dma_address(sgnext) : 0,
12358c2ecf20Sopenharmony_ci		ichan->active_buffer, curbuf);
12368c2ecf20Sopenharmony_ci
12378c2ecf20Sopenharmony_ci	/* Find the descriptor of sgnext */
12388c2ecf20Sopenharmony_ci	sgnew = idmac_sg_next(ichan, &descnew, *sg);
12398c2ecf20Sopenharmony_ci	if (sgnext != sgnew)
12408c2ecf20Sopenharmony_ci		dev_err(dev, "Submitted buffer %p, next buffer %p\n", sgnext, sgnew);
12418c2ecf20Sopenharmony_ci
12428c2ecf20Sopenharmony_ci	/*
12438c2ecf20Sopenharmony_ci	 * if sgnext == NULL sg must be the last element in a scatterlist and
12448c2ecf20Sopenharmony_ci	 * queue must be empty
12458c2ecf20Sopenharmony_ci	 */
12468c2ecf20Sopenharmony_ci	if (unlikely(!sgnext)) {
12478c2ecf20Sopenharmony_ci		if (!WARN_ON(sg_next(*sg)))
12488c2ecf20Sopenharmony_ci			dev_dbg(dev, "Underrun on channel %x\n", chan_id);
12498c2ecf20Sopenharmony_ci		ichan->sg[!ichan->active_buffer] = sgnew;
12508c2ecf20Sopenharmony_ci
12518c2ecf20Sopenharmony_ci		if (unlikely(sgnew)) {
12528c2ecf20Sopenharmony_ci			ipu_submit_buffer(ichan, descnew, sgnew, !ichan->active_buffer);
12538c2ecf20Sopenharmony_ci		} else {
12548c2ecf20Sopenharmony_ci			spin_lock_irqsave(&ipu_data.lock, flags);
12558c2ecf20Sopenharmony_ci			ipu_ic_disable_task(&ipu_data, chan_id);
12568c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&ipu_data.lock, flags);
12578c2ecf20Sopenharmony_ci			ichan->status = IPU_CHANNEL_READY;
12588c2ecf20Sopenharmony_ci			/* Continue to check for complete descriptor */
12598c2ecf20Sopenharmony_ci		}
12608c2ecf20Sopenharmony_ci	}
12618c2ecf20Sopenharmony_ci
12628c2ecf20Sopenharmony_ci	/* Calculate and submit the next sg element */
12638c2ecf20Sopenharmony_ci	sgnew = idmac_sg_next(ichan, &descnew, sgnew);
12648c2ecf20Sopenharmony_ci
12658c2ecf20Sopenharmony_ci	if (unlikely(!sg_next(*sg)) || !sgnext) {
12668c2ecf20Sopenharmony_ci		/*
12678c2ecf20Sopenharmony_ci		 * Last element in scatterlist done, remove from the queue,
12688c2ecf20Sopenharmony_ci		 * _init for debugging
12698c2ecf20Sopenharmony_ci		 */
12708c2ecf20Sopenharmony_ci		list_del_init(&desc->list);
12718c2ecf20Sopenharmony_ci		done = true;
12728c2ecf20Sopenharmony_ci	}
12738c2ecf20Sopenharmony_ci
12748c2ecf20Sopenharmony_ci	*sg = sgnew;
12758c2ecf20Sopenharmony_ci
12768c2ecf20Sopenharmony_ci	if (likely(sgnew) &&
12778c2ecf20Sopenharmony_ci	    ipu_submit_buffer(ichan, descnew, sgnew, ichan->active_buffer) < 0) {
12788c2ecf20Sopenharmony_ci		dmaengine_desc_get_callback(&descnew->txd, &cb);
12798c2ecf20Sopenharmony_ci
12808c2ecf20Sopenharmony_ci		list_del_init(&descnew->list);
12818c2ecf20Sopenharmony_ci		spin_unlock(&ichan->lock);
12828c2ecf20Sopenharmony_ci
12838c2ecf20Sopenharmony_ci		dmaengine_desc_callback_invoke(&cb, NULL);
12848c2ecf20Sopenharmony_ci		spin_lock(&ichan->lock);
12858c2ecf20Sopenharmony_ci	}
12868c2ecf20Sopenharmony_ci
12878c2ecf20Sopenharmony_ci	/* Flip the active buffer - even if update above failed */
12888c2ecf20Sopenharmony_ci	ichan->active_buffer = !ichan->active_buffer;
12898c2ecf20Sopenharmony_ci	if (done)
12908c2ecf20Sopenharmony_ci		dma_cookie_complete(&desc->txd);
12918c2ecf20Sopenharmony_ci
12928c2ecf20Sopenharmony_ci	dmaengine_desc_get_callback(&desc->txd, &cb);
12938c2ecf20Sopenharmony_ci
12948c2ecf20Sopenharmony_ci	spin_unlock(&ichan->lock);
12958c2ecf20Sopenharmony_ci
12968c2ecf20Sopenharmony_ci	if (done && (desc->txd.flags & DMA_PREP_INTERRUPT))
12978c2ecf20Sopenharmony_ci		dmaengine_desc_callback_invoke(&cb, NULL);
12988c2ecf20Sopenharmony_ci
12998c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
13008c2ecf20Sopenharmony_ci}
13018c2ecf20Sopenharmony_ci
13028c2ecf20Sopenharmony_cistatic void ipu_gc_tasklet(struct tasklet_struct *t)
13038c2ecf20Sopenharmony_ci{
13048c2ecf20Sopenharmony_ci	struct ipu *ipu = from_tasklet(ipu, t, tasklet);
13058c2ecf20Sopenharmony_ci	int i;
13068c2ecf20Sopenharmony_ci
13078c2ecf20Sopenharmony_ci	for (i = 0; i < IPU_CHANNELS_NUM; i++) {
13088c2ecf20Sopenharmony_ci		struct idmac_channel *ichan = ipu->channel + i;
13098c2ecf20Sopenharmony_ci		struct idmac_tx_desc *desc;
13108c2ecf20Sopenharmony_ci		unsigned long flags;
13118c2ecf20Sopenharmony_ci		struct scatterlist *sg;
13128c2ecf20Sopenharmony_ci		int j, k;
13138c2ecf20Sopenharmony_ci
13148c2ecf20Sopenharmony_ci		for (j = 0; j < ichan->n_tx_desc; j++) {
13158c2ecf20Sopenharmony_ci			desc = ichan->desc + j;
13168c2ecf20Sopenharmony_ci			spin_lock_irqsave(&ichan->lock, flags);
13178c2ecf20Sopenharmony_ci			if (async_tx_test_ack(&desc->txd)) {
13188c2ecf20Sopenharmony_ci				list_move(&desc->list, &ichan->free_list);
13198c2ecf20Sopenharmony_ci				for_each_sg(desc->sg, sg, desc->sg_len, k) {
13208c2ecf20Sopenharmony_ci					if (ichan->sg[0] == sg)
13218c2ecf20Sopenharmony_ci						ichan->sg[0] = NULL;
13228c2ecf20Sopenharmony_ci					else if (ichan->sg[1] == sg)
13238c2ecf20Sopenharmony_ci						ichan->sg[1] = NULL;
13248c2ecf20Sopenharmony_ci				}
13258c2ecf20Sopenharmony_ci				async_tx_clear_ack(&desc->txd);
13268c2ecf20Sopenharmony_ci			}
13278c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&ichan->lock, flags);
13288c2ecf20Sopenharmony_ci		}
13298c2ecf20Sopenharmony_ci	}
13308c2ecf20Sopenharmony_ci}
13318c2ecf20Sopenharmony_ci
13328c2ecf20Sopenharmony_ci/* Allocate and initialise a transfer descriptor. */
13338c2ecf20Sopenharmony_cistatic struct dma_async_tx_descriptor *idmac_prep_slave_sg(struct dma_chan *chan,
13348c2ecf20Sopenharmony_ci		struct scatterlist *sgl, unsigned int sg_len,
13358c2ecf20Sopenharmony_ci		enum dma_transfer_direction direction, unsigned long tx_flags,
13368c2ecf20Sopenharmony_ci		void *context)
13378c2ecf20Sopenharmony_ci{
13388c2ecf20Sopenharmony_ci	struct idmac_channel *ichan = to_idmac_chan(chan);
13398c2ecf20Sopenharmony_ci	struct idmac_tx_desc *desc = NULL;
13408c2ecf20Sopenharmony_ci	struct dma_async_tx_descriptor *txd = NULL;
13418c2ecf20Sopenharmony_ci	unsigned long flags;
13428c2ecf20Sopenharmony_ci
13438c2ecf20Sopenharmony_ci	/* We only can handle these three channels so far */
13448c2ecf20Sopenharmony_ci	if (chan->chan_id != IDMAC_SDC_0 && chan->chan_id != IDMAC_SDC_1 &&
13458c2ecf20Sopenharmony_ci	    chan->chan_id != IDMAC_IC_7)
13468c2ecf20Sopenharmony_ci		return NULL;
13478c2ecf20Sopenharmony_ci
13488c2ecf20Sopenharmony_ci	if (!is_slave_direction(direction)) {
13498c2ecf20Sopenharmony_ci		dev_err(chan->device->dev, "Invalid DMA direction %d!\n", direction);
13508c2ecf20Sopenharmony_ci		return NULL;
13518c2ecf20Sopenharmony_ci	}
13528c2ecf20Sopenharmony_ci
13538c2ecf20Sopenharmony_ci	mutex_lock(&ichan->chan_mutex);
13548c2ecf20Sopenharmony_ci
13558c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ichan->lock, flags);
13568c2ecf20Sopenharmony_ci	if (!list_empty(&ichan->free_list)) {
13578c2ecf20Sopenharmony_ci		desc = list_entry(ichan->free_list.next,
13588c2ecf20Sopenharmony_ci				  struct idmac_tx_desc, list);
13598c2ecf20Sopenharmony_ci
13608c2ecf20Sopenharmony_ci		list_del_init(&desc->list);
13618c2ecf20Sopenharmony_ci
13628c2ecf20Sopenharmony_ci		desc->sg_len	= sg_len;
13638c2ecf20Sopenharmony_ci		desc->sg	= sgl;
13648c2ecf20Sopenharmony_ci		txd		= &desc->txd;
13658c2ecf20Sopenharmony_ci		txd->flags	= tx_flags;
13668c2ecf20Sopenharmony_ci	}
13678c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ichan->lock, flags);
13688c2ecf20Sopenharmony_ci
13698c2ecf20Sopenharmony_ci	mutex_unlock(&ichan->chan_mutex);
13708c2ecf20Sopenharmony_ci
13718c2ecf20Sopenharmony_ci	tasklet_schedule(&to_ipu(to_idmac(chan->device))->tasklet);
13728c2ecf20Sopenharmony_ci
13738c2ecf20Sopenharmony_ci	return txd;
13748c2ecf20Sopenharmony_ci}
13758c2ecf20Sopenharmony_ci
13768c2ecf20Sopenharmony_ci/* Re-select the current buffer and re-activate the channel */
13778c2ecf20Sopenharmony_cistatic void idmac_issue_pending(struct dma_chan *chan)
13788c2ecf20Sopenharmony_ci{
13798c2ecf20Sopenharmony_ci	struct idmac_channel *ichan = to_idmac_chan(chan);
13808c2ecf20Sopenharmony_ci	struct idmac *idmac = to_idmac(chan->device);
13818c2ecf20Sopenharmony_ci	struct ipu *ipu = to_ipu(idmac);
13828c2ecf20Sopenharmony_ci	unsigned long flags;
13838c2ecf20Sopenharmony_ci
13848c2ecf20Sopenharmony_ci	/* This is not always needed, but doesn't hurt either */
13858c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ipu->lock, flags);
13868c2ecf20Sopenharmony_ci	ipu_select_buffer(chan->chan_id, ichan->active_buffer);
13878c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ipu->lock, flags);
13888c2ecf20Sopenharmony_ci
13898c2ecf20Sopenharmony_ci	/*
13908c2ecf20Sopenharmony_ci	 * Might need to perform some parts of initialisation from
13918c2ecf20Sopenharmony_ci	 * ipu_enable_channel(), but not all, we do not want to reset to buffer
13928c2ecf20Sopenharmony_ci	 * 0, don't need to set priority again either, but re-enabling the task
13938c2ecf20Sopenharmony_ci	 * and the channel might be a good idea.
13948c2ecf20Sopenharmony_ci	 */
13958c2ecf20Sopenharmony_ci}
13968c2ecf20Sopenharmony_ci
13978c2ecf20Sopenharmony_cistatic int idmac_pause(struct dma_chan *chan)
13988c2ecf20Sopenharmony_ci{
13998c2ecf20Sopenharmony_ci	struct idmac_channel *ichan = to_idmac_chan(chan);
14008c2ecf20Sopenharmony_ci	struct idmac *idmac = to_idmac(chan->device);
14018c2ecf20Sopenharmony_ci	struct ipu *ipu = to_ipu(idmac);
14028c2ecf20Sopenharmony_ci	struct list_head *list, *tmp;
14038c2ecf20Sopenharmony_ci	unsigned long flags;
14048c2ecf20Sopenharmony_ci
14058c2ecf20Sopenharmony_ci	mutex_lock(&ichan->chan_mutex);
14068c2ecf20Sopenharmony_ci
14078c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ipu->lock, flags);
14088c2ecf20Sopenharmony_ci	ipu_ic_disable_task(ipu, chan->chan_id);
14098c2ecf20Sopenharmony_ci
14108c2ecf20Sopenharmony_ci	/* Return all descriptors into "prepared" state */
14118c2ecf20Sopenharmony_ci	list_for_each_safe(list, tmp, &ichan->queue)
14128c2ecf20Sopenharmony_ci		list_del_init(list);
14138c2ecf20Sopenharmony_ci
14148c2ecf20Sopenharmony_ci	ichan->sg[0] = NULL;
14158c2ecf20Sopenharmony_ci	ichan->sg[1] = NULL;
14168c2ecf20Sopenharmony_ci
14178c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ipu->lock, flags);
14188c2ecf20Sopenharmony_ci
14198c2ecf20Sopenharmony_ci	ichan->status = IPU_CHANNEL_INITIALIZED;
14208c2ecf20Sopenharmony_ci
14218c2ecf20Sopenharmony_ci	mutex_unlock(&ichan->chan_mutex);
14228c2ecf20Sopenharmony_ci
14238c2ecf20Sopenharmony_ci	return 0;
14248c2ecf20Sopenharmony_ci}
14258c2ecf20Sopenharmony_ci
14268c2ecf20Sopenharmony_cistatic int __idmac_terminate_all(struct dma_chan *chan)
14278c2ecf20Sopenharmony_ci{
14288c2ecf20Sopenharmony_ci	struct idmac_channel *ichan = to_idmac_chan(chan);
14298c2ecf20Sopenharmony_ci	struct idmac *idmac = to_idmac(chan->device);
14308c2ecf20Sopenharmony_ci	struct ipu *ipu = to_ipu(idmac);
14318c2ecf20Sopenharmony_ci	unsigned long flags;
14328c2ecf20Sopenharmony_ci	int i;
14338c2ecf20Sopenharmony_ci
14348c2ecf20Sopenharmony_ci	ipu_disable_channel(idmac, ichan,
14358c2ecf20Sopenharmony_ci			    ichan->status >= IPU_CHANNEL_ENABLED);
14368c2ecf20Sopenharmony_ci
14378c2ecf20Sopenharmony_ci	tasklet_disable(&ipu->tasklet);
14388c2ecf20Sopenharmony_ci
14398c2ecf20Sopenharmony_ci	/* ichan->queue is modified in ISR, have to spinlock */
14408c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ichan->lock, flags);
14418c2ecf20Sopenharmony_ci	list_splice_init(&ichan->queue, &ichan->free_list);
14428c2ecf20Sopenharmony_ci
14438c2ecf20Sopenharmony_ci	if (ichan->desc)
14448c2ecf20Sopenharmony_ci		for (i = 0; i < ichan->n_tx_desc; i++) {
14458c2ecf20Sopenharmony_ci			struct idmac_tx_desc *desc = ichan->desc + i;
14468c2ecf20Sopenharmony_ci			if (list_empty(&desc->list))
14478c2ecf20Sopenharmony_ci				/* Descriptor was prepared, but not submitted */
14488c2ecf20Sopenharmony_ci				list_add(&desc->list, &ichan->free_list);
14498c2ecf20Sopenharmony_ci
14508c2ecf20Sopenharmony_ci			async_tx_clear_ack(&desc->txd);
14518c2ecf20Sopenharmony_ci		}
14528c2ecf20Sopenharmony_ci
14538c2ecf20Sopenharmony_ci	ichan->sg[0] = NULL;
14548c2ecf20Sopenharmony_ci	ichan->sg[1] = NULL;
14558c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ichan->lock, flags);
14568c2ecf20Sopenharmony_ci
14578c2ecf20Sopenharmony_ci	tasklet_enable(&ipu->tasklet);
14588c2ecf20Sopenharmony_ci
14598c2ecf20Sopenharmony_ci	ichan->status = IPU_CHANNEL_INITIALIZED;
14608c2ecf20Sopenharmony_ci
14618c2ecf20Sopenharmony_ci	return 0;
14628c2ecf20Sopenharmony_ci}
14638c2ecf20Sopenharmony_ci
14648c2ecf20Sopenharmony_cistatic int idmac_terminate_all(struct dma_chan *chan)
14658c2ecf20Sopenharmony_ci{
14668c2ecf20Sopenharmony_ci	struct idmac_channel *ichan = to_idmac_chan(chan);
14678c2ecf20Sopenharmony_ci	int ret;
14688c2ecf20Sopenharmony_ci
14698c2ecf20Sopenharmony_ci	mutex_lock(&ichan->chan_mutex);
14708c2ecf20Sopenharmony_ci
14718c2ecf20Sopenharmony_ci	ret = __idmac_terminate_all(chan);
14728c2ecf20Sopenharmony_ci
14738c2ecf20Sopenharmony_ci	mutex_unlock(&ichan->chan_mutex);
14748c2ecf20Sopenharmony_ci
14758c2ecf20Sopenharmony_ci	return ret;
14768c2ecf20Sopenharmony_ci}
14778c2ecf20Sopenharmony_ci
14788c2ecf20Sopenharmony_ci#ifdef DEBUG
14798c2ecf20Sopenharmony_cistatic irqreturn_t ic_sof_irq(int irq, void *dev_id)
14808c2ecf20Sopenharmony_ci{
14818c2ecf20Sopenharmony_ci	struct idmac_channel *ichan = dev_id;
14828c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "Got SOF IRQ %d on Channel %d\n",
14838c2ecf20Sopenharmony_ci	       irq, ichan->dma_chan.chan_id);
14848c2ecf20Sopenharmony_ci	disable_irq_nosync(irq);
14858c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
14868c2ecf20Sopenharmony_ci}
14878c2ecf20Sopenharmony_ci
14888c2ecf20Sopenharmony_cistatic irqreturn_t ic_eof_irq(int irq, void *dev_id)
14898c2ecf20Sopenharmony_ci{
14908c2ecf20Sopenharmony_ci	struct idmac_channel *ichan = dev_id;
14918c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "Got EOF IRQ %d on Channel %d\n",
14928c2ecf20Sopenharmony_ci	       irq, ichan->dma_chan.chan_id);
14938c2ecf20Sopenharmony_ci	disable_irq_nosync(irq);
14948c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
14958c2ecf20Sopenharmony_ci}
14968c2ecf20Sopenharmony_ci
14978c2ecf20Sopenharmony_cistatic int ic_sof = -EINVAL, ic_eof = -EINVAL;
14988c2ecf20Sopenharmony_ci#endif
14998c2ecf20Sopenharmony_ci
15008c2ecf20Sopenharmony_cistatic int idmac_alloc_chan_resources(struct dma_chan *chan)
15018c2ecf20Sopenharmony_ci{
15028c2ecf20Sopenharmony_ci	struct idmac_channel *ichan = to_idmac_chan(chan);
15038c2ecf20Sopenharmony_ci	struct idmac *idmac = to_idmac(chan->device);
15048c2ecf20Sopenharmony_ci	int ret;
15058c2ecf20Sopenharmony_ci
15068c2ecf20Sopenharmony_ci	/* dmaengine.c now guarantees to only offer free channels */
15078c2ecf20Sopenharmony_ci	BUG_ON(chan->client_count > 1);
15088c2ecf20Sopenharmony_ci	WARN_ON(ichan->status != IPU_CHANNEL_FREE);
15098c2ecf20Sopenharmony_ci
15108c2ecf20Sopenharmony_ci	dma_cookie_init(chan);
15118c2ecf20Sopenharmony_ci
15128c2ecf20Sopenharmony_ci	ret = ipu_irq_map(chan->chan_id);
15138c2ecf20Sopenharmony_ci	if (ret < 0)
15148c2ecf20Sopenharmony_ci		goto eimap;
15158c2ecf20Sopenharmony_ci
15168c2ecf20Sopenharmony_ci	ichan->eof_irq = ret;
15178c2ecf20Sopenharmony_ci
15188c2ecf20Sopenharmony_ci	/*
15198c2ecf20Sopenharmony_ci	 * Important to first disable the channel, because maybe someone
15208c2ecf20Sopenharmony_ci	 * used it before us, e.g., the bootloader
15218c2ecf20Sopenharmony_ci	 */
15228c2ecf20Sopenharmony_ci	ipu_disable_channel(idmac, ichan, true);
15238c2ecf20Sopenharmony_ci
15248c2ecf20Sopenharmony_ci	ret = ipu_init_channel(idmac, ichan);
15258c2ecf20Sopenharmony_ci	if (ret < 0)
15268c2ecf20Sopenharmony_ci		goto eichan;
15278c2ecf20Sopenharmony_ci
15288c2ecf20Sopenharmony_ci	ret = request_irq(ichan->eof_irq, idmac_interrupt, 0,
15298c2ecf20Sopenharmony_ci			  ichan->eof_name, ichan);
15308c2ecf20Sopenharmony_ci	if (ret < 0)
15318c2ecf20Sopenharmony_ci		goto erirq;
15328c2ecf20Sopenharmony_ci
15338c2ecf20Sopenharmony_ci#ifdef DEBUG
15348c2ecf20Sopenharmony_ci	if (chan->chan_id == IDMAC_IC_7) {
15358c2ecf20Sopenharmony_ci		ic_sof = ipu_irq_map(69);
15368c2ecf20Sopenharmony_ci		if (ic_sof > 0) {
15378c2ecf20Sopenharmony_ci			ret = request_irq(ic_sof, ic_sof_irq, 0, "IC SOF", ichan);
15388c2ecf20Sopenharmony_ci			if (ret)
15398c2ecf20Sopenharmony_ci				dev_err(&chan->dev->device, "request irq failed for IC SOF");
15408c2ecf20Sopenharmony_ci		}
15418c2ecf20Sopenharmony_ci		ic_eof = ipu_irq_map(70);
15428c2ecf20Sopenharmony_ci		if (ic_eof > 0) {
15438c2ecf20Sopenharmony_ci			ret = request_irq(ic_eof, ic_eof_irq, 0, "IC EOF", ichan);
15448c2ecf20Sopenharmony_ci			if (ret)
15458c2ecf20Sopenharmony_ci				dev_err(&chan->dev->device, "request irq failed for IC EOF");
15468c2ecf20Sopenharmony_ci		}
15478c2ecf20Sopenharmony_ci	}
15488c2ecf20Sopenharmony_ci#endif
15498c2ecf20Sopenharmony_ci
15508c2ecf20Sopenharmony_ci	ichan->status = IPU_CHANNEL_INITIALIZED;
15518c2ecf20Sopenharmony_ci
15528c2ecf20Sopenharmony_ci	dev_dbg(&chan->dev->device, "Found channel 0x%x, irq %d\n",
15538c2ecf20Sopenharmony_ci		chan->chan_id, ichan->eof_irq);
15548c2ecf20Sopenharmony_ci
15558c2ecf20Sopenharmony_ci	return ret;
15568c2ecf20Sopenharmony_ci
15578c2ecf20Sopenharmony_cierirq:
15588c2ecf20Sopenharmony_ci	ipu_uninit_channel(idmac, ichan);
15598c2ecf20Sopenharmony_cieichan:
15608c2ecf20Sopenharmony_ci	ipu_irq_unmap(chan->chan_id);
15618c2ecf20Sopenharmony_cieimap:
15628c2ecf20Sopenharmony_ci	return ret;
15638c2ecf20Sopenharmony_ci}
15648c2ecf20Sopenharmony_ci
15658c2ecf20Sopenharmony_cistatic void idmac_free_chan_resources(struct dma_chan *chan)
15668c2ecf20Sopenharmony_ci{
15678c2ecf20Sopenharmony_ci	struct idmac_channel *ichan = to_idmac_chan(chan);
15688c2ecf20Sopenharmony_ci	struct idmac *idmac = to_idmac(chan->device);
15698c2ecf20Sopenharmony_ci
15708c2ecf20Sopenharmony_ci	mutex_lock(&ichan->chan_mutex);
15718c2ecf20Sopenharmony_ci
15728c2ecf20Sopenharmony_ci	__idmac_terminate_all(chan);
15738c2ecf20Sopenharmony_ci
15748c2ecf20Sopenharmony_ci	if (ichan->status > IPU_CHANNEL_FREE) {
15758c2ecf20Sopenharmony_ci#ifdef DEBUG
15768c2ecf20Sopenharmony_ci		if (chan->chan_id == IDMAC_IC_7) {
15778c2ecf20Sopenharmony_ci			if (ic_sof > 0) {
15788c2ecf20Sopenharmony_ci				free_irq(ic_sof, ichan);
15798c2ecf20Sopenharmony_ci				ipu_irq_unmap(69);
15808c2ecf20Sopenharmony_ci				ic_sof = -EINVAL;
15818c2ecf20Sopenharmony_ci			}
15828c2ecf20Sopenharmony_ci			if (ic_eof > 0) {
15838c2ecf20Sopenharmony_ci				free_irq(ic_eof, ichan);
15848c2ecf20Sopenharmony_ci				ipu_irq_unmap(70);
15858c2ecf20Sopenharmony_ci				ic_eof = -EINVAL;
15868c2ecf20Sopenharmony_ci			}
15878c2ecf20Sopenharmony_ci		}
15888c2ecf20Sopenharmony_ci#endif
15898c2ecf20Sopenharmony_ci		free_irq(ichan->eof_irq, ichan);
15908c2ecf20Sopenharmony_ci		ipu_irq_unmap(chan->chan_id);
15918c2ecf20Sopenharmony_ci	}
15928c2ecf20Sopenharmony_ci
15938c2ecf20Sopenharmony_ci	ichan->status = IPU_CHANNEL_FREE;
15948c2ecf20Sopenharmony_ci
15958c2ecf20Sopenharmony_ci	ipu_uninit_channel(idmac, ichan);
15968c2ecf20Sopenharmony_ci
15978c2ecf20Sopenharmony_ci	mutex_unlock(&ichan->chan_mutex);
15988c2ecf20Sopenharmony_ci
15998c2ecf20Sopenharmony_ci	tasklet_schedule(&to_ipu(idmac)->tasklet);
16008c2ecf20Sopenharmony_ci}
16018c2ecf20Sopenharmony_ci
16028c2ecf20Sopenharmony_cistatic enum dma_status idmac_tx_status(struct dma_chan *chan,
16038c2ecf20Sopenharmony_ci		       dma_cookie_t cookie, struct dma_tx_state *txstate)
16048c2ecf20Sopenharmony_ci{
16058c2ecf20Sopenharmony_ci	return dma_cookie_status(chan, cookie, txstate);
16068c2ecf20Sopenharmony_ci}
16078c2ecf20Sopenharmony_ci
16088c2ecf20Sopenharmony_cistatic int __init ipu_idmac_init(struct ipu *ipu)
16098c2ecf20Sopenharmony_ci{
16108c2ecf20Sopenharmony_ci	struct idmac *idmac = &ipu->idmac;
16118c2ecf20Sopenharmony_ci	struct dma_device *dma = &idmac->dma;
16128c2ecf20Sopenharmony_ci	int i;
16138c2ecf20Sopenharmony_ci
16148c2ecf20Sopenharmony_ci	dma_cap_set(DMA_SLAVE, dma->cap_mask);
16158c2ecf20Sopenharmony_ci	dma_cap_set(DMA_PRIVATE, dma->cap_mask);
16168c2ecf20Sopenharmony_ci
16178c2ecf20Sopenharmony_ci	/* Compulsory common fields */
16188c2ecf20Sopenharmony_ci	dma->dev				= ipu->dev;
16198c2ecf20Sopenharmony_ci	dma->device_alloc_chan_resources	= idmac_alloc_chan_resources;
16208c2ecf20Sopenharmony_ci	dma->device_free_chan_resources		= idmac_free_chan_resources;
16218c2ecf20Sopenharmony_ci	dma->device_tx_status			= idmac_tx_status;
16228c2ecf20Sopenharmony_ci	dma->device_issue_pending		= idmac_issue_pending;
16238c2ecf20Sopenharmony_ci
16248c2ecf20Sopenharmony_ci	/* Compulsory for DMA_SLAVE fields */
16258c2ecf20Sopenharmony_ci	dma->device_prep_slave_sg		= idmac_prep_slave_sg;
16268c2ecf20Sopenharmony_ci	dma->device_pause			= idmac_pause;
16278c2ecf20Sopenharmony_ci	dma->device_terminate_all		= idmac_terminate_all;
16288c2ecf20Sopenharmony_ci
16298c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&dma->channels);
16308c2ecf20Sopenharmony_ci	for (i = 0; i < IPU_CHANNELS_NUM; i++) {
16318c2ecf20Sopenharmony_ci		struct idmac_channel *ichan = ipu->channel + i;
16328c2ecf20Sopenharmony_ci		struct dma_chan *dma_chan = &ichan->dma_chan;
16338c2ecf20Sopenharmony_ci
16348c2ecf20Sopenharmony_ci		spin_lock_init(&ichan->lock);
16358c2ecf20Sopenharmony_ci		mutex_init(&ichan->chan_mutex);
16368c2ecf20Sopenharmony_ci
16378c2ecf20Sopenharmony_ci		ichan->status		= IPU_CHANNEL_FREE;
16388c2ecf20Sopenharmony_ci		ichan->sec_chan_en	= false;
16398c2ecf20Sopenharmony_ci		snprintf(ichan->eof_name, sizeof(ichan->eof_name), "IDMAC EOF %d", i);
16408c2ecf20Sopenharmony_ci
16418c2ecf20Sopenharmony_ci		dma_chan->device	= &idmac->dma;
16428c2ecf20Sopenharmony_ci		dma_cookie_init(dma_chan);
16438c2ecf20Sopenharmony_ci		dma_chan->chan_id	= i;
16448c2ecf20Sopenharmony_ci		list_add_tail(&dma_chan->device_node, &dma->channels);
16458c2ecf20Sopenharmony_ci	}
16468c2ecf20Sopenharmony_ci
16478c2ecf20Sopenharmony_ci	idmac_write_icreg(ipu, 0x00000070, IDMAC_CONF);
16488c2ecf20Sopenharmony_ci
16498c2ecf20Sopenharmony_ci	return dma_async_device_register(&idmac->dma);
16508c2ecf20Sopenharmony_ci}
16518c2ecf20Sopenharmony_ci
16528c2ecf20Sopenharmony_cistatic void ipu_idmac_exit(struct ipu *ipu)
16538c2ecf20Sopenharmony_ci{
16548c2ecf20Sopenharmony_ci	int i;
16558c2ecf20Sopenharmony_ci	struct idmac *idmac = &ipu->idmac;
16568c2ecf20Sopenharmony_ci
16578c2ecf20Sopenharmony_ci	for (i = 0; i < IPU_CHANNELS_NUM; i++) {
16588c2ecf20Sopenharmony_ci		struct idmac_channel *ichan = ipu->channel + i;
16598c2ecf20Sopenharmony_ci
16608c2ecf20Sopenharmony_ci		idmac_terminate_all(&ichan->dma_chan);
16618c2ecf20Sopenharmony_ci	}
16628c2ecf20Sopenharmony_ci
16638c2ecf20Sopenharmony_ci	dma_async_device_unregister(&idmac->dma);
16648c2ecf20Sopenharmony_ci}
16658c2ecf20Sopenharmony_ci
16668c2ecf20Sopenharmony_ci/*****************************************************************************
16678c2ecf20Sopenharmony_ci * IPU common probe / remove
16688c2ecf20Sopenharmony_ci */
16698c2ecf20Sopenharmony_ci
16708c2ecf20Sopenharmony_cistatic int __init ipu_probe(struct platform_device *pdev)
16718c2ecf20Sopenharmony_ci{
16728c2ecf20Sopenharmony_ci	struct resource *mem_ipu, *mem_ic;
16738c2ecf20Sopenharmony_ci	int ret;
16748c2ecf20Sopenharmony_ci
16758c2ecf20Sopenharmony_ci	spin_lock_init(&ipu_data.lock);
16768c2ecf20Sopenharmony_ci
16778c2ecf20Sopenharmony_ci	mem_ipu	= platform_get_resource(pdev, IORESOURCE_MEM, 0);
16788c2ecf20Sopenharmony_ci	mem_ic	= platform_get_resource(pdev, IORESOURCE_MEM, 1);
16798c2ecf20Sopenharmony_ci	if (!mem_ipu || !mem_ic)
16808c2ecf20Sopenharmony_ci		return -EINVAL;
16818c2ecf20Sopenharmony_ci
16828c2ecf20Sopenharmony_ci	ipu_data.dev = &pdev->dev;
16838c2ecf20Sopenharmony_ci
16848c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, &ipu_data);
16858c2ecf20Sopenharmony_ci
16868c2ecf20Sopenharmony_ci	ret = platform_get_irq(pdev, 0);
16878c2ecf20Sopenharmony_ci	if (ret < 0)
16888c2ecf20Sopenharmony_ci		goto err_noirq;
16898c2ecf20Sopenharmony_ci
16908c2ecf20Sopenharmony_ci	ipu_data.irq_fn = ret;
16918c2ecf20Sopenharmony_ci	ret = platform_get_irq(pdev, 1);
16928c2ecf20Sopenharmony_ci	if (ret < 0)
16938c2ecf20Sopenharmony_ci		goto err_noirq;
16948c2ecf20Sopenharmony_ci
16958c2ecf20Sopenharmony_ci	ipu_data.irq_err = ret;
16968c2ecf20Sopenharmony_ci
16978c2ecf20Sopenharmony_ci	dev_dbg(&pdev->dev, "fn irq %u, err irq %u\n",
16988c2ecf20Sopenharmony_ci		ipu_data.irq_fn, ipu_data.irq_err);
16998c2ecf20Sopenharmony_ci
17008c2ecf20Sopenharmony_ci	/* Remap IPU common registers */
17018c2ecf20Sopenharmony_ci	ipu_data.reg_ipu = ioremap(mem_ipu->start, resource_size(mem_ipu));
17028c2ecf20Sopenharmony_ci	if (!ipu_data.reg_ipu) {
17038c2ecf20Sopenharmony_ci		ret = -ENOMEM;
17048c2ecf20Sopenharmony_ci		goto err_ioremap_ipu;
17058c2ecf20Sopenharmony_ci	}
17068c2ecf20Sopenharmony_ci
17078c2ecf20Sopenharmony_ci	/* Remap Image Converter and Image DMA Controller registers */
17088c2ecf20Sopenharmony_ci	ipu_data.reg_ic = ioremap(mem_ic->start, resource_size(mem_ic));
17098c2ecf20Sopenharmony_ci	if (!ipu_data.reg_ic) {
17108c2ecf20Sopenharmony_ci		ret = -ENOMEM;
17118c2ecf20Sopenharmony_ci		goto err_ioremap_ic;
17128c2ecf20Sopenharmony_ci	}
17138c2ecf20Sopenharmony_ci
17148c2ecf20Sopenharmony_ci	/* Get IPU clock */
17158c2ecf20Sopenharmony_ci	ipu_data.ipu_clk = clk_get(&pdev->dev, NULL);
17168c2ecf20Sopenharmony_ci	if (IS_ERR(ipu_data.ipu_clk)) {
17178c2ecf20Sopenharmony_ci		ret = PTR_ERR(ipu_data.ipu_clk);
17188c2ecf20Sopenharmony_ci		goto err_clk_get;
17198c2ecf20Sopenharmony_ci	}
17208c2ecf20Sopenharmony_ci
17218c2ecf20Sopenharmony_ci	/* Make sure IPU HSP clock is running */
17228c2ecf20Sopenharmony_ci	clk_prepare_enable(ipu_data.ipu_clk);
17238c2ecf20Sopenharmony_ci
17248c2ecf20Sopenharmony_ci	/* Disable all interrupts */
17258c2ecf20Sopenharmony_ci	idmac_write_ipureg(&ipu_data, 0, IPU_INT_CTRL_1);
17268c2ecf20Sopenharmony_ci	idmac_write_ipureg(&ipu_data, 0, IPU_INT_CTRL_2);
17278c2ecf20Sopenharmony_ci	idmac_write_ipureg(&ipu_data, 0, IPU_INT_CTRL_3);
17288c2ecf20Sopenharmony_ci	idmac_write_ipureg(&ipu_data, 0, IPU_INT_CTRL_4);
17298c2ecf20Sopenharmony_ci	idmac_write_ipureg(&ipu_data, 0, IPU_INT_CTRL_5);
17308c2ecf20Sopenharmony_ci
17318c2ecf20Sopenharmony_ci	dev_dbg(&pdev->dev, "%s @ 0x%08lx, fn irq %u, err irq %u\n", pdev->name,
17328c2ecf20Sopenharmony_ci		(unsigned long)mem_ipu->start, ipu_data.irq_fn, ipu_data.irq_err);
17338c2ecf20Sopenharmony_ci
17348c2ecf20Sopenharmony_ci	ret = ipu_irq_attach_irq(&ipu_data, pdev);
17358c2ecf20Sopenharmony_ci	if (ret < 0)
17368c2ecf20Sopenharmony_ci		goto err_attach_irq;
17378c2ecf20Sopenharmony_ci
17388c2ecf20Sopenharmony_ci	/* Initialize DMA engine */
17398c2ecf20Sopenharmony_ci	ret = ipu_idmac_init(&ipu_data);
17408c2ecf20Sopenharmony_ci	if (ret < 0)
17418c2ecf20Sopenharmony_ci		goto err_idmac_init;
17428c2ecf20Sopenharmony_ci
17438c2ecf20Sopenharmony_ci	tasklet_setup(&ipu_data.tasklet, ipu_gc_tasklet);
17448c2ecf20Sopenharmony_ci
17458c2ecf20Sopenharmony_ci	ipu_data.dev = &pdev->dev;
17468c2ecf20Sopenharmony_ci
17478c2ecf20Sopenharmony_ci	dev_dbg(ipu_data.dev, "IPU initialized\n");
17488c2ecf20Sopenharmony_ci
17498c2ecf20Sopenharmony_ci	return 0;
17508c2ecf20Sopenharmony_ci
17518c2ecf20Sopenharmony_cierr_idmac_init:
17528c2ecf20Sopenharmony_cierr_attach_irq:
17538c2ecf20Sopenharmony_ci	ipu_irq_detach_irq(&ipu_data, pdev);
17548c2ecf20Sopenharmony_ci	clk_disable_unprepare(ipu_data.ipu_clk);
17558c2ecf20Sopenharmony_ci	clk_put(ipu_data.ipu_clk);
17568c2ecf20Sopenharmony_cierr_clk_get:
17578c2ecf20Sopenharmony_ci	iounmap(ipu_data.reg_ic);
17588c2ecf20Sopenharmony_cierr_ioremap_ic:
17598c2ecf20Sopenharmony_ci	iounmap(ipu_data.reg_ipu);
17608c2ecf20Sopenharmony_cierr_ioremap_ipu:
17618c2ecf20Sopenharmony_cierr_noirq:
17628c2ecf20Sopenharmony_ci	dev_err(&pdev->dev, "Failed to probe IPU: %d\n", ret);
17638c2ecf20Sopenharmony_ci	return ret;
17648c2ecf20Sopenharmony_ci}
17658c2ecf20Sopenharmony_ci
17668c2ecf20Sopenharmony_cistatic int ipu_remove(struct platform_device *pdev)
17678c2ecf20Sopenharmony_ci{
17688c2ecf20Sopenharmony_ci	struct ipu *ipu = platform_get_drvdata(pdev);
17698c2ecf20Sopenharmony_ci
17708c2ecf20Sopenharmony_ci	ipu_idmac_exit(ipu);
17718c2ecf20Sopenharmony_ci	ipu_irq_detach_irq(ipu, pdev);
17728c2ecf20Sopenharmony_ci	clk_disable_unprepare(ipu->ipu_clk);
17738c2ecf20Sopenharmony_ci	clk_put(ipu->ipu_clk);
17748c2ecf20Sopenharmony_ci	iounmap(ipu->reg_ic);
17758c2ecf20Sopenharmony_ci	iounmap(ipu->reg_ipu);
17768c2ecf20Sopenharmony_ci	tasklet_kill(&ipu->tasklet);
17778c2ecf20Sopenharmony_ci
17788c2ecf20Sopenharmony_ci	return 0;
17798c2ecf20Sopenharmony_ci}
17808c2ecf20Sopenharmony_ci
17818c2ecf20Sopenharmony_ci/*
17828c2ecf20Sopenharmony_ci * We need two MEM resources - with IPU-common and Image Converter registers,
17838c2ecf20Sopenharmony_ci * including PF_CONF and IDMAC_* registers, and two IRQs - function and error
17848c2ecf20Sopenharmony_ci */
17858c2ecf20Sopenharmony_cistatic struct platform_driver ipu_platform_driver = {
17868c2ecf20Sopenharmony_ci	.driver = {
17878c2ecf20Sopenharmony_ci		.name	= "ipu-core",
17888c2ecf20Sopenharmony_ci	},
17898c2ecf20Sopenharmony_ci	.remove		= ipu_remove,
17908c2ecf20Sopenharmony_ci};
17918c2ecf20Sopenharmony_ci
17928c2ecf20Sopenharmony_cistatic int __init ipu_init(void)
17938c2ecf20Sopenharmony_ci{
17948c2ecf20Sopenharmony_ci	return platform_driver_probe(&ipu_platform_driver, ipu_probe);
17958c2ecf20Sopenharmony_ci}
17968c2ecf20Sopenharmony_cisubsys_initcall(ipu_init);
17978c2ecf20Sopenharmony_ci
17988c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("IPU core driver");
17998c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
18008c2ecf20Sopenharmony_ciMODULE_AUTHOR("Guennadi Liakhovetski <lg@denx.de>");
18018c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:ipu-core");
1802