18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * VPDMA helper library
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2013 Texas Instruments Inc.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * David Griego, <dagriego@biglakesoftware.com>
88c2ecf20Sopenharmony_ci * Dale Farnsworth, <dale@farnsworth.org>
98c2ecf20Sopenharmony_ci * Archit Taneja, <archit@ti.com>
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/delay.h>
138c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
148c2ecf20Sopenharmony_ci#include <linux/err.h>
158c2ecf20Sopenharmony_ci#include <linux/firmware.h>
168c2ecf20Sopenharmony_ci#include <linux/io.h>
178c2ecf20Sopenharmony_ci#include <linux/module.h>
188c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
198c2ecf20Sopenharmony_ci#include <linux/sched.h>
208c2ecf20Sopenharmony_ci#include <linux/slab.h>
218c2ecf20Sopenharmony_ci#include <linux/videodev2.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include "vpdma.h"
248c2ecf20Sopenharmony_ci#include "vpdma_priv.h"
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#define VPDMA_FIRMWARE	"vpdma-1b8.bin"
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ciconst struct vpdma_data_format vpdma_yuv_fmts[] = {
298c2ecf20Sopenharmony_ci	[VPDMA_DATA_FMT_Y444] = {
308c2ecf20Sopenharmony_ci		.type		= VPDMA_DATA_FMT_TYPE_YUV,
318c2ecf20Sopenharmony_ci		.data_type	= DATA_TYPE_Y444,
328c2ecf20Sopenharmony_ci		.depth		= 8,
338c2ecf20Sopenharmony_ci	},
348c2ecf20Sopenharmony_ci	[VPDMA_DATA_FMT_Y422] = {
358c2ecf20Sopenharmony_ci		.type		= VPDMA_DATA_FMT_TYPE_YUV,
368c2ecf20Sopenharmony_ci		.data_type	= DATA_TYPE_Y422,
378c2ecf20Sopenharmony_ci		.depth		= 8,
388c2ecf20Sopenharmony_ci	},
398c2ecf20Sopenharmony_ci	[VPDMA_DATA_FMT_Y420] = {
408c2ecf20Sopenharmony_ci		.type		= VPDMA_DATA_FMT_TYPE_YUV,
418c2ecf20Sopenharmony_ci		.data_type	= DATA_TYPE_Y420,
428c2ecf20Sopenharmony_ci		.depth		= 8,
438c2ecf20Sopenharmony_ci	},
448c2ecf20Sopenharmony_ci	[VPDMA_DATA_FMT_C444] = {
458c2ecf20Sopenharmony_ci		.type		= VPDMA_DATA_FMT_TYPE_YUV,
468c2ecf20Sopenharmony_ci		.data_type	= DATA_TYPE_C444,
478c2ecf20Sopenharmony_ci		.depth		= 8,
488c2ecf20Sopenharmony_ci	},
498c2ecf20Sopenharmony_ci	[VPDMA_DATA_FMT_C422] = {
508c2ecf20Sopenharmony_ci		.type		= VPDMA_DATA_FMT_TYPE_YUV,
518c2ecf20Sopenharmony_ci		.data_type	= DATA_TYPE_C422,
528c2ecf20Sopenharmony_ci		.depth		= 8,
538c2ecf20Sopenharmony_ci	},
548c2ecf20Sopenharmony_ci	[VPDMA_DATA_FMT_C420] = {
558c2ecf20Sopenharmony_ci		.type		= VPDMA_DATA_FMT_TYPE_YUV,
568c2ecf20Sopenharmony_ci		.data_type	= DATA_TYPE_C420,
578c2ecf20Sopenharmony_ci		.depth		= 4,
588c2ecf20Sopenharmony_ci	},
598c2ecf20Sopenharmony_ci	[VPDMA_DATA_FMT_CB420] = {
608c2ecf20Sopenharmony_ci		.type		= VPDMA_DATA_FMT_TYPE_YUV,
618c2ecf20Sopenharmony_ci		.data_type	= DATA_TYPE_CB420,
628c2ecf20Sopenharmony_ci		.depth		= 4,
638c2ecf20Sopenharmony_ci	},
648c2ecf20Sopenharmony_ci	[VPDMA_DATA_FMT_YCR422] = {
658c2ecf20Sopenharmony_ci		.type		= VPDMA_DATA_FMT_TYPE_YUV,
668c2ecf20Sopenharmony_ci		.data_type	= DATA_TYPE_YCR422,
678c2ecf20Sopenharmony_ci		.depth		= 16,
688c2ecf20Sopenharmony_ci	},
698c2ecf20Sopenharmony_ci	[VPDMA_DATA_FMT_YC444] = {
708c2ecf20Sopenharmony_ci		.type		= VPDMA_DATA_FMT_TYPE_YUV,
718c2ecf20Sopenharmony_ci		.data_type	= DATA_TYPE_YC444,
728c2ecf20Sopenharmony_ci		.depth		= 24,
738c2ecf20Sopenharmony_ci	},
748c2ecf20Sopenharmony_ci	[VPDMA_DATA_FMT_CRY422] = {
758c2ecf20Sopenharmony_ci		.type		= VPDMA_DATA_FMT_TYPE_YUV,
768c2ecf20Sopenharmony_ci		.data_type	= DATA_TYPE_CRY422,
778c2ecf20Sopenharmony_ci		.depth		= 16,
788c2ecf20Sopenharmony_ci	},
798c2ecf20Sopenharmony_ci	[VPDMA_DATA_FMT_CBY422] = {
808c2ecf20Sopenharmony_ci		.type		= VPDMA_DATA_FMT_TYPE_YUV,
818c2ecf20Sopenharmony_ci		.data_type	= DATA_TYPE_CBY422,
828c2ecf20Sopenharmony_ci		.depth		= 16,
838c2ecf20Sopenharmony_ci	},
848c2ecf20Sopenharmony_ci	[VPDMA_DATA_FMT_YCB422] = {
858c2ecf20Sopenharmony_ci		.type		= VPDMA_DATA_FMT_TYPE_YUV,
868c2ecf20Sopenharmony_ci		.data_type	= DATA_TYPE_YCB422,
878c2ecf20Sopenharmony_ci		.depth		= 16,
888c2ecf20Sopenharmony_ci	},
898c2ecf20Sopenharmony_ci};
908c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vpdma_yuv_fmts);
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ciconst struct vpdma_data_format vpdma_rgb_fmts[] = {
938c2ecf20Sopenharmony_ci	[VPDMA_DATA_FMT_RGB565] = {
948c2ecf20Sopenharmony_ci		.type		= VPDMA_DATA_FMT_TYPE_RGB,
958c2ecf20Sopenharmony_ci		.data_type	= DATA_TYPE_RGB16_565,
968c2ecf20Sopenharmony_ci		.depth		= 16,
978c2ecf20Sopenharmony_ci	},
988c2ecf20Sopenharmony_ci	[VPDMA_DATA_FMT_ARGB16_1555] = {
998c2ecf20Sopenharmony_ci		.type		= VPDMA_DATA_FMT_TYPE_RGB,
1008c2ecf20Sopenharmony_ci		.data_type	= DATA_TYPE_ARGB_1555,
1018c2ecf20Sopenharmony_ci		.depth		= 16,
1028c2ecf20Sopenharmony_ci	},
1038c2ecf20Sopenharmony_ci	[VPDMA_DATA_FMT_ARGB16] = {
1048c2ecf20Sopenharmony_ci		.type		= VPDMA_DATA_FMT_TYPE_RGB,
1058c2ecf20Sopenharmony_ci		.data_type	= DATA_TYPE_ARGB_4444,
1068c2ecf20Sopenharmony_ci		.depth		= 16,
1078c2ecf20Sopenharmony_ci	},
1088c2ecf20Sopenharmony_ci	[VPDMA_DATA_FMT_RGBA16_5551] = {
1098c2ecf20Sopenharmony_ci		.type		= VPDMA_DATA_FMT_TYPE_RGB,
1108c2ecf20Sopenharmony_ci		.data_type	= DATA_TYPE_RGBA_5551,
1118c2ecf20Sopenharmony_ci		.depth		= 16,
1128c2ecf20Sopenharmony_ci	},
1138c2ecf20Sopenharmony_ci	[VPDMA_DATA_FMT_RGBA16] = {
1148c2ecf20Sopenharmony_ci		.type		= VPDMA_DATA_FMT_TYPE_RGB,
1158c2ecf20Sopenharmony_ci		.data_type	= DATA_TYPE_RGBA_4444,
1168c2ecf20Sopenharmony_ci		.depth		= 16,
1178c2ecf20Sopenharmony_ci	},
1188c2ecf20Sopenharmony_ci	[VPDMA_DATA_FMT_ARGB24] = {
1198c2ecf20Sopenharmony_ci		.type		= VPDMA_DATA_FMT_TYPE_RGB,
1208c2ecf20Sopenharmony_ci		.data_type	= DATA_TYPE_ARGB24_6666,
1218c2ecf20Sopenharmony_ci		.depth		= 24,
1228c2ecf20Sopenharmony_ci	},
1238c2ecf20Sopenharmony_ci	[VPDMA_DATA_FMT_RGB24] = {
1248c2ecf20Sopenharmony_ci		.type		= VPDMA_DATA_FMT_TYPE_RGB,
1258c2ecf20Sopenharmony_ci		.data_type	= DATA_TYPE_RGB24_888,
1268c2ecf20Sopenharmony_ci		.depth		= 24,
1278c2ecf20Sopenharmony_ci	},
1288c2ecf20Sopenharmony_ci	[VPDMA_DATA_FMT_ARGB32] = {
1298c2ecf20Sopenharmony_ci		.type		= VPDMA_DATA_FMT_TYPE_RGB,
1308c2ecf20Sopenharmony_ci		.data_type	= DATA_TYPE_ARGB32_8888,
1318c2ecf20Sopenharmony_ci		.depth		= 32,
1328c2ecf20Sopenharmony_ci	},
1338c2ecf20Sopenharmony_ci	[VPDMA_DATA_FMT_RGBA24] = {
1348c2ecf20Sopenharmony_ci		.type		= VPDMA_DATA_FMT_TYPE_RGB,
1358c2ecf20Sopenharmony_ci		.data_type	= DATA_TYPE_RGBA24_6666,
1368c2ecf20Sopenharmony_ci		.depth		= 24,
1378c2ecf20Sopenharmony_ci	},
1388c2ecf20Sopenharmony_ci	[VPDMA_DATA_FMT_RGBA32] = {
1398c2ecf20Sopenharmony_ci		.type		= VPDMA_DATA_FMT_TYPE_RGB,
1408c2ecf20Sopenharmony_ci		.data_type	= DATA_TYPE_RGBA32_8888,
1418c2ecf20Sopenharmony_ci		.depth		= 32,
1428c2ecf20Sopenharmony_ci	},
1438c2ecf20Sopenharmony_ci	[VPDMA_DATA_FMT_BGR565] = {
1448c2ecf20Sopenharmony_ci		.type		= VPDMA_DATA_FMT_TYPE_RGB,
1458c2ecf20Sopenharmony_ci		.data_type	= DATA_TYPE_BGR16_565,
1468c2ecf20Sopenharmony_ci		.depth		= 16,
1478c2ecf20Sopenharmony_ci	},
1488c2ecf20Sopenharmony_ci	[VPDMA_DATA_FMT_ABGR16_1555] = {
1498c2ecf20Sopenharmony_ci		.type		= VPDMA_DATA_FMT_TYPE_RGB,
1508c2ecf20Sopenharmony_ci		.data_type	= DATA_TYPE_ABGR_1555,
1518c2ecf20Sopenharmony_ci		.depth		= 16,
1528c2ecf20Sopenharmony_ci	},
1538c2ecf20Sopenharmony_ci	[VPDMA_DATA_FMT_ABGR16] = {
1548c2ecf20Sopenharmony_ci		.type		= VPDMA_DATA_FMT_TYPE_RGB,
1558c2ecf20Sopenharmony_ci		.data_type	= DATA_TYPE_ABGR_4444,
1568c2ecf20Sopenharmony_ci		.depth		= 16,
1578c2ecf20Sopenharmony_ci	},
1588c2ecf20Sopenharmony_ci	[VPDMA_DATA_FMT_BGRA16_5551] = {
1598c2ecf20Sopenharmony_ci		.type		= VPDMA_DATA_FMT_TYPE_RGB,
1608c2ecf20Sopenharmony_ci		.data_type	= DATA_TYPE_BGRA_5551,
1618c2ecf20Sopenharmony_ci		.depth		= 16,
1628c2ecf20Sopenharmony_ci	},
1638c2ecf20Sopenharmony_ci	[VPDMA_DATA_FMT_BGRA16] = {
1648c2ecf20Sopenharmony_ci		.type		= VPDMA_DATA_FMT_TYPE_RGB,
1658c2ecf20Sopenharmony_ci		.data_type	= DATA_TYPE_BGRA_4444,
1668c2ecf20Sopenharmony_ci		.depth		= 16,
1678c2ecf20Sopenharmony_ci	},
1688c2ecf20Sopenharmony_ci	[VPDMA_DATA_FMT_ABGR24] = {
1698c2ecf20Sopenharmony_ci		.type		= VPDMA_DATA_FMT_TYPE_RGB,
1708c2ecf20Sopenharmony_ci		.data_type	= DATA_TYPE_ABGR24_6666,
1718c2ecf20Sopenharmony_ci		.depth		= 24,
1728c2ecf20Sopenharmony_ci	},
1738c2ecf20Sopenharmony_ci	[VPDMA_DATA_FMT_BGR24] = {
1748c2ecf20Sopenharmony_ci		.type		= VPDMA_DATA_FMT_TYPE_RGB,
1758c2ecf20Sopenharmony_ci		.data_type	= DATA_TYPE_BGR24_888,
1768c2ecf20Sopenharmony_ci		.depth		= 24,
1778c2ecf20Sopenharmony_ci	},
1788c2ecf20Sopenharmony_ci	[VPDMA_DATA_FMT_ABGR32] = {
1798c2ecf20Sopenharmony_ci		.type		= VPDMA_DATA_FMT_TYPE_RGB,
1808c2ecf20Sopenharmony_ci		.data_type	= DATA_TYPE_ABGR32_8888,
1818c2ecf20Sopenharmony_ci		.depth		= 32,
1828c2ecf20Sopenharmony_ci	},
1838c2ecf20Sopenharmony_ci	[VPDMA_DATA_FMT_BGRA24] = {
1848c2ecf20Sopenharmony_ci		.type		= VPDMA_DATA_FMT_TYPE_RGB,
1858c2ecf20Sopenharmony_ci		.data_type	= DATA_TYPE_BGRA24_6666,
1868c2ecf20Sopenharmony_ci		.depth		= 24,
1878c2ecf20Sopenharmony_ci	},
1888c2ecf20Sopenharmony_ci	[VPDMA_DATA_FMT_BGRA32] = {
1898c2ecf20Sopenharmony_ci		.type		= VPDMA_DATA_FMT_TYPE_RGB,
1908c2ecf20Sopenharmony_ci		.data_type	= DATA_TYPE_BGRA32_8888,
1918c2ecf20Sopenharmony_ci		.depth		= 32,
1928c2ecf20Sopenharmony_ci	},
1938c2ecf20Sopenharmony_ci};
1948c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vpdma_rgb_fmts);
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci/*
1978c2ecf20Sopenharmony_ci * To handle RAW format we are re-using the CBY422
1988c2ecf20Sopenharmony_ci * vpdma data type so that we use the vpdma to re-order
1998c2ecf20Sopenharmony_ci * the incoming bytes, as the parser assumes that the
2008c2ecf20Sopenharmony_ci * first byte presented on the bus is the MSB of a 2
2018c2ecf20Sopenharmony_ci * bytes value.
2028c2ecf20Sopenharmony_ci * RAW8 handles from 1 to 8 bits
2038c2ecf20Sopenharmony_ci * RAW16 handles from 9 to 16 bits
2048c2ecf20Sopenharmony_ci */
2058c2ecf20Sopenharmony_ciconst struct vpdma_data_format vpdma_raw_fmts[] = {
2068c2ecf20Sopenharmony_ci	[VPDMA_DATA_FMT_RAW8] = {
2078c2ecf20Sopenharmony_ci		.type		= VPDMA_DATA_FMT_TYPE_YUV,
2088c2ecf20Sopenharmony_ci		.data_type	= DATA_TYPE_CBY422,
2098c2ecf20Sopenharmony_ci		.depth		= 8,
2108c2ecf20Sopenharmony_ci	},
2118c2ecf20Sopenharmony_ci	[VPDMA_DATA_FMT_RAW16] = {
2128c2ecf20Sopenharmony_ci		.type		= VPDMA_DATA_FMT_TYPE_YUV,
2138c2ecf20Sopenharmony_ci		.data_type	= DATA_TYPE_CBY422,
2148c2ecf20Sopenharmony_ci		.depth		= 16,
2158c2ecf20Sopenharmony_ci	},
2168c2ecf20Sopenharmony_ci};
2178c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vpdma_raw_fmts);
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ciconst struct vpdma_data_format vpdma_misc_fmts[] = {
2208c2ecf20Sopenharmony_ci	[VPDMA_DATA_FMT_MV] = {
2218c2ecf20Sopenharmony_ci		.type		= VPDMA_DATA_FMT_TYPE_MISC,
2228c2ecf20Sopenharmony_ci		.data_type	= DATA_TYPE_MV,
2238c2ecf20Sopenharmony_ci		.depth		= 4,
2248c2ecf20Sopenharmony_ci	},
2258c2ecf20Sopenharmony_ci};
2268c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vpdma_misc_fmts);
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_cistruct vpdma_channel_info {
2298c2ecf20Sopenharmony_ci	int num;		/* VPDMA channel number */
2308c2ecf20Sopenharmony_ci	int cstat_offset;	/* client CSTAT register offset */
2318c2ecf20Sopenharmony_ci};
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_cistatic const struct vpdma_channel_info chan_info[] = {
2348c2ecf20Sopenharmony_ci	[VPE_CHAN_LUMA1_IN] = {
2358c2ecf20Sopenharmony_ci		.num		= VPE_CHAN_NUM_LUMA1_IN,
2368c2ecf20Sopenharmony_ci		.cstat_offset	= VPDMA_DEI_LUMA1_CSTAT,
2378c2ecf20Sopenharmony_ci	},
2388c2ecf20Sopenharmony_ci	[VPE_CHAN_CHROMA1_IN] = {
2398c2ecf20Sopenharmony_ci		.num		= VPE_CHAN_NUM_CHROMA1_IN,
2408c2ecf20Sopenharmony_ci		.cstat_offset	= VPDMA_DEI_CHROMA1_CSTAT,
2418c2ecf20Sopenharmony_ci	},
2428c2ecf20Sopenharmony_ci	[VPE_CHAN_LUMA2_IN] = {
2438c2ecf20Sopenharmony_ci		.num		= VPE_CHAN_NUM_LUMA2_IN,
2448c2ecf20Sopenharmony_ci		.cstat_offset	= VPDMA_DEI_LUMA2_CSTAT,
2458c2ecf20Sopenharmony_ci	},
2468c2ecf20Sopenharmony_ci	[VPE_CHAN_CHROMA2_IN] = {
2478c2ecf20Sopenharmony_ci		.num		= VPE_CHAN_NUM_CHROMA2_IN,
2488c2ecf20Sopenharmony_ci		.cstat_offset	= VPDMA_DEI_CHROMA2_CSTAT,
2498c2ecf20Sopenharmony_ci	},
2508c2ecf20Sopenharmony_ci	[VPE_CHAN_LUMA3_IN] = {
2518c2ecf20Sopenharmony_ci		.num		= VPE_CHAN_NUM_LUMA3_IN,
2528c2ecf20Sopenharmony_ci		.cstat_offset	= VPDMA_DEI_LUMA3_CSTAT,
2538c2ecf20Sopenharmony_ci	},
2548c2ecf20Sopenharmony_ci	[VPE_CHAN_CHROMA3_IN] = {
2558c2ecf20Sopenharmony_ci		.num		= VPE_CHAN_NUM_CHROMA3_IN,
2568c2ecf20Sopenharmony_ci		.cstat_offset	= VPDMA_DEI_CHROMA3_CSTAT,
2578c2ecf20Sopenharmony_ci	},
2588c2ecf20Sopenharmony_ci	[VPE_CHAN_MV_IN] = {
2598c2ecf20Sopenharmony_ci		.num		= VPE_CHAN_NUM_MV_IN,
2608c2ecf20Sopenharmony_ci		.cstat_offset	= VPDMA_DEI_MV_IN_CSTAT,
2618c2ecf20Sopenharmony_ci	},
2628c2ecf20Sopenharmony_ci	[VPE_CHAN_MV_OUT] = {
2638c2ecf20Sopenharmony_ci		.num		= VPE_CHAN_NUM_MV_OUT,
2648c2ecf20Sopenharmony_ci		.cstat_offset	= VPDMA_DEI_MV_OUT_CSTAT,
2658c2ecf20Sopenharmony_ci	},
2668c2ecf20Sopenharmony_ci	[VPE_CHAN_LUMA_OUT] = {
2678c2ecf20Sopenharmony_ci		.num		= VPE_CHAN_NUM_LUMA_OUT,
2688c2ecf20Sopenharmony_ci		.cstat_offset	= VPDMA_VIP_UP_Y_CSTAT,
2698c2ecf20Sopenharmony_ci	},
2708c2ecf20Sopenharmony_ci	[VPE_CHAN_CHROMA_OUT] = {
2718c2ecf20Sopenharmony_ci		.num		= VPE_CHAN_NUM_CHROMA_OUT,
2728c2ecf20Sopenharmony_ci		.cstat_offset	= VPDMA_VIP_UP_UV_CSTAT,
2738c2ecf20Sopenharmony_ci	},
2748c2ecf20Sopenharmony_ci	[VPE_CHAN_RGB_OUT] = {
2758c2ecf20Sopenharmony_ci		.num		= VPE_CHAN_NUM_RGB_OUT,
2768c2ecf20Sopenharmony_ci		.cstat_offset	= VPDMA_VIP_UP_Y_CSTAT,
2778c2ecf20Sopenharmony_ci	},
2788c2ecf20Sopenharmony_ci};
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_cistatic u32 read_reg(struct vpdma_data *vpdma, int offset)
2818c2ecf20Sopenharmony_ci{
2828c2ecf20Sopenharmony_ci	return ioread32(vpdma->base + offset);
2838c2ecf20Sopenharmony_ci}
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_cistatic void write_reg(struct vpdma_data *vpdma, int offset, u32 value)
2868c2ecf20Sopenharmony_ci{
2878c2ecf20Sopenharmony_ci	iowrite32(value, vpdma->base + offset);
2888c2ecf20Sopenharmony_ci}
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_cistatic int read_field_reg(struct vpdma_data *vpdma, int offset,
2918c2ecf20Sopenharmony_ci		u32 mask, int shift)
2928c2ecf20Sopenharmony_ci{
2938c2ecf20Sopenharmony_ci	return (read_reg(vpdma, offset) & (mask << shift)) >> shift;
2948c2ecf20Sopenharmony_ci}
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_cistatic void write_field_reg(struct vpdma_data *vpdma, int offset, u32 field,
2978c2ecf20Sopenharmony_ci		u32 mask, int shift)
2988c2ecf20Sopenharmony_ci{
2998c2ecf20Sopenharmony_ci	u32 val = read_reg(vpdma, offset);
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	val &= ~(mask << shift);
3028c2ecf20Sopenharmony_ci	val |= (field & mask) << shift;
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	write_reg(vpdma, offset, val);
3058c2ecf20Sopenharmony_ci}
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_civoid vpdma_dump_regs(struct vpdma_data *vpdma)
3088c2ecf20Sopenharmony_ci{
3098c2ecf20Sopenharmony_ci	struct device *dev = &vpdma->pdev->dev;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci#define DUMPREG(r) dev_dbg(dev, "%-35s %08x\n", #r, read_reg(vpdma, VPDMA_##r))
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	dev_dbg(dev, "VPDMA Registers:\n");
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	DUMPREG(PID);
3168c2ecf20Sopenharmony_ci	DUMPREG(LIST_ADDR);
3178c2ecf20Sopenharmony_ci	DUMPREG(LIST_ATTR);
3188c2ecf20Sopenharmony_ci	DUMPREG(LIST_STAT_SYNC);
3198c2ecf20Sopenharmony_ci	DUMPREG(BG_RGB);
3208c2ecf20Sopenharmony_ci	DUMPREG(BG_YUV);
3218c2ecf20Sopenharmony_ci	DUMPREG(SETUP);
3228c2ecf20Sopenharmony_ci	DUMPREG(MAX_SIZE1);
3238c2ecf20Sopenharmony_ci	DUMPREG(MAX_SIZE2);
3248c2ecf20Sopenharmony_ci	DUMPREG(MAX_SIZE3);
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	/*
3278c2ecf20Sopenharmony_ci	 * dumping registers of only group0 and group3, because VPE channels
3288c2ecf20Sopenharmony_ci	 * lie within group0 and group3 registers
3298c2ecf20Sopenharmony_ci	 */
3308c2ecf20Sopenharmony_ci	DUMPREG(INT_CHAN_STAT(0));
3318c2ecf20Sopenharmony_ci	DUMPREG(INT_CHAN_MASK(0));
3328c2ecf20Sopenharmony_ci	DUMPREG(INT_CHAN_STAT(3));
3338c2ecf20Sopenharmony_ci	DUMPREG(INT_CHAN_MASK(3));
3348c2ecf20Sopenharmony_ci	DUMPREG(INT_CLIENT0_STAT);
3358c2ecf20Sopenharmony_ci	DUMPREG(INT_CLIENT0_MASK);
3368c2ecf20Sopenharmony_ci	DUMPREG(INT_CLIENT1_STAT);
3378c2ecf20Sopenharmony_ci	DUMPREG(INT_CLIENT1_MASK);
3388c2ecf20Sopenharmony_ci	DUMPREG(INT_LIST0_STAT);
3398c2ecf20Sopenharmony_ci	DUMPREG(INT_LIST0_MASK);
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	/*
3428c2ecf20Sopenharmony_ci	 * these are registers specific to VPE clients, we can make this
3438c2ecf20Sopenharmony_ci	 * function dump client registers specific to VPE or VIP based on
3448c2ecf20Sopenharmony_ci	 * who is using it
3458c2ecf20Sopenharmony_ci	 */
3468c2ecf20Sopenharmony_ci	DUMPREG(DEI_CHROMA1_CSTAT);
3478c2ecf20Sopenharmony_ci	DUMPREG(DEI_LUMA1_CSTAT);
3488c2ecf20Sopenharmony_ci	DUMPREG(DEI_CHROMA2_CSTAT);
3498c2ecf20Sopenharmony_ci	DUMPREG(DEI_LUMA2_CSTAT);
3508c2ecf20Sopenharmony_ci	DUMPREG(DEI_CHROMA3_CSTAT);
3518c2ecf20Sopenharmony_ci	DUMPREG(DEI_LUMA3_CSTAT);
3528c2ecf20Sopenharmony_ci	DUMPREG(DEI_MV_IN_CSTAT);
3538c2ecf20Sopenharmony_ci	DUMPREG(DEI_MV_OUT_CSTAT);
3548c2ecf20Sopenharmony_ci	DUMPREG(VIP_UP_Y_CSTAT);
3558c2ecf20Sopenharmony_ci	DUMPREG(VIP_UP_UV_CSTAT);
3568c2ecf20Sopenharmony_ci	DUMPREG(VPI_CTL_CSTAT);
3578c2ecf20Sopenharmony_ci}
3588c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vpdma_dump_regs);
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci/*
3618c2ecf20Sopenharmony_ci * Allocate a DMA buffer
3628c2ecf20Sopenharmony_ci */
3638c2ecf20Sopenharmony_ciint vpdma_alloc_desc_buf(struct vpdma_buf *buf, size_t size)
3648c2ecf20Sopenharmony_ci{
3658c2ecf20Sopenharmony_ci	buf->size = size;
3668c2ecf20Sopenharmony_ci	buf->mapped = false;
3678c2ecf20Sopenharmony_ci	buf->addr = kzalloc(size, GFP_KERNEL);
3688c2ecf20Sopenharmony_ci	if (!buf->addr)
3698c2ecf20Sopenharmony_ci		return -ENOMEM;
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	WARN_ON(((unsigned long)buf->addr & VPDMA_DESC_ALIGN) != 0);
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	return 0;
3748c2ecf20Sopenharmony_ci}
3758c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vpdma_alloc_desc_buf);
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_civoid vpdma_free_desc_buf(struct vpdma_buf *buf)
3788c2ecf20Sopenharmony_ci{
3798c2ecf20Sopenharmony_ci	WARN_ON(buf->mapped);
3808c2ecf20Sopenharmony_ci	kfree(buf->addr);
3818c2ecf20Sopenharmony_ci	buf->addr = NULL;
3828c2ecf20Sopenharmony_ci	buf->size = 0;
3838c2ecf20Sopenharmony_ci}
3848c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vpdma_free_desc_buf);
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci/*
3878c2ecf20Sopenharmony_ci * map descriptor/payload DMA buffer, enabling DMA access
3888c2ecf20Sopenharmony_ci */
3898c2ecf20Sopenharmony_ciint vpdma_map_desc_buf(struct vpdma_data *vpdma, struct vpdma_buf *buf)
3908c2ecf20Sopenharmony_ci{
3918c2ecf20Sopenharmony_ci	struct device *dev = &vpdma->pdev->dev;
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	WARN_ON(buf->mapped);
3948c2ecf20Sopenharmony_ci	buf->dma_addr = dma_map_single(dev, buf->addr, buf->size,
3958c2ecf20Sopenharmony_ci				DMA_BIDIRECTIONAL);
3968c2ecf20Sopenharmony_ci	if (dma_mapping_error(dev, buf->dma_addr)) {
3978c2ecf20Sopenharmony_ci		dev_err(dev, "failed to map buffer\n");
3988c2ecf20Sopenharmony_ci		return -EINVAL;
3998c2ecf20Sopenharmony_ci	}
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	buf->mapped = true;
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	return 0;
4048c2ecf20Sopenharmony_ci}
4058c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vpdma_map_desc_buf);
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci/*
4088c2ecf20Sopenharmony_ci * unmap descriptor/payload DMA buffer, disabling DMA access and
4098c2ecf20Sopenharmony_ci * allowing the main processor to access the data
4108c2ecf20Sopenharmony_ci */
4118c2ecf20Sopenharmony_civoid vpdma_unmap_desc_buf(struct vpdma_data *vpdma, struct vpdma_buf *buf)
4128c2ecf20Sopenharmony_ci{
4138c2ecf20Sopenharmony_ci	struct device *dev = &vpdma->pdev->dev;
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	if (buf->mapped)
4168c2ecf20Sopenharmony_ci		dma_unmap_single(dev, buf->dma_addr, buf->size,
4178c2ecf20Sopenharmony_ci				DMA_BIDIRECTIONAL);
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	buf->mapped = false;
4208c2ecf20Sopenharmony_ci}
4218c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vpdma_unmap_desc_buf);
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci/*
4248c2ecf20Sopenharmony_ci * Cleanup all pending descriptors of a list
4258c2ecf20Sopenharmony_ci * First, stop the current list being processed.
4268c2ecf20Sopenharmony_ci * If the VPDMA was busy, this step makes vpdma to accept post lists.
4278c2ecf20Sopenharmony_ci * To cleanup the internal FSM, post abort list descriptor for all the
4288c2ecf20Sopenharmony_ci * channels from @channels array of size @size.
4298c2ecf20Sopenharmony_ci */
4308c2ecf20Sopenharmony_ciint vpdma_list_cleanup(struct vpdma_data *vpdma, int list_num,
4318c2ecf20Sopenharmony_ci		int *channels, int size)
4328c2ecf20Sopenharmony_ci{
4338c2ecf20Sopenharmony_ci	struct vpdma_desc_list abort_list;
4348c2ecf20Sopenharmony_ci	int i, ret, timeout = 500;
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	write_reg(vpdma, VPDMA_LIST_ATTR,
4378c2ecf20Sopenharmony_ci			(list_num << VPDMA_LIST_NUM_SHFT) |
4388c2ecf20Sopenharmony_ci			(1 << VPDMA_LIST_STOP_SHFT));
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	if (size <= 0 || !channels)
4418c2ecf20Sopenharmony_ci		return 0;
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	ret = vpdma_create_desc_list(&abort_list,
4448c2ecf20Sopenharmony_ci		size * sizeof(struct vpdma_dtd), VPDMA_LIST_TYPE_NORMAL);
4458c2ecf20Sopenharmony_ci	if (ret)
4468c2ecf20Sopenharmony_ci		return ret;
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	for (i = 0; i < size; i++)
4498c2ecf20Sopenharmony_ci		vpdma_add_abort_channel_ctd(&abort_list, channels[i]);
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	ret = vpdma_map_desc_buf(vpdma, &abort_list.buf);
4528c2ecf20Sopenharmony_ci	if (ret)
4538c2ecf20Sopenharmony_ci		goto free_desc;
4548c2ecf20Sopenharmony_ci	ret = vpdma_submit_descs(vpdma, &abort_list, list_num);
4558c2ecf20Sopenharmony_ci	if (ret)
4568c2ecf20Sopenharmony_ci		goto unmap_desc;
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	while (vpdma_list_busy(vpdma, list_num) && --timeout)
4598c2ecf20Sopenharmony_ci		;
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	if (timeout == 0) {
4628c2ecf20Sopenharmony_ci		dev_err(&vpdma->pdev->dev, "Timed out cleaning up VPDMA list\n");
4638c2ecf20Sopenharmony_ci		ret = -EBUSY;
4648c2ecf20Sopenharmony_ci	}
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ciunmap_desc:
4678c2ecf20Sopenharmony_ci	vpdma_unmap_desc_buf(vpdma, &abort_list.buf);
4688c2ecf20Sopenharmony_cifree_desc:
4698c2ecf20Sopenharmony_ci	vpdma_free_desc_buf(&abort_list.buf);
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	return ret;
4728c2ecf20Sopenharmony_ci}
4738c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vpdma_list_cleanup);
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci/*
4768c2ecf20Sopenharmony_ci * create a descriptor list, the user of this list will append configuration,
4778c2ecf20Sopenharmony_ci * control and data descriptors to this list, this list will be submitted to
4788c2ecf20Sopenharmony_ci * VPDMA. VPDMA's list parser will go through each descriptor and perform the
4798c2ecf20Sopenharmony_ci * required DMA operations
4808c2ecf20Sopenharmony_ci */
4818c2ecf20Sopenharmony_ciint vpdma_create_desc_list(struct vpdma_desc_list *list, size_t size, int type)
4828c2ecf20Sopenharmony_ci{
4838c2ecf20Sopenharmony_ci	int r;
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	r = vpdma_alloc_desc_buf(&list->buf, size);
4868c2ecf20Sopenharmony_ci	if (r)
4878c2ecf20Sopenharmony_ci		return r;
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	list->next = list->buf.addr;
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	list->type = type;
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	return 0;
4948c2ecf20Sopenharmony_ci}
4958c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vpdma_create_desc_list);
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci/*
4988c2ecf20Sopenharmony_ci * once a descriptor list is parsed by VPDMA, we reset the list by emptying it,
4998c2ecf20Sopenharmony_ci * to allow new descriptors to be added to the list.
5008c2ecf20Sopenharmony_ci */
5018c2ecf20Sopenharmony_civoid vpdma_reset_desc_list(struct vpdma_desc_list *list)
5028c2ecf20Sopenharmony_ci{
5038c2ecf20Sopenharmony_ci	list->next = list->buf.addr;
5048c2ecf20Sopenharmony_ci}
5058c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vpdma_reset_desc_list);
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci/*
5088c2ecf20Sopenharmony_ci * free the buffer allocated for the VPDMA descriptor list, this should be
5098c2ecf20Sopenharmony_ci * called when the user doesn't want to use VPDMA any more.
5108c2ecf20Sopenharmony_ci */
5118c2ecf20Sopenharmony_civoid vpdma_free_desc_list(struct vpdma_desc_list *list)
5128c2ecf20Sopenharmony_ci{
5138c2ecf20Sopenharmony_ci	vpdma_free_desc_buf(&list->buf);
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci	list->next = NULL;
5168c2ecf20Sopenharmony_ci}
5178c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vpdma_free_desc_list);
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_cibool vpdma_list_busy(struct vpdma_data *vpdma, int list_num)
5208c2ecf20Sopenharmony_ci{
5218c2ecf20Sopenharmony_ci	return read_reg(vpdma, VPDMA_LIST_STAT_SYNC) & BIT(list_num + 16);
5228c2ecf20Sopenharmony_ci}
5238c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vpdma_list_busy);
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci/*
5268c2ecf20Sopenharmony_ci * submit a list of DMA descriptors to the VPE VPDMA, do not wait for completion
5278c2ecf20Sopenharmony_ci */
5288c2ecf20Sopenharmony_ciint vpdma_submit_descs(struct vpdma_data *vpdma,
5298c2ecf20Sopenharmony_ci			struct vpdma_desc_list *list, int list_num)
5308c2ecf20Sopenharmony_ci{
5318c2ecf20Sopenharmony_ci	int list_size;
5328c2ecf20Sopenharmony_ci	unsigned long flags;
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci	if (vpdma_list_busy(vpdma, list_num))
5358c2ecf20Sopenharmony_ci		return -EBUSY;
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci	/* 16-byte granularity */
5388c2ecf20Sopenharmony_ci	list_size = (list->next - list->buf.addr) >> 4;
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	spin_lock_irqsave(&vpdma->lock, flags);
5418c2ecf20Sopenharmony_ci	write_reg(vpdma, VPDMA_LIST_ADDR, (u32) list->buf.dma_addr);
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci	write_reg(vpdma, VPDMA_LIST_ATTR,
5448c2ecf20Sopenharmony_ci			(list_num << VPDMA_LIST_NUM_SHFT) |
5458c2ecf20Sopenharmony_ci			(list->type << VPDMA_LIST_TYPE_SHFT) |
5468c2ecf20Sopenharmony_ci			list_size);
5478c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&vpdma->lock, flags);
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci	return 0;
5508c2ecf20Sopenharmony_ci}
5518c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vpdma_submit_descs);
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_cistatic void dump_dtd(struct vpdma_dtd *dtd);
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_civoid vpdma_update_dma_addr(struct vpdma_data *vpdma,
5568c2ecf20Sopenharmony_ci	struct vpdma_desc_list *list, dma_addr_t dma_addr,
5578c2ecf20Sopenharmony_ci	void *write_dtd, int drop, int idx)
5588c2ecf20Sopenharmony_ci{
5598c2ecf20Sopenharmony_ci	struct vpdma_dtd *dtd = list->buf.addr;
5608c2ecf20Sopenharmony_ci	dma_addr_t write_desc_addr;
5618c2ecf20Sopenharmony_ci	int offset;
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	dtd += idx;
5648c2ecf20Sopenharmony_ci	vpdma_unmap_desc_buf(vpdma, &list->buf);
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	dtd->start_addr = dma_addr;
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	/* Calculate write address from the offset of write_dtd from start
5698c2ecf20Sopenharmony_ci	 * of the list->buf
5708c2ecf20Sopenharmony_ci	 */
5718c2ecf20Sopenharmony_ci	offset = (void *)write_dtd - list->buf.addr;
5728c2ecf20Sopenharmony_ci	write_desc_addr = list->buf.dma_addr + offset;
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	if (drop)
5758c2ecf20Sopenharmony_ci		dtd->desc_write_addr = dtd_desc_write_addr(write_desc_addr,
5768c2ecf20Sopenharmony_ci							   1, 1, 0);
5778c2ecf20Sopenharmony_ci	else
5788c2ecf20Sopenharmony_ci		dtd->desc_write_addr = dtd_desc_write_addr(write_desc_addr,
5798c2ecf20Sopenharmony_ci							   1, 0, 0);
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci	vpdma_map_desc_buf(vpdma, &list->buf);
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	dump_dtd(dtd);
5848c2ecf20Sopenharmony_ci}
5858c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vpdma_update_dma_addr);
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_civoid vpdma_set_max_size(struct vpdma_data *vpdma, int reg_addr,
5888c2ecf20Sopenharmony_ci			u32 width, u32 height)
5898c2ecf20Sopenharmony_ci{
5908c2ecf20Sopenharmony_ci	if (reg_addr != VPDMA_MAX_SIZE1 && reg_addr != VPDMA_MAX_SIZE2 &&
5918c2ecf20Sopenharmony_ci	    reg_addr != VPDMA_MAX_SIZE3)
5928c2ecf20Sopenharmony_ci		reg_addr = VPDMA_MAX_SIZE1;
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci	write_field_reg(vpdma, reg_addr, width - 1,
5958c2ecf20Sopenharmony_ci			VPDMA_MAX_SIZE_WIDTH_MASK, VPDMA_MAX_SIZE_WIDTH_SHFT);
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci	write_field_reg(vpdma, reg_addr, height - 1,
5988c2ecf20Sopenharmony_ci			VPDMA_MAX_SIZE_HEIGHT_MASK, VPDMA_MAX_SIZE_HEIGHT_SHFT);
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci}
6018c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vpdma_set_max_size);
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_cistatic void dump_cfd(struct vpdma_cfd *cfd)
6048c2ecf20Sopenharmony_ci{
6058c2ecf20Sopenharmony_ci	int class;
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci	class = cfd_get_class(cfd);
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci	pr_debug("config descriptor of payload class: %s\n",
6108c2ecf20Sopenharmony_ci		class == CFD_CLS_BLOCK ? "simple block" :
6118c2ecf20Sopenharmony_ci		"address data block");
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	if (class == CFD_CLS_BLOCK)
6148c2ecf20Sopenharmony_ci		pr_debug("word0: dst_addr_offset = 0x%08x\n",
6158c2ecf20Sopenharmony_ci			cfd->dest_addr_offset);
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci	if (class == CFD_CLS_BLOCK)
6188c2ecf20Sopenharmony_ci		pr_debug("word1: num_data_wrds = %d\n", cfd->block_len);
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci	pr_debug("word2: payload_addr = 0x%08x\n", cfd->payload_addr);
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci	pr_debug("word3: pkt_type = %d, direct = %d, class = %d, dest = %d, payload_len = %d\n",
6238c2ecf20Sopenharmony_ci		 cfd_get_pkt_type(cfd),
6248c2ecf20Sopenharmony_ci		 cfd_get_direct(cfd), class, cfd_get_dest(cfd),
6258c2ecf20Sopenharmony_ci		 cfd_get_payload_len(cfd));
6268c2ecf20Sopenharmony_ci}
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci/*
6298c2ecf20Sopenharmony_ci * append a configuration descriptor to the given descriptor list, where the
6308c2ecf20Sopenharmony_ci * payload is in the form of a simple data block specified in the descriptor
6318c2ecf20Sopenharmony_ci * header, this is used to upload scaler coefficients to the scaler module
6328c2ecf20Sopenharmony_ci */
6338c2ecf20Sopenharmony_civoid vpdma_add_cfd_block(struct vpdma_desc_list *list, int client,
6348c2ecf20Sopenharmony_ci		struct vpdma_buf *blk, u32 dest_offset)
6358c2ecf20Sopenharmony_ci{
6368c2ecf20Sopenharmony_ci	struct vpdma_cfd *cfd;
6378c2ecf20Sopenharmony_ci	int len = blk->size;
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci	WARN_ON(blk->dma_addr & VPDMA_DESC_ALIGN);
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci	cfd = list->next;
6428c2ecf20Sopenharmony_ci	WARN_ON((void *)(cfd + 1) > (list->buf.addr + list->buf.size));
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ci	cfd->dest_addr_offset = dest_offset;
6458c2ecf20Sopenharmony_ci	cfd->block_len = len;
6468c2ecf20Sopenharmony_ci	cfd->payload_addr = (u32) blk->dma_addr;
6478c2ecf20Sopenharmony_ci	cfd->ctl_payload_len = cfd_pkt_payload_len(CFD_INDIRECT, CFD_CLS_BLOCK,
6488c2ecf20Sopenharmony_ci				client, len >> 4);
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci	list->next = cfd + 1;
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_ci	dump_cfd(cfd);
6538c2ecf20Sopenharmony_ci}
6548c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vpdma_add_cfd_block);
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci/*
6578c2ecf20Sopenharmony_ci * append a configuration descriptor to the given descriptor list, where the
6588c2ecf20Sopenharmony_ci * payload is in the address data block format, this is used to a configure a
6598c2ecf20Sopenharmony_ci * discontiguous set of MMRs
6608c2ecf20Sopenharmony_ci */
6618c2ecf20Sopenharmony_civoid vpdma_add_cfd_adb(struct vpdma_desc_list *list, int client,
6628c2ecf20Sopenharmony_ci		struct vpdma_buf *adb)
6638c2ecf20Sopenharmony_ci{
6648c2ecf20Sopenharmony_ci	struct vpdma_cfd *cfd;
6658c2ecf20Sopenharmony_ci	unsigned int len = adb->size;
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_ci	WARN_ON(len & VPDMA_ADB_SIZE_ALIGN);
6688c2ecf20Sopenharmony_ci	WARN_ON(adb->dma_addr & VPDMA_DESC_ALIGN);
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_ci	cfd = list->next;
6718c2ecf20Sopenharmony_ci	BUG_ON((void *)(cfd + 1) > (list->buf.addr + list->buf.size));
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci	cfd->w0 = 0;
6748c2ecf20Sopenharmony_ci	cfd->w1 = 0;
6758c2ecf20Sopenharmony_ci	cfd->payload_addr = (u32) adb->dma_addr;
6768c2ecf20Sopenharmony_ci	cfd->ctl_payload_len = cfd_pkt_payload_len(CFD_INDIRECT, CFD_CLS_ADB,
6778c2ecf20Sopenharmony_ci				client, len >> 4);
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_ci	list->next = cfd + 1;
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci	dump_cfd(cfd);
6828c2ecf20Sopenharmony_ci};
6838c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vpdma_add_cfd_adb);
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci/*
6868c2ecf20Sopenharmony_ci * control descriptor format change based on what type of control descriptor it
6878c2ecf20Sopenharmony_ci * is, we only use 'sync on channel' control descriptors for now, so assume it's
6888c2ecf20Sopenharmony_ci * that
6898c2ecf20Sopenharmony_ci */
6908c2ecf20Sopenharmony_cistatic void dump_ctd(struct vpdma_ctd *ctd)
6918c2ecf20Sopenharmony_ci{
6928c2ecf20Sopenharmony_ci	pr_debug("control descriptor\n");
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_ci	pr_debug("word3: pkt_type = %d, source = %d, ctl_type = %d\n",
6958c2ecf20Sopenharmony_ci		ctd_get_pkt_type(ctd), ctd_get_source(ctd), ctd_get_ctl(ctd));
6968c2ecf20Sopenharmony_ci}
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci/*
6998c2ecf20Sopenharmony_ci * append a 'sync on channel' type control descriptor to the given descriptor
7008c2ecf20Sopenharmony_ci * list, this descriptor stalls the VPDMA list till the time DMA is completed
7018c2ecf20Sopenharmony_ci * on the specified channel
7028c2ecf20Sopenharmony_ci */
7038c2ecf20Sopenharmony_civoid vpdma_add_sync_on_channel_ctd(struct vpdma_desc_list *list,
7048c2ecf20Sopenharmony_ci		enum vpdma_channel chan)
7058c2ecf20Sopenharmony_ci{
7068c2ecf20Sopenharmony_ci	struct vpdma_ctd *ctd;
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci	ctd = list->next;
7098c2ecf20Sopenharmony_ci	WARN_ON((void *)(ctd + 1) > (list->buf.addr + list->buf.size));
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci	ctd->w0 = 0;
7128c2ecf20Sopenharmony_ci	ctd->w1 = 0;
7138c2ecf20Sopenharmony_ci	ctd->w2 = 0;
7148c2ecf20Sopenharmony_ci	ctd->type_source_ctl = ctd_type_source_ctl(chan_info[chan].num,
7158c2ecf20Sopenharmony_ci				CTD_TYPE_SYNC_ON_CHANNEL);
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci	list->next = ctd + 1;
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci	dump_ctd(ctd);
7208c2ecf20Sopenharmony_ci}
7218c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vpdma_add_sync_on_channel_ctd);
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci/*
7248c2ecf20Sopenharmony_ci * append an 'abort_channel' type control descriptor to the given descriptor
7258c2ecf20Sopenharmony_ci * list, this descriptor aborts any DMA transaction happening using the
7268c2ecf20Sopenharmony_ci * specified channel
7278c2ecf20Sopenharmony_ci */
7288c2ecf20Sopenharmony_civoid vpdma_add_abort_channel_ctd(struct vpdma_desc_list *list,
7298c2ecf20Sopenharmony_ci		int chan_num)
7308c2ecf20Sopenharmony_ci{
7318c2ecf20Sopenharmony_ci	struct vpdma_ctd *ctd;
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci	ctd = list->next;
7348c2ecf20Sopenharmony_ci	WARN_ON((void *)(ctd + 1) > (list->buf.addr + list->buf.size));
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci	ctd->w0 = 0;
7378c2ecf20Sopenharmony_ci	ctd->w1 = 0;
7388c2ecf20Sopenharmony_ci	ctd->w2 = 0;
7398c2ecf20Sopenharmony_ci	ctd->type_source_ctl = ctd_type_source_ctl(chan_num,
7408c2ecf20Sopenharmony_ci				CTD_TYPE_ABORT_CHANNEL);
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_ci	list->next = ctd + 1;
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci	dump_ctd(ctd);
7458c2ecf20Sopenharmony_ci}
7468c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vpdma_add_abort_channel_ctd);
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_cistatic void dump_dtd(struct vpdma_dtd *dtd)
7498c2ecf20Sopenharmony_ci{
7508c2ecf20Sopenharmony_ci	int dir, chan;
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_ci	dir = dtd_get_dir(dtd);
7538c2ecf20Sopenharmony_ci	chan = dtd_get_chan(dtd);
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci	pr_debug("%s data transfer descriptor for channel %d\n",
7568c2ecf20Sopenharmony_ci		dir == DTD_DIR_OUT ? "outbound" : "inbound", chan);
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_ci	pr_debug("word0: data_type = %d, notify = %d, field = %d, 1D = %d, even_ln_skp = %d, odd_ln_skp = %d, line_stride = %d\n",
7598c2ecf20Sopenharmony_ci		dtd_get_data_type(dtd), dtd_get_notify(dtd), dtd_get_field(dtd),
7608c2ecf20Sopenharmony_ci		dtd_get_1d(dtd), dtd_get_even_line_skip(dtd),
7618c2ecf20Sopenharmony_ci		dtd_get_odd_line_skip(dtd), dtd_get_line_stride(dtd));
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_ci	if (dir == DTD_DIR_IN)
7648c2ecf20Sopenharmony_ci		pr_debug("word1: line_length = %d, xfer_height = %d\n",
7658c2ecf20Sopenharmony_ci			dtd_get_line_length(dtd), dtd_get_xfer_height(dtd));
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci	pr_debug("word2: start_addr = %x\n", dtd->start_addr);
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci	pr_debug("word3: pkt_type = %d, mode = %d, dir = %d, chan = %d, pri = %d, next_chan = %d\n",
7708c2ecf20Sopenharmony_ci		 dtd_get_pkt_type(dtd),
7718c2ecf20Sopenharmony_ci		 dtd_get_mode(dtd), dir, chan, dtd_get_priority(dtd),
7728c2ecf20Sopenharmony_ci		 dtd_get_next_chan(dtd));
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_ci	if (dir == DTD_DIR_IN)
7758c2ecf20Sopenharmony_ci		pr_debug("word4: frame_width = %d, frame_height = %d\n",
7768c2ecf20Sopenharmony_ci			dtd_get_frame_width(dtd), dtd_get_frame_height(dtd));
7778c2ecf20Sopenharmony_ci	else
7788c2ecf20Sopenharmony_ci		pr_debug("word4: desc_write_addr = 0x%08x, write_desc = %d, drp_data = %d, use_desc_reg = %d\n",
7798c2ecf20Sopenharmony_ci			dtd_get_desc_write_addr(dtd), dtd_get_write_desc(dtd),
7808c2ecf20Sopenharmony_ci			dtd_get_drop_data(dtd), dtd_get_use_desc(dtd));
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci	if (dir == DTD_DIR_IN)
7838c2ecf20Sopenharmony_ci		pr_debug("word5: hor_start = %d, ver_start = %d\n",
7848c2ecf20Sopenharmony_ci			dtd_get_h_start(dtd), dtd_get_v_start(dtd));
7858c2ecf20Sopenharmony_ci	else
7868c2ecf20Sopenharmony_ci		pr_debug("word5: max_width %d, max_height %d\n",
7878c2ecf20Sopenharmony_ci			dtd_get_max_width(dtd), dtd_get_max_height(dtd));
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci	pr_debug("word6: client specific attr0 = 0x%08x\n", dtd->client_attr0);
7908c2ecf20Sopenharmony_ci	pr_debug("word7: client specific attr1 = 0x%08x\n", dtd->client_attr1);
7918c2ecf20Sopenharmony_ci}
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_ci/*
7948c2ecf20Sopenharmony_ci * append an outbound data transfer descriptor to the given descriptor list,
7958c2ecf20Sopenharmony_ci * this sets up a 'client to memory' VPDMA transfer for the given VPDMA channel
7968c2ecf20Sopenharmony_ci *
7978c2ecf20Sopenharmony_ci * @list: vpdma desc list to which we add this descriptor
7988c2ecf20Sopenharmony_ci * @width: width of the image in pixels in memory
7998c2ecf20Sopenharmony_ci * @c_rect: compose params of output image
8008c2ecf20Sopenharmony_ci * @fmt: vpdma data format of the buffer
8018c2ecf20Sopenharmony_ci * dma_addr: dma address as seen by VPDMA
8028c2ecf20Sopenharmony_ci * max_width: enum for maximum width of data transfer
8038c2ecf20Sopenharmony_ci * max_height: enum for maximum height of data transfer
8048c2ecf20Sopenharmony_ci * chan: VPDMA channel
8058c2ecf20Sopenharmony_ci * flags: VPDMA flags to configure some descriptor fields
8068c2ecf20Sopenharmony_ci */
8078c2ecf20Sopenharmony_civoid vpdma_add_out_dtd(struct vpdma_desc_list *list, int width,
8088c2ecf20Sopenharmony_ci		int stride, const struct v4l2_rect *c_rect,
8098c2ecf20Sopenharmony_ci		const struct vpdma_data_format *fmt, dma_addr_t dma_addr,
8108c2ecf20Sopenharmony_ci		int max_w, int max_h, enum vpdma_channel chan, u32 flags)
8118c2ecf20Sopenharmony_ci{
8128c2ecf20Sopenharmony_ci	vpdma_rawchan_add_out_dtd(list, width, stride, c_rect, fmt, dma_addr,
8138c2ecf20Sopenharmony_ci				  max_w, max_h, chan_info[chan].num, flags);
8148c2ecf20Sopenharmony_ci}
8158c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vpdma_add_out_dtd);
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_civoid vpdma_rawchan_add_out_dtd(struct vpdma_desc_list *list, int width,
8188c2ecf20Sopenharmony_ci		int stride, const struct v4l2_rect *c_rect,
8198c2ecf20Sopenharmony_ci		const struct vpdma_data_format *fmt, dma_addr_t dma_addr,
8208c2ecf20Sopenharmony_ci		int max_w, int max_h, int raw_vpdma_chan, u32 flags)
8218c2ecf20Sopenharmony_ci{
8228c2ecf20Sopenharmony_ci	int priority = 0;
8238c2ecf20Sopenharmony_ci	int field = 0;
8248c2ecf20Sopenharmony_ci	int notify = 1;
8258c2ecf20Sopenharmony_ci	int channel, next_chan;
8268c2ecf20Sopenharmony_ci	struct v4l2_rect rect = *c_rect;
8278c2ecf20Sopenharmony_ci	int depth = fmt->depth;
8288c2ecf20Sopenharmony_ci	struct vpdma_dtd *dtd;
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci	channel = next_chan = raw_vpdma_chan;
8318c2ecf20Sopenharmony_ci
8328c2ecf20Sopenharmony_ci	if (fmt->type == VPDMA_DATA_FMT_TYPE_YUV &&
8338c2ecf20Sopenharmony_ci	    (fmt->data_type == DATA_TYPE_C420 ||
8348c2ecf20Sopenharmony_ci	     fmt->data_type == DATA_TYPE_CB420)) {
8358c2ecf20Sopenharmony_ci		rect.height >>= 1;
8368c2ecf20Sopenharmony_ci		rect.top >>= 1;
8378c2ecf20Sopenharmony_ci		depth = 8;
8388c2ecf20Sopenharmony_ci	}
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci	dma_addr += rect.top * stride + (rect.left * depth >> 3);
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_ci	dtd = list->next;
8438c2ecf20Sopenharmony_ci	WARN_ON((void *)(dtd + 1) > (list->buf.addr + list->buf.size));
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_ci	dtd->type_ctl_stride = dtd_type_ctl_stride(fmt->data_type,
8468c2ecf20Sopenharmony_ci					notify,
8478c2ecf20Sopenharmony_ci					field,
8488c2ecf20Sopenharmony_ci					!!(flags & VPDMA_DATA_FRAME_1D),
8498c2ecf20Sopenharmony_ci					!!(flags & VPDMA_DATA_EVEN_LINE_SKIP),
8508c2ecf20Sopenharmony_ci					!!(flags & VPDMA_DATA_ODD_LINE_SKIP),
8518c2ecf20Sopenharmony_ci					stride);
8528c2ecf20Sopenharmony_ci	dtd->w1 = 0;
8538c2ecf20Sopenharmony_ci	dtd->start_addr = (u32) dma_addr;
8548c2ecf20Sopenharmony_ci	dtd->pkt_ctl = dtd_pkt_ctl(!!(flags & VPDMA_DATA_MODE_TILED),
8558c2ecf20Sopenharmony_ci				DTD_DIR_OUT, channel, priority, next_chan);
8568c2ecf20Sopenharmony_ci	dtd->desc_write_addr = dtd_desc_write_addr(0, 0, 0, 0);
8578c2ecf20Sopenharmony_ci	dtd->max_width_height = dtd_max_width_height(max_w, max_h);
8588c2ecf20Sopenharmony_ci	dtd->client_attr0 = 0;
8598c2ecf20Sopenharmony_ci	dtd->client_attr1 = 0;
8608c2ecf20Sopenharmony_ci
8618c2ecf20Sopenharmony_ci	list->next = dtd + 1;
8628c2ecf20Sopenharmony_ci
8638c2ecf20Sopenharmony_ci	dump_dtd(dtd);
8648c2ecf20Sopenharmony_ci}
8658c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vpdma_rawchan_add_out_dtd);
8668c2ecf20Sopenharmony_ci
8678c2ecf20Sopenharmony_ci/*
8688c2ecf20Sopenharmony_ci * append an inbound data transfer descriptor to the given descriptor list,
8698c2ecf20Sopenharmony_ci * this sets up a 'memory to client' VPDMA transfer for the given VPDMA channel
8708c2ecf20Sopenharmony_ci *
8718c2ecf20Sopenharmony_ci * @list: vpdma desc list to which we add this descriptor
8728c2ecf20Sopenharmony_ci * @width: width of the image in pixels in memory(not the cropped width)
8738c2ecf20Sopenharmony_ci * @c_rect: crop params of input image
8748c2ecf20Sopenharmony_ci * @fmt: vpdma data format of the buffer
8758c2ecf20Sopenharmony_ci * dma_addr: dma address as seen by VPDMA
8768c2ecf20Sopenharmony_ci * chan: VPDMA channel
8778c2ecf20Sopenharmony_ci * field: top or bottom field info of the input image
8788c2ecf20Sopenharmony_ci * flags: VPDMA flags to configure some descriptor fields
8798c2ecf20Sopenharmony_ci * frame_width/height: the complete width/height of the image presented to the
8808c2ecf20Sopenharmony_ci *			client (this makes sense when multiple channels are
8818c2ecf20Sopenharmony_ci *			connected to the same client, forming a larger frame)
8828c2ecf20Sopenharmony_ci * start_h, start_v: position where the given channel starts providing pixel
8838c2ecf20Sopenharmony_ci *			data to the client (makes sense when multiple channels
8848c2ecf20Sopenharmony_ci *			contribute to the client)
8858c2ecf20Sopenharmony_ci */
8868c2ecf20Sopenharmony_civoid vpdma_add_in_dtd(struct vpdma_desc_list *list, int width,
8878c2ecf20Sopenharmony_ci		int stride, const struct v4l2_rect *c_rect,
8888c2ecf20Sopenharmony_ci		const struct vpdma_data_format *fmt, dma_addr_t dma_addr,
8898c2ecf20Sopenharmony_ci		enum vpdma_channel chan, int field, u32 flags, int frame_width,
8908c2ecf20Sopenharmony_ci		int frame_height, int start_h, int start_v)
8918c2ecf20Sopenharmony_ci{
8928c2ecf20Sopenharmony_ci	int priority = 0;
8938c2ecf20Sopenharmony_ci	int notify = 1;
8948c2ecf20Sopenharmony_ci	int depth = fmt->depth;
8958c2ecf20Sopenharmony_ci	int channel, next_chan;
8968c2ecf20Sopenharmony_ci	struct v4l2_rect rect = *c_rect;
8978c2ecf20Sopenharmony_ci	struct vpdma_dtd *dtd;
8988c2ecf20Sopenharmony_ci
8998c2ecf20Sopenharmony_ci	channel = next_chan = chan_info[chan].num;
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_ci	if (fmt->type == VPDMA_DATA_FMT_TYPE_YUV &&
9028c2ecf20Sopenharmony_ci	    (fmt->data_type == DATA_TYPE_C420 ||
9038c2ecf20Sopenharmony_ci	     fmt->data_type == DATA_TYPE_CB420)) {
9048c2ecf20Sopenharmony_ci		rect.height >>= 1;
9058c2ecf20Sopenharmony_ci		rect.top >>= 1;
9068c2ecf20Sopenharmony_ci		depth = 8;
9078c2ecf20Sopenharmony_ci	}
9088c2ecf20Sopenharmony_ci
9098c2ecf20Sopenharmony_ci	dma_addr += rect.top * stride + (rect.left * depth >> 3);
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_ci	dtd = list->next;
9128c2ecf20Sopenharmony_ci	WARN_ON((void *)(dtd + 1) > (list->buf.addr + list->buf.size));
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_ci	dtd->type_ctl_stride = dtd_type_ctl_stride(fmt->data_type,
9158c2ecf20Sopenharmony_ci					notify,
9168c2ecf20Sopenharmony_ci					field,
9178c2ecf20Sopenharmony_ci					!!(flags & VPDMA_DATA_FRAME_1D),
9188c2ecf20Sopenharmony_ci					!!(flags & VPDMA_DATA_EVEN_LINE_SKIP),
9198c2ecf20Sopenharmony_ci					!!(flags & VPDMA_DATA_ODD_LINE_SKIP),
9208c2ecf20Sopenharmony_ci					stride);
9218c2ecf20Sopenharmony_ci
9228c2ecf20Sopenharmony_ci	dtd->xfer_length_height = dtd_xfer_length_height(rect.width,
9238c2ecf20Sopenharmony_ci					rect.height);
9248c2ecf20Sopenharmony_ci	dtd->start_addr = (u32) dma_addr;
9258c2ecf20Sopenharmony_ci	dtd->pkt_ctl = dtd_pkt_ctl(!!(flags & VPDMA_DATA_MODE_TILED),
9268c2ecf20Sopenharmony_ci				DTD_DIR_IN, channel, priority, next_chan);
9278c2ecf20Sopenharmony_ci	dtd->frame_width_height = dtd_frame_width_height(frame_width,
9288c2ecf20Sopenharmony_ci					frame_height);
9298c2ecf20Sopenharmony_ci	dtd->start_h_v = dtd_start_h_v(start_h, start_v);
9308c2ecf20Sopenharmony_ci	dtd->client_attr0 = 0;
9318c2ecf20Sopenharmony_ci	dtd->client_attr1 = 0;
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_ci	list->next = dtd + 1;
9348c2ecf20Sopenharmony_ci
9358c2ecf20Sopenharmony_ci	dump_dtd(dtd);
9368c2ecf20Sopenharmony_ci}
9378c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vpdma_add_in_dtd);
9388c2ecf20Sopenharmony_ci
9398c2ecf20Sopenharmony_ciint vpdma_hwlist_alloc(struct vpdma_data *vpdma, void *priv)
9408c2ecf20Sopenharmony_ci{
9418c2ecf20Sopenharmony_ci	int i, list_num = -1;
9428c2ecf20Sopenharmony_ci	unsigned long flags;
9438c2ecf20Sopenharmony_ci
9448c2ecf20Sopenharmony_ci	spin_lock_irqsave(&vpdma->lock, flags);
9458c2ecf20Sopenharmony_ci	for (i = 0; i < VPDMA_MAX_NUM_LIST &&
9468c2ecf20Sopenharmony_ci	    vpdma->hwlist_used[i] == true; i++)
9478c2ecf20Sopenharmony_ci		;
9488c2ecf20Sopenharmony_ci
9498c2ecf20Sopenharmony_ci	if (i < VPDMA_MAX_NUM_LIST) {
9508c2ecf20Sopenharmony_ci		list_num = i;
9518c2ecf20Sopenharmony_ci		vpdma->hwlist_used[i] = true;
9528c2ecf20Sopenharmony_ci		vpdma->hwlist_priv[i] = priv;
9538c2ecf20Sopenharmony_ci	}
9548c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&vpdma->lock, flags);
9558c2ecf20Sopenharmony_ci
9568c2ecf20Sopenharmony_ci	return list_num;
9578c2ecf20Sopenharmony_ci}
9588c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vpdma_hwlist_alloc);
9598c2ecf20Sopenharmony_ci
9608c2ecf20Sopenharmony_civoid *vpdma_hwlist_get_priv(struct vpdma_data *vpdma, int list_num)
9618c2ecf20Sopenharmony_ci{
9628c2ecf20Sopenharmony_ci	if (!vpdma || list_num >= VPDMA_MAX_NUM_LIST)
9638c2ecf20Sopenharmony_ci		return NULL;
9648c2ecf20Sopenharmony_ci
9658c2ecf20Sopenharmony_ci	return vpdma->hwlist_priv[list_num];
9668c2ecf20Sopenharmony_ci}
9678c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vpdma_hwlist_get_priv);
9688c2ecf20Sopenharmony_ci
9698c2ecf20Sopenharmony_civoid *vpdma_hwlist_release(struct vpdma_data *vpdma, int list_num)
9708c2ecf20Sopenharmony_ci{
9718c2ecf20Sopenharmony_ci	void *priv;
9728c2ecf20Sopenharmony_ci	unsigned long flags;
9738c2ecf20Sopenharmony_ci
9748c2ecf20Sopenharmony_ci	spin_lock_irqsave(&vpdma->lock, flags);
9758c2ecf20Sopenharmony_ci	vpdma->hwlist_used[list_num] = false;
9768c2ecf20Sopenharmony_ci	priv = vpdma->hwlist_priv;
9778c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&vpdma->lock, flags);
9788c2ecf20Sopenharmony_ci
9798c2ecf20Sopenharmony_ci	return priv;
9808c2ecf20Sopenharmony_ci}
9818c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vpdma_hwlist_release);
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_ci/* set or clear the mask for list complete interrupt */
9848c2ecf20Sopenharmony_civoid vpdma_enable_list_complete_irq(struct vpdma_data *vpdma, int irq_num,
9858c2ecf20Sopenharmony_ci		int list_num, bool enable)
9868c2ecf20Sopenharmony_ci{
9878c2ecf20Sopenharmony_ci	u32 reg_addr = VPDMA_INT_LIST0_MASK + VPDMA_INTX_OFFSET * irq_num;
9888c2ecf20Sopenharmony_ci	u32 val;
9898c2ecf20Sopenharmony_ci
9908c2ecf20Sopenharmony_ci	val = read_reg(vpdma, reg_addr);
9918c2ecf20Sopenharmony_ci	if (enable)
9928c2ecf20Sopenharmony_ci		val |= (1 << (list_num * 2));
9938c2ecf20Sopenharmony_ci	else
9948c2ecf20Sopenharmony_ci		val &= ~(1 << (list_num * 2));
9958c2ecf20Sopenharmony_ci	write_reg(vpdma, reg_addr, val);
9968c2ecf20Sopenharmony_ci}
9978c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vpdma_enable_list_complete_irq);
9988c2ecf20Sopenharmony_ci
9998c2ecf20Sopenharmony_ci/* get the LIST_STAT register */
10008c2ecf20Sopenharmony_ciunsigned int vpdma_get_list_stat(struct vpdma_data *vpdma, int irq_num)
10018c2ecf20Sopenharmony_ci{
10028c2ecf20Sopenharmony_ci	u32 reg_addr = VPDMA_INT_LIST0_STAT + VPDMA_INTX_OFFSET * irq_num;
10038c2ecf20Sopenharmony_ci
10048c2ecf20Sopenharmony_ci	return read_reg(vpdma, reg_addr);
10058c2ecf20Sopenharmony_ci}
10068c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vpdma_get_list_stat);
10078c2ecf20Sopenharmony_ci
10088c2ecf20Sopenharmony_ci/* get the LIST_MASK register */
10098c2ecf20Sopenharmony_ciunsigned int vpdma_get_list_mask(struct vpdma_data *vpdma, int irq_num)
10108c2ecf20Sopenharmony_ci{
10118c2ecf20Sopenharmony_ci	u32 reg_addr = VPDMA_INT_LIST0_MASK + VPDMA_INTX_OFFSET * irq_num;
10128c2ecf20Sopenharmony_ci
10138c2ecf20Sopenharmony_ci	return read_reg(vpdma, reg_addr);
10148c2ecf20Sopenharmony_ci}
10158c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vpdma_get_list_mask);
10168c2ecf20Sopenharmony_ci
10178c2ecf20Sopenharmony_ci/* clear previously occurred list interrupts in the LIST_STAT register */
10188c2ecf20Sopenharmony_civoid vpdma_clear_list_stat(struct vpdma_data *vpdma, int irq_num,
10198c2ecf20Sopenharmony_ci			   int list_num)
10208c2ecf20Sopenharmony_ci{
10218c2ecf20Sopenharmony_ci	u32 reg_addr = VPDMA_INT_LIST0_STAT + VPDMA_INTX_OFFSET * irq_num;
10228c2ecf20Sopenharmony_ci
10238c2ecf20Sopenharmony_ci	write_reg(vpdma, reg_addr, 3 << (list_num * 2));
10248c2ecf20Sopenharmony_ci}
10258c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vpdma_clear_list_stat);
10268c2ecf20Sopenharmony_ci
10278c2ecf20Sopenharmony_civoid vpdma_set_bg_color(struct vpdma_data *vpdma,
10288c2ecf20Sopenharmony_ci		struct vpdma_data_format *fmt, u32 color)
10298c2ecf20Sopenharmony_ci{
10308c2ecf20Sopenharmony_ci	if (fmt->type == VPDMA_DATA_FMT_TYPE_RGB)
10318c2ecf20Sopenharmony_ci		write_reg(vpdma, VPDMA_BG_RGB, color);
10328c2ecf20Sopenharmony_ci	else if (fmt->type == VPDMA_DATA_FMT_TYPE_YUV)
10338c2ecf20Sopenharmony_ci		write_reg(vpdma, VPDMA_BG_YUV, color);
10348c2ecf20Sopenharmony_ci}
10358c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vpdma_set_bg_color);
10368c2ecf20Sopenharmony_ci
10378c2ecf20Sopenharmony_ci/*
10388c2ecf20Sopenharmony_ci * configures the output mode of the line buffer for the given client, the
10398c2ecf20Sopenharmony_ci * line buffer content can either be mirrored(each line repeated twice) or
10408c2ecf20Sopenharmony_ci * passed to the client as is
10418c2ecf20Sopenharmony_ci */
10428c2ecf20Sopenharmony_civoid vpdma_set_line_mode(struct vpdma_data *vpdma, int line_mode,
10438c2ecf20Sopenharmony_ci		enum vpdma_channel chan)
10448c2ecf20Sopenharmony_ci{
10458c2ecf20Sopenharmony_ci	int client_cstat = chan_info[chan].cstat_offset;
10468c2ecf20Sopenharmony_ci
10478c2ecf20Sopenharmony_ci	write_field_reg(vpdma, client_cstat, line_mode,
10488c2ecf20Sopenharmony_ci		VPDMA_CSTAT_LINE_MODE_MASK, VPDMA_CSTAT_LINE_MODE_SHIFT);
10498c2ecf20Sopenharmony_ci}
10508c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vpdma_set_line_mode);
10518c2ecf20Sopenharmony_ci
10528c2ecf20Sopenharmony_ci/*
10538c2ecf20Sopenharmony_ci * configures the event which should trigger VPDMA transfer for the given
10548c2ecf20Sopenharmony_ci * client
10558c2ecf20Sopenharmony_ci */
10568c2ecf20Sopenharmony_civoid vpdma_set_frame_start_event(struct vpdma_data *vpdma,
10578c2ecf20Sopenharmony_ci		enum vpdma_frame_start_event fs_event,
10588c2ecf20Sopenharmony_ci		enum vpdma_channel chan)
10598c2ecf20Sopenharmony_ci{
10608c2ecf20Sopenharmony_ci	int client_cstat = chan_info[chan].cstat_offset;
10618c2ecf20Sopenharmony_ci
10628c2ecf20Sopenharmony_ci	write_field_reg(vpdma, client_cstat, fs_event,
10638c2ecf20Sopenharmony_ci		VPDMA_CSTAT_FRAME_START_MASK, VPDMA_CSTAT_FRAME_START_SHIFT);
10648c2ecf20Sopenharmony_ci}
10658c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vpdma_set_frame_start_event);
10668c2ecf20Sopenharmony_ci
10678c2ecf20Sopenharmony_cistatic void vpdma_firmware_cb(const struct firmware *f, void *context)
10688c2ecf20Sopenharmony_ci{
10698c2ecf20Sopenharmony_ci	struct vpdma_data *vpdma = context;
10708c2ecf20Sopenharmony_ci	struct vpdma_buf fw_dma_buf;
10718c2ecf20Sopenharmony_ci	int i, r;
10728c2ecf20Sopenharmony_ci
10738c2ecf20Sopenharmony_ci	dev_dbg(&vpdma->pdev->dev, "firmware callback\n");
10748c2ecf20Sopenharmony_ci
10758c2ecf20Sopenharmony_ci	if (!f || !f->data) {
10768c2ecf20Sopenharmony_ci		dev_err(&vpdma->pdev->dev, "couldn't get firmware\n");
10778c2ecf20Sopenharmony_ci		return;
10788c2ecf20Sopenharmony_ci	}
10798c2ecf20Sopenharmony_ci
10808c2ecf20Sopenharmony_ci	/* already initialized */
10818c2ecf20Sopenharmony_ci	if (read_field_reg(vpdma, VPDMA_LIST_ATTR, VPDMA_LIST_RDY_MASK,
10828c2ecf20Sopenharmony_ci			VPDMA_LIST_RDY_SHFT)) {
10838c2ecf20Sopenharmony_ci		vpdma->cb(vpdma->pdev);
10848c2ecf20Sopenharmony_ci		return;
10858c2ecf20Sopenharmony_ci	}
10868c2ecf20Sopenharmony_ci
10878c2ecf20Sopenharmony_ci	r = vpdma_alloc_desc_buf(&fw_dma_buf, f->size);
10888c2ecf20Sopenharmony_ci	if (r) {
10898c2ecf20Sopenharmony_ci		dev_err(&vpdma->pdev->dev,
10908c2ecf20Sopenharmony_ci			"failed to allocate dma buffer for firmware\n");
10918c2ecf20Sopenharmony_ci		goto rel_fw;
10928c2ecf20Sopenharmony_ci	}
10938c2ecf20Sopenharmony_ci
10948c2ecf20Sopenharmony_ci	memcpy(fw_dma_buf.addr, f->data, f->size);
10958c2ecf20Sopenharmony_ci
10968c2ecf20Sopenharmony_ci	vpdma_map_desc_buf(vpdma, &fw_dma_buf);
10978c2ecf20Sopenharmony_ci
10988c2ecf20Sopenharmony_ci	write_reg(vpdma, VPDMA_LIST_ADDR, (u32) fw_dma_buf.dma_addr);
10998c2ecf20Sopenharmony_ci
11008c2ecf20Sopenharmony_ci	for (i = 0; i < 100; i++) {		/* max 1 second */
11018c2ecf20Sopenharmony_ci		msleep_interruptible(10);
11028c2ecf20Sopenharmony_ci
11038c2ecf20Sopenharmony_ci		if (read_field_reg(vpdma, VPDMA_LIST_ATTR, VPDMA_LIST_RDY_MASK,
11048c2ecf20Sopenharmony_ci				VPDMA_LIST_RDY_SHFT))
11058c2ecf20Sopenharmony_ci			break;
11068c2ecf20Sopenharmony_ci	}
11078c2ecf20Sopenharmony_ci
11088c2ecf20Sopenharmony_ci	if (i == 100) {
11098c2ecf20Sopenharmony_ci		dev_err(&vpdma->pdev->dev, "firmware upload failed\n");
11108c2ecf20Sopenharmony_ci		goto free_buf;
11118c2ecf20Sopenharmony_ci	}
11128c2ecf20Sopenharmony_ci
11138c2ecf20Sopenharmony_ci	vpdma->cb(vpdma->pdev);
11148c2ecf20Sopenharmony_ci
11158c2ecf20Sopenharmony_cifree_buf:
11168c2ecf20Sopenharmony_ci	vpdma_unmap_desc_buf(vpdma, &fw_dma_buf);
11178c2ecf20Sopenharmony_ci
11188c2ecf20Sopenharmony_ci	vpdma_free_desc_buf(&fw_dma_buf);
11198c2ecf20Sopenharmony_cirel_fw:
11208c2ecf20Sopenharmony_ci	release_firmware(f);
11218c2ecf20Sopenharmony_ci}
11228c2ecf20Sopenharmony_ci
11238c2ecf20Sopenharmony_cistatic int vpdma_load_firmware(struct vpdma_data *vpdma)
11248c2ecf20Sopenharmony_ci{
11258c2ecf20Sopenharmony_ci	int r;
11268c2ecf20Sopenharmony_ci	struct device *dev = &vpdma->pdev->dev;
11278c2ecf20Sopenharmony_ci
11288c2ecf20Sopenharmony_ci	r = request_firmware_nowait(THIS_MODULE, 1,
11298c2ecf20Sopenharmony_ci		(const char *) VPDMA_FIRMWARE, dev, GFP_KERNEL, vpdma,
11308c2ecf20Sopenharmony_ci		vpdma_firmware_cb);
11318c2ecf20Sopenharmony_ci	if (r) {
11328c2ecf20Sopenharmony_ci		dev_err(dev, "firmware not available %s\n", VPDMA_FIRMWARE);
11338c2ecf20Sopenharmony_ci		return r;
11348c2ecf20Sopenharmony_ci	} else {
11358c2ecf20Sopenharmony_ci		dev_info(dev, "loading firmware %s\n", VPDMA_FIRMWARE);
11368c2ecf20Sopenharmony_ci	}
11378c2ecf20Sopenharmony_ci
11388c2ecf20Sopenharmony_ci	return 0;
11398c2ecf20Sopenharmony_ci}
11408c2ecf20Sopenharmony_ci
11418c2ecf20Sopenharmony_ciint vpdma_create(struct platform_device *pdev, struct vpdma_data *vpdma,
11428c2ecf20Sopenharmony_ci		void (*cb)(struct platform_device *pdev))
11438c2ecf20Sopenharmony_ci{
11448c2ecf20Sopenharmony_ci	struct resource *res;
11458c2ecf20Sopenharmony_ci	int r;
11468c2ecf20Sopenharmony_ci
11478c2ecf20Sopenharmony_ci	dev_dbg(&pdev->dev, "vpdma_create\n");
11488c2ecf20Sopenharmony_ci
11498c2ecf20Sopenharmony_ci	vpdma->pdev = pdev;
11508c2ecf20Sopenharmony_ci	vpdma->cb = cb;
11518c2ecf20Sopenharmony_ci	spin_lock_init(&vpdma->lock);
11528c2ecf20Sopenharmony_ci
11538c2ecf20Sopenharmony_ci	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vpdma");
11548c2ecf20Sopenharmony_ci	if (res == NULL) {
11558c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "missing platform resources data\n");
11568c2ecf20Sopenharmony_ci		return -ENODEV;
11578c2ecf20Sopenharmony_ci	}
11588c2ecf20Sopenharmony_ci
11598c2ecf20Sopenharmony_ci	vpdma->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
11608c2ecf20Sopenharmony_ci	if (!vpdma->base) {
11618c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to ioremap\n");
11628c2ecf20Sopenharmony_ci		return -ENOMEM;
11638c2ecf20Sopenharmony_ci	}
11648c2ecf20Sopenharmony_ci
11658c2ecf20Sopenharmony_ci	r = vpdma_load_firmware(vpdma);
11668c2ecf20Sopenharmony_ci	if (r) {
11678c2ecf20Sopenharmony_ci		pr_err("failed to load firmware %s\n", VPDMA_FIRMWARE);
11688c2ecf20Sopenharmony_ci		return r;
11698c2ecf20Sopenharmony_ci	}
11708c2ecf20Sopenharmony_ci
11718c2ecf20Sopenharmony_ci	return 0;
11728c2ecf20Sopenharmony_ci}
11738c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vpdma_create);
11748c2ecf20Sopenharmony_ci
11758c2ecf20Sopenharmony_ciMODULE_AUTHOR("Texas Instruments Inc.");
11768c2ecf20Sopenharmony_ciMODULE_FIRMWARE(VPDMA_FIRMWARE);
11778c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
1178