diff options
Diffstat (limited to 'drivers/media/video/v4l2-subdev.c')
-rw-r--r-- | drivers/media/video/v4l2-subdev.c | 143 |
1 files changed, 131 insertions, 12 deletions
diff --git a/drivers/media/video/v4l2-subdev.c b/drivers/media/video/v4l2-subdev.c index 6fe88e965a8c..db6e859b93d4 100644 --- a/drivers/media/video/v4l2-subdev.c +++ b/drivers/media/video/v4l2-subdev.c | |||
@@ -35,14 +35,9 @@ | |||
35 | static int subdev_fh_init(struct v4l2_subdev_fh *fh, struct v4l2_subdev *sd) | 35 | static int subdev_fh_init(struct v4l2_subdev_fh *fh, struct v4l2_subdev *sd) |
36 | { | 36 | { |
37 | #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) | 37 | #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) |
38 | /* Allocate try format and crop in the same memory block */ | 38 | fh->pad = kzalloc(sizeof(*fh->pad) * sd->entity.num_pads, GFP_KERNEL); |
39 | fh->try_fmt = kzalloc((sizeof(*fh->try_fmt) + sizeof(*fh->try_crop)) | 39 | if (fh->pad == NULL) |
40 | * sd->entity.num_pads, GFP_KERNEL); | ||
41 | if (fh->try_fmt == NULL) | ||
42 | return -ENOMEM; | 40 | return -ENOMEM; |
43 | |||
44 | fh->try_crop = (struct v4l2_rect *) | ||
45 | (fh->try_fmt + sd->entity.num_pads); | ||
46 | #endif | 41 | #endif |
47 | return 0; | 42 | return 0; |
48 | } | 43 | } |
@@ -50,9 +45,8 @@ static int subdev_fh_init(struct v4l2_subdev_fh *fh, struct v4l2_subdev *sd) | |||
50 | static void subdev_fh_free(struct v4l2_subdev_fh *fh) | 45 | static void subdev_fh_free(struct v4l2_subdev_fh *fh) |
51 | { | 46 | { |
52 | #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) | 47 | #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) |
53 | kfree(fh->try_fmt); | 48 | kfree(fh->pad); |
54 | fh->try_fmt = NULL; | 49 | fh->pad = NULL; |
55 | fh->try_crop = NULL; | ||
56 | #endif | 50 | #endif |
57 | } | 51 | } |
58 | 52 | ||
@@ -234,6 +228,8 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) | |||
234 | 228 | ||
235 | case VIDIOC_SUBDEV_G_CROP: { | 229 | case VIDIOC_SUBDEV_G_CROP: { |
236 | struct v4l2_subdev_crop *crop = arg; | 230 | struct v4l2_subdev_crop *crop = arg; |
231 | struct v4l2_subdev_selection sel; | ||
232 | int rval; | ||
237 | 233 | ||
238 | if (crop->which != V4L2_SUBDEV_FORMAT_TRY && | 234 | if (crop->which != V4L2_SUBDEV_FORMAT_TRY && |
239 | crop->which != V4L2_SUBDEV_FORMAT_ACTIVE) | 235 | crop->which != V4L2_SUBDEV_FORMAT_ACTIVE) |
@@ -242,11 +238,27 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) | |||
242 | if (crop->pad >= sd->entity.num_pads) | 238 | if (crop->pad >= sd->entity.num_pads) |
243 | return -EINVAL; | 239 | return -EINVAL; |
244 | 240 | ||
245 | return v4l2_subdev_call(sd, pad, get_crop, subdev_fh, crop); | 241 | rval = v4l2_subdev_call(sd, pad, get_crop, subdev_fh, crop); |
242 | if (rval != -ENOIOCTLCMD) | ||
243 | return rval; | ||
244 | |||
245 | memset(&sel, 0, sizeof(sel)); | ||
246 | sel.which = crop->which; | ||
247 | sel.pad = crop->pad; | ||
248 | sel.target = V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL; | ||
249 | |||
250 | rval = v4l2_subdev_call( | ||
251 | sd, pad, get_selection, subdev_fh, &sel); | ||
252 | |||
253 | crop->rect = sel.r; | ||
254 | |||
255 | return rval; | ||
246 | } | 256 | } |
247 | 257 | ||
248 | case VIDIOC_SUBDEV_S_CROP: { | 258 | case VIDIOC_SUBDEV_S_CROP: { |
249 | struct v4l2_subdev_crop *crop = arg; | 259 | struct v4l2_subdev_crop *crop = arg; |
260 | struct v4l2_subdev_selection sel; | ||
261 | int rval; | ||
250 | 262 | ||
251 | if (crop->which != V4L2_SUBDEV_FORMAT_TRY && | 263 | if (crop->which != V4L2_SUBDEV_FORMAT_TRY && |
252 | crop->which != V4L2_SUBDEV_FORMAT_ACTIVE) | 264 | crop->which != V4L2_SUBDEV_FORMAT_ACTIVE) |
@@ -255,7 +267,22 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) | |||
255 | if (crop->pad >= sd->entity.num_pads) | 267 | if (crop->pad >= sd->entity.num_pads) |
256 | return -EINVAL; | 268 | return -EINVAL; |
257 | 269 | ||
258 | return v4l2_subdev_call(sd, pad, set_crop, subdev_fh, crop); | 270 | rval = v4l2_subdev_call(sd, pad, set_crop, subdev_fh, crop); |
271 | if (rval != -ENOIOCTLCMD) | ||
272 | return rval; | ||
273 | |||
274 | memset(&sel, 0, sizeof(sel)); | ||
275 | sel.which = crop->which; | ||
276 | sel.pad = crop->pad; | ||
277 | sel.target = V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL; | ||
278 | sel.r = crop->rect; | ||
279 | |||
280 | rval = v4l2_subdev_call( | ||
281 | sd, pad, set_selection, subdev_fh, &sel); | ||
282 | |||
283 | crop->rect = sel.r; | ||
284 | |||
285 | return rval; | ||
259 | } | 286 | } |
260 | 287 | ||
261 | case VIDIOC_SUBDEV_ENUM_MBUS_CODE: { | 288 | case VIDIOC_SUBDEV_ENUM_MBUS_CODE: { |
@@ -293,6 +320,34 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) | |||
293 | return v4l2_subdev_call(sd, pad, enum_frame_interval, subdev_fh, | 320 | return v4l2_subdev_call(sd, pad, enum_frame_interval, subdev_fh, |
294 | fie); | 321 | fie); |
295 | } | 322 | } |
323 | |||
324 | case VIDIOC_SUBDEV_G_SELECTION: { | ||
325 | struct v4l2_subdev_selection *sel = arg; | ||
326 | |||
327 | if (sel->which != V4L2_SUBDEV_FORMAT_TRY && | ||
328 | sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) | ||
329 | return -EINVAL; | ||
330 | |||
331 | if (sel->pad >= sd->entity.num_pads) | ||
332 | return -EINVAL; | ||
333 | |||
334 | return v4l2_subdev_call( | ||
335 | sd, pad, get_selection, subdev_fh, sel); | ||
336 | } | ||
337 | |||
338 | case VIDIOC_SUBDEV_S_SELECTION: { | ||
339 | struct v4l2_subdev_selection *sel = arg; | ||
340 | |||
341 | if (sel->which != V4L2_SUBDEV_FORMAT_TRY && | ||
342 | sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) | ||
343 | return -EINVAL; | ||
344 | |||
345 | if (sel->pad >= sd->entity.num_pads) | ||
346 | return -EINVAL; | ||
347 | |||
348 | return v4l2_subdev_call( | ||
349 | sd, pad, set_selection, subdev_fh, sel); | ||
350 | } | ||
296 | #endif | 351 | #endif |
297 | default: | 352 | default: |
298 | return v4l2_subdev_call(sd, core, ioctl, cmd, arg); | 353 | return v4l2_subdev_call(sd, core, ioctl, cmd, arg); |
@@ -332,6 +387,70 @@ const struct v4l2_file_operations v4l2_subdev_fops = { | |||
332 | .poll = subdev_poll, | 387 | .poll = subdev_poll, |
333 | }; | 388 | }; |
334 | 389 | ||
390 | #ifdef CONFIG_MEDIA_CONTROLLER | ||
391 | int v4l2_subdev_link_validate_default(struct v4l2_subdev *sd, | ||
392 | struct media_link *link, | ||
393 | struct v4l2_subdev_format *source_fmt, | ||
394 | struct v4l2_subdev_format *sink_fmt) | ||
395 | { | ||
396 | if (source_fmt->format.width != sink_fmt->format.width | ||
397 | || source_fmt->format.height != sink_fmt->format.height | ||
398 | || source_fmt->format.code != sink_fmt->format.code) | ||
399 | return -EINVAL; | ||
400 | |||
401 | return 0; | ||
402 | } | ||
403 | EXPORT_SYMBOL_GPL(v4l2_subdev_link_validate_default); | ||
404 | |||
405 | static int | ||
406 | v4l2_subdev_link_validate_get_format(struct media_pad *pad, | ||
407 | struct v4l2_subdev_format *fmt) | ||
408 | { | ||
409 | switch (media_entity_type(pad->entity)) { | ||
410 | case MEDIA_ENT_T_V4L2_SUBDEV: | ||
411 | fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE; | ||
412 | fmt->pad = pad->index; | ||
413 | return v4l2_subdev_call(media_entity_to_v4l2_subdev( | ||
414 | pad->entity), | ||
415 | pad, get_fmt, NULL, fmt); | ||
416 | default: | ||
417 | WARN(1, "Driver bug! Wrong media entity type %d, entity %s\n", | ||
418 | media_entity_type(pad->entity), pad->entity->name); | ||
419 | /* Fall through */ | ||
420 | case MEDIA_ENT_T_DEVNODE_V4L: | ||
421 | return -EINVAL; | ||
422 | } | ||
423 | } | ||
424 | |||
425 | int v4l2_subdev_link_validate(struct media_link *link) | ||
426 | { | ||
427 | struct v4l2_subdev *sink; | ||
428 | struct v4l2_subdev_format sink_fmt, source_fmt; | ||
429 | int rval; | ||
430 | |||
431 | rval = v4l2_subdev_link_validate_get_format( | ||
432 | link->source, &source_fmt); | ||
433 | if (rval < 0) | ||
434 | return 0; | ||
435 | |||
436 | rval = v4l2_subdev_link_validate_get_format( | ||
437 | link->sink, &sink_fmt); | ||
438 | if (rval < 0) | ||
439 | return 0; | ||
440 | |||
441 | sink = media_entity_to_v4l2_subdev(link->sink->entity); | ||
442 | |||
443 | rval = v4l2_subdev_call(sink, pad, link_validate, link, | ||
444 | &source_fmt, &sink_fmt); | ||
445 | if (rval != -ENOIOCTLCMD) | ||
446 | return rval; | ||
447 | |||
448 | return v4l2_subdev_link_validate_default( | ||
449 | sink, link, &source_fmt, &sink_fmt); | ||
450 | } | ||
451 | EXPORT_SYMBOL_GPL(v4l2_subdev_link_validate); | ||
452 | #endif /* CONFIG_MEDIA_CONTROLLER */ | ||
453 | |||
335 | void v4l2_subdev_init(struct v4l2_subdev *sd, const struct v4l2_subdev_ops *ops) | 454 | void v4l2_subdev_init(struct v4l2_subdev *sd, const struct v4l2_subdev_ops *ops) |
336 | { | 455 | { |
337 | INIT_LIST_HEAD(&sd->list); | 456 | INIT_LIST_HEAD(&sd->list); |