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