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