18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * vsp1_wpf.c  --  R-Car VSP1 Write Pixel Formatter
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2013-2014 Renesas Electronics Corporation
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/device.h>
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <media/v4l2-subdev.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include "vsp1.h"
158c2ecf20Sopenharmony_ci#include "vsp1_dl.h"
168c2ecf20Sopenharmony_ci#include "vsp1_pipe.h"
178c2ecf20Sopenharmony_ci#include "vsp1_rwpf.h"
188c2ecf20Sopenharmony_ci#include "vsp1_video.h"
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#define WPF_GEN2_MAX_WIDTH			2048U
218c2ecf20Sopenharmony_ci#define WPF_GEN2_MAX_HEIGHT			2048U
228c2ecf20Sopenharmony_ci#define WPF_GEN3_MAX_WIDTH			8190U
238c2ecf20Sopenharmony_ci#define WPF_GEN3_MAX_HEIGHT			8190U
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci/* -----------------------------------------------------------------------------
268c2ecf20Sopenharmony_ci * Device Access
278c2ecf20Sopenharmony_ci */
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistatic inline void vsp1_wpf_write(struct vsp1_rwpf *wpf,
308c2ecf20Sopenharmony_ci				  struct vsp1_dl_body *dlb, u32 reg, u32 data)
318c2ecf20Sopenharmony_ci{
328c2ecf20Sopenharmony_ci	vsp1_dl_body_write(dlb, reg + wpf->entity.index * VI6_WPF_OFFSET, data);
338c2ecf20Sopenharmony_ci}
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci/* -----------------------------------------------------------------------------
368c2ecf20Sopenharmony_ci * Controls
378c2ecf20Sopenharmony_ci */
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cienum wpf_flip_ctrl {
408c2ecf20Sopenharmony_ci	WPF_CTRL_VFLIP = 0,
418c2ecf20Sopenharmony_ci	WPF_CTRL_HFLIP = 1,
428c2ecf20Sopenharmony_ci};
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_cistatic int vsp1_wpf_set_rotation(struct vsp1_rwpf *wpf, unsigned int rotation)
458c2ecf20Sopenharmony_ci{
468c2ecf20Sopenharmony_ci	struct vsp1_video *video = wpf->video;
478c2ecf20Sopenharmony_ci	struct v4l2_mbus_framefmt *sink_format;
488c2ecf20Sopenharmony_ci	struct v4l2_mbus_framefmt *source_format;
498c2ecf20Sopenharmony_ci	bool rotate;
508c2ecf20Sopenharmony_ci	int ret = 0;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	/*
538c2ecf20Sopenharmony_ci	 * Only consider the 0°/180° from/to 90°/270° modifications, the rest
548c2ecf20Sopenharmony_ci	 * is taken care of by the flipping configuration.
558c2ecf20Sopenharmony_ci	 */
568c2ecf20Sopenharmony_ci	rotate = rotation == 90 || rotation == 270;
578c2ecf20Sopenharmony_ci	if (rotate == wpf->flip.rotate)
588c2ecf20Sopenharmony_ci		return 0;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	/* Changing rotation isn't allowed when buffers are allocated. */
618c2ecf20Sopenharmony_ci	mutex_lock(&video->lock);
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	if (vb2_is_busy(&video->queue)) {
648c2ecf20Sopenharmony_ci		ret = -EBUSY;
658c2ecf20Sopenharmony_ci		goto done;
668c2ecf20Sopenharmony_ci	}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	sink_format = vsp1_entity_get_pad_format(&wpf->entity,
698c2ecf20Sopenharmony_ci						 wpf->entity.config,
708c2ecf20Sopenharmony_ci						 RWPF_PAD_SINK);
718c2ecf20Sopenharmony_ci	source_format = vsp1_entity_get_pad_format(&wpf->entity,
728c2ecf20Sopenharmony_ci						   wpf->entity.config,
738c2ecf20Sopenharmony_ci						   RWPF_PAD_SOURCE);
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	mutex_lock(&wpf->entity.lock);
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	if (rotate) {
788c2ecf20Sopenharmony_ci		source_format->width = sink_format->height;
798c2ecf20Sopenharmony_ci		source_format->height = sink_format->width;
808c2ecf20Sopenharmony_ci	} else {
818c2ecf20Sopenharmony_ci		source_format->width = sink_format->width;
828c2ecf20Sopenharmony_ci		source_format->height = sink_format->height;
838c2ecf20Sopenharmony_ci	}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	wpf->flip.rotate = rotate;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	mutex_unlock(&wpf->entity.lock);
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_cidone:
908c2ecf20Sopenharmony_ci	mutex_unlock(&video->lock);
918c2ecf20Sopenharmony_ci	return ret;
928c2ecf20Sopenharmony_ci}
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_cistatic int vsp1_wpf_s_ctrl(struct v4l2_ctrl *ctrl)
958c2ecf20Sopenharmony_ci{
968c2ecf20Sopenharmony_ci	struct vsp1_rwpf *wpf =
978c2ecf20Sopenharmony_ci		container_of(ctrl->handler, struct vsp1_rwpf, ctrls);
988c2ecf20Sopenharmony_ci	unsigned int rotation;
998c2ecf20Sopenharmony_ci	u32 flip = 0;
1008c2ecf20Sopenharmony_ci	int ret;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	/* Update the rotation. */
1038c2ecf20Sopenharmony_ci	rotation = wpf->flip.ctrls.rotate ? wpf->flip.ctrls.rotate->val : 0;
1048c2ecf20Sopenharmony_ci	ret = vsp1_wpf_set_rotation(wpf, rotation);
1058c2ecf20Sopenharmony_ci	if (ret < 0)
1068c2ecf20Sopenharmony_ci		return ret;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	/*
1098c2ecf20Sopenharmony_ci	 * Compute the flip value resulting from all three controls, with
1108c2ecf20Sopenharmony_ci	 * rotation by 180° flipping the image in both directions. Store the
1118c2ecf20Sopenharmony_ci	 * result in the pending flip field for the next frame that will be
1128c2ecf20Sopenharmony_ci	 * processed.
1138c2ecf20Sopenharmony_ci	 */
1148c2ecf20Sopenharmony_ci	if (wpf->flip.ctrls.vflip->val)
1158c2ecf20Sopenharmony_ci		flip |= BIT(WPF_CTRL_VFLIP);
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	if (wpf->flip.ctrls.hflip && wpf->flip.ctrls.hflip->val)
1188c2ecf20Sopenharmony_ci		flip |= BIT(WPF_CTRL_HFLIP);
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	if (rotation == 180 || rotation == 270)
1218c2ecf20Sopenharmony_ci		flip ^= BIT(WPF_CTRL_VFLIP) | BIT(WPF_CTRL_HFLIP);
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	spin_lock_irq(&wpf->flip.lock);
1248c2ecf20Sopenharmony_ci	wpf->flip.pending = flip;
1258c2ecf20Sopenharmony_ci	spin_unlock_irq(&wpf->flip.lock);
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	return 0;
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops vsp1_wpf_ctrl_ops = {
1318c2ecf20Sopenharmony_ci	.s_ctrl = vsp1_wpf_s_ctrl,
1328c2ecf20Sopenharmony_ci};
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_cistatic int wpf_init_controls(struct vsp1_rwpf *wpf)
1358c2ecf20Sopenharmony_ci{
1368c2ecf20Sopenharmony_ci	struct vsp1_device *vsp1 = wpf->entity.vsp1;
1378c2ecf20Sopenharmony_ci	unsigned int num_flip_ctrls;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	spin_lock_init(&wpf->flip.lock);
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	if (wpf->entity.index != 0) {
1428c2ecf20Sopenharmony_ci		/* Only WPF0 supports flipping. */
1438c2ecf20Sopenharmony_ci		num_flip_ctrls = 0;
1448c2ecf20Sopenharmony_ci	} else if (vsp1_feature(vsp1, VSP1_HAS_WPF_HFLIP)) {
1458c2ecf20Sopenharmony_ci		/*
1468c2ecf20Sopenharmony_ci		 * When horizontal flip is supported the WPF implements three
1478c2ecf20Sopenharmony_ci		 * controls (horizontal flip, vertical flip and rotation).
1488c2ecf20Sopenharmony_ci		 */
1498c2ecf20Sopenharmony_ci		num_flip_ctrls = 3;
1508c2ecf20Sopenharmony_ci	} else if (vsp1_feature(vsp1, VSP1_HAS_WPF_VFLIP)) {
1518c2ecf20Sopenharmony_ci		/*
1528c2ecf20Sopenharmony_ci		 * When only vertical flip is supported the WPF implements a
1538c2ecf20Sopenharmony_ci		 * single control (vertical flip).
1548c2ecf20Sopenharmony_ci		 */
1558c2ecf20Sopenharmony_ci		num_flip_ctrls = 1;
1568c2ecf20Sopenharmony_ci	} else {
1578c2ecf20Sopenharmony_ci		/* Otherwise flipping is not supported. */
1588c2ecf20Sopenharmony_ci		num_flip_ctrls = 0;
1598c2ecf20Sopenharmony_ci	}
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	vsp1_rwpf_init_ctrls(wpf, num_flip_ctrls);
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	if (num_flip_ctrls >= 1) {
1648c2ecf20Sopenharmony_ci		wpf->flip.ctrls.vflip =
1658c2ecf20Sopenharmony_ci			v4l2_ctrl_new_std(&wpf->ctrls, &vsp1_wpf_ctrl_ops,
1668c2ecf20Sopenharmony_ci					  V4L2_CID_VFLIP, 0, 1, 1, 0);
1678c2ecf20Sopenharmony_ci	}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	if (num_flip_ctrls == 3) {
1708c2ecf20Sopenharmony_ci		wpf->flip.ctrls.hflip =
1718c2ecf20Sopenharmony_ci			v4l2_ctrl_new_std(&wpf->ctrls, &vsp1_wpf_ctrl_ops,
1728c2ecf20Sopenharmony_ci					  V4L2_CID_HFLIP, 0, 1, 1, 0);
1738c2ecf20Sopenharmony_ci		wpf->flip.ctrls.rotate =
1748c2ecf20Sopenharmony_ci			v4l2_ctrl_new_std(&wpf->ctrls, &vsp1_wpf_ctrl_ops,
1758c2ecf20Sopenharmony_ci					  V4L2_CID_ROTATE, 0, 270, 90, 0);
1768c2ecf20Sopenharmony_ci		v4l2_ctrl_cluster(3, &wpf->flip.ctrls.vflip);
1778c2ecf20Sopenharmony_ci	}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	if (wpf->ctrls.error) {
1808c2ecf20Sopenharmony_ci		dev_err(vsp1->dev, "wpf%u: failed to initialize controls\n",
1818c2ecf20Sopenharmony_ci			wpf->entity.index);
1828c2ecf20Sopenharmony_ci		return wpf->ctrls.error;
1838c2ecf20Sopenharmony_ci	}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	return 0;
1868c2ecf20Sopenharmony_ci}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci/* -----------------------------------------------------------------------------
1898c2ecf20Sopenharmony_ci * V4L2 Subdevice Core Operations
1908c2ecf20Sopenharmony_ci */
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_cistatic int wpf_s_stream(struct v4l2_subdev *subdev, int enable)
1938c2ecf20Sopenharmony_ci{
1948c2ecf20Sopenharmony_ci	struct vsp1_rwpf *wpf = to_rwpf(subdev);
1958c2ecf20Sopenharmony_ci	struct vsp1_device *vsp1 = wpf->entity.vsp1;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	if (enable)
1988c2ecf20Sopenharmony_ci		return 0;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	/*
2018c2ecf20Sopenharmony_ci	 * Write to registers directly when stopping the stream as there will be
2028c2ecf20Sopenharmony_ci	 * no pipeline run to apply the display list.
2038c2ecf20Sopenharmony_ci	 */
2048c2ecf20Sopenharmony_ci	vsp1_write(vsp1, VI6_WPF_IRQ_ENB(wpf->entity.index), 0);
2058c2ecf20Sopenharmony_ci	vsp1_write(vsp1, wpf->entity.index * VI6_WPF_OFFSET +
2068c2ecf20Sopenharmony_ci		   VI6_WPF_SRCRPF, 0);
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	return 0;
2098c2ecf20Sopenharmony_ci}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci/* -----------------------------------------------------------------------------
2128c2ecf20Sopenharmony_ci * V4L2 Subdevice Operations
2138c2ecf20Sopenharmony_ci */
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_video_ops wpf_video_ops = {
2168c2ecf20Sopenharmony_ci	.s_stream = wpf_s_stream,
2178c2ecf20Sopenharmony_ci};
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_ops wpf_ops = {
2208c2ecf20Sopenharmony_ci	.video	= &wpf_video_ops,
2218c2ecf20Sopenharmony_ci	.pad    = &vsp1_rwpf_pad_ops,
2228c2ecf20Sopenharmony_ci};
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci/* -----------------------------------------------------------------------------
2258c2ecf20Sopenharmony_ci * VSP1 Entity Operations
2268c2ecf20Sopenharmony_ci */
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_cistatic void vsp1_wpf_destroy(struct vsp1_entity *entity)
2298c2ecf20Sopenharmony_ci{
2308c2ecf20Sopenharmony_ci	struct vsp1_rwpf *wpf = entity_to_rwpf(entity);
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	vsp1_dlm_destroy(wpf->dlm);
2338c2ecf20Sopenharmony_ci}
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_cistatic int wpf_configure_writeback_chain(struct vsp1_rwpf *wpf,
2368c2ecf20Sopenharmony_ci					 struct vsp1_dl_list *dl)
2378c2ecf20Sopenharmony_ci{
2388c2ecf20Sopenharmony_ci	unsigned int index = wpf->entity.index;
2398c2ecf20Sopenharmony_ci	struct vsp1_dl_list *dl_next;
2408c2ecf20Sopenharmony_ci	struct vsp1_dl_body *dlb;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	dl_next = vsp1_dl_list_get(wpf->dlm);
2438c2ecf20Sopenharmony_ci	if (!dl_next) {
2448c2ecf20Sopenharmony_ci		dev_err(wpf->entity.vsp1->dev,
2458c2ecf20Sopenharmony_ci			"Failed to obtain a dl list, disabling writeback\n");
2468c2ecf20Sopenharmony_ci		return -ENOMEM;
2478c2ecf20Sopenharmony_ci	}
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	dlb = vsp1_dl_list_get_body0(dl_next);
2508c2ecf20Sopenharmony_ci	vsp1_dl_body_write(dlb, VI6_WPF_WRBCK_CTRL(index), 0);
2518c2ecf20Sopenharmony_ci	vsp1_dl_list_add_chain(dl, dl_next);
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	return 0;
2548c2ecf20Sopenharmony_ci}
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_cistatic void wpf_configure_stream(struct vsp1_entity *entity,
2578c2ecf20Sopenharmony_ci				 struct vsp1_pipeline *pipe,
2588c2ecf20Sopenharmony_ci				 struct vsp1_dl_list *dl,
2598c2ecf20Sopenharmony_ci				 struct vsp1_dl_body *dlb)
2608c2ecf20Sopenharmony_ci{
2618c2ecf20Sopenharmony_ci	struct vsp1_rwpf *wpf = to_rwpf(&entity->subdev);
2628c2ecf20Sopenharmony_ci	struct vsp1_device *vsp1 = wpf->entity.vsp1;
2638c2ecf20Sopenharmony_ci	const struct v4l2_mbus_framefmt *source_format;
2648c2ecf20Sopenharmony_ci	const struct v4l2_mbus_framefmt *sink_format;
2658c2ecf20Sopenharmony_ci	unsigned int index = wpf->entity.index;
2668c2ecf20Sopenharmony_ci	unsigned int i;
2678c2ecf20Sopenharmony_ci	u32 outfmt = 0;
2688c2ecf20Sopenharmony_ci	u32 srcrpf = 0;
2698c2ecf20Sopenharmony_ci	int ret;
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	sink_format = vsp1_entity_get_pad_format(&wpf->entity,
2728c2ecf20Sopenharmony_ci						 wpf->entity.config,
2738c2ecf20Sopenharmony_ci						 RWPF_PAD_SINK);
2748c2ecf20Sopenharmony_ci	source_format = vsp1_entity_get_pad_format(&wpf->entity,
2758c2ecf20Sopenharmony_ci						   wpf->entity.config,
2768c2ecf20Sopenharmony_ci						   RWPF_PAD_SOURCE);
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	/* Format */
2798c2ecf20Sopenharmony_ci	if (!pipe->lif || wpf->writeback) {
2808c2ecf20Sopenharmony_ci		const struct v4l2_pix_format_mplane *format = &wpf->format;
2818c2ecf20Sopenharmony_ci		const struct vsp1_format_info *fmtinfo = wpf->fmtinfo;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci		outfmt = fmtinfo->hwfmt << VI6_WPF_OUTFMT_WRFMT_SHIFT;
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci		if (wpf->flip.rotate)
2868c2ecf20Sopenharmony_ci			outfmt |= VI6_WPF_OUTFMT_ROT;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci		if (fmtinfo->alpha)
2898c2ecf20Sopenharmony_ci			outfmt |= VI6_WPF_OUTFMT_PXA;
2908c2ecf20Sopenharmony_ci		if (fmtinfo->swap_yc)
2918c2ecf20Sopenharmony_ci			outfmt |= VI6_WPF_OUTFMT_SPYCS;
2928c2ecf20Sopenharmony_ci		if (fmtinfo->swap_uv)
2938c2ecf20Sopenharmony_ci			outfmt |= VI6_WPF_OUTFMT_SPUVS;
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci		/* Destination stride and byte swapping. */
2968c2ecf20Sopenharmony_ci		vsp1_wpf_write(wpf, dlb, VI6_WPF_DSTM_STRIDE_Y,
2978c2ecf20Sopenharmony_ci			       format->plane_fmt[0].bytesperline);
2988c2ecf20Sopenharmony_ci		if (format->num_planes > 1)
2998c2ecf20Sopenharmony_ci			vsp1_wpf_write(wpf, dlb, VI6_WPF_DSTM_STRIDE_C,
3008c2ecf20Sopenharmony_ci				       format->plane_fmt[1].bytesperline);
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci		vsp1_wpf_write(wpf, dlb, VI6_WPF_DSWAP, fmtinfo->swap);
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci		if (vsp1_feature(vsp1, VSP1_HAS_WPF_HFLIP) && index == 0)
3058c2ecf20Sopenharmony_ci			vsp1_wpf_write(wpf, dlb, VI6_WPF_ROT_CTRL,
3068c2ecf20Sopenharmony_ci				       VI6_WPF_ROT_CTRL_LN16 |
3078c2ecf20Sopenharmony_ci				       (256 << VI6_WPF_ROT_CTRL_LMEM_WD_SHIFT));
3088c2ecf20Sopenharmony_ci	}
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	if (sink_format->code != source_format->code)
3118c2ecf20Sopenharmony_ci		outfmt |= VI6_WPF_OUTFMT_CSC;
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	wpf->outfmt = outfmt;
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	vsp1_dl_body_write(dlb, VI6_DPR_WPF_FPORCH(index),
3168c2ecf20Sopenharmony_ci			   VI6_DPR_WPF_FPORCH_FP_WPFN);
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	/*
3198c2ecf20Sopenharmony_ci	 * Sources. If the pipeline has a single input and BRx is not used,
3208c2ecf20Sopenharmony_ci	 * configure it as the master layer. Otherwise configure all
3218c2ecf20Sopenharmony_ci	 * inputs as sub-layers and select the virtual RPF as the master
3228c2ecf20Sopenharmony_ci	 * layer.
3238c2ecf20Sopenharmony_ci	 */
3248c2ecf20Sopenharmony_ci	for (i = 0; i < vsp1->info->rpf_count; ++i) {
3258c2ecf20Sopenharmony_ci		struct vsp1_rwpf *input = pipe->inputs[i];
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci		if (!input)
3288c2ecf20Sopenharmony_ci			continue;
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci		srcrpf |= (!pipe->brx && pipe->num_inputs == 1)
3318c2ecf20Sopenharmony_ci			? VI6_WPF_SRCRPF_RPF_ACT_MST(input->entity.index)
3328c2ecf20Sopenharmony_ci			: VI6_WPF_SRCRPF_RPF_ACT_SUB(input->entity.index);
3338c2ecf20Sopenharmony_ci	}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	if (pipe->brx)
3368c2ecf20Sopenharmony_ci		srcrpf |= pipe->brx->type == VSP1_ENTITY_BRU
3378c2ecf20Sopenharmony_ci			? VI6_WPF_SRCRPF_VIRACT_MST
3388c2ecf20Sopenharmony_ci			: VI6_WPF_SRCRPF_VIRACT2_MST;
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	vsp1_wpf_write(wpf, dlb, VI6_WPF_SRCRPF, srcrpf);
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	/* Enable interrupts. */
3438c2ecf20Sopenharmony_ci	vsp1_dl_body_write(dlb, VI6_WPF_IRQ_STA(index), 0);
3448c2ecf20Sopenharmony_ci	vsp1_dl_body_write(dlb, VI6_WPF_IRQ_ENB(index),
3458c2ecf20Sopenharmony_ci			   VI6_WFP_IRQ_ENB_DFEE);
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	/*
3488c2ecf20Sopenharmony_ci	 * Configure writeback for display pipelines (the wpf writeback flag is
3498c2ecf20Sopenharmony_ci	 * never set for memory-to-memory pipelines). Start by adding a chained
3508c2ecf20Sopenharmony_ci	 * display list to disable writeback after a single frame, and process
3518c2ecf20Sopenharmony_ci	 * to enable writeback. If the display list allocation fails don't
3528c2ecf20Sopenharmony_ci	 * enable writeback as we wouldn't be able to safely disable it,
3538c2ecf20Sopenharmony_ci	 * resulting in possible memory corruption.
3548c2ecf20Sopenharmony_ci	 */
3558c2ecf20Sopenharmony_ci	if (wpf->writeback) {
3568c2ecf20Sopenharmony_ci		ret = wpf_configure_writeback_chain(wpf, dl);
3578c2ecf20Sopenharmony_ci		if (ret < 0)
3588c2ecf20Sopenharmony_ci			wpf->writeback = false;
3598c2ecf20Sopenharmony_ci	}
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	vsp1_dl_body_write(dlb, VI6_WPF_WRBCK_CTRL(index),
3628c2ecf20Sopenharmony_ci			   wpf->writeback ? VI6_WPF_WRBCK_CTRL_WBMD : 0);
3638c2ecf20Sopenharmony_ci}
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_cistatic void wpf_configure_frame(struct vsp1_entity *entity,
3668c2ecf20Sopenharmony_ci				struct vsp1_pipeline *pipe,
3678c2ecf20Sopenharmony_ci				struct vsp1_dl_list *dl,
3688c2ecf20Sopenharmony_ci				struct vsp1_dl_body *dlb)
3698c2ecf20Sopenharmony_ci{
3708c2ecf20Sopenharmony_ci	const unsigned int mask = BIT(WPF_CTRL_VFLIP)
3718c2ecf20Sopenharmony_ci				| BIT(WPF_CTRL_HFLIP);
3728c2ecf20Sopenharmony_ci	struct vsp1_rwpf *wpf = to_rwpf(&entity->subdev);
3738c2ecf20Sopenharmony_ci	unsigned long flags;
3748c2ecf20Sopenharmony_ci	u32 outfmt;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	spin_lock_irqsave(&wpf->flip.lock, flags);
3778c2ecf20Sopenharmony_ci	wpf->flip.active = (wpf->flip.active & ~mask)
3788c2ecf20Sopenharmony_ci			 | (wpf->flip.pending & mask);
3798c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&wpf->flip.lock, flags);
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	outfmt = (wpf->alpha << VI6_WPF_OUTFMT_PDV_SHIFT) | wpf->outfmt;
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	if (wpf->flip.active & BIT(WPF_CTRL_VFLIP))
3848c2ecf20Sopenharmony_ci		outfmt |= VI6_WPF_OUTFMT_FLP;
3858c2ecf20Sopenharmony_ci	if (wpf->flip.active & BIT(WPF_CTRL_HFLIP))
3868c2ecf20Sopenharmony_ci		outfmt |= VI6_WPF_OUTFMT_HFLP;
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	vsp1_wpf_write(wpf, dlb, VI6_WPF_OUTFMT, outfmt);
3898c2ecf20Sopenharmony_ci}
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_cistatic void wpf_configure_partition(struct vsp1_entity *entity,
3928c2ecf20Sopenharmony_ci				    struct vsp1_pipeline *pipe,
3938c2ecf20Sopenharmony_ci				    struct vsp1_dl_list *dl,
3948c2ecf20Sopenharmony_ci				    struct vsp1_dl_body *dlb)
3958c2ecf20Sopenharmony_ci{
3968c2ecf20Sopenharmony_ci	struct vsp1_rwpf *wpf = to_rwpf(&entity->subdev);
3978c2ecf20Sopenharmony_ci	struct vsp1_device *vsp1 = wpf->entity.vsp1;
3988c2ecf20Sopenharmony_ci	struct vsp1_rwpf_memory mem = wpf->mem;
3998c2ecf20Sopenharmony_ci	const struct v4l2_mbus_framefmt *sink_format;
4008c2ecf20Sopenharmony_ci	const struct v4l2_pix_format_mplane *format = &wpf->format;
4018c2ecf20Sopenharmony_ci	const struct vsp1_format_info *fmtinfo = wpf->fmtinfo;
4028c2ecf20Sopenharmony_ci	unsigned int width;
4038c2ecf20Sopenharmony_ci	unsigned int height;
4048c2ecf20Sopenharmony_ci	unsigned int left;
4058c2ecf20Sopenharmony_ci	unsigned int offset;
4068c2ecf20Sopenharmony_ci	unsigned int flip;
4078c2ecf20Sopenharmony_ci	unsigned int i;
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	sink_format = vsp1_entity_get_pad_format(&wpf->entity,
4108c2ecf20Sopenharmony_ci						 wpf->entity.config,
4118c2ecf20Sopenharmony_ci						 RWPF_PAD_SINK);
4128c2ecf20Sopenharmony_ci	width = sink_format->width;
4138c2ecf20Sopenharmony_ci	height = sink_format->height;
4148c2ecf20Sopenharmony_ci	left = 0;
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	/*
4178c2ecf20Sopenharmony_ci	 * Cropping. The partition algorithm can split the image into
4188c2ecf20Sopenharmony_ci	 * multiple slices.
4198c2ecf20Sopenharmony_ci	 */
4208c2ecf20Sopenharmony_ci	if (pipe->partitions > 1) {
4218c2ecf20Sopenharmony_ci		width = pipe->partition->wpf.width;
4228c2ecf20Sopenharmony_ci		left = pipe->partition->wpf.left;
4238c2ecf20Sopenharmony_ci	}
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	vsp1_wpf_write(wpf, dlb, VI6_WPF_HSZCLIP, VI6_WPF_SZCLIP_EN |
4268c2ecf20Sopenharmony_ci		       (0 << VI6_WPF_SZCLIP_OFST_SHIFT) |
4278c2ecf20Sopenharmony_ci		       (width << VI6_WPF_SZCLIP_SIZE_SHIFT));
4288c2ecf20Sopenharmony_ci	vsp1_wpf_write(wpf, dlb, VI6_WPF_VSZCLIP, VI6_WPF_SZCLIP_EN |
4298c2ecf20Sopenharmony_ci		       (0 << VI6_WPF_SZCLIP_OFST_SHIFT) |
4308c2ecf20Sopenharmony_ci		       (height << VI6_WPF_SZCLIP_SIZE_SHIFT));
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	/*
4338c2ecf20Sopenharmony_ci	 * For display pipelines without writeback enabled there's no memory
4348c2ecf20Sopenharmony_ci	 * address to configure, return now.
4358c2ecf20Sopenharmony_ci	 */
4368c2ecf20Sopenharmony_ci	if (pipe->lif && !wpf->writeback)
4378c2ecf20Sopenharmony_ci		return;
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	/*
4408c2ecf20Sopenharmony_ci	 * Update the memory offsets based on flipping configuration.
4418c2ecf20Sopenharmony_ci	 * The destination addresses point to the locations where the
4428c2ecf20Sopenharmony_ci	 * VSP starts writing to memory, which can be any corner of the
4438c2ecf20Sopenharmony_ci	 * image depending on the combination of flipping and rotation.
4448c2ecf20Sopenharmony_ci	 */
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	/*
4478c2ecf20Sopenharmony_ci	 * First take the partition left coordinate into account.
4488c2ecf20Sopenharmony_ci	 * Compute the offset to order the partitions correctly on the
4498c2ecf20Sopenharmony_ci	 * output based on whether flipping is enabled. Consider
4508c2ecf20Sopenharmony_ci	 * horizontal flipping when rotation is disabled but vertical
4518c2ecf20Sopenharmony_ci	 * flipping when rotation is enabled, as rotating the image
4528c2ecf20Sopenharmony_ci	 * switches the horizontal and vertical directions. The offset
4538c2ecf20Sopenharmony_ci	 * is applied horizontally or vertically accordingly.
4548c2ecf20Sopenharmony_ci	 */
4558c2ecf20Sopenharmony_ci	flip = wpf->flip.active;
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	if (flip & BIT(WPF_CTRL_HFLIP) && !wpf->flip.rotate)
4588c2ecf20Sopenharmony_ci		offset = format->width - left - width;
4598c2ecf20Sopenharmony_ci	else if (flip & BIT(WPF_CTRL_VFLIP) && wpf->flip.rotate)
4608c2ecf20Sopenharmony_ci		offset = format->height - left - width;
4618c2ecf20Sopenharmony_ci	else
4628c2ecf20Sopenharmony_ci		offset = left;
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	for (i = 0; i < format->num_planes; ++i) {
4658c2ecf20Sopenharmony_ci		unsigned int hsub = i > 0 ? fmtinfo->hsub : 1;
4668c2ecf20Sopenharmony_ci		unsigned int vsub = i > 0 ? fmtinfo->vsub : 1;
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci		if (wpf->flip.rotate)
4698c2ecf20Sopenharmony_ci			mem.addr[i] += offset / vsub
4708c2ecf20Sopenharmony_ci				     * format->plane_fmt[i].bytesperline;
4718c2ecf20Sopenharmony_ci		else
4728c2ecf20Sopenharmony_ci			mem.addr[i] += offset / hsub
4738c2ecf20Sopenharmony_ci				     * fmtinfo->bpp[i] / 8;
4748c2ecf20Sopenharmony_ci	}
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	if (flip & BIT(WPF_CTRL_VFLIP)) {
4778c2ecf20Sopenharmony_ci		/*
4788c2ecf20Sopenharmony_ci		 * When rotating the output (after rotation) image
4798c2ecf20Sopenharmony_ci		 * height is equal to the partition width (before
4808c2ecf20Sopenharmony_ci		 * rotation). Otherwise it is equal to the output
4818c2ecf20Sopenharmony_ci		 * image height.
4828c2ecf20Sopenharmony_ci		 */
4838c2ecf20Sopenharmony_ci		if (wpf->flip.rotate)
4848c2ecf20Sopenharmony_ci			height = width;
4858c2ecf20Sopenharmony_ci		else
4868c2ecf20Sopenharmony_ci			height = format->height;
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci		mem.addr[0] += (height - 1)
4898c2ecf20Sopenharmony_ci			     * format->plane_fmt[0].bytesperline;
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci		if (format->num_planes > 1) {
4928c2ecf20Sopenharmony_ci			offset = (height / fmtinfo->vsub - 1)
4938c2ecf20Sopenharmony_ci			       * format->plane_fmt[1].bytesperline;
4948c2ecf20Sopenharmony_ci			mem.addr[1] += offset;
4958c2ecf20Sopenharmony_ci			mem.addr[2] += offset;
4968c2ecf20Sopenharmony_ci		}
4978c2ecf20Sopenharmony_ci	}
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	if (wpf->flip.rotate && !(flip & BIT(WPF_CTRL_HFLIP))) {
5008c2ecf20Sopenharmony_ci		unsigned int hoffset = max(0, (int)format->width - 16);
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci		/*
5038c2ecf20Sopenharmony_ci		 * Compute the output coordinate. The partition
5048c2ecf20Sopenharmony_ci		 * horizontal (left) offset becomes a vertical offset.
5058c2ecf20Sopenharmony_ci		 */
5068c2ecf20Sopenharmony_ci		for (i = 0; i < format->num_planes; ++i) {
5078c2ecf20Sopenharmony_ci			unsigned int hsub = i > 0 ? fmtinfo->hsub : 1;
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci			mem.addr[i] += hoffset / hsub
5108c2ecf20Sopenharmony_ci				     * fmtinfo->bpp[i] / 8;
5118c2ecf20Sopenharmony_ci		}
5128c2ecf20Sopenharmony_ci	}
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci	/*
5158c2ecf20Sopenharmony_ci	 * On Gen3 hardware the SPUVS bit has no effect on 3-planar
5168c2ecf20Sopenharmony_ci	 * formats. Swap the U and V planes manually in that case.
5178c2ecf20Sopenharmony_ci	 */
5188c2ecf20Sopenharmony_ci	if (vsp1->info->gen == 3 && format->num_planes == 3 &&
5198c2ecf20Sopenharmony_ci	    fmtinfo->swap_uv)
5208c2ecf20Sopenharmony_ci		swap(mem.addr[1], mem.addr[2]);
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci	vsp1_wpf_write(wpf, dlb, VI6_WPF_DSTM_ADDR_Y, mem.addr[0]);
5238c2ecf20Sopenharmony_ci	vsp1_wpf_write(wpf, dlb, VI6_WPF_DSTM_ADDR_C0, mem.addr[1]);
5248c2ecf20Sopenharmony_ci	vsp1_wpf_write(wpf, dlb, VI6_WPF_DSTM_ADDR_C1, mem.addr[2]);
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	/*
5278c2ecf20Sopenharmony_ci	 * Writeback operates in single-shot mode and lasts for a single frame,
5288c2ecf20Sopenharmony_ci	 * reset the writeback flag to false for the next frame.
5298c2ecf20Sopenharmony_ci	 */
5308c2ecf20Sopenharmony_ci	wpf->writeback = false;
5318c2ecf20Sopenharmony_ci}
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_cistatic unsigned int wpf_max_width(struct vsp1_entity *entity,
5348c2ecf20Sopenharmony_ci				  struct vsp1_pipeline *pipe)
5358c2ecf20Sopenharmony_ci{
5368c2ecf20Sopenharmony_ci	struct vsp1_rwpf *wpf = to_rwpf(&entity->subdev);
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci	return wpf->flip.rotate ? 256 : wpf->max_width;
5398c2ecf20Sopenharmony_ci}
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_cistatic void wpf_partition(struct vsp1_entity *entity,
5428c2ecf20Sopenharmony_ci			  struct vsp1_pipeline *pipe,
5438c2ecf20Sopenharmony_ci			  struct vsp1_partition *partition,
5448c2ecf20Sopenharmony_ci			  unsigned int partition_idx,
5458c2ecf20Sopenharmony_ci			  struct vsp1_partition_window *window)
5468c2ecf20Sopenharmony_ci{
5478c2ecf20Sopenharmony_ci	partition->wpf = *window;
5488c2ecf20Sopenharmony_ci}
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_cistatic const struct vsp1_entity_operations wpf_entity_ops = {
5518c2ecf20Sopenharmony_ci	.destroy = vsp1_wpf_destroy,
5528c2ecf20Sopenharmony_ci	.configure_stream = wpf_configure_stream,
5538c2ecf20Sopenharmony_ci	.configure_frame = wpf_configure_frame,
5548c2ecf20Sopenharmony_ci	.configure_partition = wpf_configure_partition,
5558c2ecf20Sopenharmony_ci	.max_width = wpf_max_width,
5568c2ecf20Sopenharmony_ci	.partition = wpf_partition,
5578c2ecf20Sopenharmony_ci};
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci/* -----------------------------------------------------------------------------
5608c2ecf20Sopenharmony_ci * Initialization and Cleanup
5618c2ecf20Sopenharmony_ci */
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_cistruct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index)
5648c2ecf20Sopenharmony_ci{
5658c2ecf20Sopenharmony_ci	struct vsp1_rwpf *wpf;
5668c2ecf20Sopenharmony_ci	char name[6];
5678c2ecf20Sopenharmony_ci	int ret;
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci	wpf = devm_kzalloc(vsp1->dev, sizeof(*wpf), GFP_KERNEL);
5708c2ecf20Sopenharmony_ci	if (wpf == NULL)
5718c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	if (vsp1->info->gen == 2) {
5748c2ecf20Sopenharmony_ci		wpf->max_width = WPF_GEN2_MAX_WIDTH;
5758c2ecf20Sopenharmony_ci		wpf->max_height = WPF_GEN2_MAX_HEIGHT;
5768c2ecf20Sopenharmony_ci	} else {
5778c2ecf20Sopenharmony_ci		wpf->max_width = WPF_GEN3_MAX_WIDTH;
5788c2ecf20Sopenharmony_ci		wpf->max_height = WPF_GEN3_MAX_HEIGHT;
5798c2ecf20Sopenharmony_ci	}
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci	wpf->entity.ops = &wpf_entity_ops;
5828c2ecf20Sopenharmony_ci	wpf->entity.type = VSP1_ENTITY_WPF;
5838c2ecf20Sopenharmony_ci	wpf->entity.index = index;
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci	sprintf(name, "wpf.%u", index);
5868c2ecf20Sopenharmony_ci	ret = vsp1_entity_init(vsp1, &wpf->entity, name, 2, &wpf_ops,
5878c2ecf20Sopenharmony_ci			       MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER);
5888c2ecf20Sopenharmony_ci	if (ret < 0)
5898c2ecf20Sopenharmony_ci		return ERR_PTR(ret);
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	/* Initialize the display list manager. */
5928c2ecf20Sopenharmony_ci	wpf->dlm = vsp1_dlm_create(vsp1, index, 64);
5938c2ecf20Sopenharmony_ci	if (!wpf->dlm) {
5948c2ecf20Sopenharmony_ci		ret = -ENOMEM;
5958c2ecf20Sopenharmony_ci		goto error;
5968c2ecf20Sopenharmony_ci	}
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ci	/* Initialize the control handler. */
5998c2ecf20Sopenharmony_ci	ret = wpf_init_controls(wpf);
6008c2ecf20Sopenharmony_ci	if (ret < 0) {
6018c2ecf20Sopenharmony_ci		dev_err(vsp1->dev, "wpf%u: failed to initialize controls\n",
6028c2ecf20Sopenharmony_ci			index);
6038c2ecf20Sopenharmony_ci		goto error;
6048c2ecf20Sopenharmony_ci	}
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci	v4l2_ctrl_handler_setup(&wpf->ctrls);
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci	return wpf;
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_cierror:
6118c2ecf20Sopenharmony_ci	vsp1_entity_destroy(&wpf->entity);
6128c2ecf20Sopenharmony_ci	return ERR_PTR(ret);
6138c2ecf20Sopenharmony_ci}
614