]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blame - drivers/media/pci/intel/ipu-isys-subdev.c
UBUNTU: SAUCE: IPU6 driver release for kernel 5.14 on 2021-11-01
[mirror_ubuntu-jammy-kernel.git] / drivers / media / pci / intel / ipu-isys-subdev.c
CommitLineData
f2efa4ee
WY
1// SPDX-License-Identifier: GPL-2.0
2// Copyright (C) 2013 - 2020 Intel Corporation
3
4#include <linux/types.h>
5#include <linux/videodev2.h>
6
7#include <media/media-entity.h>
8
9#include <uapi/linux/media-bus-format.h>
10
11#include "ipu-isys.h"
12#include "ipu-isys-video.h"
13#include "ipu-isys-subdev.h"
14
15unsigned int ipu_isys_mbus_code_to_bpp(u32 code)
16{
17 switch (code) {
18 case MEDIA_BUS_FMT_RGB888_1X24:
19 return 24;
20 case MEDIA_BUS_FMT_YUYV10_1X20:
21 return 20;
22 case MEDIA_BUS_FMT_Y10_1X10:
23 case MEDIA_BUS_FMT_RGB565_1X16:
24 case MEDIA_BUS_FMT_UYVY8_1X16:
25 case MEDIA_BUS_FMT_YUYV8_1X16:
26 return 16;
27 case MEDIA_BUS_FMT_SBGGR12_1X12:
28 case MEDIA_BUS_FMT_SGBRG12_1X12:
29 case MEDIA_BUS_FMT_SGRBG12_1X12:
30 case MEDIA_BUS_FMT_SRGGB12_1X12:
31 return 12;
32 case MEDIA_BUS_FMT_SBGGR10_1X10:
33 case MEDIA_BUS_FMT_SGBRG10_1X10:
34 case MEDIA_BUS_FMT_SGRBG10_1X10:
35 case MEDIA_BUS_FMT_SRGGB10_1X10:
36 return 10;
37 case MEDIA_BUS_FMT_SBGGR8_1X8:
38 case MEDIA_BUS_FMT_SGBRG8_1X8:
39 case MEDIA_BUS_FMT_SGRBG8_1X8:
40 case MEDIA_BUS_FMT_SRGGB8_1X8:
41 case MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8:
42 case MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8:
43 case MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8:
44 case MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8:
45 return 8;
46 default:
47 WARN_ON(1);
48 return -EINVAL;
49 }
50}
51
52unsigned int ipu_isys_mbus_code_to_mipi(u32 code)
53{
54 switch (code) {
55 case MEDIA_BUS_FMT_RGB565_1X16:
56 return IPU_ISYS_MIPI_CSI2_TYPE_RGB565;
57 case MEDIA_BUS_FMT_RGB888_1X24:
58 return IPU_ISYS_MIPI_CSI2_TYPE_RGB888;
59 case MEDIA_BUS_FMT_YUYV10_1X20:
60 return IPU_ISYS_MIPI_CSI2_TYPE_YUV422_10;
61 case MEDIA_BUS_FMT_UYVY8_1X16:
62 case MEDIA_BUS_FMT_YUYV8_1X16:
63 return IPU_ISYS_MIPI_CSI2_TYPE_YUV422_8;
64 case MEDIA_BUS_FMT_SBGGR12_1X12:
65 case MEDIA_BUS_FMT_SGBRG12_1X12:
66 case MEDIA_BUS_FMT_SGRBG12_1X12:
67 case MEDIA_BUS_FMT_SRGGB12_1X12:
68 return IPU_ISYS_MIPI_CSI2_TYPE_RAW12;
69 case MEDIA_BUS_FMT_Y10_1X10:
70 case MEDIA_BUS_FMT_SBGGR10_1X10:
71 case MEDIA_BUS_FMT_SGBRG10_1X10:
72 case MEDIA_BUS_FMT_SGRBG10_1X10:
73 case MEDIA_BUS_FMT_SRGGB10_1X10:
74 return IPU_ISYS_MIPI_CSI2_TYPE_RAW10;
75 case MEDIA_BUS_FMT_SBGGR8_1X8:
76 case MEDIA_BUS_FMT_SGBRG8_1X8:
77 case MEDIA_BUS_FMT_SGRBG8_1X8:
78 case MEDIA_BUS_FMT_SRGGB8_1X8:
79 return IPU_ISYS_MIPI_CSI2_TYPE_RAW8;
80 case MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8:
81 case MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8:
82 case MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8:
83 case MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8:
84 return IPU_ISYS_MIPI_CSI2_TYPE_USER_DEF(1);
85 default:
86 WARN_ON(1);
87 return -EINVAL;
88 }
89}
90
91enum ipu_isys_subdev_pixelorder ipu_isys_subdev_get_pixelorder(u32 code)
92{
93 switch (code) {
94 case MEDIA_BUS_FMT_SBGGR12_1X12:
95 case MEDIA_BUS_FMT_SBGGR10_1X10:
96 case MEDIA_BUS_FMT_SBGGR8_1X8:
97 case MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8:
98 return IPU_ISYS_SUBDEV_PIXELORDER_BGGR;
99 case MEDIA_BUS_FMT_SGBRG12_1X12:
100 case MEDIA_BUS_FMT_SGBRG10_1X10:
101 case MEDIA_BUS_FMT_SGBRG8_1X8:
102 case MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8:
103 return IPU_ISYS_SUBDEV_PIXELORDER_GBRG;
104 case MEDIA_BUS_FMT_SGRBG12_1X12:
105 case MEDIA_BUS_FMT_SGRBG10_1X10:
106 case MEDIA_BUS_FMT_SGRBG8_1X8:
107 case MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8:
108 return IPU_ISYS_SUBDEV_PIXELORDER_GRBG;
109 case MEDIA_BUS_FMT_SRGGB12_1X12:
110 case MEDIA_BUS_FMT_SRGGB10_1X10:
111 case MEDIA_BUS_FMT_SRGGB8_1X8:
112 case MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8:
113 return IPU_ISYS_SUBDEV_PIXELORDER_RGGB;
114 default:
115 WARN_ON(1);
116 return -EINVAL;
117 }
118}
119
120u32 ipu_isys_subdev_code_to_uncompressed(u32 sink_code)
121{
122 switch (sink_code) {
123 case MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8:
124 return MEDIA_BUS_FMT_SBGGR10_1X10;
125 case MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8:
126 return MEDIA_BUS_FMT_SGBRG10_1X10;
127 case MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8:
128 return MEDIA_BUS_FMT_SGRBG10_1X10;
129 case MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8:
130 return MEDIA_BUS_FMT_SRGGB10_1X10;
131 default:
132 return sink_code;
133 }
134}
135
136struct v4l2_mbus_framefmt *__ipu_isys_get_ffmt(struct v4l2_subdev *sd,
5a771b35 137 struct v4l2_subdev_state *sd_state,
f2efa4ee
WY
138 unsigned int pad,
139 unsigned int which)
140{
141 struct ipu_isys_subdev *asd = to_ipu_isys_subdev(sd);
142
143 if (which == V4L2_SUBDEV_FORMAT_ACTIVE)
144 return &asd->ffmt[pad];
145 else
5a771b35 146 return v4l2_subdev_get_try_format(sd, sd_state, pad);
f2efa4ee
WY
147}
148
149struct v4l2_rect *__ipu_isys_get_selection(struct v4l2_subdev *sd,
5a771b35 150 struct v4l2_subdev_state *sd_state,
f2efa4ee
WY
151 unsigned int target,
152 unsigned int pad, unsigned int which)
153{
154 struct ipu_isys_subdev *asd = to_ipu_isys_subdev(sd);
155
156 if (which == V4L2_SUBDEV_FORMAT_ACTIVE) {
157 switch (target) {
158 case V4L2_SEL_TGT_CROP:
159 return &asd->crop[pad];
160 case V4L2_SEL_TGT_COMPOSE:
161 return &asd->compose[pad];
162 }
163 } else {
164 switch (target) {
165 case V4L2_SEL_TGT_CROP:
5a771b35 166 return v4l2_subdev_get_try_crop(sd, sd_state, pad);
f2efa4ee 167 case V4L2_SEL_TGT_COMPOSE:
5a771b35 168 return v4l2_subdev_get_try_compose(sd, sd_state, pad);
f2efa4ee
WY
169 }
170 }
171 WARN_ON(1);
172 return NULL;
173}
174
175static int target_valid(struct v4l2_subdev *sd, unsigned int target,
176 unsigned int pad)
177{
178 struct ipu_isys_subdev *asd = to_ipu_isys_subdev(sd);
179
180 switch (target) {
181 case V4L2_SEL_TGT_CROP:
182 return asd->valid_tgts[pad].crop;
183 case V4L2_SEL_TGT_COMPOSE:
184 return asd->valid_tgts[pad].compose;
185 default:
186 return 0;
187 }
188}
189
190int ipu_isys_subdev_fmt_propagate(struct v4l2_subdev *sd,
5a771b35 191 struct v4l2_subdev_state *sd_state,
f2efa4ee
WY
192 struct v4l2_mbus_framefmt *ffmt,
193 struct v4l2_rect *r,
194 enum isys_subdev_prop_tgt tgt,
195 unsigned int pad, unsigned int which)
196{
197 struct ipu_isys_subdev *asd = to_ipu_isys_subdev(sd);
198 struct v4l2_mbus_framefmt **ffmts = NULL;
199 struct v4l2_rect **crops = NULL;
200 struct v4l2_rect **compose = NULL;
201 unsigned int i;
202 int rval = 0;
203
204 if (tgt == IPU_ISYS_SUBDEV_PROP_TGT_NR_OF)
205 return 0;
206
207 if (WARN_ON(pad >= sd->entity.num_pads))
208 return -EINVAL;
209
210 ffmts = kcalloc(sd->entity.num_pads,
211 sizeof(*ffmts), GFP_KERNEL);
212 if (!ffmts) {
213 rval = -ENOMEM;
214 goto out_subdev_fmt_propagate;
215 }
216 crops = kcalloc(sd->entity.num_pads,
217 sizeof(*crops), GFP_KERNEL);
218 if (!crops) {
219 rval = -ENOMEM;
220 goto out_subdev_fmt_propagate;
221 }
222 compose = kcalloc(sd->entity.num_pads,
223 sizeof(*compose), GFP_KERNEL);
224 if (!compose) {
225 rval = -ENOMEM;
226 goto out_subdev_fmt_propagate;
227 }
228
229 for (i = 0; i < sd->entity.num_pads; i++) {
5a771b35
HY
230 ffmts[i] = __ipu_isys_get_ffmt(sd, sd_state, i, which);
231 crops[i] = __ipu_isys_get_selection(sd, sd_state,
232 V4L2_SEL_TGT_CROP, i, which);
233 compose[i] = __ipu_isys_get_selection(sd, sd_state,
234 V4L2_SEL_TGT_COMPOSE, i, which);
f2efa4ee
WY
235 }
236
237 switch (tgt) {
238 case IPU_ISYS_SUBDEV_PROP_TGT_SINK_FMT:
239 crops[pad]->left = 0;
240 crops[pad]->top = 0;
241 crops[pad]->width = ffmt->width;
242 crops[pad]->height = ffmt->height;
5a771b35
HY
243 rval = ipu_isys_subdev_fmt_propagate(sd, sd_state, ffmt,
244 crops[pad], tgt + 1, pad, which);
f2efa4ee
WY
245 goto out_subdev_fmt_propagate;
246 case IPU_ISYS_SUBDEV_PROP_TGT_SINK_CROP:
247 if (WARN_ON(sd->entity.pads[pad].flags & MEDIA_PAD_FL_SOURCE))
248 goto out_subdev_fmt_propagate;
249
250 compose[pad]->left = 0;
251 compose[pad]->top = 0;
252 compose[pad]->width = r->width;
253 compose[pad]->height = r->height;
5a771b35
HY
254 rval = ipu_isys_subdev_fmt_propagate(sd, sd_state, ffmt,
255 compose[pad], tgt + 1, pad, which);
f2efa4ee
WY
256 goto out_subdev_fmt_propagate;
257 case IPU_ISYS_SUBDEV_PROP_TGT_SINK_COMPOSE:
258 if (WARN_ON(sd->entity.pads[pad].flags & MEDIA_PAD_FL_SOURCE)) {
259 rval = -EINVAL;
260 goto out_subdev_fmt_propagate;
261 }
262
263 for (i = 1; i < sd->entity.num_pads; i++) {
264 if (!(sd->entity.pads[i].flags &
265 MEDIA_PAD_FL_SOURCE))
266 continue;
267
268 compose[i]->left = 0;
269 compose[i]->top = 0;
270 compose[i]->width = r->width;
271 compose[i]->height = r->height;
5a771b35
HY
272 rval = ipu_isys_subdev_fmt_propagate(sd, sd_state,
273 ffmt, compose[i], tgt + 1, i, which);
f2efa4ee
WY
274 if (rval)
275 goto out_subdev_fmt_propagate;
276 }
277 goto out_subdev_fmt_propagate;
278 case IPU_ISYS_SUBDEV_PROP_TGT_SOURCE_COMPOSE:
279 if (WARN_ON(sd->entity.pads[pad].flags & MEDIA_PAD_FL_SINK)) {
280 rval = -EINVAL;
281 goto out_subdev_fmt_propagate;
282 }
283
284 crops[pad]->left = 0;
285 crops[pad]->top = 0;
286 crops[pad]->width = r->width;
287 crops[pad]->height = r->height;
5a771b35
HY
288 rval = ipu_isys_subdev_fmt_propagate(sd, sd_state, ffmt,
289 crops[pad], tgt + 1, pad, which);
f2efa4ee
WY
290 goto out_subdev_fmt_propagate;
291 case IPU_ISYS_SUBDEV_PROP_TGT_SOURCE_CROP:{
292 struct v4l2_subdev_format fmt = {
293 .which = which,
294 .pad = pad,
295 .format = {
296 .width = r->width,
297 .height = r->height,
298 /*
299 * Either use the code from sink pad
300 * or the current one.
301 */
302 .code = ffmt ? ffmt->code :
303 ffmts[pad]->code,
304 .field = ffmt ? ffmt->field :
305 ffmts[pad]->field,
306 },
307 };
308
5a771b35 309 asd->set_ffmt(sd, sd_state, &fmt);
f2efa4ee
WY
310 goto out_subdev_fmt_propagate;
311 }
312 }
313
314out_subdev_fmt_propagate:
315 kfree(ffmts);
316 kfree(crops);
317 kfree(compose);
318 return rval;
319}
320
321int ipu_isys_subdev_set_ffmt_default(struct v4l2_subdev *sd,
5a771b35 322 struct v4l2_subdev_state *sd_state,
f2efa4ee
WY
323 struct v4l2_subdev_format *fmt)
324{
325 struct v4l2_mbus_framefmt *ffmt =
5a771b35 326 __ipu_isys_get_ffmt(sd, sd_state, fmt->pad, fmt->which);
f2efa4ee
WY
327
328 /* No propagation for non-zero pads. */
329 if (fmt->pad) {
330 struct v4l2_mbus_framefmt *sink_ffmt =
5a771b35 331 __ipu_isys_get_ffmt(sd, sd_state, 0, fmt->which);
f2efa4ee
WY
332
333 ffmt->width = sink_ffmt->width;
334 ffmt->height = sink_ffmt->height;
335 ffmt->code = sink_ffmt->code;
336 ffmt->field = sink_ffmt->field;
337
338 return 0;
339 }
340
341 ffmt->width = fmt->format.width;
342 ffmt->height = fmt->format.height;
343 ffmt->code = fmt->format.code;
344 ffmt->field = fmt->format.field;
345
5a771b35 346 return ipu_isys_subdev_fmt_propagate(sd, sd_state, &fmt->format, NULL,
f2efa4ee
WY
347 IPU_ISYS_SUBDEV_PROP_TGT_SINK_FMT,
348 fmt->pad, fmt->which);
349}
350
351int __ipu_isys_subdev_set_ffmt(struct v4l2_subdev *sd,
5a771b35 352 struct v4l2_subdev_state *sd_state,
f2efa4ee
WY
353 struct v4l2_subdev_format *fmt)
354{
355 struct ipu_isys_subdev *asd = to_ipu_isys_subdev(sd);
356 struct v4l2_mbus_framefmt *ffmt =
5a771b35 357 __ipu_isys_get_ffmt(sd, sd_state, fmt->pad, fmt->which);
f2efa4ee
WY
358 u32 code = asd->supported_codes[fmt->pad][0];
359 unsigned int i;
360
361 WARN_ON(!mutex_is_locked(&asd->mutex));
362
363 fmt->format.width = clamp(fmt->format.width, IPU_ISYS_MIN_WIDTH,
364 IPU_ISYS_MAX_WIDTH);
365 fmt->format.height = clamp(fmt->format.height,
366 IPU_ISYS_MIN_HEIGHT, IPU_ISYS_MAX_HEIGHT);
367
368 for (i = 0; asd->supported_codes[fmt->pad][i]; i++) {
369 if (asd->supported_codes[fmt->pad][i] == fmt->format.code) {
370 code = asd->supported_codes[fmt->pad][i];
371 break;
372 }
373 }
374
375 fmt->format.code = code;
376
5a771b35 377 asd->set_ffmt(sd, sd_state, fmt);
f2efa4ee
WY
378
379 fmt->format = *ffmt;
380
381 return 0;
382}
383
384int ipu_isys_subdev_set_ffmt(struct v4l2_subdev *sd,
5a771b35 385 struct v4l2_subdev_state *sd_state,
f2efa4ee
WY
386 struct v4l2_subdev_format *fmt)
387{
388 struct ipu_isys_subdev *asd = to_ipu_isys_subdev(sd);
389 int rval;
390
391 mutex_lock(&asd->mutex);
5a771b35 392 rval = __ipu_isys_subdev_set_ffmt(sd, sd_state, fmt);
f2efa4ee
WY
393 mutex_unlock(&asd->mutex);
394
395 return rval;
396}
397
398int ipu_isys_subdev_get_ffmt(struct v4l2_subdev *sd,
5a771b35 399 struct v4l2_subdev_state *sd_state,
f2efa4ee
WY
400 struct v4l2_subdev_format *fmt)
401{
402 struct ipu_isys_subdev *asd = to_ipu_isys_subdev(sd);
403
404 mutex_lock(&asd->mutex);
5a771b35 405 fmt->format = *__ipu_isys_get_ffmt(sd, sd_state, fmt->pad,
f2efa4ee
WY
406 fmt->which);
407 mutex_unlock(&asd->mutex);
408
409 return 0;
410}
411
412int ipu_isys_subdev_set_sel(struct v4l2_subdev *sd,
5a771b35 413 struct v4l2_subdev_state *sd_state,
f2efa4ee
WY
414 struct v4l2_subdev_selection *sel)
415{
416 struct ipu_isys_subdev *asd = to_ipu_isys_subdev(sd);
417 struct media_pad *pad = &asd->sd.entity.pads[sel->pad];
418 struct v4l2_rect *r, __r = { 0 };
419 unsigned int tgt;
420
421 if (!target_valid(sd, sel->target, sel->pad))
422 return -EINVAL;
423
424 switch (sel->target) {
425 case V4L2_SEL_TGT_CROP:
426 if (pad->flags & MEDIA_PAD_FL_SINK) {
427 struct v4l2_mbus_framefmt *ffmt =
5a771b35 428 __ipu_isys_get_ffmt(sd, sd_state, sel->pad,
f2efa4ee
WY
429 sel->which);
430
431 __r.width = ffmt->width;
432 __r.height = ffmt->height;
433 r = &__r;
434 tgt = IPU_ISYS_SUBDEV_PROP_TGT_SINK_CROP;
435 } else {
436 /* 0 is the sink pad. */
5a771b35 437 r = __ipu_isys_get_selection(sd, sd_state, sel->target, 0,
f2efa4ee
WY
438 sel->which);
439 tgt = IPU_ISYS_SUBDEV_PROP_TGT_SOURCE_CROP;
440 }
441
442 break;
443 case V4L2_SEL_TGT_COMPOSE:
444 if (pad->flags & MEDIA_PAD_FL_SINK) {
5a771b35 445 r = __ipu_isys_get_selection(sd, sd_state, V4L2_SEL_TGT_CROP,
f2efa4ee
WY
446 sel->pad, sel->which);
447 tgt = IPU_ISYS_SUBDEV_PROP_TGT_SINK_COMPOSE;
448 } else {
5a771b35 449 r = __ipu_isys_get_selection(sd, sd_state,
f2efa4ee
WY
450 V4L2_SEL_TGT_COMPOSE, 0,
451 sel->which);
452 tgt = IPU_ISYS_SUBDEV_PROP_TGT_SOURCE_COMPOSE;
453 }
454 break;
455 default:
456 return -EINVAL;
457 }
458
459 sel->r.width = clamp(sel->r.width, IPU_ISYS_MIN_WIDTH, r->width);
460 sel->r.height = clamp(sel->r.height, IPU_ISYS_MIN_HEIGHT, r->height);
5a771b35 461 *__ipu_isys_get_selection(sd, sd_state, sel->target, sel->pad,
f2efa4ee 462 sel->which) = sel->r;
5a771b35 463 return ipu_isys_subdev_fmt_propagate(sd, sd_state, NULL, &sel->r, tgt,
f2efa4ee
WY
464 sel->pad, sel->which);
465}
466
467int ipu_isys_subdev_get_sel(struct v4l2_subdev *sd,
5a771b35 468 struct v4l2_subdev_state *sd_state,
f2efa4ee
WY
469 struct v4l2_subdev_selection *sel)
470{
471 if (!target_valid(sd, sel->target, sel->pad))
472 return -EINVAL;
473
5a771b35 474 sel->r = *__ipu_isys_get_selection(sd, sd_state, sel->target,
f2efa4ee
WY
475 sel->pad, sel->which);
476
477 return 0;
478}
479
480int ipu_isys_subdev_enum_mbus_code(struct v4l2_subdev *sd,
5a771b35 481 struct v4l2_subdev_state *sd_state,
f2efa4ee
WY
482 struct v4l2_subdev_mbus_code_enum *code)
483{
484 struct ipu_isys_subdev *asd = to_ipu_isys_subdev(sd);
485 const u32 *supported_codes = asd->supported_codes[code->pad];
486 u32 index;
487
488 for (index = 0; supported_codes[index]; index++) {
489 if (index == code->index) {
490 code->code = supported_codes[index];
491 return 0;
492 }
493 }
494
495 return -EINVAL;
496}
497
498/*
499 * Besides validating the link, figure out the external pad and the
500 * ISYS FW ABI source.
501 */
502int ipu_isys_subdev_link_validate(struct v4l2_subdev *sd,
503 struct media_link *link,
504 struct v4l2_subdev_format *source_fmt,
505 struct v4l2_subdev_format *sink_fmt)
506{
507 struct v4l2_subdev *source_sd =
508 media_entity_to_v4l2_subdev(link->source->entity);
509 struct ipu_isys_pipeline *ip = container_of(sd->entity.pipe,
510 struct ipu_isys_pipeline,
511 pipe);
512 struct ipu_isys_subdev *asd = to_ipu_isys_subdev(sd);
513
514 if (!source_sd)
515 return -ENODEV;
516 if (strncmp(source_sd->name, IPU_ISYS_ENTITY_PREFIX,
517 strlen(IPU_ISYS_ENTITY_PREFIX)) != 0) {
518 /*
519 * source_sd isn't ours --- sd must be the external
520 * sub-device.
521 */
522 ip->external = link->source;
523 ip->source = to_ipu_isys_subdev(sd)->source;
524 dev_dbg(&asd->isys->adev->dev, "%s: using source %d\n",
525 sd->entity.name, ip->source);
526 } else if (source_sd->entity.num_pads == 1) {
527 /* All internal sources have a single pad. */
528 ip->external = link->source;
529 ip->source = to_ipu_isys_subdev(source_sd)->source;
530
531 dev_dbg(&asd->isys->adev->dev, "%s: using source %d\n",
532 sd->entity.name, ip->source);
533 }
534
535 if (asd->isl_mode != IPU_ISL_OFF)
536 ip->isl_mode = asd->isl_mode;
537
538 return v4l2_subdev_link_validate_default(sd, link, source_fmt,
539 sink_fmt);
540}
541
542int ipu_isys_subdev_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
543{
544 struct ipu_isys_subdev *asd = to_ipu_isys_subdev(sd);
545 unsigned int i;
546
547 mutex_lock(&asd->mutex);
548
549 for (i = 0; i < asd->sd.entity.num_pads; i++) {
550 struct v4l2_mbus_framefmt *try_fmt =
5a771b35 551 v4l2_subdev_get_try_format(sd, fh->state, i);
f2efa4ee 552 struct v4l2_rect *try_crop =
5a771b35 553 v4l2_subdev_get_try_crop(sd, fh->state, i);
f2efa4ee 554 struct v4l2_rect *try_compose =
5a771b35 555 v4l2_subdev_get_try_compose(sd, fh->state, i);
f2efa4ee
WY
556
557 *try_fmt = asd->ffmt[i];
558 *try_crop = asd->crop[i];
559 *try_compose = asd->compose[i];
560 }
561
562 mutex_unlock(&asd->mutex);
563
564 return 0;
565}
566
567int ipu_isys_subdev_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
568{
569 return 0;
570}
571
572int ipu_isys_subdev_init(struct ipu_isys_subdev *asd,
573 struct v4l2_subdev_ops *ops,
574 unsigned int nr_ctrls,
575 unsigned int num_pads,
576 unsigned int num_source,
577 unsigned int num_sink,
578 unsigned int sd_flags)
579{
580 int rval = -EINVAL;
581
582 mutex_init(&asd->mutex);
583
584 v4l2_subdev_init(&asd->sd, ops);
585
586 asd->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | sd_flags;
587 asd->sd.owner = THIS_MODULE;
588 asd->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
589
590 asd->nsources = num_source;
591 asd->nsinks = num_sink;
592
593 asd->pad = devm_kcalloc(&asd->isys->adev->dev, num_pads,
594 sizeof(*asd->pad), GFP_KERNEL);
595
596 asd->ffmt = devm_kcalloc(&asd->isys->adev->dev, num_pads,
597 sizeof(*asd->ffmt), GFP_KERNEL);
598
599 asd->crop = devm_kcalloc(&asd->isys->adev->dev, num_pads,
600 sizeof(*asd->crop), GFP_KERNEL);
601
602 asd->compose = devm_kcalloc(&asd->isys->adev->dev, num_pads,
603 sizeof(*asd->compose), GFP_KERNEL);
604
605 asd->valid_tgts = devm_kcalloc(&asd->isys->adev->dev, num_pads,
606 sizeof(*asd->valid_tgts), GFP_KERNEL);
607 if (!asd->pad || !asd->ffmt || !asd->crop || !asd->compose ||
608 !asd->valid_tgts)
609 return -ENOMEM;
610
611 rval = media_entity_pads_init(&asd->sd.entity, num_pads, asd->pad);
612 if (rval)
613 goto out_mutex_destroy;
614
615 if (asd->ctrl_init) {
616 rval = v4l2_ctrl_handler_init(&asd->ctrl_handler, nr_ctrls);
617 if (rval)
618 goto out_media_entity_cleanup;
619
620 asd->ctrl_init(&asd->sd);
621 if (asd->ctrl_handler.error) {
622 rval = asd->ctrl_handler.error;
623 goto out_v4l2_ctrl_handler_free;
624 }
625
626 asd->sd.ctrl_handler = &asd->ctrl_handler;
627 }
628
629 asd->source = -1;
630
631 return 0;
632
633out_v4l2_ctrl_handler_free:
634 v4l2_ctrl_handler_free(&asd->ctrl_handler);
635
636out_media_entity_cleanup:
637 media_entity_cleanup(&asd->sd.entity);
638
639out_mutex_destroy:
640 mutex_destroy(&asd->mutex);
641
642 return rval;
643}
644
645void ipu_isys_subdev_cleanup(struct ipu_isys_subdev *asd)
646{
647 media_entity_cleanup(&asd->sd.entity);
648 v4l2_ctrl_handler_free(&asd->ctrl_handler);
649 mutex_destroy(&asd->mutex);
650}