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_entity.h"
14 #include "vsp1_rwpf.h"
15 #include "vsp1_video.h"
16
17 #define RWPF_MIN_WIDTH 1
18 #define RWPF_MIN_HEIGHT 1
19
20 /* -----------------------------------------------------------------------------
21 * V4L2 Subdevice Operations
22 */
23
vsp1_rwpf_enum_mbus_code(struct v4l2_subdev * subdev,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_mbus_code_enum * code)24 static int vsp1_rwpf_enum_mbus_code(struct v4l2_subdev *subdev,
25 struct v4l2_subdev_state *sd_state,
26 struct v4l2_subdev_mbus_code_enum *code)
27 {
28 static const unsigned int codes[] = {
29 MEDIA_BUS_FMT_ARGB8888_1X32,
30 MEDIA_BUS_FMT_AHSV8888_1X32,
31 MEDIA_BUS_FMT_AYUV8_1X32,
32 };
33
34 if (code->index >= ARRAY_SIZE(codes))
35 return -EINVAL;
36
37 code->code = codes[code->index];
38
39 if (code->pad == RWPF_PAD_SOURCE &&
40 code->code == MEDIA_BUS_FMT_AYUV8_1X32)
41 code->flags = V4L2_SUBDEV_MBUS_CODE_CSC_YCBCR_ENC
42 | V4L2_SUBDEV_MBUS_CODE_CSC_QUANTIZATION;
43
44 return 0;
45 }
46
vsp1_rwpf_enum_frame_size(struct v4l2_subdev * subdev,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_frame_size_enum * fse)47 static int vsp1_rwpf_enum_frame_size(struct v4l2_subdev *subdev,
48 struct v4l2_subdev_state *sd_state,
49 struct v4l2_subdev_frame_size_enum *fse)
50 {
51 struct vsp1_rwpf *rwpf = to_rwpf(subdev);
52
53 return vsp1_subdev_enum_frame_size(subdev, sd_state, fse,
54 RWPF_MIN_WIDTH,
55 RWPF_MIN_HEIGHT, rwpf->max_width,
56 rwpf->max_height);
57 }
58
vsp1_rwpf_set_format(struct v4l2_subdev * subdev,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_format * fmt)59 static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev,
60 struct v4l2_subdev_state *sd_state,
61 struct v4l2_subdev_format *fmt)
62 {
63 struct vsp1_rwpf *rwpf = to_rwpf(subdev);
64 struct v4l2_subdev_state *state;
65 struct v4l2_mbus_framefmt *format;
66 int ret = 0;
67
68 mutex_lock(&rwpf->entity.lock);
69
70 state = vsp1_entity_get_state(&rwpf->entity, sd_state, fmt->which);
71 if (!state) {
72 ret = -EINVAL;
73 goto done;
74 }
75
76 /* Default to YUV if the requested format is not supported. */
77 if (fmt->format.code != MEDIA_BUS_FMT_ARGB8888_1X32 &&
78 fmt->format.code != MEDIA_BUS_FMT_AHSV8888_1X32 &&
79 fmt->format.code != MEDIA_BUS_FMT_AYUV8_1X32)
80 fmt->format.code = MEDIA_BUS_FMT_AYUV8_1X32;
81
82 format = v4l2_subdev_state_get_format(state, fmt->pad);
83
84 if (fmt->pad == RWPF_PAD_SOURCE) {
85 const struct v4l2_mbus_framefmt *sink_format =
86 v4l2_subdev_state_get_format(state, RWPF_PAD_SINK);
87 u16 flags = fmt->format.flags & V4L2_MBUS_FRAMEFMT_SET_CSC;
88 bool csc;
89
90 /*
91 * The RWPF performs format conversion but can't scale, only the
92 * format code, encoding and quantization can be changed on the
93 * source pad when converting between RGB and YUV.
94 */
95 if (sink_format->code != MEDIA_BUS_FMT_AHSV8888_1X32 &&
96 fmt->format.code != MEDIA_BUS_FMT_AHSV8888_1X32)
97 format->code = fmt->format.code;
98 else
99 format->code = sink_format->code;
100
101 /*
102 * Encoding and quantization can only be configured when YCbCr
103 * <-> RGB is enabled. The V4L2 API requires userspace to set
104 * the V4L2_MBUS_FRAMEFMT_SET_CSC flag. If either of these
105 * conditions is not met, use the encoding and quantization
106 * values from the sink pad.
107 */
108 csc = (format->code == MEDIA_BUS_FMT_AYUV8_1X32) !=
109 (sink_format->code == MEDIA_BUS_FMT_AYUV8_1X32);
110
111 if (csc && (flags & V4L2_MBUS_FRAMEFMT_SET_CSC)) {
112 format->ycbcr_enc = fmt->format.ycbcr_enc;
113 format->quantization = fmt->format.quantization;
114 } else {
115 format->ycbcr_enc = sink_format->ycbcr_enc;
116 format->quantization = sink_format->quantization;
117 }
118
119 vsp1_entity_adjust_color_space(format);
120
121 fmt->format = *format;
122 fmt->format.flags = flags;
123
124 goto done;
125 }
126
127 format->code = fmt->format.code;
128 format->width = clamp_t(unsigned int, fmt->format.width,
129 RWPF_MIN_WIDTH, rwpf->max_width);
130 format->height = clamp_t(unsigned int, fmt->format.height,
131 RWPF_MIN_HEIGHT, rwpf->max_height);
132 format->field = V4L2_FIELD_NONE;
133
134 format->colorspace = fmt->format.colorspace;
135 format->xfer_func = fmt->format.xfer_func;
136 format->ycbcr_enc = fmt->format.ycbcr_enc;
137 format->quantization = fmt->format.quantization;
138
139 vsp1_entity_adjust_color_space(format);
140
141 fmt->format = *format;
142
143 if (rwpf->entity.type == VSP1_ENTITY_RPF) {
144 struct v4l2_rect *crop;
145
146 /* Update the sink crop rectangle. */
147 crop = v4l2_subdev_state_get_crop(state, RWPF_PAD_SINK);
148 crop->left = 0;
149 crop->top = 0;
150 crop->width = fmt->format.width;
151 crop->height = fmt->format.height;
152 }
153
154 /* Propagate the format to the source pad. */
155 format = v4l2_subdev_state_get_format(state, RWPF_PAD_SOURCE);
156 *format = fmt->format;
157
158 if (rwpf->flip.rotate) {
159 format->width = fmt->format.height;
160 format->height = fmt->format.width;
161 }
162
163 done:
164 mutex_unlock(&rwpf->entity.lock);
165 return ret;
166 }
167
vsp1_rwpf_get_selection(struct v4l2_subdev * subdev,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_selection * sel)168 static int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev,
169 struct v4l2_subdev_state *sd_state,
170 struct v4l2_subdev_selection *sel)
171 {
172 struct vsp1_rwpf *rwpf = to_rwpf(subdev);
173 struct v4l2_subdev_state *state;
174 struct v4l2_mbus_framefmt *format;
175 int ret = 0;
176
177 /*
178 * Cropping is only supported on the RPF and is implemented on the sink
179 * pad.
180 */
181 if (rwpf->entity.type == VSP1_ENTITY_WPF || sel->pad != RWPF_PAD_SINK)
182 return -EINVAL;
183
184 mutex_lock(&rwpf->entity.lock);
185
186 state = vsp1_entity_get_state(&rwpf->entity, sd_state, sel->which);
187 if (!state) {
188 ret = -EINVAL;
189 goto done;
190 }
191
192 switch (sel->target) {
193 case V4L2_SEL_TGT_CROP:
194 sel->r = *v4l2_subdev_state_get_crop(state, RWPF_PAD_SINK);
195 break;
196
197 case V4L2_SEL_TGT_CROP_BOUNDS:
198 format = v4l2_subdev_state_get_format(state, RWPF_PAD_SINK);
199 sel->r.left = 0;
200 sel->r.top = 0;
201 sel->r.width = format->width;
202 sel->r.height = format->height;
203 break;
204
205 default:
206 ret = -EINVAL;
207 break;
208 }
209
210 done:
211 mutex_unlock(&rwpf->entity.lock);
212 return ret;
213 }
214
vsp1_rwpf_set_selection(struct v4l2_subdev * subdev,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_selection * sel)215 static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev,
216 struct v4l2_subdev_state *sd_state,
217 struct v4l2_subdev_selection *sel)
218 {
219 struct vsp1_rwpf *rwpf = to_rwpf(subdev);
220 struct v4l2_subdev_state *state;
221 struct v4l2_mbus_framefmt *format;
222 struct v4l2_rect *crop;
223 int ret = 0;
224
225 /*
226 * Cropping is only supported on the RPF and is implemented on the sink
227 * pad.
228 */
229 if (rwpf->entity.type == VSP1_ENTITY_WPF || sel->pad != RWPF_PAD_SINK)
230 return -EINVAL;
231
232 if (sel->target != V4L2_SEL_TGT_CROP)
233 return -EINVAL;
234
235 mutex_lock(&rwpf->entity.lock);
236
237 state = vsp1_entity_get_state(&rwpf->entity, sd_state, sel->which);
238 if (!state) {
239 ret = -EINVAL;
240 goto done;
241 }
242
243 /* Make sure the crop rectangle is entirely contained in the image. */
244 format = v4l2_subdev_state_get_format(state, RWPF_PAD_SINK);
245
246 /*
247 * Restrict the crop rectangle coordinates to multiples of 2 to avoid
248 * shifting the color plane.
249 */
250 if (format->code == MEDIA_BUS_FMT_AYUV8_1X32) {
251 sel->r.left = ALIGN(sel->r.left, 2);
252 sel->r.top = ALIGN(sel->r.top, 2);
253 sel->r.width = round_down(sel->r.width, 2);
254 sel->r.height = round_down(sel->r.height, 2);
255 }
256
257 sel->r.left = min_t(unsigned int, sel->r.left, format->width - 2);
258 sel->r.top = min_t(unsigned int, sel->r.top, format->height - 2);
259 sel->r.width = min_t(unsigned int, sel->r.width,
260 format->width - sel->r.left);
261 sel->r.height = min_t(unsigned int, sel->r.height,
262 format->height - sel->r.top);
263
264 crop = v4l2_subdev_state_get_crop(state, RWPF_PAD_SINK);
265 *crop = sel->r;
266
267 /* Propagate the format to the source pad. */
268 format = v4l2_subdev_state_get_format(state, RWPF_PAD_SOURCE);
269 format->width = crop->width;
270 format->height = crop->height;
271
272 done:
273 mutex_unlock(&rwpf->entity.lock);
274 return ret;
275 }
276
277 static const struct v4l2_subdev_pad_ops vsp1_rwpf_pad_ops = {
278 .enum_mbus_code = vsp1_rwpf_enum_mbus_code,
279 .enum_frame_size = vsp1_rwpf_enum_frame_size,
280 .get_fmt = vsp1_subdev_get_pad_format,
281 .set_fmt = vsp1_rwpf_set_format,
282 .get_selection = vsp1_rwpf_get_selection,
283 .set_selection = vsp1_rwpf_set_selection,
284 };
285
286 const struct v4l2_subdev_ops vsp1_rwpf_subdev_ops = {
287 .pad = &vsp1_rwpf_pad_ops,
288 };
289
290 /* -----------------------------------------------------------------------------
291 * Controls
292 */
293
vsp1_rwpf_s_ctrl(struct v4l2_ctrl * ctrl)294 static int vsp1_rwpf_s_ctrl(struct v4l2_ctrl *ctrl)
295 {
296 struct vsp1_rwpf *rwpf =
297 container_of(ctrl->handler, struct vsp1_rwpf, ctrls);
298
299 switch (ctrl->id) {
300 case V4L2_CID_ALPHA_COMPONENT:
301 rwpf->alpha = ctrl->val;
302 break;
303 }
304
305 return 0;
306 }
307
308 static const struct v4l2_ctrl_ops vsp1_rwpf_ctrl_ops = {
309 .s_ctrl = vsp1_rwpf_s_ctrl,
310 };
311
vsp1_rwpf_init_ctrls(struct vsp1_rwpf * rwpf,unsigned int ncontrols)312 int vsp1_rwpf_init_ctrls(struct vsp1_rwpf *rwpf, unsigned int ncontrols)
313 {
314 v4l2_ctrl_handler_init(&rwpf->ctrls, ncontrols + 1);
315 v4l2_ctrl_new_std(&rwpf->ctrls, &vsp1_rwpf_ctrl_ops,
316 V4L2_CID_ALPHA_COMPONENT, 0, 255, 1, 255);
317
318 rwpf->entity.subdev.ctrl_handler = &rwpf->ctrls;
319
320 return rwpf->ctrls.error;
321 }
322