xref: /OK3568_Linux_fs/kernel/drivers/media/platform/vsp1/vsp1_wpf.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0+
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * vsp1_wpf.c  --  R-Car VSP1 Write Pixel Formatter
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Copyright (C) 2013-2014 Renesas Electronics Corporation
6*4882a593Smuzhiyun  *
7*4882a593Smuzhiyun  * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
8*4882a593Smuzhiyun  */
9*4882a593Smuzhiyun 
10*4882a593Smuzhiyun #include <linux/device.h>
11*4882a593Smuzhiyun 
12*4882a593Smuzhiyun #include <media/v4l2-subdev.h>
13*4882a593Smuzhiyun 
14*4882a593Smuzhiyun #include "vsp1.h"
15*4882a593Smuzhiyun #include "vsp1_dl.h"
16*4882a593Smuzhiyun #include "vsp1_pipe.h"
17*4882a593Smuzhiyun #include "vsp1_rwpf.h"
18*4882a593Smuzhiyun #include "vsp1_video.h"
19*4882a593Smuzhiyun 
20*4882a593Smuzhiyun #define WPF_GEN2_MAX_WIDTH			2048U
21*4882a593Smuzhiyun #define WPF_GEN2_MAX_HEIGHT			2048U
22*4882a593Smuzhiyun #define WPF_GEN3_MAX_WIDTH			8190U
23*4882a593Smuzhiyun #define WPF_GEN3_MAX_HEIGHT			8190U
24*4882a593Smuzhiyun 
25*4882a593Smuzhiyun /* -----------------------------------------------------------------------------
26*4882a593Smuzhiyun  * Device Access
27*4882a593Smuzhiyun  */
28*4882a593Smuzhiyun 
vsp1_wpf_write(struct vsp1_rwpf * wpf,struct vsp1_dl_body * dlb,u32 reg,u32 data)29*4882a593Smuzhiyun static inline void vsp1_wpf_write(struct vsp1_rwpf *wpf,
30*4882a593Smuzhiyun 				  struct vsp1_dl_body *dlb, u32 reg, u32 data)
31*4882a593Smuzhiyun {
32*4882a593Smuzhiyun 	vsp1_dl_body_write(dlb, reg + wpf->entity.index * VI6_WPF_OFFSET, data);
33*4882a593Smuzhiyun }
34*4882a593Smuzhiyun 
35*4882a593Smuzhiyun /* -----------------------------------------------------------------------------
36*4882a593Smuzhiyun  * Controls
37*4882a593Smuzhiyun  */
38*4882a593Smuzhiyun 
39*4882a593Smuzhiyun enum wpf_flip_ctrl {
40*4882a593Smuzhiyun 	WPF_CTRL_VFLIP = 0,
41*4882a593Smuzhiyun 	WPF_CTRL_HFLIP = 1,
42*4882a593Smuzhiyun };
43*4882a593Smuzhiyun 
vsp1_wpf_set_rotation(struct vsp1_rwpf * wpf,unsigned int rotation)44*4882a593Smuzhiyun static int vsp1_wpf_set_rotation(struct vsp1_rwpf *wpf, unsigned int rotation)
45*4882a593Smuzhiyun {
46*4882a593Smuzhiyun 	struct vsp1_video *video = wpf->video;
47*4882a593Smuzhiyun 	struct v4l2_mbus_framefmt *sink_format;
48*4882a593Smuzhiyun 	struct v4l2_mbus_framefmt *source_format;
49*4882a593Smuzhiyun 	bool rotate;
50*4882a593Smuzhiyun 	int ret = 0;
51*4882a593Smuzhiyun 
52*4882a593Smuzhiyun 	/*
53*4882a593Smuzhiyun 	 * Only consider the 0°/180° from/to 90°/270° modifications, the rest
54*4882a593Smuzhiyun 	 * is taken care of by the flipping configuration.
55*4882a593Smuzhiyun 	 */
56*4882a593Smuzhiyun 	rotate = rotation == 90 || rotation == 270;
57*4882a593Smuzhiyun 	if (rotate == wpf->flip.rotate)
58*4882a593Smuzhiyun 		return 0;
59*4882a593Smuzhiyun 
60*4882a593Smuzhiyun 	/* Changing rotation isn't allowed when buffers are allocated. */
61*4882a593Smuzhiyun 	mutex_lock(&video->lock);
62*4882a593Smuzhiyun 
63*4882a593Smuzhiyun 	if (vb2_is_busy(&video->queue)) {
64*4882a593Smuzhiyun 		ret = -EBUSY;
65*4882a593Smuzhiyun 		goto done;
66*4882a593Smuzhiyun 	}
67*4882a593Smuzhiyun 
68*4882a593Smuzhiyun 	sink_format = vsp1_entity_get_pad_format(&wpf->entity,
69*4882a593Smuzhiyun 						 wpf->entity.config,
70*4882a593Smuzhiyun 						 RWPF_PAD_SINK);
71*4882a593Smuzhiyun 	source_format = vsp1_entity_get_pad_format(&wpf->entity,
72*4882a593Smuzhiyun 						   wpf->entity.config,
73*4882a593Smuzhiyun 						   RWPF_PAD_SOURCE);
74*4882a593Smuzhiyun 
75*4882a593Smuzhiyun 	mutex_lock(&wpf->entity.lock);
76*4882a593Smuzhiyun 
77*4882a593Smuzhiyun 	if (rotate) {
78*4882a593Smuzhiyun 		source_format->width = sink_format->height;
79*4882a593Smuzhiyun 		source_format->height = sink_format->width;
80*4882a593Smuzhiyun 	} else {
81*4882a593Smuzhiyun 		source_format->width = sink_format->width;
82*4882a593Smuzhiyun 		source_format->height = sink_format->height;
83*4882a593Smuzhiyun 	}
84*4882a593Smuzhiyun 
85*4882a593Smuzhiyun 	wpf->flip.rotate = rotate;
86*4882a593Smuzhiyun 
87*4882a593Smuzhiyun 	mutex_unlock(&wpf->entity.lock);
88*4882a593Smuzhiyun 
89*4882a593Smuzhiyun done:
90*4882a593Smuzhiyun 	mutex_unlock(&video->lock);
91*4882a593Smuzhiyun 	return ret;
92*4882a593Smuzhiyun }
93*4882a593Smuzhiyun 
vsp1_wpf_s_ctrl(struct v4l2_ctrl * ctrl)94*4882a593Smuzhiyun static int vsp1_wpf_s_ctrl(struct v4l2_ctrl *ctrl)
95*4882a593Smuzhiyun {
96*4882a593Smuzhiyun 	struct vsp1_rwpf *wpf =
97*4882a593Smuzhiyun 		container_of(ctrl->handler, struct vsp1_rwpf, ctrls);
98*4882a593Smuzhiyun 	unsigned int rotation;
99*4882a593Smuzhiyun 	u32 flip = 0;
100*4882a593Smuzhiyun 	int ret;
101*4882a593Smuzhiyun 
102*4882a593Smuzhiyun 	/* Update the rotation. */
103*4882a593Smuzhiyun 	rotation = wpf->flip.ctrls.rotate ? wpf->flip.ctrls.rotate->val : 0;
104*4882a593Smuzhiyun 	ret = vsp1_wpf_set_rotation(wpf, rotation);
105*4882a593Smuzhiyun 	if (ret < 0)
106*4882a593Smuzhiyun 		return ret;
107*4882a593Smuzhiyun 
108*4882a593Smuzhiyun 	/*
109*4882a593Smuzhiyun 	 * Compute the flip value resulting from all three controls, with
110*4882a593Smuzhiyun 	 * rotation by 180° flipping the image in both directions. Store the
111*4882a593Smuzhiyun 	 * result in the pending flip field for the next frame that will be
112*4882a593Smuzhiyun 	 * processed.
113*4882a593Smuzhiyun 	 */
114*4882a593Smuzhiyun 	if (wpf->flip.ctrls.vflip->val)
115*4882a593Smuzhiyun 		flip |= BIT(WPF_CTRL_VFLIP);
116*4882a593Smuzhiyun 
117*4882a593Smuzhiyun 	if (wpf->flip.ctrls.hflip && wpf->flip.ctrls.hflip->val)
118*4882a593Smuzhiyun 		flip |= BIT(WPF_CTRL_HFLIP);
119*4882a593Smuzhiyun 
120*4882a593Smuzhiyun 	if (rotation == 180 || rotation == 270)
121*4882a593Smuzhiyun 		flip ^= BIT(WPF_CTRL_VFLIP) | BIT(WPF_CTRL_HFLIP);
122*4882a593Smuzhiyun 
123*4882a593Smuzhiyun 	spin_lock_irq(&wpf->flip.lock);
124*4882a593Smuzhiyun 	wpf->flip.pending = flip;
125*4882a593Smuzhiyun 	spin_unlock_irq(&wpf->flip.lock);
126*4882a593Smuzhiyun 
127*4882a593Smuzhiyun 	return 0;
128*4882a593Smuzhiyun }
129*4882a593Smuzhiyun 
130*4882a593Smuzhiyun static const struct v4l2_ctrl_ops vsp1_wpf_ctrl_ops = {
131*4882a593Smuzhiyun 	.s_ctrl = vsp1_wpf_s_ctrl,
132*4882a593Smuzhiyun };
133*4882a593Smuzhiyun 
wpf_init_controls(struct vsp1_rwpf * wpf)134*4882a593Smuzhiyun static int wpf_init_controls(struct vsp1_rwpf *wpf)
135*4882a593Smuzhiyun {
136*4882a593Smuzhiyun 	struct vsp1_device *vsp1 = wpf->entity.vsp1;
137*4882a593Smuzhiyun 	unsigned int num_flip_ctrls;
138*4882a593Smuzhiyun 
139*4882a593Smuzhiyun 	spin_lock_init(&wpf->flip.lock);
140*4882a593Smuzhiyun 
141*4882a593Smuzhiyun 	if (wpf->entity.index != 0) {
142*4882a593Smuzhiyun 		/* Only WPF0 supports flipping. */
143*4882a593Smuzhiyun 		num_flip_ctrls = 0;
144*4882a593Smuzhiyun 	} else if (vsp1_feature(vsp1, VSP1_HAS_WPF_HFLIP)) {
145*4882a593Smuzhiyun 		/*
146*4882a593Smuzhiyun 		 * When horizontal flip is supported the WPF implements three
147*4882a593Smuzhiyun 		 * controls (horizontal flip, vertical flip and rotation).
148*4882a593Smuzhiyun 		 */
149*4882a593Smuzhiyun 		num_flip_ctrls = 3;
150*4882a593Smuzhiyun 	} else if (vsp1_feature(vsp1, VSP1_HAS_WPF_VFLIP)) {
151*4882a593Smuzhiyun 		/*
152*4882a593Smuzhiyun 		 * When only vertical flip is supported the WPF implements a
153*4882a593Smuzhiyun 		 * single control (vertical flip).
154*4882a593Smuzhiyun 		 */
155*4882a593Smuzhiyun 		num_flip_ctrls = 1;
156*4882a593Smuzhiyun 	} else {
157*4882a593Smuzhiyun 		/* Otherwise flipping is not supported. */
158*4882a593Smuzhiyun 		num_flip_ctrls = 0;
159*4882a593Smuzhiyun 	}
160*4882a593Smuzhiyun 
161*4882a593Smuzhiyun 	vsp1_rwpf_init_ctrls(wpf, num_flip_ctrls);
162*4882a593Smuzhiyun 
163*4882a593Smuzhiyun 	if (num_flip_ctrls >= 1) {
164*4882a593Smuzhiyun 		wpf->flip.ctrls.vflip =
165*4882a593Smuzhiyun 			v4l2_ctrl_new_std(&wpf->ctrls, &vsp1_wpf_ctrl_ops,
166*4882a593Smuzhiyun 					  V4L2_CID_VFLIP, 0, 1, 1, 0);
167*4882a593Smuzhiyun 	}
168*4882a593Smuzhiyun 
169*4882a593Smuzhiyun 	if (num_flip_ctrls == 3) {
170*4882a593Smuzhiyun 		wpf->flip.ctrls.hflip =
171*4882a593Smuzhiyun 			v4l2_ctrl_new_std(&wpf->ctrls, &vsp1_wpf_ctrl_ops,
172*4882a593Smuzhiyun 					  V4L2_CID_HFLIP, 0, 1, 1, 0);
173*4882a593Smuzhiyun 		wpf->flip.ctrls.rotate =
174*4882a593Smuzhiyun 			v4l2_ctrl_new_std(&wpf->ctrls, &vsp1_wpf_ctrl_ops,
175*4882a593Smuzhiyun 					  V4L2_CID_ROTATE, 0, 270, 90, 0);
176*4882a593Smuzhiyun 		v4l2_ctrl_cluster(3, &wpf->flip.ctrls.vflip);
177*4882a593Smuzhiyun 	}
178*4882a593Smuzhiyun 
179*4882a593Smuzhiyun 	if (wpf->ctrls.error) {
180*4882a593Smuzhiyun 		dev_err(vsp1->dev, "wpf%u: failed to initialize controls\n",
181*4882a593Smuzhiyun 			wpf->entity.index);
182*4882a593Smuzhiyun 		return wpf->ctrls.error;
183*4882a593Smuzhiyun 	}
184*4882a593Smuzhiyun 
185*4882a593Smuzhiyun 	return 0;
186*4882a593Smuzhiyun }
187*4882a593Smuzhiyun 
188*4882a593Smuzhiyun /* -----------------------------------------------------------------------------
189*4882a593Smuzhiyun  * V4L2 Subdevice Core Operations
190*4882a593Smuzhiyun  */
191*4882a593Smuzhiyun 
wpf_s_stream(struct v4l2_subdev * subdev,int enable)192*4882a593Smuzhiyun static int wpf_s_stream(struct v4l2_subdev *subdev, int enable)
193*4882a593Smuzhiyun {
194*4882a593Smuzhiyun 	struct vsp1_rwpf *wpf = to_rwpf(subdev);
195*4882a593Smuzhiyun 	struct vsp1_device *vsp1 = wpf->entity.vsp1;
196*4882a593Smuzhiyun 
197*4882a593Smuzhiyun 	if (enable)
198*4882a593Smuzhiyun 		return 0;
199*4882a593Smuzhiyun 
200*4882a593Smuzhiyun 	/*
201*4882a593Smuzhiyun 	 * Write to registers directly when stopping the stream as there will be
202*4882a593Smuzhiyun 	 * no pipeline run to apply the display list.
203*4882a593Smuzhiyun 	 */
204*4882a593Smuzhiyun 	vsp1_write(vsp1, VI6_WPF_IRQ_ENB(wpf->entity.index), 0);
205*4882a593Smuzhiyun 	vsp1_write(vsp1, wpf->entity.index * VI6_WPF_OFFSET +
206*4882a593Smuzhiyun 		   VI6_WPF_SRCRPF, 0);
207*4882a593Smuzhiyun 
208*4882a593Smuzhiyun 	return 0;
209*4882a593Smuzhiyun }
210*4882a593Smuzhiyun 
211*4882a593Smuzhiyun /* -----------------------------------------------------------------------------
212*4882a593Smuzhiyun  * V4L2 Subdevice Operations
213*4882a593Smuzhiyun  */
214*4882a593Smuzhiyun 
215*4882a593Smuzhiyun static const struct v4l2_subdev_video_ops wpf_video_ops = {
216*4882a593Smuzhiyun 	.s_stream = wpf_s_stream,
217*4882a593Smuzhiyun };
218*4882a593Smuzhiyun 
219*4882a593Smuzhiyun static const struct v4l2_subdev_ops wpf_ops = {
220*4882a593Smuzhiyun 	.video	= &wpf_video_ops,
221*4882a593Smuzhiyun 	.pad    = &vsp1_rwpf_pad_ops,
222*4882a593Smuzhiyun };
223*4882a593Smuzhiyun 
224*4882a593Smuzhiyun /* -----------------------------------------------------------------------------
225*4882a593Smuzhiyun  * VSP1 Entity Operations
226*4882a593Smuzhiyun  */
227*4882a593Smuzhiyun 
vsp1_wpf_destroy(struct vsp1_entity * entity)228*4882a593Smuzhiyun static void vsp1_wpf_destroy(struct vsp1_entity *entity)
229*4882a593Smuzhiyun {
230*4882a593Smuzhiyun 	struct vsp1_rwpf *wpf = entity_to_rwpf(entity);
231*4882a593Smuzhiyun 
232*4882a593Smuzhiyun 	vsp1_dlm_destroy(wpf->dlm);
233*4882a593Smuzhiyun }
234*4882a593Smuzhiyun 
wpf_configure_writeback_chain(struct vsp1_rwpf * wpf,struct vsp1_dl_list * dl)235*4882a593Smuzhiyun static int wpf_configure_writeback_chain(struct vsp1_rwpf *wpf,
236*4882a593Smuzhiyun 					 struct vsp1_dl_list *dl)
237*4882a593Smuzhiyun {
238*4882a593Smuzhiyun 	unsigned int index = wpf->entity.index;
239*4882a593Smuzhiyun 	struct vsp1_dl_list *dl_next;
240*4882a593Smuzhiyun 	struct vsp1_dl_body *dlb;
241*4882a593Smuzhiyun 
242*4882a593Smuzhiyun 	dl_next = vsp1_dl_list_get(wpf->dlm);
243*4882a593Smuzhiyun 	if (!dl_next) {
244*4882a593Smuzhiyun 		dev_err(wpf->entity.vsp1->dev,
245*4882a593Smuzhiyun 			"Failed to obtain a dl list, disabling writeback\n");
246*4882a593Smuzhiyun 		return -ENOMEM;
247*4882a593Smuzhiyun 	}
248*4882a593Smuzhiyun 
249*4882a593Smuzhiyun 	dlb = vsp1_dl_list_get_body0(dl_next);
250*4882a593Smuzhiyun 	vsp1_dl_body_write(dlb, VI6_WPF_WRBCK_CTRL(index), 0);
251*4882a593Smuzhiyun 	vsp1_dl_list_add_chain(dl, dl_next);
252*4882a593Smuzhiyun 
253*4882a593Smuzhiyun 	return 0;
254*4882a593Smuzhiyun }
255*4882a593Smuzhiyun 
wpf_configure_stream(struct vsp1_entity * entity,struct vsp1_pipeline * pipe,struct vsp1_dl_list * dl,struct vsp1_dl_body * dlb)256*4882a593Smuzhiyun static void wpf_configure_stream(struct vsp1_entity *entity,
257*4882a593Smuzhiyun 				 struct vsp1_pipeline *pipe,
258*4882a593Smuzhiyun 				 struct vsp1_dl_list *dl,
259*4882a593Smuzhiyun 				 struct vsp1_dl_body *dlb)
260*4882a593Smuzhiyun {
261*4882a593Smuzhiyun 	struct vsp1_rwpf *wpf = to_rwpf(&entity->subdev);
262*4882a593Smuzhiyun 	struct vsp1_device *vsp1 = wpf->entity.vsp1;
263*4882a593Smuzhiyun 	const struct v4l2_mbus_framefmt *source_format;
264*4882a593Smuzhiyun 	const struct v4l2_mbus_framefmt *sink_format;
265*4882a593Smuzhiyun 	unsigned int index = wpf->entity.index;
266*4882a593Smuzhiyun 	unsigned int i;
267*4882a593Smuzhiyun 	u32 outfmt = 0;
268*4882a593Smuzhiyun 	u32 srcrpf = 0;
269*4882a593Smuzhiyun 	int ret;
270*4882a593Smuzhiyun 
271*4882a593Smuzhiyun 	sink_format = vsp1_entity_get_pad_format(&wpf->entity,
272*4882a593Smuzhiyun 						 wpf->entity.config,
273*4882a593Smuzhiyun 						 RWPF_PAD_SINK);
274*4882a593Smuzhiyun 	source_format = vsp1_entity_get_pad_format(&wpf->entity,
275*4882a593Smuzhiyun 						   wpf->entity.config,
276*4882a593Smuzhiyun 						   RWPF_PAD_SOURCE);
277*4882a593Smuzhiyun 
278*4882a593Smuzhiyun 	/* Format */
279*4882a593Smuzhiyun 	if (!pipe->lif || wpf->writeback) {
280*4882a593Smuzhiyun 		const struct v4l2_pix_format_mplane *format = &wpf->format;
281*4882a593Smuzhiyun 		const struct vsp1_format_info *fmtinfo = wpf->fmtinfo;
282*4882a593Smuzhiyun 
283*4882a593Smuzhiyun 		outfmt = fmtinfo->hwfmt << VI6_WPF_OUTFMT_WRFMT_SHIFT;
284*4882a593Smuzhiyun 
285*4882a593Smuzhiyun 		if (wpf->flip.rotate)
286*4882a593Smuzhiyun 			outfmt |= VI6_WPF_OUTFMT_ROT;
287*4882a593Smuzhiyun 
288*4882a593Smuzhiyun 		if (fmtinfo->alpha)
289*4882a593Smuzhiyun 			outfmt |= VI6_WPF_OUTFMT_PXA;
290*4882a593Smuzhiyun 		if (fmtinfo->swap_yc)
291*4882a593Smuzhiyun 			outfmt |= VI6_WPF_OUTFMT_SPYCS;
292*4882a593Smuzhiyun 		if (fmtinfo->swap_uv)
293*4882a593Smuzhiyun 			outfmt |= VI6_WPF_OUTFMT_SPUVS;
294*4882a593Smuzhiyun 
295*4882a593Smuzhiyun 		/* Destination stride and byte swapping. */
296*4882a593Smuzhiyun 		vsp1_wpf_write(wpf, dlb, VI6_WPF_DSTM_STRIDE_Y,
297*4882a593Smuzhiyun 			       format->plane_fmt[0].bytesperline);
298*4882a593Smuzhiyun 		if (format->num_planes > 1)
299*4882a593Smuzhiyun 			vsp1_wpf_write(wpf, dlb, VI6_WPF_DSTM_STRIDE_C,
300*4882a593Smuzhiyun 				       format->plane_fmt[1].bytesperline);
301*4882a593Smuzhiyun 
302*4882a593Smuzhiyun 		vsp1_wpf_write(wpf, dlb, VI6_WPF_DSWAP, fmtinfo->swap);
303*4882a593Smuzhiyun 
304*4882a593Smuzhiyun 		if (vsp1_feature(vsp1, VSP1_HAS_WPF_HFLIP) && index == 0)
305*4882a593Smuzhiyun 			vsp1_wpf_write(wpf, dlb, VI6_WPF_ROT_CTRL,
306*4882a593Smuzhiyun 				       VI6_WPF_ROT_CTRL_LN16 |
307*4882a593Smuzhiyun 				       (256 << VI6_WPF_ROT_CTRL_LMEM_WD_SHIFT));
308*4882a593Smuzhiyun 	}
309*4882a593Smuzhiyun 
310*4882a593Smuzhiyun 	if (sink_format->code != source_format->code)
311*4882a593Smuzhiyun 		outfmt |= VI6_WPF_OUTFMT_CSC;
312*4882a593Smuzhiyun 
313*4882a593Smuzhiyun 	wpf->outfmt = outfmt;
314*4882a593Smuzhiyun 
315*4882a593Smuzhiyun 	vsp1_dl_body_write(dlb, VI6_DPR_WPF_FPORCH(index),
316*4882a593Smuzhiyun 			   VI6_DPR_WPF_FPORCH_FP_WPFN);
317*4882a593Smuzhiyun 
318*4882a593Smuzhiyun 	/*
319*4882a593Smuzhiyun 	 * Sources. If the pipeline has a single input and BRx is not used,
320*4882a593Smuzhiyun 	 * configure it as the master layer. Otherwise configure all
321*4882a593Smuzhiyun 	 * inputs as sub-layers and select the virtual RPF as the master
322*4882a593Smuzhiyun 	 * layer.
323*4882a593Smuzhiyun 	 */
324*4882a593Smuzhiyun 	for (i = 0; i < vsp1->info->rpf_count; ++i) {
325*4882a593Smuzhiyun 		struct vsp1_rwpf *input = pipe->inputs[i];
326*4882a593Smuzhiyun 
327*4882a593Smuzhiyun 		if (!input)
328*4882a593Smuzhiyun 			continue;
329*4882a593Smuzhiyun 
330*4882a593Smuzhiyun 		srcrpf |= (!pipe->brx && pipe->num_inputs == 1)
331*4882a593Smuzhiyun 			? VI6_WPF_SRCRPF_RPF_ACT_MST(input->entity.index)
332*4882a593Smuzhiyun 			: VI6_WPF_SRCRPF_RPF_ACT_SUB(input->entity.index);
333*4882a593Smuzhiyun 	}
334*4882a593Smuzhiyun 
335*4882a593Smuzhiyun 	if (pipe->brx)
336*4882a593Smuzhiyun 		srcrpf |= pipe->brx->type == VSP1_ENTITY_BRU
337*4882a593Smuzhiyun 			? VI6_WPF_SRCRPF_VIRACT_MST
338*4882a593Smuzhiyun 			: VI6_WPF_SRCRPF_VIRACT2_MST;
339*4882a593Smuzhiyun 
340*4882a593Smuzhiyun 	vsp1_wpf_write(wpf, dlb, VI6_WPF_SRCRPF, srcrpf);
341*4882a593Smuzhiyun 
342*4882a593Smuzhiyun 	/* Enable interrupts. */
343*4882a593Smuzhiyun 	vsp1_dl_body_write(dlb, VI6_WPF_IRQ_STA(index), 0);
344*4882a593Smuzhiyun 	vsp1_dl_body_write(dlb, VI6_WPF_IRQ_ENB(index),
345*4882a593Smuzhiyun 			   VI6_WFP_IRQ_ENB_DFEE);
346*4882a593Smuzhiyun 
347*4882a593Smuzhiyun 	/*
348*4882a593Smuzhiyun 	 * Configure writeback for display pipelines (the wpf writeback flag is
349*4882a593Smuzhiyun 	 * never set for memory-to-memory pipelines). Start by adding a chained
350*4882a593Smuzhiyun 	 * display list to disable writeback after a single frame, and process
351*4882a593Smuzhiyun 	 * to enable writeback. If the display list allocation fails don't
352*4882a593Smuzhiyun 	 * enable writeback as we wouldn't be able to safely disable it,
353*4882a593Smuzhiyun 	 * resulting in possible memory corruption.
354*4882a593Smuzhiyun 	 */
355*4882a593Smuzhiyun 	if (wpf->writeback) {
356*4882a593Smuzhiyun 		ret = wpf_configure_writeback_chain(wpf, dl);
357*4882a593Smuzhiyun 		if (ret < 0)
358*4882a593Smuzhiyun 			wpf->writeback = false;
359*4882a593Smuzhiyun 	}
360*4882a593Smuzhiyun 
361*4882a593Smuzhiyun 	vsp1_dl_body_write(dlb, VI6_WPF_WRBCK_CTRL(index),
362*4882a593Smuzhiyun 			   wpf->writeback ? VI6_WPF_WRBCK_CTRL_WBMD : 0);
363*4882a593Smuzhiyun }
364*4882a593Smuzhiyun 
wpf_configure_frame(struct vsp1_entity * entity,struct vsp1_pipeline * pipe,struct vsp1_dl_list * dl,struct vsp1_dl_body * dlb)365*4882a593Smuzhiyun static void wpf_configure_frame(struct vsp1_entity *entity,
366*4882a593Smuzhiyun 				struct vsp1_pipeline *pipe,
367*4882a593Smuzhiyun 				struct vsp1_dl_list *dl,
368*4882a593Smuzhiyun 				struct vsp1_dl_body *dlb)
369*4882a593Smuzhiyun {
370*4882a593Smuzhiyun 	const unsigned int mask = BIT(WPF_CTRL_VFLIP)
371*4882a593Smuzhiyun 				| BIT(WPF_CTRL_HFLIP);
372*4882a593Smuzhiyun 	struct vsp1_rwpf *wpf = to_rwpf(&entity->subdev);
373*4882a593Smuzhiyun 	unsigned long flags;
374*4882a593Smuzhiyun 	u32 outfmt;
375*4882a593Smuzhiyun 
376*4882a593Smuzhiyun 	spin_lock_irqsave(&wpf->flip.lock, flags);
377*4882a593Smuzhiyun 	wpf->flip.active = (wpf->flip.active & ~mask)
378*4882a593Smuzhiyun 			 | (wpf->flip.pending & mask);
379*4882a593Smuzhiyun 	spin_unlock_irqrestore(&wpf->flip.lock, flags);
380*4882a593Smuzhiyun 
381*4882a593Smuzhiyun 	outfmt = (wpf->alpha << VI6_WPF_OUTFMT_PDV_SHIFT) | wpf->outfmt;
382*4882a593Smuzhiyun 
383*4882a593Smuzhiyun 	if (wpf->flip.active & BIT(WPF_CTRL_VFLIP))
384*4882a593Smuzhiyun 		outfmt |= VI6_WPF_OUTFMT_FLP;
385*4882a593Smuzhiyun 	if (wpf->flip.active & BIT(WPF_CTRL_HFLIP))
386*4882a593Smuzhiyun 		outfmt |= VI6_WPF_OUTFMT_HFLP;
387*4882a593Smuzhiyun 
388*4882a593Smuzhiyun 	vsp1_wpf_write(wpf, dlb, VI6_WPF_OUTFMT, outfmt);
389*4882a593Smuzhiyun }
390*4882a593Smuzhiyun 
wpf_configure_partition(struct vsp1_entity * entity,struct vsp1_pipeline * pipe,struct vsp1_dl_list * dl,struct vsp1_dl_body * dlb)391*4882a593Smuzhiyun static void wpf_configure_partition(struct vsp1_entity *entity,
392*4882a593Smuzhiyun 				    struct vsp1_pipeline *pipe,
393*4882a593Smuzhiyun 				    struct vsp1_dl_list *dl,
394*4882a593Smuzhiyun 				    struct vsp1_dl_body *dlb)
395*4882a593Smuzhiyun {
396*4882a593Smuzhiyun 	struct vsp1_rwpf *wpf = to_rwpf(&entity->subdev);
397*4882a593Smuzhiyun 	struct vsp1_device *vsp1 = wpf->entity.vsp1;
398*4882a593Smuzhiyun 	struct vsp1_rwpf_memory mem = wpf->mem;
399*4882a593Smuzhiyun 	const struct v4l2_mbus_framefmt *sink_format;
400*4882a593Smuzhiyun 	const struct v4l2_pix_format_mplane *format = &wpf->format;
401*4882a593Smuzhiyun 	const struct vsp1_format_info *fmtinfo = wpf->fmtinfo;
402*4882a593Smuzhiyun 	unsigned int width;
403*4882a593Smuzhiyun 	unsigned int height;
404*4882a593Smuzhiyun 	unsigned int left;
405*4882a593Smuzhiyun 	unsigned int offset;
406*4882a593Smuzhiyun 	unsigned int flip;
407*4882a593Smuzhiyun 	unsigned int i;
408*4882a593Smuzhiyun 
409*4882a593Smuzhiyun 	sink_format = vsp1_entity_get_pad_format(&wpf->entity,
410*4882a593Smuzhiyun 						 wpf->entity.config,
411*4882a593Smuzhiyun 						 RWPF_PAD_SINK);
412*4882a593Smuzhiyun 	width = sink_format->width;
413*4882a593Smuzhiyun 	height = sink_format->height;
414*4882a593Smuzhiyun 	left = 0;
415*4882a593Smuzhiyun 
416*4882a593Smuzhiyun 	/*
417*4882a593Smuzhiyun 	 * Cropping. The partition algorithm can split the image into
418*4882a593Smuzhiyun 	 * multiple slices.
419*4882a593Smuzhiyun 	 */
420*4882a593Smuzhiyun 	if (pipe->partitions > 1) {
421*4882a593Smuzhiyun 		width = pipe->partition->wpf.width;
422*4882a593Smuzhiyun 		left = pipe->partition->wpf.left;
423*4882a593Smuzhiyun 	}
424*4882a593Smuzhiyun 
425*4882a593Smuzhiyun 	vsp1_wpf_write(wpf, dlb, VI6_WPF_HSZCLIP, VI6_WPF_SZCLIP_EN |
426*4882a593Smuzhiyun 		       (0 << VI6_WPF_SZCLIP_OFST_SHIFT) |
427*4882a593Smuzhiyun 		       (width << VI6_WPF_SZCLIP_SIZE_SHIFT));
428*4882a593Smuzhiyun 	vsp1_wpf_write(wpf, dlb, VI6_WPF_VSZCLIP, VI6_WPF_SZCLIP_EN |
429*4882a593Smuzhiyun 		       (0 << VI6_WPF_SZCLIP_OFST_SHIFT) |
430*4882a593Smuzhiyun 		       (height << VI6_WPF_SZCLIP_SIZE_SHIFT));
431*4882a593Smuzhiyun 
432*4882a593Smuzhiyun 	/*
433*4882a593Smuzhiyun 	 * For display pipelines without writeback enabled there's no memory
434*4882a593Smuzhiyun 	 * address to configure, return now.
435*4882a593Smuzhiyun 	 */
436*4882a593Smuzhiyun 	if (pipe->lif && !wpf->writeback)
437*4882a593Smuzhiyun 		return;
438*4882a593Smuzhiyun 
439*4882a593Smuzhiyun 	/*
440*4882a593Smuzhiyun 	 * Update the memory offsets based on flipping configuration.
441*4882a593Smuzhiyun 	 * The destination addresses point to the locations where the
442*4882a593Smuzhiyun 	 * VSP starts writing to memory, which can be any corner of the
443*4882a593Smuzhiyun 	 * image depending on the combination of flipping and rotation.
444*4882a593Smuzhiyun 	 */
445*4882a593Smuzhiyun 
446*4882a593Smuzhiyun 	/*
447*4882a593Smuzhiyun 	 * First take the partition left coordinate into account.
448*4882a593Smuzhiyun 	 * Compute the offset to order the partitions correctly on the
449*4882a593Smuzhiyun 	 * output based on whether flipping is enabled. Consider
450*4882a593Smuzhiyun 	 * horizontal flipping when rotation is disabled but vertical
451*4882a593Smuzhiyun 	 * flipping when rotation is enabled, as rotating the image
452*4882a593Smuzhiyun 	 * switches the horizontal and vertical directions. The offset
453*4882a593Smuzhiyun 	 * is applied horizontally or vertically accordingly.
454*4882a593Smuzhiyun 	 */
455*4882a593Smuzhiyun 	flip = wpf->flip.active;
456*4882a593Smuzhiyun 
457*4882a593Smuzhiyun 	if (flip & BIT(WPF_CTRL_HFLIP) && !wpf->flip.rotate)
458*4882a593Smuzhiyun 		offset = format->width - left - width;
459*4882a593Smuzhiyun 	else if (flip & BIT(WPF_CTRL_VFLIP) && wpf->flip.rotate)
460*4882a593Smuzhiyun 		offset = format->height - left - width;
461*4882a593Smuzhiyun 	else
462*4882a593Smuzhiyun 		offset = left;
463*4882a593Smuzhiyun 
464*4882a593Smuzhiyun 	for (i = 0; i < format->num_planes; ++i) {
465*4882a593Smuzhiyun 		unsigned int hsub = i > 0 ? fmtinfo->hsub : 1;
466*4882a593Smuzhiyun 		unsigned int vsub = i > 0 ? fmtinfo->vsub : 1;
467*4882a593Smuzhiyun 
468*4882a593Smuzhiyun 		if (wpf->flip.rotate)
469*4882a593Smuzhiyun 			mem.addr[i] += offset / vsub
470*4882a593Smuzhiyun 				     * format->plane_fmt[i].bytesperline;
471*4882a593Smuzhiyun 		else
472*4882a593Smuzhiyun 			mem.addr[i] += offset / hsub
473*4882a593Smuzhiyun 				     * fmtinfo->bpp[i] / 8;
474*4882a593Smuzhiyun 	}
475*4882a593Smuzhiyun 
476*4882a593Smuzhiyun 	if (flip & BIT(WPF_CTRL_VFLIP)) {
477*4882a593Smuzhiyun 		/*
478*4882a593Smuzhiyun 		 * When rotating the output (after rotation) image
479*4882a593Smuzhiyun 		 * height is equal to the partition width (before
480*4882a593Smuzhiyun 		 * rotation). Otherwise it is equal to the output
481*4882a593Smuzhiyun 		 * image height.
482*4882a593Smuzhiyun 		 */
483*4882a593Smuzhiyun 		if (wpf->flip.rotate)
484*4882a593Smuzhiyun 			height = width;
485*4882a593Smuzhiyun 		else
486*4882a593Smuzhiyun 			height = format->height;
487*4882a593Smuzhiyun 
488*4882a593Smuzhiyun 		mem.addr[0] += (height - 1)
489*4882a593Smuzhiyun 			     * format->plane_fmt[0].bytesperline;
490*4882a593Smuzhiyun 
491*4882a593Smuzhiyun 		if (format->num_planes > 1) {
492*4882a593Smuzhiyun 			offset = (height / fmtinfo->vsub - 1)
493*4882a593Smuzhiyun 			       * format->plane_fmt[1].bytesperline;
494*4882a593Smuzhiyun 			mem.addr[1] += offset;
495*4882a593Smuzhiyun 			mem.addr[2] += offset;
496*4882a593Smuzhiyun 		}
497*4882a593Smuzhiyun 	}
498*4882a593Smuzhiyun 
499*4882a593Smuzhiyun 	if (wpf->flip.rotate && !(flip & BIT(WPF_CTRL_HFLIP))) {
500*4882a593Smuzhiyun 		unsigned int hoffset = max(0, (int)format->width - 16);
501*4882a593Smuzhiyun 
502*4882a593Smuzhiyun 		/*
503*4882a593Smuzhiyun 		 * Compute the output coordinate. The partition
504*4882a593Smuzhiyun 		 * horizontal (left) offset becomes a vertical offset.
505*4882a593Smuzhiyun 		 */
506*4882a593Smuzhiyun 		for (i = 0; i < format->num_planes; ++i) {
507*4882a593Smuzhiyun 			unsigned int hsub = i > 0 ? fmtinfo->hsub : 1;
508*4882a593Smuzhiyun 
509*4882a593Smuzhiyun 			mem.addr[i] += hoffset / hsub
510*4882a593Smuzhiyun 				     * fmtinfo->bpp[i] / 8;
511*4882a593Smuzhiyun 		}
512*4882a593Smuzhiyun 	}
513*4882a593Smuzhiyun 
514*4882a593Smuzhiyun 	/*
515*4882a593Smuzhiyun 	 * On Gen3 hardware the SPUVS bit has no effect on 3-planar
516*4882a593Smuzhiyun 	 * formats. Swap the U and V planes manually in that case.
517*4882a593Smuzhiyun 	 */
518*4882a593Smuzhiyun 	if (vsp1->info->gen == 3 && format->num_planes == 3 &&
519*4882a593Smuzhiyun 	    fmtinfo->swap_uv)
520*4882a593Smuzhiyun 		swap(mem.addr[1], mem.addr[2]);
521*4882a593Smuzhiyun 
522*4882a593Smuzhiyun 	vsp1_wpf_write(wpf, dlb, VI6_WPF_DSTM_ADDR_Y, mem.addr[0]);
523*4882a593Smuzhiyun 	vsp1_wpf_write(wpf, dlb, VI6_WPF_DSTM_ADDR_C0, mem.addr[1]);
524*4882a593Smuzhiyun 	vsp1_wpf_write(wpf, dlb, VI6_WPF_DSTM_ADDR_C1, mem.addr[2]);
525*4882a593Smuzhiyun 
526*4882a593Smuzhiyun 	/*
527*4882a593Smuzhiyun 	 * Writeback operates in single-shot mode and lasts for a single frame,
528*4882a593Smuzhiyun 	 * reset the writeback flag to false for the next frame.
529*4882a593Smuzhiyun 	 */
530*4882a593Smuzhiyun 	wpf->writeback = false;
531*4882a593Smuzhiyun }
532*4882a593Smuzhiyun 
wpf_max_width(struct vsp1_entity * entity,struct vsp1_pipeline * pipe)533*4882a593Smuzhiyun static unsigned int wpf_max_width(struct vsp1_entity *entity,
534*4882a593Smuzhiyun 				  struct vsp1_pipeline *pipe)
535*4882a593Smuzhiyun {
536*4882a593Smuzhiyun 	struct vsp1_rwpf *wpf = to_rwpf(&entity->subdev);
537*4882a593Smuzhiyun 
538*4882a593Smuzhiyun 	return wpf->flip.rotate ? 256 : wpf->max_width;
539*4882a593Smuzhiyun }
540*4882a593Smuzhiyun 
wpf_partition(struct vsp1_entity * entity,struct vsp1_pipeline * pipe,struct vsp1_partition * partition,unsigned int partition_idx,struct vsp1_partition_window * window)541*4882a593Smuzhiyun static void wpf_partition(struct vsp1_entity *entity,
542*4882a593Smuzhiyun 			  struct vsp1_pipeline *pipe,
543*4882a593Smuzhiyun 			  struct vsp1_partition *partition,
544*4882a593Smuzhiyun 			  unsigned int partition_idx,
545*4882a593Smuzhiyun 			  struct vsp1_partition_window *window)
546*4882a593Smuzhiyun {
547*4882a593Smuzhiyun 	partition->wpf = *window;
548*4882a593Smuzhiyun }
549*4882a593Smuzhiyun 
550*4882a593Smuzhiyun static const struct vsp1_entity_operations wpf_entity_ops = {
551*4882a593Smuzhiyun 	.destroy = vsp1_wpf_destroy,
552*4882a593Smuzhiyun 	.configure_stream = wpf_configure_stream,
553*4882a593Smuzhiyun 	.configure_frame = wpf_configure_frame,
554*4882a593Smuzhiyun 	.configure_partition = wpf_configure_partition,
555*4882a593Smuzhiyun 	.max_width = wpf_max_width,
556*4882a593Smuzhiyun 	.partition = wpf_partition,
557*4882a593Smuzhiyun };
558*4882a593Smuzhiyun 
559*4882a593Smuzhiyun /* -----------------------------------------------------------------------------
560*4882a593Smuzhiyun  * Initialization and Cleanup
561*4882a593Smuzhiyun  */
562*4882a593Smuzhiyun 
vsp1_wpf_create(struct vsp1_device * vsp1,unsigned int index)563*4882a593Smuzhiyun struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index)
564*4882a593Smuzhiyun {
565*4882a593Smuzhiyun 	struct vsp1_rwpf *wpf;
566*4882a593Smuzhiyun 	char name[6];
567*4882a593Smuzhiyun 	int ret;
568*4882a593Smuzhiyun 
569*4882a593Smuzhiyun 	wpf = devm_kzalloc(vsp1->dev, sizeof(*wpf), GFP_KERNEL);
570*4882a593Smuzhiyun 	if (wpf == NULL)
571*4882a593Smuzhiyun 		return ERR_PTR(-ENOMEM);
572*4882a593Smuzhiyun 
573*4882a593Smuzhiyun 	if (vsp1->info->gen == 2) {
574*4882a593Smuzhiyun 		wpf->max_width = WPF_GEN2_MAX_WIDTH;
575*4882a593Smuzhiyun 		wpf->max_height = WPF_GEN2_MAX_HEIGHT;
576*4882a593Smuzhiyun 	} else {
577*4882a593Smuzhiyun 		wpf->max_width = WPF_GEN3_MAX_WIDTH;
578*4882a593Smuzhiyun 		wpf->max_height = WPF_GEN3_MAX_HEIGHT;
579*4882a593Smuzhiyun 	}
580*4882a593Smuzhiyun 
581*4882a593Smuzhiyun 	wpf->entity.ops = &wpf_entity_ops;
582*4882a593Smuzhiyun 	wpf->entity.type = VSP1_ENTITY_WPF;
583*4882a593Smuzhiyun 	wpf->entity.index = index;
584*4882a593Smuzhiyun 
585*4882a593Smuzhiyun 	sprintf(name, "wpf.%u", index);
586*4882a593Smuzhiyun 	ret = vsp1_entity_init(vsp1, &wpf->entity, name, 2, &wpf_ops,
587*4882a593Smuzhiyun 			       MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER);
588*4882a593Smuzhiyun 	if (ret < 0)
589*4882a593Smuzhiyun 		return ERR_PTR(ret);
590*4882a593Smuzhiyun 
591*4882a593Smuzhiyun 	/* Initialize the display list manager. */
592*4882a593Smuzhiyun 	wpf->dlm = vsp1_dlm_create(vsp1, index, 64);
593*4882a593Smuzhiyun 	if (!wpf->dlm) {
594*4882a593Smuzhiyun 		ret = -ENOMEM;
595*4882a593Smuzhiyun 		goto error;
596*4882a593Smuzhiyun 	}
597*4882a593Smuzhiyun 
598*4882a593Smuzhiyun 	/* Initialize the control handler. */
599*4882a593Smuzhiyun 	ret = wpf_init_controls(wpf);
600*4882a593Smuzhiyun 	if (ret < 0) {
601*4882a593Smuzhiyun 		dev_err(vsp1->dev, "wpf%u: failed to initialize controls\n",
602*4882a593Smuzhiyun 			index);
603*4882a593Smuzhiyun 		goto error;
604*4882a593Smuzhiyun 	}
605*4882a593Smuzhiyun 
606*4882a593Smuzhiyun 	v4l2_ctrl_handler_setup(&wpf->ctrls);
607*4882a593Smuzhiyun 
608*4882a593Smuzhiyun 	return wpf;
609*4882a593Smuzhiyun 
610*4882a593Smuzhiyun error:
611*4882a593Smuzhiyun 	vsp1_entity_destroy(&wpf->entity);
612*4882a593Smuzhiyun 	return ERR_PTR(ret);
613*4882a593Smuzhiyun }
614