xref: /linux/drivers/media/platform/rockchip/rga/rga3-hw.c (revision 67f8bc848ee31831336bd478e57d2f993551902e)
124a63d4cSSven Püschel // SPDX-License-Identifier: GPL-2.0-only
224a63d4cSSven Püschel /*
324a63d4cSSven Püschel  * Copyright (C) 2025-2026 Pengutronix e.K.
424a63d4cSSven Püschel  * Author: Sven Püschel <s.pueschel@pengutronix.de>
524a63d4cSSven Püschel  */
624a63d4cSSven Püschel 
724a63d4cSSven Püschel #include <linux/pm_runtime.h>
824a63d4cSSven Püschel #include <linux/bitfield.h>
924a63d4cSSven Püschel #include <linux/delay.h>
1024a63d4cSSven Püschel #include <linux/printk.h>
1124a63d4cSSven Püschel 
1224a63d4cSSven Püschel #include <media/v4l2-common.h>
1324a63d4cSSven Püschel 
1424a63d4cSSven Püschel #include "rga3-hw.h"
1524a63d4cSSven Püschel #include "rga.h"
1624a63d4cSSven Püschel 
1724a63d4cSSven Püschel static unsigned int rga3_get_scaling(unsigned int src, unsigned int dst)
1824a63d4cSSven Püschel {
1924a63d4cSSven Püschel 	/*
2024a63d4cSSven Püschel 	 * RGA3 scaling factor calculation as described in chapter 5.4.7 Resize
2124a63d4cSSven Püschel 	 * of the TRM Part 2. The resulting scaling factor is a 16-bit value
2224a63d4cSSven Püschel 	 * and therefore normalized with 2^16.
2324a63d4cSSven Püschel 	 *
2424a63d4cSSven Püschel 	 * While the TRM also mentions (dst-1)/(src-1) for the up-scaling case,
2524a63d4cSSven Püschel 	 * it didn't work as the value always exceeds 16 bit. Flipping the
2624a63d4cSSven Püschel 	 * factors results in a correct up-scaling. This is possible as the
2724a63d4cSSven Püschel 	 * RGA3 has the RGA3_WIN_SCALE_XXX_UP bit to determine if it does
2824a63d4cSSven Püschel 	 * an up or downscale.
2924a63d4cSSven Püschel 	 *
3024a63d4cSSven Püschel 	 * The scaling factor can potentially cause a slightly larger scaling
3124a63d4cSSven Püschel 	 * (e.g. 1/2px larger scale and then cropped to the destination size).
3224a63d4cSSven Püschel 	 * This can be seen when scaling 128x128px RGBA to 256x256px RGBA.
3324a63d4cSSven Püschel 	 * The RGA2 scaling factor calculation (without the various +/-1
3424a63d4cSSven Püschel 	 * doesn't work for the RGA3. It's assumed that this is an hardware
3524a63d4cSSven Püschel 	 * accuracy limitation, as the vendor kernel driver uses the same
3624a63d4cSSven Püschel 	 * scaling factor calculation.
3724a63d4cSSven Püschel 	 *
3824a63d4cSSven Püschel 	 * With a scaling factor of 1.0 the calculation technically also
3924a63d4cSSven Püschel 	 * overflows 16 bit. This isn't relevant, as in this case the
4024a63d4cSSven Püschel 	 * RGA3_WIN_SCALE_XXX_BYPASS bit completely skips the scaling operation.
4124a63d4cSSven Püschel 	 */
4224a63d4cSSven Püschel 	if (dst > src) {
4324a63d4cSSven Püschel 		if (((src - 1) << 16) % (dst - 1) == 0)
4424a63d4cSSven Püschel 			return ((src - 1) << 16) / (dst - 1) - 1;
4524a63d4cSSven Püschel 		else
4624a63d4cSSven Püschel 			return ((src - 1) << 16) / (dst - 1);
4724a63d4cSSven Püschel 	} else {
4824a63d4cSSven Püschel 		return ((dst - 1) << 16) / (src - 1) + 1;
4924a63d4cSSven Püschel 	}
5024a63d4cSSven Püschel }
5124a63d4cSSven Püschel 
5224a63d4cSSven Püschel /*
5324a63d4cSSven Püschel  * Check if the given format can be captured, as the RGA3 doesn't support all
5424a63d4cSSven Püschel  * input formats also on it's output.
5524a63d4cSSven Püschel  */
5624a63d4cSSven Püschel static bool rga3_can_capture(const struct rga3_fmt *fmt)
5724a63d4cSSven Püschel {
5824a63d4cSSven Püschel 	return fmt->hw_format <= RGA3_COLOR_FMT_LAST_OUTPUT;
5924a63d4cSSven Püschel }
6024a63d4cSSven Püschel 
6124a63d4cSSven Püschel /*
6224a63d4cSSven Püschel  * Map the transformations to the RGA3 command buffer.
6324a63d4cSSven Püschel  * Currently this is just the scaling settings and a fixed alpha value.
6424a63d4cSSven Püschel  */
6524a63d4cSSven Püschel static void rga3_cmd_set_trans_info(struct rga_ctx *ctx)
6624a63d4cSSven Püschel {
6724a63d4cSSven Püschel 	u32 *cmd = ctx->cmdbuf_virt;
6824a63d4cSSven Püschel 	unsigned int src_h, src_w, dst_h, dst_w;
6924a63d4cSSven Püschel 	unsigned int reg;
7024a63d4cSSven Püschel 	u16 hor_scl_fac, ver_scl_fac;
7124a63d4cSSven Püschel 	const struct rga3_fmt *in = ctx->in.fmt;
7224a63d4cSSven Püschel 
7324a63d4cSSven Püschel 	/* Support basic input cropping to support 1088px inputs */
7424a63d4cSSven Püschel 	src_h = ctx->in.crop.height;
7524a63d4cSSven Püschel 	src_w = ctx->in.crop.width;
7624a63d4cSSven Püschel 	dst_h = ctx->out.pix.height;
7724a63d4cSSven Püschel 	dst_w = ctx->out.pix.width;
7824a63d4cSSven Püschel 
7924a63d4cSSven Püschel 	reg = RGA3_WIN0_RD_CTRL - RGA3_FIRST_CMD_REG;
8024a63d4cSSven Püschel 	cmd[reg >> 2] |= FIELD_PREP(RGA3_WIN_SCALE_HOR_UP, dst_w > src_w)
8124a63d4cSSven Püschel 		      |  FIELD_PREP(RGA3_WIN_SCALE_HOR_BYPASS, dst_w == src_w)
8224a63d4cSSven Püschel 		      |  FIELD_PREP(RGA3_WIN_SCALE_VER_UP, dst_h > src_h)
8324a63d4cSSven Püschel 		      |  FIELD_PREP(RGA3_WIN_SCALE_VER_BYPASS, dst_h == src_h);
8424a63d4cSSven Püschel 
8524a63d4cSSven Püschel 	hor_scl_fac = rga3_get_scaling(src_w, dst_w);
8624a63d4cSSven Püschel 	ver_scl_fac = rga3_get_scaling(src_h, dst_h);
8724a63d4cSSven Püschel 	reg = RGA3_WIN0_SCL_FAC - RGA3_FIRST_CMD_REG;
8824a63d4cSSven Püschel 	cmd[reg >> 2] = FIELD_PREP(RGA3_SCALE_HOR_FAC, hor_scl_fac)
8924a63d4cSSven Püschel 		      | FIELD_PREP(RGA3_SCALE_VER_FAC, ver_scl_fac);
9024a63d4cSSven Püschel 
9124a63d4cSSven Püschel 	if (v4l2_format_info(in->fourcc)->has_alpha) {
9224a63d4cSSven Püschel 		/* copy alpha from input */
9324a63d4cSSven Püschel 		reg = RGA3_OVLP_TOP_ALPHA - RGA3_FIRST_CMD_REG;
9424a63d4cSSven Püschel 		cmd[reg >> 2] = FIELD_PREP(RGA3_ALPHA_SELECT_MODE, 1)
9524a63d4cSSven Püschel 			      | FIELD_PREP(RGA3_ALPHA_BLEND_MODE, 1);
9624a63d4cSSven Püschel 		reg = RGA3_OVLP_BOT_ALPHA - RGA3_FIRST_CMD_REG;
9724a63d4cSSven Püschel 		cmd[reg >> 2] = FIELD_PREP(RGA3_ALPHA_SELECT_MODE, 1)
9824a63d4cSSven Püschel 			      | FIELD_PREP(RGA3_ALPHA_BLEND_MODE, 1);
9924a63d4cSSven Püschel 	} else {
10024a63d4cSSven Püschel 		/* just use a 255 alpha value */
10124a63d4cSSven Püschel 		reg = RGA3_OVLP_TOP_CTRL - RGA3_FIRST_CMD_REG;
10224a63d4cSSven Püschel 		cmd[reg >> 2] = FIELD_PREP(RGA3_OVLP_GLOBAL_ALPHA, 0xff)
10324a63d4cSSven Püschel 			      | FIELD_PREP(RGA3_OVLP_COLOR_MODE, 1);
10424a63d4cSSven Püschel 		reg = RGA3_OVLP_BOT_CTRL - RGA3_FIRST_CMD_REG;
10524a63d4cSSven Püschel 		cmd[reg >> 2] = FIELD_PREP(RGA3_OVLP_GLOBAL_ALPHA, 0xff)
10624a63d4cSSven Püschel 			      | FIELD_PREP(RGA3_OVLP_COLOR_MODE, 1);
10724a63d4cSSven Püschel 	}
10824a63d4cSSven Püschel }
10924a63d4cSSven Püschel 
11024a63d4cSSven Püschel static void rga3_cmd_set_win0_addr(struct rga_ctx *ctx,
11124a63d4cSSven Püschel 				   const struct rga_addrs *addrs)
11224a63d4cSSven Püschel {
11324a63d4cSSven Püschel 	u32 *cmd = ctx->cmdbuf_virt;
11424a63d4cSSven Püschel 	unsigned int reg;
11524a63d4cSSven Püschel 
11624a63d4cSSven Püschel 	reg = RGA3_WIN0_Y_BASE - RGA3_FIRST_CMD_REG;
11724a63d4cSSven Püschel 	cmd[reg >> 2] = addrs->y_addr;
11824a63d4cSSven Püschel 	reg = RGA3_WIN0_U_BASE - RGA3_FIRST_CMD_REG;
11924a63d4cSSven Püschel 	cmd[reg >> 2] = addrs->u_addr;
12024a63d4cSSven Püschel }
12124a63d4cSSven Püschel 
12224a63d4cSSven Püschel static void rga3_cmd_set_wr_addr(struct rga_ctx *ctx,
12324a63d4cSSven Püschel 				 const struct rga_addrs *addrs)
12424a63d4cSSven Püschel {
12524a63d4cSSven Püschel 	u32 *cmd = ctx->cmdbuf_virt;
12624a63d4cSSven Püschel 	unsigned int reg;
12724a63d4cSSven Püschel 
12824a63d4cSSven Püschel 	reg = RGA3_WR_Y_BASE - RGA3_FIRST_CMD_REG;
12924a63d4cSSven Püschel 	cmd[reg >> 2] = addrs->y_addr;
13024a63d4cSSven Püschel 	reg = RGA3_WR_U_BASE - RGA3_FIRST_CMD_REG;
13124a63d4cSSven Püschel 	cmd[reg >> 2] = addrs->u_addr;
13224a63d4cSSven Püschel }
13324a63d4cSSven Püschel 
13424a63d4cSSven Püschel /* Map the input pixel format to win0 of the comamnd buffer. */
13524a63d4cSSven Püschel static void rga3_cmd_set_win0_format(struct rga_ctx *ctx)
13624a63d4cSSven Püschel {
13724a63d4cSSven Püschel 	u32 *cmd = ctx->cmdbuf_virt;
13824a63d4cSSven Püschel 	const struct rga3_fmt *in = ctx->in.fmt;
13924a63d4cSSven Püschel 	const struct rga3_fmt *out = ctx->out.fmt;
14024a63d4cSSven Püschel 	const struct v4l2_format_info *in_fmt, *out_fmt;
14124a63d4cSSven Püschel 	unsigned int act_h, act_w, src_h, src_w;
14224a63d4cSSven Püschel 	bool r2y, y2r;
14324a63d4cSSven Püschel 	u8 rd_format;
14424a63d4cSSven Püschel 	const struct v4l2_pix_format_mplane *csc_pix;
14524a63d4cSSven Püschel 	u8 csc_mode;
14624a63d4cSSven Püschel 	unsigned int reg;
14724a63d4cSSven Püschel 
14824a63d4cSSven Püschel 	act_h = ctx->in.pix.height;
14924a63d4cSSven Püschel 	act_w = ctx->in.pix.width;
15024a63d4cSSven Püschel 	/* Support basic input cropping to support 1088px inputs */
15124a63d4cSSven Püschel 	src_h = ctx->in.crop.height;
15224a63d4cSSven Püschel 	src_w = ctx->in.crop.width;
15324a63d4cSSven Püschel 
15424a63d4cSSven Püschel 	in_fmt = v4l2_format_info(in->fourcc);
15524a63d4cSSven Püschel 	out_fmt = v4l2_format_info(out->fourcc);
15624a63d4cSSven Püschel 	r2y = v4l2_is_format_rgb(in_fmt) && v4l2_is_format_yuv(out_fmt);
15724a63d4cSSven Püschel 	y2r = v4l2_is_format_yuv(in_fmt) && v4l2_is_format_rgb(out_fmt);
15824a63d4cSSven Püschel 
15924a63d4cSSven Püschel 	/* The Hardware only supports formats with 1/2 planes */
16024a63d4cSSven Püschel 	if (in_fmt->comp_planes == 2)
16124a63d4cSSven Püschel 		rd_format = RGA3_RDWR_FORMAT_SEMI_PLANAR;
16224a63d4cSSven Püschel 	else
16324a63d4cSSven Püschel 		rd_format = RGA3_RDWR_FORMAT_INTERLEAVED;
16424a63d4cSSven Püschel 
16524a63d4cSSven Püschel 	/* set pixel format and CSC */
16624a63d4cSSven Püschel 	csc_pix = r2y ? &ctx->out.pix : &ctx->in.pix;
16724a63d4cSSven Püschel 	switch (csc_pix->ycbcr_enc) {
16824a63d4cSSven Püschel 	case V4L2_YCBCR_ENC_BT2020:
16924a63d4cSSven Püschel 		csc_mode = RGA3_WIN_CSC_MODE_BT2020_L;
17024a63d4cSSven Püschel 		break;
17124a63d4cSSven Püschel 	case V4L2_YCBCR_ENC_709:
17224a63d4cSSven Püschel 		csc_mode = RGA3_WIN_CSC_MODE_BT709_L;
17324a63d4cSSven Püschel 		break;
17424a63d4cSSven Püschel 	default: /* should be fixed to BT601 in adjust_and_map_format */
17524a63d4cSSven Püschel 		if (csc_pix->quantization == V4L2_QUANTIZATION_LIM_RANGE)
17624a63d4cSSven Püschel 			csc_mode = RGA3_WIN_CSC_MODE_BT601_L;
17724a63d4cSSven Püschel 		else
17824a63d4cSSven Püschel 			csc_mode = RGA3_WIN_CSC_MODE_BT601_F;
17924a63d4cSSven Püschel 		break;
18024a63d4cSSven Püschel 	}
18124a63d4cSSven Püschel 
18224a63d4cSSven Püschel 	reg = RGA3_WIN0_RD_CTRL - RGA3_FIRST_CMD_REG;
18324a63d4cSSven Püschel 	cmd[reg >> 2] |= FIELD_PREP(RGA3_WIN_ENABLE, 1)
18424a63d4cSSven Püschel 		      |  FIELD_PREP(RGA3_WIN_PIC_FORMAT, in->hw_format)
18524a63d4cSSven Püschel 		      |  FIELD_PREP(RGA3_WIN_YC_SWAP, in->yc_swap)
18624a63d4cSSven Püschel 		      |  FIELD_PREP(RGA3_WIN_RBUV_SWAP, in->rbuv_swap)
18724a63d4cSSven Püschel 		      |  FIELD_PREP(RGA3_WIN_RD_FORMAT, rd_format)
18824a63d4cSSven Püschel 		      |  FIELD_PREP(RGA3_WIN_R2Y, r2y)
18924a63d4cSSven Püschel 		      |  FIELD_PREP(RGA3_WIN_Y2R, y2r)
19024a63d4cSSven Püschel 		      |  FIELD_PREP(RGA3_WIN_CSC_MODE, csc_mode);
19124a63d4cSSven Püschel 
19224a63d4cSSven Püschel 	/* set stride */
19324a63d4cSSven Püschel 	reg = RGA3_WIN0_VIR_STRIDE - RGA3_FIRST_CMD_REG;
19424a63d4cSSven Püschel 	/* stride needs to be in words */
19524a63d4cSSven Püschel 	cmd[reg >> 2] = ctx->in.pix.plane_fmt[0].bytesperline >> 2;
19624a63d4cSSven Püschel 	reg = RGA3_WIN0_UV_VIR_STRIDE - RGA3_FIRST_CMD_REG;
19724a63d4cSSven Püschel 	/* The Hardware only supports formats with 1/2 planes */
19824a63d4cSSven Püschel 	if (ctx->in.pix.num_planes == 2)
19924a63d4cSSven Püschel 		cmd[reg >> 2] = ctx->in.pix.plane_fmt[1].bytesperline >> 2;
20024a63d4cSSven Püschel 	else
20124a63d4cSSven Püschel 		cmd[reg >> 2] = ctx->in.pix.plane_fmt[0].bytesperline >> 2;
20224a63d4cSSven Püschel 
20324a63d4cSSven Püschel 	/* set size */
20424a63d4cSSven Püschel 	reg = RGA3_WIN0_ACT_SIZE - RGA3_FIRST_CMD_REG;
20524a63d4cSSven Püschel 	cmd[reg >> 2] = FIELD_PREP(RGA3_WIDTH, act_w)
20624a63d4cSSven Püschel 		      | FIELD_PREP(RGA3_HEIGHT, act_h);
20724a63d4cSSven Püschel 	reg = RGA3_WIN0_SRC_SIZE - RGA3_FIRST_CMD_REG;
20824a63d4cSSven Püschel 	cmd[reg >> 2] = FIELD_PREP(RGA3_WIDTH, src_w)
20924a63d4cSSven Püschel 		      | FIELD_PREP(RGA3_HEIGHT, src_h);
21024a63d4cSSven Püschel }
21124a63d4cSSven Püschel 
21224a63d4cSSven Püschel /* Map the output pixel format to the command buffer */
21324a63d4cSSven Püschel static void rga3_cmd_set_wr_format(struct rga_ctx *ctx)
21424a63d4cSSven Püschel {
21524a63d4cSSven Püschel 	u32 *cmd = ctx->cmdbuf_virt;
21624a63d4cSSven Püschel 	const struct rga3_fmt *out = ctx->out.fmt;
21724a63d4cSSven Püschel 	const struct v4l2_format_info *out_fmt;
21824a63d4cSSven Püschel 	unsigned int dst_h, dst_w;
21924a63d4cSSven Püschel 	u8 wr_format;
22024a63d4cSSven Püschel 	unsigned int reg;
22124a63d4cSSven Püschel 
22224a63d4cSSven Püschel 	dst_h = ctx->out.pix.height;
22324a63d4cSSven Püschel 	dst_w = ctx->out.pix.width;
22424a63d4cSSven Püschel 
22524a63d4cSSven Püschel 	out_fmt = v4l2_format_info(out->fourcc);
22624a63d4cSSven Püschel 
22724a63d4cSSven Püschel 	/* The Hardware only supports formats with 1/2 planes */
22824a63d4cSSven Püschel 	if (out_fmt->comp_planes == 2)
22924a63d4cSSven Püschel 		wr_format = RGA3_RDWR_FORMAT_SEMI_PLANAR;
23024a63d4cSSven Püschel 	else
23124a63d4cSSven Püschel 		wr_format = RGA3_RDWR_FORMAT_INTERLEAVED;
23224a63d4cSSven Püschel 
23324a63d4cSSven Püschel 	/* set pixel format */
23424a63d4cSSven Püschel 	reg = RGA3_WR_CTRL - RGA3_FIRST_CMD_REG;
23524a63d4cSSven Püschel 	cmd[reg >> 2] = FIELD_PREP(RGA3_WR_PIC_FORMAT, out->hw_format)
23624a63d4cSSven Püschel 		     |  FIELD_PREP(RGA3_WR_YC_SWAP, out->yc_swap)
23724a63d4cSSven Püschel 		     |  FIELD_PREP(RGA3_WR_RBUV_SWAP, out->rbuv_swap)
23824a63d4cSSven Püschel 		     |  FIELD_PREP(RGA3_WR_FORMAT, wr_format)
23924a63d4cSSven Püschel 	/* Use the max value to avoid limiting the write speed */
24024a63d4cSSven Püschel 		     |  FIELD_PREP(RGA3_WR_SW_OUTSTANDING_MAX, 63);
24124a63d4cSSven Püschel 
24224a63d4cSSven Püschel 	/* set stride */
24324a63d4cSSven Püschel 	reg = RGA3_WR_VIR_STRIDE - RGA3_FIRST_CMD_REG;
24424a63d4cSSven Püschel 	/* stride needs to be in words */
24524a63d4cSSven Püschel 	cmd[reg >> 2] = ctx->out.pix.plane_fmt[0].bytesperline >> 2;
24624a63d4cSSven Püschel 	reg = RGA3_WR_PL_VIR_STRIDE - RGA3_FIRST_CMD_REG;
24724a63d4cSSven Püschel 	/* The Hardware only supports formats with 1/2 planes */
24824a63d4cSSven Püschel 	if (ctx->out.pix.num_planes == 2)
24924a63d4cSSven Püschel 		cmd[reg >> 2] = ctx->out.pix.plane_fmt[1].bytesperline >> 2;
25024a63d4cSSven Püschel 	else
25124a63d4cSSven Püschel 		cmd[reg >> 2] = ctx->out.pix.plane_fmt[0].bytesperline >> 2;
25224a63d4cSSven Püschel 
25324a63d4cSSven Püschel 	/* Set size.
25424a63d4cSSven Püschel 	 * As two inputs are not supported, we don't use win1.
25524a63d4cSSven Püschel 	 * Therefore only set the size for win0.
25624a63d4cSSven Püschel 	 */
25724a63d4cSSven Püschel 	reg = RGA3_WIN0_DST_SIZE - RGA3_FIRST_CMD_REG;
25824a63d4cSSven Püschel 	cmd[reg >> 2] = FIELD_PREP(RGA3_WIDTH, dst_w)
25924a63d4cSSven Püschel 		      | FIELD_PREP(RGA3_HEIGHT, dst_h);
26024a63d4cSSven Püschel }
26124a63d4cSSven Püschel 
26224a63d4cSSven Püschel static void rga3_hw_setup_cmdbuf(struct rga_ctx *ctx)
26324a63d4cSSven Püschel {
26424a63d4cSSven Püschel 	memset(ctx->cmdbuf_virt, 0, RGA3_CMDBUF_SIZE);
26524a63d4cSSven Püschel 
26624a63d4cSSven Püschel 	rga3_cmd_set_win0_format(ctx);
26724a63d4cSSven Püschel 	rga3_cmd_set_trans_info(ctx);
26824a63d4cSSven Püschel 	rga3_cmd_set_wr_format(ctx);
26924a63d4cSSven Püschel }
27024a63d4cSSven Püschel 
27124a63d4cSSven Püschel static void rga3_hw_start(struct rockchip_rga *rga,
27224a63d4cSSven Püschel 			  struct rga_vb_buffer *src, struct rga_vb_buffer *dst)
27324a63d4cSSven Püschel {
27424a63d4cSSven Püschel 	struct rga_ctx *ctx = rga->curr;
27524a63d4cSSven Püschel 
27624a63d4cSSven Püschel 	rga3_cmd_set_win0_addr(ctx, &src->dma_addrs);
27724a63d4cSSven Püschel 	rga3_cmd_set_wr_addr(ctx, &dst->dma_addrs);
27824a63d4cSSven Püschel 
27924a63d4cSSven Püschel 	rga_write(rga, RGA3_CMD_ADDR, ctx->cmdbuf_phy);
28024a63d4cSSven Püschel 
28124a63d4cSSven Püschel 	/* sync CMD buf for RGA */
28224a63d4cSSven Püschel 	dma_sync_single_for_device(rga->dev, ctx->cmdbuf_phy,
28324a63d4cSSven Püschel 				   PAGE_SIZE, DMA_BIDIRECTIONAL);
28424a63d4cSSven Püschel 
28524a63d4cSSven Püschel 	/* set to master mode and start the conversion */
28624a63d4cSSven Püschel 	rga_write(rga, RGA3_SYS_CTRL,
28724a63d4cSSven Püschel 		  FIELD_PREP(RGA3_CMD_MODE, RGA3_CMD_MODE_MASTER));
28824a63d4cSSven Püschel 	rga_write(rga, RGA3_INT_EN, FIELD_PREP(RGA3_INT_FRM_DONE, 1));
28924a63d4cSSven Püschel 	rga_write(rga, RGA3_CMD_CTRL,
29024a63d4cSSven Püschel 		  FIELD_PREP(RGA3_CMD_LINE_START_PULSE, 1));
29124a63d4cSSven Püschel }
29224a63d4cSSven Püschel 
29324a63d4cSSven Püschel static bool rga3_handle_irq(struct rockchip_rga *rga)
29424a63d4cSSven Püschel {
29524a63d4cSSven Püschel 	u32 intr;
29624a63d4cSSven Püschel 
29724a63d4cSSven Püschel 	intr = rga_read(rga, RGA3_INT_RAW);
29824a63d4cSSven Püschel 	/* clear all interrupts */
29924a63d4cSSven Püschel 	rga_write(rga, RGA3_INT_CLR, intr);
30024a63d4cSSven Püschel 
30124a63d4cSSven Püschel 	return FIELD_GET(RGA3_INT_FRM_DONE, intr);
30224a63d4cSSven Püschel }
30324a63d4cSSven Püschel 
30424a63d4cSSven Püschel static void rga3_get_version(struct rockchip_rga *rga)
30524a63d4cSSven Püschel {
30624a63d4cSSven Püschel 	u32 version = rga_read(rga, RGA3_VERSION_NUM);
30724a63d4cSSven Püschel 
30824a63d4cSSven Püschel 	rga->version.major = FIELD_GET(RGA3_VERSION_NUM_MAJOR, version);
30924a63d4cSSven Püschel 	rga->version.minor = FIELD_GET(RGA3_VERSION_NUM_MINOR, version);
31024a63d4cSSven Püschel }
31124a63d4cSSven Püschel 
31224a63d4cSSven Püschel static struct rga3_fmt rga3_formats[] = {
31324a63d4cSSven Püschel 	{
31424a63d4cSSven Püschel 		.fourcc = V4L2_PIX_FMT_RGB24,
31524a63d4cSSven Püschel 		.hw_format = RGA3_COLOR_FMT_BGR888,
31624a63d4cSSven Püschel 		.rbuv_swap = 1,
31724a63d4cSSven Püschel 	},
31824a63d4cSSven Püschel 	{
31924a63d4cSSven Püschel 		.fourcc = V4L2_PIX_FMT_BGR24,
32024a63d4cSSven Püschel 		.hw_format = RGA3_COLOR_FMT_BGR888,
32124a63d4cSSven Püschel 	},
32224a63d4cSSven Püschel 	{
32324a63d4cSSven Püschel 		.fourcc = V4L2_PIX_FMT_ABGR32,
32424a63d4cSSven Püschel 		.hw_format = RGA3_COLOR_FMT_BGRA8888,
32524a63d4cSSven Püschel 	},
32624a63d4cSSven Püschel 	{
32724a63d4cSSven Püschel 		.fourcc = V4L2_PIX_FMT_RGBA32,
32824a63d4cSSven Püschel 		.hw_format = RGA3_COLOR_FMT_BGRA8888,
32924a63d4cSSven Püschel 		.rbuv_swap = 1,
33024a63d4cSSven Püschel 	},
33124a63d4cSSven Püschel 	{
33224a63d4cSSven Püschel 		.fourcc = V4L2_PIX_FMT_XBGR32,
33324a63d4cSSven Püschel 		.hw_format = RGA3_COLOR_FMT_BGRA8888,
33424a63d4cSSven Püschel 	},
33524a63d4cSSven Püschel 	{
33624a63d4cSSven Püschel 		.fourcc = V4L2_PIX_FMT_RGBX32,
33724a63d4cSSven Püschel 		.hw_format = RGA3_COLOR_FMT_BGRA8888,
33824a63d4cSSven Püschel 		.rbuv_swap = 1,
33924a63d4cSSven Püschel 	},
34024a63d4cSSven Püschel 	{
34124a63d4cSSven Püschel 		.fourcc = V4L2_PIX_FMT_RGB565,
34224a63d4cSSven Püschel 		.hw_format = RGA3_COLOR_FMT_BGR565,
34324a63d4cSSven Püschel 		.rbuv_swap = 1,
34424a63d4cSSven Püschel 	},
34524a63d4cSSven Püschel 	{
34624a63d4cSSven Püschel 		.fourcc = V4L2_PIX_FMT_NV12M,
34724a63d4cSSven Püschel 		.hw_format = RGA3_COLOR_FMT_YUV420,
34824a63d4cSSven Püschel 	},
34924a63d4cSSven Püschel 	{
35024a63d4cSSven Püschel 		.fourcc = V4L2_PIX_FMT_NV12,
35124a63d4cSSven Püschel 		.hw_format = RGA3_COLOR_FMT_YUV420,
35224a63d4cSSven Püschel 	},
35324a63d4cSSven Püschel 	{
35424a63d4cSSven Püschel 		.fourcc = V4L2_PIX_FMT_NV21M,
35524a63d4cSSven Püschel 		.hw_format = RGA3_COLOR_FMT_YUV420,
35624a63d4cSSven Püschel 		.rbuv_swap = 1,
35724a63d4cSSven Püschel 	},
35824a63d4cSSven Püschel 	{
35924a63d4cSSven Püschel 		.fourcc = V4L2_PIX_FMT_NV21,
36024a63d4cSSven Püschel 		.hw_format = RGA3_COLOR_FMT_YUV420,
36124a63d4cSSven Püschel 		.rbuv_swap = 1,
36224a63d4cSSven Püschel 	},
36324a63d4cSSven Püschel 	{
36424a63d4cSSven Püschel 		.fourcc = V4L2_PIX_FMT_NV16M,
36524a63d4cSSven Püschel 		.hw_format = RGA3_COLOR_FMT_YUV422,
36624a63d4cSSven Püschel 	},
36724a63d4cSSven Püschel 	{
36824a63d4cSSven Püschel 		.fourcc = V4L2_PIX_FMT_NV16,
36924a63d4cSSven Püschel 		.hw_format = RGA3_COLOR_FMT_YUV422,
37024a63d4cSSven Püschel 	},
37124a63d4cSSven Püschel 	{
37224a63d4cSSven Püschel 		.fourcc = V4L2_PIX_FMT_NV61M,
37324a63d4cSSven Püschel 		.hw_format = RGA3_COLOR_FMT_YUV422,
37424a63d4cSSven Püschel 		.rbuv_swap = 1,
37524a63d4cSSven Püschel 	},
37624a63d4cSSven Püschel 	{
37724a63d4cSSven Püschel 		.fourcc = V4L2_PIX_FMT_NV61,
37824a63d4cSSven Püschel 		.hw_format = RGA3_COLOR_FMT_YUV422,
37924a63d4cSSven Püschel 		.rbuv_swap = 1,
38024a63d4cSSven Püschel 	},
38124a63d4cSSven Püschel 	{
38224a63d4cSSven Püschel 		.fourcc = V4L2_PIX_FMT_YUYV,
38324a63d4cSSven Püschel 		.hw_format = RGA3_COLOR_FMT_YUV422,
38424a63d4cSSven Püschel 		.yc_swap = 1,
38524a63d4cSSven Püschel 	},
38624a63d4cSSven Püschel 	{
38724a63d4cSSven Püschel 		.fourcc = V4L2_PIX_FMT_YVYU,
38824a63d4cSSven Püschel 		.hw_format = RGA3_COLOR_FMT_YUV422,
38924a63d4cSSven Püschel 		.yc_swap = 1,
39024a63d4cSSven Püschel 		.rbuv_swap = 1,
39124a63d4cSSven Püschel 	},
39224a63d4cSSven Püschel 	{
39324a63d4cSSven Püschel 		.fourcc = V4L2_PIX_FMT_UYVY,
39424a63d4cSSven Püschel 		.hw_format = RGA3_COLOR_FMT_YUV422,
39524a63d4cSSven Püschel 	},
39624a63d4cSSven Püschel 	{
39724a63d4cSSven Püschel 		.fourcc = V4L2_PIX_FMT_VYUY,
39824a63d4cSSven Püschel 		.hw_format = RGA3_COLOR_FMT_YUV422,
39924a63d4cSSven Püschel 		.rbuv_swap = 1,
40024a63d4cSSven Püschel 	},
40124a63d4cSSven Püschel 	/* Input only formats last to keep rga3_enum_format simple */
40224a63d4cSSven Püschel 	{
40324a63d4cSSven Püschel 		.fourcc = V4L2_PIX_FMT_ARGB32,
40424a63d4cSSven Püschel 		.hw_format = RGA3_COLOR_FMT_ABGR8888,
40524a63d4cSSven Püschel 		.rbuv_swap = 1,
40624a63d4cSSven Püschel 	},
40724a63d4cSSven Püschel 	{
40824a63d4cSSven Püschel 		.fourcc = V4L2_PIX_FMT_BGRA32,
40924a63d4cSSven Püschel 		.hw_format = RGA3_COLOR_FMT_ABGR8888,
41024a63d4cSSven Püschel 	},
41124a63d4cSSven Püschel 	{
41224a63d4cSSven Püschel 		.fourcc = V4L2_PIX_FMT_XRGB32,
41324a63d4cSSven Püschel 		.hw_format = RGA3_COLOR_FMT_ABGR8888,
41424a63d4cSSven Püschel 		.rbuv_swap = 1,
41524a63d4cSSven Püschel 	},
41624a63d4cSSven Püschel 	{
41724a63d4cSSven Püschel 		.fourcc = V4L2_PIX_FMT_BGRX32,
41824a63d4cSSven Püschel 		.hw_format = RGA3_COLOR_FMT_ABGR8888,
41924a63d4cSSven Püschel 	},
42024a63d4cSSven Püschel };
42124a63d4cSSven Püschel 
42224a63d4cSSven Püschel static int rga3_enum_format(struct v4l2_fmtdesc *f)
42324a63d4cSSven Püschel {
42424a63d4cSSven Püschel 	struct rga3_fmt *fmt;
42524a63d4cSSven Püschel 
42624a63d4cSSven Püschel 	if (f->index >= ARRAY_SIZE(rga3_formats))
42724a63d4cSSven Püschel 		return -EINVAL;
42824a63d4cSSven Püschel 
42924a63d4cSSven Püschel 	fmt = &rga3_formats[f->index];
43024a63d4cSSven Püschel 	if (V4L2_TYPE_IS_CAPTURE(f->type) && !rga3_can_capture(fmt))
43124a63d4cSSven Püschel 		return -EINVAL;
43224a63d4cSSven Püschel 
43324a63d4cSSven Püschel 	f->pixelformat = fmt->fourcc;
43424a63d4cSSven Püschel 	return 0;
43524a63d4cSSven Püschel }
43624a63d4cSSven Püschel 
43724a63d4cSSven Püschel static void *rga3_adjust_and_map_format(struct rga_ctx *ctx,
43824a63d4cSSven Püschel 					struct v4l2_pix_format_mplane *format,
43924a63d4cSSven Püschel 					bool is_output)
44024a63d4cSSven Püschel {
44124a63d4cSSven Püschel 	unsigned int i;
44224a63d4cSSven Püschel 	const struct v4l2_format_info *format_info;
44324a63d4cSSven Püschel 	const struct v4l2_pix_format_mplane *other_format;
44424a63d4cSSven Püschel 	const struct v4l2_format_info *other_format_info;
44524a63d4cSSven Püschel 
44624a63d4cSSven Püschel 	if (!format)
44724a63d4cSSven Püschel 		return &rga3_formats[0];
44824a63d4cSSven Püschel 
44924a63d4cSSven Püschel 	format_info = v4l2_format_info(format->pixelformat);
45024a63d4cSSven Püschel 	other_format = is_output ? &ctx->out.pix : &ctx->in.pix;
45124a63d4cSSven Püschel 	other_format_info = v4l2_format_info(other_format->pixelformat);
45224a63d4cSSven Püschel 
453*7acc334cSSven Püschel 	/*
454*7acc334cSSven Püschel 	 * Only apply the quantization restrictions when we need to
455*7acc334cSSven Püschel 	 * convert between RGB and YUV. Otherwise there is no point
456*7acc334cSSven Püschel 	 * to limit the quantization for operations like scaling or
457*7acc334cSSven Püschel 	 * rotations.
458*7acc334cSSven Püschel 	 */
459*7acc334cSSven Püschel 	if (v4l2_is_format_yuv(format_info) &&
460*7acc334cSSven Püschel 	    v4l2_is_format_rgb(other_format_info)) {
46124a63d4cSSven Püschel 		/*
46224a63d4cSSven Püschel 		 * The RGA3 only supports BT601, BT709 and BT2020 RGB<->YUV conversions
46324a63d4cSSven Püschel 		 * Additionally BT709 and BT2020 only support limited range YUV.
46424a63d4cSSven Püschel 		 */
46524a63d4cSSven Püschel 		switch (format->ycbcr_enc) {
46624a63d4cSSven Püschel 		case V4L2_YCBCR_ENC_601:
46724a63d4cSSven Püschel 			/* supports full and limited range */
46824a63d4cSSven Püschel 			break;
46924a63d4cSSven Püschel 		case V4L2_YCBCR_ENC_709:
47024a63d4cSSven Püschel 		case V4L2_YCBCR_ENC_BT2020:
47124a63d4cSSven Püschel 			format->quantization = V4L2_QUANTIZATION_LIM_RANGE;
47224a63d4cSSven Püschel 			break;
47324a63d4cSSven Püschel 		default:
47424a63d4cSSven Püschel 			format->ycbcr_enc = V4L2_YCBCR_ENC_601;
47524a63d4cSSven Püschel 			format->quantization = V4L2_QUANTIZATION_FULL_RANGE;
47624a63d4cSSven Püschel 			break;
47724a63d4cSSven Püschel 		}
47824a63d4cSSven Püschel 	}
47924a63d4cSSven Püschel 
48024a63d4cSSven Püschel 	for (i = 0; i < ARRAY_SIZE(rga3_formats); i++) {
48124a63d4cSSven Püschel 		if (!is_output && !rga3_can_capture(&rga3_formats[i]))
48224a63d4cSSven Püschel 			continue;
48324a63d4cSSven Püschel 
48424a63d4cSSven Püschel 		if (rga3_formats[i].fourcc == format->pixelformat)
48524a63d4cSSven Püschel 			return &rga3_formats[i];
48624a63d4cSSven Püschel 	}
48724a63d4cSSven Püschel 
48824a63d4cSSven Püschel 	format->pixelformat = rga3_formats[0].fourcc;
48924a63d4cSSven Püschel 	return &rga3_formats[0];
49024a63d4cSSven Püschel }
49124a63d4cSSven Püschel 
49224a63d4cSSven Püschel const struct rga_hw rga3_hw = {
49324a63d4cSSven Püschel 	.card_type = "rga3",
49424a63d4cSSven Püschel 	.has_internal_iommu = false,
49524a63d4cSSven Püschel 	.cmdbuf_size = RGA3_CMDBUF_SIZE,
49624a63d4cSSven Püschel 	.min_width = RGA3_MIN_WIDTH,
49724a63d4cSSven Püschel 	.min_height = RGA3_MIN_HEIGHT,
49824a63d4cSSven Püschel 	/* use output size, as it's a bit smaller than the input size */
49924a63d4cSSven Püschel 	.max_width = RGA3_MAX_OUTPUT_WIDTH,
50024a63d4cSSven Püschel 	.max_height = RGA3_MAX_OUTPUT_HEIGHT,
50124a63d4cSSven Püschel 	.max_scaling_factor = RGA3_MAX_SCALING_FACTOR,
50224a63d4cSSven Püschel 	.stride_alignment = 16,
50324a63d4cSSven Püschel 	.features = 0,
50424a63d4cSSven Püschel 
50524a63d4cSSven Püschel 	.setup_cmdbuf = rga3_hw_setup_cmdbuf,
50624a63d4cSSven Püschel 	.start = rga3_hw_start,
50724a63d4cSSven Püschel 	.handle_irq = rga3_handle_irq,
50824a63d4cSSven Püschel 	.get_version = rga3_get_version,
50924a63d4cSSven Püschel 	.enum_format = rga3_enum_format,
51024a63d4cSSven Püschel 	.adjust_and_map_format = rga3_adjust_and_map_format,
51124a63d4cSSven Püschel };
512