1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * vimc-scaler.c Virtual Media Controller Driver
4 *
5 * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com>
6 */
7
8 #include <linux/moduleparam.h>
9 #include <linux/string.h>
10 #include <linux/vmalloc.h>
11 #include <linux/v4l2-mediabus.h>
12 #include <media/v4l2-rect.h>
13 #include <media/v4l2-subdev.h>
14
15 #include "vimc-common.h"
16
17 /* Pad identifier */
18 enum vimc_scaler_pad {
19 VIMC_SCALER_SINK = 0,
20 VIMC_SCALER_SRC = 1,
21 };
22
23 #define VIMC_SCALER_FMT_WIDTH_DEFAULT 640
24 #define VIMC_SCALER_FMT_HEIGHT_DEFAULT 480
25
26 struct vimc_scaler_device {
27 struct vimc_ent_device ved;
28 struct v4l2_subdev sd;
29 struct media_pad pads[2];
30
31 u8 *src_frame;
32
33 /*
34 * Virtual "hardware" configuration, filled when the stream starts or
35 * when controls are set.
36 */
37 struct {
38 struct v4l2_mbus_framefmt sink_fmt;
39 struct v4l2_mbus_framefmt src_fmt;
40 struct v4l2_rect sink_crop;
41 unsigned int bpp;
42 } hw;
43 };
44
45 static const struct v4l2_mbus_framefmt fmt_default = {
46 .width = VIMC_SCALER_FMT_WIDTH_DEFAULT,
47 .height = VIMC_SCALER_FMT_HEIGHT_DEFAULT,
48 .code = MEDIA_BUS_FMT_RGB888_1X24,
49 .field = V4L2_FIELD_NONE,
50 .colorspace = V4L2_COLORSPACE_SRGB,
51 };
52
53 static const struct v4l2_rect crop_rect_default = {
54 .width = VIMC_SCALER_FMT_WIDTH_DEFAULT,
55 .height = VIMC_SCALER_FMT_HEIGHT_DEFAULT,
56 .top = 0,
57 .left = 0,
58 };
59
60 static const struct v4l2_rect crop_rect_min = {
61 .width = VIMC_FRAME_MIN_WIDTH,
62 .height = VIMC_FRAME_MIN_HEIGHT,
63 .top = 0,
64 .left = 0,
65 };
66
67 static struct v4l2_rect
vimc_scaler_get_crop_bound_sink(const struct v4l2_mbus_framefmt * sink_fmt)68 vimc_scaler_get_crop_bound_sink(const struct v4l2_mbus_framefmt *sink_fmt)
69 {
70 /* Get the crop bounds to clamp the crop rectangle correctly */
71 struct v4l2_rect r = {
72 .left = 0,
73 .top = 0,
74 .width = sink_fmt->width,
75 .height = sink_fmt->height,
76 };
77 return r;
78 }
79
vimc_scaler_init_state(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state)80 static int vimc_scaler_init_state(struct v4l2_subdev *sd,
81 struct v4l2_subdev_state *sd_state)
82 {
83 struct v4l2_mbus_framefmt *mf;
84 struct v4l2_rect *r;
85 unsigned int i;
86
87 for (i = 0; i < sd->entity.num_pads; i++) {
88 mf = v4l2_subdev_state_get_format(sd_state, i);
89 *mf = fmt_default;
90 }
91
92 r = v4l2_subdev_state_get_crop(sd_state, VIMC_SCALER_SINK);
93 *r = crop_rect_default;
94
95 return 0;
96 }
97
vimc_scaler_enum_mbus_code(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_mbus_code_enum * code)98 static int vimc_scaler_enum_mbus_code(struct v4l2_subdev *sd,
99 struct v4l2_subdev_state *sd_state,
100 struct v4l2_subdev_mbus_code_enum *code)
101 {
102 u32 mbus_code = vimc_mbus_code_by_index(code->index);
103 const struct vimc_pix_map *vpix;
104
105 if (!mbus_code)
106 return -EINVAL;
107
108 vpix = vimc_pix_map_by_code(mbus_code);
109
110 /* We don't support bayer format */
111 if (!vpix || vpix->bayer)
112 return -EINVAL;
113
114 code->code = mbus_code;
115
116 return 0;
117 }
118
vimc_scaler_enum_frame_size(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_frame_size_enum * fse)119 static int vimc_scaler_enum_frame_size(struct v4l2_subdev *sd,
120 struct v4l2_subdev_state *sd_state,
121 struct v4l2_subdev_frame_size_enum *fse)
122 {
123 const struct vimc_pix_map *vpix;
124
125 if (fse->index)
126 return -EINVAL;
127
128 /* Only accept code in the pix map table in non bayer format */
129 vpix = vimc_pix_map_by_code(fse->code);
130 if (!vpix || vpix->bayer)
131 return -EINVAL;
132
133 fse->min_width = VIMC_FRAME_MIN_WIDTH;
134 fse->min_height = VIMC_FRAME_MIN_HEIGHT;
135
136 fse->max_width = VIMC_FRAME_MAX_WIDTH;
137 fse->max_height = VIMC_FRAME_MAX_HEIGHT;
138
139 return 0;
140 }
141
vimc_scaler_set_fmt(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_format * format)142 static int vimc_scaler_set_fmt(struct v4l2_subdev *sd,
143 struct v4l2_subdev_state *sd_state,
144 struct v4l2_subdev_format *format)
145 {
146 struct vimc_scaler_device *vscaler = v4l2_get_subdevdata(sd);
147 struct v4l2_mbus_framefmt *fmt;
148
149 /* Do not change the active format while stream is on */
150 if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE && vscaler->src_frame)
151 return -EBUSY;
152
153 fmt = v4l2_subdev_state_get_format(sd_state, format->pad);
154
155 /*
156 * The media bus code and colorspace can only be changed on the sink
157 * pad, the source pad only follows.
158 */
159 if (format->pad == VIMC_SCALER_SINK) {
160 const struct vimc_pix_map *vpix;
161
162 /* Only accept code in the pix map table in non bayer format. */
163 vpix = vimc_pix_map_by_code(format->format.code);
164 if (vpix && !vpix->bayer)
165 fmt->code = format->format.code;
166 else
167 fmt->code = fmt_default.code;
168
169 /* Clamp the colorspace to valid values. */
170 fmt->colorspace = format->format.colorspace;
171 fmt->ycbcr_enc = format->format.ycbcr_enc;
172 fmt->quantization = format->format.quantization;
173 fmt->xfer_func = format->format.xfer_func;
174 vimc_colorimetry_clamp(fmt);
175 }
176
177 /* Clamp and align the width and height */
178 fmt->width = clamp_t(u32, format->format.width, VIMC_FRAME_MIN_WIDTH,
179 VIMC_FRAME_MAX_WIDTH) & ~1;
180 fmt->height = clamp_t(u32, format->format.height, VIMC_FRAME_MIN_HEIGHT,
181 VIMC_FRAME_MAX_HEIGHT) & ~1;
182
183 /*
184 * Propagate the sink pad format to the crop rectangle and the source
185 * pad.
186 */
187 if (format->pad == VIMC_SCALER_SINK) {
188 struct v4l2_mbus_framefmt *src_fmt;
189 struct v4l2_rect *crop;
190
191 crop = v4l2_subdev_state_get_crop(sd_state, VIMC_SCALER_SINK);
192 crop->width = fmt->width;
193 crop->height = fmt->height;
194 crop->top = 0;
195 crop->left = 0;
196
197 src_fmt = v4l2_subdev_state_get_format(sd_state, VIMC_SCALER_SRC);
198 *src_fmt = *fmt;
199 }
200
201 format->format = *fmt;
202
203 return 0;
204 }
205
vimc_scaler_get_selection(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_selection * sel)206 static int vimc_scaler_get_selection(struct v4l2_subdev *sd,
207 struct v4l2_subdev_state *sd_state,
208 struct v4l2_subdev_selection *sel)
209 {
210 struct v4l2_mbus_framefmt *sink_fmt;
211
212 if (VIMC_IS_SRC(sel->pad))
213 return -EINVAL;
214
215 switch (sel->target) {
216 case V4L2_SEL_TGT_CROP:
217 sel->r = *v4l2_subdev_state_get_crop(sd_state, VIMC_SCALER_SINK);
218 break;
219 case V4L2_SEL_TGT_CROP_BOUNDS:
220 sink_fmt = v4l2_subdev_state_get_format(sd_state, VIMC_SCALER_SINK);
221 sel->r = vimc_scaler_get_crop_bound_sink(sink_fmt);
222 break;
223 default:
224 return -EINVAL;
225 }
226
227 return 0;
228 }
229
vimc_scaler_adjust_sink_crop(struct v4l2_rect * r,const struct v4l2_mbus_framefmt * sink_fmt)230 static void vimc_scaler_adjust_sink_crop(struct v4l2_rect *r,
231 const struct v4l2_mbus_framefmt *sink_fmt)
232 {
233 const struct v4l2_rect sink_rect =
234 vimc_scaler_get_crop_bound_sink(sink_fmt);
235
236 /* Disallow rectangles smaller than the minimal one. */
237 v4l2_rect_set_min_size(r, &crop_rect_min);
238 v4l2_rect_map_inside(r, &sink_rect);
239 }
240
vimc_scaler_set_selection(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_selection * sel)241 static int vimc_scaler_set_selection(struct v4l2_subdev *sd,
242 struct v4l2_subdev_state *sd_state,
243 struct v4l2_subdev_selection *sel)
244 {
245 struct vimc_scaler_device *vscaler = v4l2_get_subdevdata(sd);
246 struct v4l2_mbus_framefmt *sink_fmt;
247 struct v4l2_rect *crop_rect;
248
249 /* Only support setting the crop of the sink pad */
250 if (VIMC_IS_SRC(sel->pad) || sel->target != V4L2_SEL_TGT_CROP)
251 return -EINVAL;
252
253 if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE && vscaler->src_frame)
254 return -EBUSY;
255
256 crop_rect = v4l2_subdev_state_get_crop(sd_state, VIMC_SCALER_SINK);
257 sink_fmt = v4l2_subdev_state_get_format(sd_state, VIMC_SCALER_SINK);
258 vimc_scaler_adjust_sink_crop(&sel->r, sink_fmt);
259 *crop_rect = sel->r;
260
261 return 0;
262 }
263
264 static const struct v4l2_subdev_pad_ops vimc_scaler_pad_ops = {
265 .enum_mbus_code = vimc_scaler_enum_mbus_code,
266 .enum_frame_size = vimc_scaler_enum_frame_size,
267 .get_fmt = v4l2_subdev_get_fmt,
268 .set_fmt = vimc_scaler_set_fmt,
269 .get_selection = vimc_scaler_get_selection,
270 .set_selection = vimc_scaler_set_selection,
271 };
272
vimc_scaler_s_stream(struct v4l2_subdev * sd,int enable)273 static int vimc_scaler_s_stream(struct v4l2_subdev *sd, int enable)
274 {
275 struct vimc_scaler_device *vscaler = v4l2_get_subdevdata(sd);
276
277 if (enable) {
278 struct v4l2_subdev_state *state;
279 const struct v4l2_mbus_framefmt *format;
280 const struct v4l2_rect *rect;
281 unsigned int frame_size;
282
283 if (vscaler->src_frame)
284 return 0;
285
286 state = v4l2_subdev_lock_and_get_active_state(sd);
287
288 /* Save the bytes per pixel of the sink. */
289 format = v4l2_subdev_state_get_format(state, VIMC_SCALER_SINK);
290 vscaler->hw.sink_fmt = *format;
291 vscaler->hw.bpp = vimc_pix_map_by_code(format->code)->bpp;
292
293 /* Calculate the frame size of the source pad. */
294 format = v4l2_subdev_state_get_format(state, VIMC_SCALER_SRC);
295 vscaler->hw.src_fmt = *format;
296 frame_size = format->width * format->height * vscaler->hw.bpp;
297
298 rect = v4l2_subdev_state_get_crop(state, VIMC_SCALER_SINK);
299 vscaler->hw.sink_crop = *rect;
300
301 v4l2_subdev_unlock_state(state);
302
303 /*
304 * Allocate the frame buffer. Use vmalloc to be able to allocate
305 * a large amount of memory.
306 */
307 vscaler->src_frame = vmalloc(frame_size);
308 if (!vscaler->src_frame)
309 return -ENOMEM;
310 } else {
311 if (!vscaler->src_frame)
312 return 0;
313
314 vfree(vscaler->src_frame);
315 vscaler->src_frame = NULL;
316 }
317
318 return 0;
319 }
320
321 static const struct v4l2_subdev_video_ops vimc_scaler_video_ops = {
322 .s_stream = vimc_scaler_s_stream,
323 };
324
325 static const struct v4l2_subdev_ops vimc_scaler_ops = {
326 .pad = &vimc_scaler_pad_ops,
327 .video = &vimc_scaler_video_ops,
328 };
329
330 static const struct v4l2_subdev_internal_ops vimc_scaler_internal_ops = {
331 .init_state = vimc_scaler_init_state,
332 };
333
vimc_scaler_fill_src_frame(const struct vimc_scaler_device * const vscaler,const u8 * const sink_frame)334 static void vimc_scaler_fill_src_frame(const struct vimc_scaler_device *const vscaler,
335 const u8 *const sink_frame)
336 {
337 const struct v4l2_mbus_framefmt *sink_fmt = &vscaler->hw.sink_fmt;
338 const struct v4l2_mbus_framefmt *src_fmt = &vscaler->hw.src_fmt;
339 const struct v4l2_rect *r = &vscaler->hw.sink_crop;
340 unsigned int src_x, src_y;
341 u8 *walker = vscaler->src_frame;
342
343 /* Set each pixel at the src_frame to its sink_frame equivalent */
344 for (src_y = 0; src_y < src_fmt->height; src_y++) {
345 unsigned int snk_y, y_offset;
346
347 snk_y = (src_y * r->height) / src_fmt->height + r->top;
348 y_offset = snk_y * sink_fmt->width * vscaler->hw.bpp;
349
350 for (src_x = 0; src_x < src_fmt->width; src_x++) {
351 unsigned int snk_x, x_offset, index;
352
353 snk_x = (src_x * r->width) / src_fmt->width + r->left;
354 x_offset = snk_x * vscaler->hw.bpp;
355 index = y_offset + x_offset;
356 memcpy(walker, &sink_frame[index], vscaler->hw.bpp);
357 walker += vscaler->hw.bpp;
358 }
359 }
360 }
361
vimc_scaler_process_frame(struct vimc_ent_device * ved,const void * sink_frame)362 static void *vimc_scaler_process_frame(struct vimc_ent_device *ved,
363 const void *sink_frame)
364 {
365 struct vimc_scaler_device *vscaler = container_of(ved, struct vimc_scaler_device,
366 ved);
367
368 /* If the stream in this node is not active, just return */
369 if (!vscaler->src_frame)
370 return ERR_PTR(-EINVAL);
371
372 vimc_scaler_fill_src_frame(vscaler, sink_frame);
373
374 return vscaler->src_frame;
375 };
376
vimc_scaler_release(struct vimc_ent_device * ved)377 static void vimc_scaler_release(struct vimc_ent_device *ved)
378 {
379 struct vimc_scaler_device *vscaler =
380 container_of(ved, struct vimc_scaler_device, ved);
381
382 v4l2_subdev_cleanup(&vscaler->sd);
383 media_entity_cleanup(vscaler->ved.ent);
384 kfree(vscaler);
385 }
386
vimc_scaler_add(struct vimc_device * vimc,const char * vcfg_name)387 static struct vimc_ent_device *vimc_scaler_add(struct vimc_device *vimc,
388 const char *vcfg_name)
389 {
390 struct v4l2_device *v4l2_dev = &vimc->v4l2_dev;
391 struct vimc_scaler_device *vscaler;
392 int ret;
393
394 /* Allocate the vscaler struct */
395 vscaler = kzalloc(sizeof(*vscaler), GFP_KERNEL);
396 if (!vscaler)
397 return ERR_PTR(-ENOMEM);
398
399 /* Initialize ved and sd */
400 vscaler->pads[VIMC_SCALER_SINK].flags = MEDIA_PAD_FL_SINK;
401 vscaler->pads[VIMC_SCALER_SRC].flags = MEDIA_PAD_FL_SOURCE;
402
403 ret = vimc_ent_sd_register(&vscaler->ved, &vscaler->sd, v4l2_dev,
404 vcfg_name,
405 MEDIA_ENT_F_PROC_VIDEO_SCALER, 2,
406 vscaler->pads, &vimc_scaler_internal_ops,
407 &vimc_scaler_ops);
408 if (ret) {
409 kfree(vscaler);
410 return ERR_PTR(ret);
411 }
412
413 vscaler->ved.process_frame = vimc_scaler_process_frame;
414 vscaler->ved.dev = vimc->mdev.dev;
415
416 return &vscaler->ved;
417 }
418
419 const struct vimc_ent_type vimc_scaler_type = {
420 .add = vimc_scaler_add,
421 .release = vimc_scaler_release
422 };
423