1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * vsp1_rwpf.c  --  R-Car VSP1 Read and Write Pixel Formatters
4  *
5  * Copyright (C) 2013-2014 Renesas Electronics Corporation
6  *
7  * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
8  */
9 
10 #include <media/v4l2-subdev.h>
11 
12 #include "vsp1.h"
13 #include "vsp1_rwpf.h"
14 #include "vsp1_video.h"
15 
16 #define RWPF_MIN_WIDTH				1
17 #define RWPF_MIN_HEIGHT				1
18 
vsp1_rwpf_get_crop(struct vsp1_rwpf * rwpf,struct v4l2_subdev_state * sd_state)19 struct v4l2_rect *vsp1_rwpf_get_crop(struct vsp1_rwpf *rwpf,
20 				     struct v4l2_subdev_state *sd_state)
21 {
22 	return v4l2_subdev_get_try_crop(&rwpf->entity.subdev, sd_state,
23 					RWPF_PAD_SINK);
24 }
25 
26 /* -----------------------------------------------------------------------------
27  * V4L2 Subdevice Pad Operations
28  */
29 
vsp1_rwpf_enum_mbus_code(struct v4l2_subdev * subdev,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_mbus_code_enum * code)30 static int vsp1_rwpf_enum_mbus_code(struct v4l2_subdev *subdev,
31 				    struct v4l2_subdev_state *sd_state,
32 				    struct v4l2_subdev_mbus_code_enum *code)
33 {
34 	static const unsigned int codes[] = {
35 		MEDIA_BUS_FMT_ARGB8888_1X32,
36 		MEDIA_BUS_FMT_AHSV8888_1X32,
37 		MEDIA_BUS_FMT_AYUV8_1X32,
38 	};
39 
40 	if (code->index >= ARRAY_SIZE(codes))
41 		return -EINVAL;
42 
43 	code->code = codes[code->index];
44 
45 	return 0;
46 }
47 
vsp1_rwpf_enum_frame_size(struct v4l2_subdev * subdev,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_frame_size_enum * fse)48 static int vsp1_rwpf_enum_frame_size(struct v4l2_subdev *subdev,
49 				     struct v4l2_subdev_state *sd_state,
50 				     struct v4l2_subdev_frame_size_enum *fse)
51 {
52 	struct vsp1_rwpf *rwpf = to_rwpf(subdev);
53 
54 	return vsp1_subdev_enum_frame_size(subdev, sd_state, fse,
55 					   RWPF_MIN_WIDTH,
56 					   RWPF_MIN_HEIGHT, rwpf->max_width,
57 					   rwpf->max_height);
58 }
59 
vsp1_rwpf_set_format(struct v4l2_subdev * subdev,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_format * fmt)60 static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev,
61 				struct v4l2_subdev_state *sd_state,
62 				struct v4l2_subdev_format *fmt)
63 {
64 	struct vsp1_rwpf *rwpf = to_rwpf(subdev);
65 	struct v4l2_subdev_state *config;
66 	struct v4l2_mbus_framefmt *format;
67 	int ret = 0;
68 
69 	mutex_lock(&rwpf->entity.lock);
70 
71 	config = vsp1_entity_get_pad_config(&rwpf->entity, sd_state,
72 					    fmt->which);
73 	if (!config) {
74 		ret = -EINVAL;
75 		goto done;
76 	}
77 
78 	/* Default to YUV if the requested format is not supported. */
79 	if (fmt->format.code != MEDIA_BUS_FMT_ARGB8888_1X32 &&
80 	    fmt->format.code != MEDIA_BUS_FMT_AHSV8888_1X32 &&
81 	    fmt->format.code != MEDIA_BUS_FMT_AYUV8_1X32)
82 		fmt->format.code = MEDIA_BUS_FMT_AYUV8_1X32;
83 
84 	format = vsp1_entity_get_pad_format(&rwpf->entity, config, fmt->pad);
85 
86 	if (fmt->pad == RWPF_PAD_SOURCE) {
87 		/*
88 		 * The RWPF performs format conversion but can't scale, only the
89 		 * format code can be changed on the source pad.
90 		 */
91 		format->code = fmt->format.code;
92 		fmt->format = *format;
93 		goto done;
94 	}
95 
96 	format->code = fmt->format.code;
97 	format->width = clamp_t(unsigned int, fmt->format.width,
98 				RWPF_MIN_WIDTH, rwpf->max_width);
99 	format->height = clamp_t(unsigned int, fmt->format.height,
100 				 RWPF_MIN_HEIGHT, rwpf->max_height);
101 	format->field = V4L2_FIELD_NONE;
102 	format->colorspace = V4L2_COLORSPACE_SRGB;
103 
104 	fmt->format = *format;
105 
106 	if (rwpf->entity.type == VSP1_ENTITY_RPF) {
107 		struct v4l2_rect *crop;
108 
109 		/* Update the sink crop rectangle. */
110 		crop = vsp1_rwpf_get_crop(rwpf, config);
111 		crop->left = 0;
112 		crop->top = 0;
113 		crop->width = fmt->format.width;
114 		crop->height = fmt->format.height;
115 	}
116 
117 	/* Propagate the format to the source pad. */
118 	format = vsp1_entity_get_pad_format(&rwpf->entity, config,
119 					    RWPF_PAD_SOURCE);
120 	*format = fmt->format;
121 
122 	if (rwpf->flip.rotate) {
123 		format->width = fmt->format.height;
124 		format->height = fmt->format.width;
125 	}
126 
127 done:
128 	mutex_unlock(&rwpf->entity.lock);
129 	return ret;
130 }
131 
vsp1_rwpf_get_selection(struct v4l2_subdev * subdev,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_selection * sel)132 static int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev,
133 				   struct v4l2_subdev_state *sd_state,
134 				   struct v4l2_subdev_selection *sel)
135 {
136 	struct vsp1_rwpf *rwpf = to_rwpf(subdev);
137 	struct v4l2_subdev_state *config;
138 	struct v4l2_mbus_framefmt *format;
139 	int ret = 0;
140 
141 	/*
142 	 * Cropping is only supported on the RPF and is implemented on the sink
143 	 * pad.
144 	 */
145 	if (rwpf->entity.type == VSP1_ENTITY_WPF || sel->pad != RWPF_PAD_SINK)
146 		return -EINVAL;
147 
148 	mutex_lock(&rwpf->entity.lock);
149 
150 	config = vsp1_entity_get_pad_config(&rwpf->entity, sd_state,
151 					    sel->which);
152 	if (!config) {
153 		ret = -EINVAL;
154 		goto done;
155 	}
156 
157 	switch (sel->target) {
158 	case V4L2_SEL_TGT_CROP:
159 		sel->r = *vsp1_rwpf_get_crop(rwpf, config);
160 		break;
161 
162 	case V4L2_SEL_TGT_CROP_BOUNDS:
163 		format = vsp1_entity_get_pad_format(&rwpf->entity, config,
164 						    RWPF_PAD_SINK);
165 		sel->r.left = 0;
166 		sel->r.top = 0;
167 		sel->r.width = format->width;
168 		sel->r.height = format->height;
169 		break;
170 
171 	default:
172 		ret = -EINVAL;
173 		break;
174 	}
175 
176 done:
177 	mutex_unlock(&rwpf->entity.lock);
178 	return ret;
179 }
180 
vsp1_rwpf_set_selection(struct v4l2_subdev * subdev,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_selection * sel)181 static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev,
182 				   struct v4l2_subdev_state *sd_state,
183 				   struct v4l2_subdev_selection *sel)
184 {
185 	struct vsp1_rwpf *rwpf = to_rwpf(subdev);
186 	struct v4l2_subdev_state *config;
187 	struct v4l2_mbus_framefmt *format;
188 	struct v4l2_rect *crop;
189 	int ret = 0;
190 
191 	/*
192 	 * Cropping is only supported on the RPF and is implemented on the sink
193 	 * pad.
194 	 */
195 	if (rwpf->entity.type == VSP1_ENTITY_WPF || sel->pad != RWPF_PAD_SINK)
196 		return -EINVAL;
197 
198 	if (sel->target != V4L2_SEL_TGT_CROP)
199 		return -EINVAL;
200 
201 	mutex_lock(&rwpf->entity.lock);
202 
203 	config = vsp1_entity_get_pad_config(&rwpf->entity, sd_state,
204 					    sel->which);
205 	if (!config) {
206 		ret = -EINVAL;
207 		goto done;
208 	}
209 
210 	/* Make sure the crop rectangle is entirely contained in the image. */
211 	format = vsp1_entity_get_pad_format(&rwpf->entity, config,
212 					    RWPF_PAD_SINK);
213 
214 	/*
215 	 * Restrict the crop rectangle coordinates to multiples of 2 to avoid
216 	 * shifting the color plane.
217 	 */
218 	if (format->code == MEDIA_BUS_FMT_AYUV8_1X32) {
219 		sel->r.left = ALIGN(sel->r.left, 2);
220 		sel->r.top = ALIGN(sel->r.top, 2);
221 		sel->r.width = round_down(sel->r.width, 2);
222 		sel->r.height = round_down(sel->r.height, 2);
223 	}
224 
225 	sel->r.left = min_t(unsigned int, sel->r.left, format->width - 2);
226 	sel->r.top = min_t(unsigned int, sel->r.top, format->height - 2);
227 	sel->r.width = min_t(unsigned int, sel->r.width,
228 			     format->width - sel->r.left);
229 	sel->r.height = min_t(unsigned int, sel->r.height,
230 			      format->height - sel->r.top);
231 
232 	crop = vsp1_rwpf_get_crop(rwpf, config);
233 	*crop = sel->r;
234 
235 	/* Propagate the format to the source pad. */
236 	format = vsp1_entity_get_pad_format(&rwpf->entity, config,
237 					    RWPF_PAD_SOURCE);
238 	format->width = crop->width;
239 	format->height = crop->height;
240 
241 done:
242 	mutex_unlock(&rwpf->entity.lock);
243 	return ret;
244 }
245 
246 const struct v4l2_subdev_pad_ops vsp1_rwpf_pad_ops = {
247 	.init_cfg = vsp1_entity_init_cfg,
248 	.enum_mbus_code = vsp1_rwpf_enum_mbus_code,
249 	.enum_frame_size = vsp1_rwpf_enum_frame_size,
250 	.get_fmt = vsp1_subdev_get_pad_format,
251 	.set_fmt = vsp1_rwpf_set_format,
252 	.get_selection = vsp1_rwpf_get_selection,
253 	.set_selection = vsp1_rwpf_set_selection,
254 };
255 
256 /* -----------------------------------------------------------------------------
257  * Controls
258  */
259 
vsp1_rwpf_s_ctrl(struct v4l2_ctrl * ctrl)260 static int vsp1_rwpf_s_ctrl(struct v4l2_ctrl *ctrl)
261 {
262 	struct vsp1_rwpf *rwpf =
263 		container_of(ctrl->handler, struct vsp1_rwpf, ctrls);
264 
265 	switch (ctrl->id) {
266 	case V4L2_CID_ALPHA_COMPONENT:
267 		rwpf->alpha = ctrl->val;
268 		break;
269 	}
270 
271 	return 0;
272 }
273 
274 static const struct v4l2_ctrl_ops vsp1_rwpf_ctrl_ops = {
275 	.s_ctrl = vsp1_rwpf_s_ctrl,
276 };
277 
vsp1_rwpf_init_ctrls(struct vsp1_rwpf * rwpf,unsigned int ncontrols)278 int vsp1_rwpf_init_ctrls(struct vsp1_rwpf *rwpf, unsigned int ncontrols)
279 {
280 	v4l2_ctrl_handler_init(&rwpf->ctrls, ncontrols + 1);
281 	v4l2_ctrl_new_std(&rwpf->ctrls, &vsp1_rwpf_ctrl_ops,
282 			  V4L2_CID_ALPHA_COMPONENT, 0, 255, 1, 255);
283 
284 	rwpf->entity.subdev.ctrl_handler = &rwpf->ctrls;
285 
286 	return rwpf->ctrls.error;
287 }
288