diff options
author | Guennadi Liakhovetski <g.liakhovetski@gmx.de> | 2011-03-28 12:39:16 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2011-05-20 10:56:16 -0400 |
commit | 3dac322db63473901ba9484690ee6864bb1292d0 (patch) | |
tree | 357b3ee568b0dc35ba78529070fe40ba17cd5aa4 /drivers/media/video/sh_mobile_ceu_camera.c | |
parent | aee5c2f1fc9c7cd2502ff14f818fcedef666f038 (diff) |
[media] V4L: sh_mobile_ceu_camera: implement live cropping
PRELIMINARY: break out spinlock changes; consider multiple completing
feames, causing multiple complete() calles.
Add live crop support to the sh_mobile_ceu driver.
Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/video/sh_mobile_ceu_camera.c')
-rw-r--r-- | drivers/media/video/sh_mobile_ceu_camera.c | 129 |
1 files changed, 111 insertions, 18 deletions
diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c index e6839bb70acc..bf10ffcc5783 100644 --- a/drivers/media/video/sh_mobile_ceu_camera.c +++ b/drivers/media/video/sh_mobile_ceu_camera.c | |||
@@ -17,6 +17,7 @@ | |||
17 | #include <linux/init.h> | 17 | #include <linux/init.h> |
18 | #include <linux/module.h> | 18 | #include <linux/module.h> |
19 | #include <linux/io.h> | 19 | #include <linux/io.h> |
20 | #include <linux/completion.h> | ||
20 | #include <linux/delay.h> | 21 | #include <linux/delay.h> |
21 | #include <linux/dma-mapping.h> | 22 | #include <linux/dma-mapping.h> |
22 | #include <linux/errno.h> | 23 | #include <linux/errno.h> |
@@ -106,6 +107,7 @@ struct sh_mobile_ceu_dev { | |||
106 | struct vb2_alloc_ctx *alloc_ctx; | 107 | struct vb2_alloc_ctx *alloc_ctx; |
107 | 108 | ||
108 | struct sh_mobile_ceu_info *pdata; | 109 | struct sh_mobile_ceu_info *pdata; |
110 | struct completion complete; | ||
109 | 111 | ||
110 | u32 cflcr; | 112 | u32 cflcr; |
111 | 113 | ||
@@ -114,6 +116,7 @@ struct sh_mobile_ceu_dev { | |||
114 | 116 | ||
115 | unsigned int image_mode:1; | 117 | unsigned int image_mode:1; |
116 | unsigned int is_16bit:1; | 118 | unsigned int is_16bit:1; |
119 | unsigned int frozen:1; | ||
117 | }; | 120 | }; |
118 | 121 | ||
119 | struct sh_mobile_ceu_cam { | 122 | struct sh_mobile_ceu_cam { |
@@ -273,7 +276,8 @@ static int sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev) | |||
273 | ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) & ~CEU_CEIER_MASK); | 276 | ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) & ~CEU_CEIER_MASK); |
274 | status = ceu_read(pcdev, CETCR); | 277 | status = ceu_read(pcdev, CETCR); |
275 | ceu_write(pcdev, CETCR, ~status & CEU_CETCR_MAGIC); | 278 | ceu_write(pcdev, CETCR, ~status & CEU_CETCR_MAGIC); |
276 | ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) | CEU_CEIER_MASK); | 279 | if (!pcdev->frozen) |
280 | ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) | CEU_CEIER_MASK); | ||
277 | ceu_write(pcdev, CAPCR, ceu_read(pcdev, CAPCR) & ~CEU_CAPCR_CTNCP); | 281 | ceu_write(pcdev, CAPCR, ceu_read(pcdev, CAPCR) & ~CEU_CAPCR_CTNCP); |
278 | ceu_write(pcdev, CETCR, CEU_CETCR_MAGIC ^ CEU_CETCR_IGRW); | 282 | ceu_write(pcdev, CETCR, CEU_CETCR_MAGIC ^ CEU_CETCR_IGRW); |
279 | 283 | ||
@@ -287,6 +291,11 @@ static int sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev) | |||
287 | ret = -EIO; | 291 | ret = -EIO; |
288 | } | 292 | } |
289 | 293 | ||
294 | if (pcdev->frozen) { | ||
295 | complete(&pcdev->complete); | ||
296 | return ret; | ||
297 | } | ||
298 | |||
290 | if (!pcdev->active) | 299 | if (!pcdev->active) |
291 | return ret; | 300 | return ret; |
292 | 301 | ||
@@ -378,12 +387,11 @@ static void sh_mobile_ceu_videobuf_queue(struct vb2_buffer *vb) | |||
378 | struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); | 387 | struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); |
379 | struct sh_mobile_ceu_dev *pcdev = ici->priv; | 388 | struct sh_mobile_ceu_dev *pcdev = ici->priv; |
380 | struct sh_mobile_ceu_buffer *buf = to_ceu_vb(vb); | 389 | struct sh_mobile_ceu_buffer *buf = to_ceu_vb(vb); |
381 | unsigned long flags; | ||
382 | 390 | ||
383 | dev_dbg(icd->dev.parent, "%s (vb=0x%p) 0x%p %lu\n", __func__, | 391 | dev_dbg(icd->dev.parent, "%s (vb=0x%p) 0x%p %lu\n", __func__, |
384 | vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0)); | 392 | vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0)); |
385 | 393 | ||
386 | spin_lock_irqsave(&pcdev->lock, flags); | 394 | spin_lock_irq(&pcdev->lock); |
387 | list_add_tail(&buf->queue, &pcdev->capture); | 395 | list_add_tail(&buf->queue, &pcdev->capture); |
388 | 396 | ||
389 | if (!pcdev->active) { | 397 | if (!pcdev->active) { |
@@ -395,7 +403,7 @@ static void sh_mobile_ceu_videobuf_queue(struct vb2_buffer *vb) | |||
395 | pcdev->active = vb; | 403 | pcdev->active = vb; |
396 | sh_mobile_ceu_capture(pcdev); | 404 | sh_mobile_ceu_capture(pcdev); |
397 | } | 405 | } |
398 | spin_unlock_irqrestore(&pcdev->lock, flags); | 406 | spin_unlock_irq(&pcdev->lock); |
399 | } | 407 | } |
400 | 408 | ||
401 | static void sh_mobile_ceu_videobuf_release(struct vb2_buffer *vb) | 409 | static void sh_mobile_ceu_videobuf_release(struct vb2_buffer *vb) |
@@ -404,9 +412,8 @@ static void sh_mobile_ceu_videobuf_release(struct vb2_buffer *vb) | |||
404 | struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); | 412 | struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); |
405 | struct sh_mobile_ceu_buffer *buf = to_ceu_vb(vb); | 413 | struct sh_mobile_ceu_buffer *buf = to_ceu_vb(vb); |
406 | struct sh_mobile_ceu_dev *pcdev = ici->priv; | 414 | struct sh_mobile_ceu_dev *pcdev = ici->priv; |
407 | unsigned long flags; | ||
408 | 415 | ||
409 | spin_lock_irqsave(&pcdev->lock, flags); | 416 | spin_lock_irq(&pcdev->lock); |
410 | 417 | ||
411 | if (pcdev->active == vb) { | 418 | if (pcdev->active == vb) { |
412 | /* disable capture (release DMA buffer), reset */ | 419 | /* disable capture (release DMA buffer), reset */ |
@@ -417,7 +424,7 @@ static void sh_mobile_ceu_videobuf_release(struct vb2_buffer *vb) | |||
417 | /* Doesn't hurt also if the list is empty */ | 424 | /* Doesn't hurt also if the list is empty */ |
418 | list_del_init(&buf->queue); | 425 | list_del_init(&buf->queue); |
419 | 426 | ||
420 | spin_unlock_irqrestore(&pcdev->lock, flags); | 427 | spin_unlock_irq(&pcdev->lock); |
421 | } | 428 | } |
422 | 429 | ||
423 | static int sh_mobile_ceu_videobuf_init(struct vb2_buffer *vb) | 430 | static int sh_mobile_ceu_videobuf_init(struct vb2_buffer *vb) |
@@ -433,16 +440,15 @@ static int sh_mobile_ceu_stop_streaming(struct vb2_queue *q) | |||
433 | struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); | 440 | struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); |
434 | struct sh_mobile_ceu_dev *pcdev = ici->priv; | 441 | struct sh_mobile_ceu_dev *pcdev = ici->priv; |
435 | struct list_head *buf_head, *tmp; | 442 | struct list_head *buf_head, *tmp; |
436 | unsigned long flags; | ||
437 | 443 | ||
438 | spin_lock_irqsave(&pcdev->lock, flags); | 444 | spin_lock_irq(&pcdev->lock); |
439 | 445 | ||
440 | pcdev->active = NULL; | 446 | pcdev->active = NULL; |
441 | 447 | ||
442 | list_for_each_safe(buf_head, tmp, &pcdev->capture) | 448 | list_for_each_safe(buf_head, tmp, &pcdev->capture) |
443 | list_del_init(buf_head); | 449 | list_del_init(buf_head); |
444 | 450 | ||
445 | spin_unlock_irqrestore(&pcdev->lock, flags); | 451 | spin_unlock_irq(&pcdev->lock); |
446 | 452 | ||
447 | return sh_mobile_ceu_soft_reset(pcdev); | 453 | return sh_mobile_ceu_soft_reset(pcdev); |
448 | } | 454 | } |
@@ -521,7 +527,6 @@ static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd) | |||
521 | { | 527 | { |
522 | struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); | 528 | struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); |
523 | struct sh_mobile_ceu_dev *pcdev = ici->priv; | 529 | struct sh_mobile_ceu_dev *pcdev = ici->priv; |
524 | unsigned long flags; | ||
525 | 530 | ||
526 | BUG_ON(icd != pcdev->icd); | 531 | BUG_ON(icd != pcdev->icd); |
527 | 532 | ||
@@ -530,13 +535,13 @@ static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd) | |||
530 | sh_mobile_ceu_soft_reset(pcdev); | 535 | sh_mobile_ceu_soft_reset(pcdev); |
531 | 536 | ||
532 | /* make sure active buffer is canceled */ | 537 | /* make sure active buffer is canceled */ |
533 | spin_lock_irqsave(&pcdev->lock, flags); | 538 | spin_lock_irq(&pcdev->lock); |
534 | if (pcdev->active) { | 539 | if (pcdev->active) { |
535 | list_del_init(&to_ceu_vb(pcdev->active)->queue); | 540 | list_del_init(&to_ceu_vb(pcdev->active)->queue); |
536 | vb2_buffer_done(pcdev->active, VB2_BUF_STATE_ERROR); | 541 | vb2_buffer_done(pcdev->active, VB2_BUF_STATE_ERROR); |
537 | pcdev->active = NULL; | 542 | pcdev->active = NULL; |
538 | } | 543 | } |
539 | spin_unlock_irqrestore(&pcdev->lock, flags); | 544 | spin_unlock_irq(&pcdev->lock); |
540 | 545 | ||
541 | pm_runtime_put_sync(ici->v4l2_dev.dev); | 546 | pm_runtime_put_sync(ici->v4l2_dev.dev); |
542 | 547 | ||
@@ -1351,7 +1356,7 @@ static int client_scale(struct soc_camera_device *icd, | |||
1351 | /* | 1356 | /* |
1352 | * CEU can scale and crop, but we don't want to waste bandwidth and kill the | 1357 | * CEU can scale and crop, but we don't want to waste bandwidth and kill the |
1353 | * framerate by always requesting the maximum image from the client. See | 1358 | * framerate by always requesting the maximum image from the client. See |
1354 | * Documentation/video4linux/sh_mobile_camera_ceu.txt for a description of | 1359 | * Documentation/video4linux/sh_mobile_ceu_camera.txt for a description of |
1355 | * scaling and cropping algorithms and for the meaning of referenced here steps. | 1360 | * scaling and cropping algorithms and for the meaning of referenced here steps. |
1356 | */ | 1361 | */ |
1357 | static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd, | 1362 | static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd, |
@@ -1398,10 +1403,6 @@ static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd, | |||
1398 | if (mf.width > 2560 || mf.height > 1920) | 1403 | if (mf.width > 2560 || mf.height > 1920) |
1399 | return -EINVAL; | 1404 | return -EINVAL; |
1400 | 1405 | ||
1401 | /* Cache camera output window */ | ||
1402 | cam->width = mf.width; | ||
1403 | cam->height = mf.height; | ||
1404 | |||
1405 | /* 4. Calculate camera scales */ | 1406 | /* 4. Calculate camera scales */ |
1406 | scale_cam_h = calc_generic_scale(cam_rect->width, mf.width); | 1407 | scale_cam_h = calc_generic_scale(cam_rect->width, mf.width); |
1407 | scale_cam_v = calc_generic_scale(cam_rect->height, mf.height); | 1408 | scale_cam_v = calc_generic_scale(cam_rect->height, mf.height); |
@@ -1410,6 +1411,39 @@ static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd, | |||
1410 | interm_width = scale_down(rect->width, scale_cam_h); | 1411 | interm_width = scale_down(rect->width, scale_cam_h); |
1411 | interm_height = scale_down(rect->height, scale_cam_v); | 1412 | interm_height = scale_down(rect->height, scale_cam_v); |
1412 | 1413 | ||
1414 | if (interm_width < icd->user_width) { | ||
1415 | u32 new_scale_h; | ||
1416 | |||
1417 | new_scale_h = calc_generic_scale(rect->width, icd->user_width); | ||
1418 | |||
1419 | mf.width = scale_down(cam_rect->width, new_scale_h); | ||
1420 | } | ||
1421 | |||
1422 | if (interm_height < icd->user_height) { | ||
1423 | u32 new_scale_v; | ||
1424 | |||
1425 | new_scale_v = calc_generic_scale(rect->height, icd->user_height); | ||
1426 | |||
1427 | mf.height = scale_down(cam_rect->height, new_scale_v); | ||
1428 | } | ||
1429 | |||
1430 | if (interm_width < icd->user_width || interm_height < icd->user_height) { | ||
1431 | ret = v4l2_device_call_until_err(sd->v4l2_dev, (int)icd, video, | ||
1432 | s_mbus_fmt, &mf); | ||
1433 | if (ret < 0) | ||
1434 | return ret; | ||
1435 | |||
1436 | dev_geo(dev, "New camera output %ux%u\n", mf.width, mf.height); | ||
1437 | scale_cam_h = calc_generic_scale(cam_rect->width, mf.width); | ||
1438 | scale_cam_v = calc_generic_scale(cam_rect->height, mf.height); | ||
1439 | interm_width = scale_down(rect->width, scale_cam_h); | ||
1440 | interm_height = scale_down(rect->height, scale_cam_v); | ||
1441 | } | ||
1442 | |||
1443 | /* Cache camera output window */ | ||
1444 | cam->width = mf.width; | ||
1445 | cam->height = mf.height; | ||
1446 | |||
1413 | if (pcdev->image_mode) { | 1447 | if (pcdev->image_mode) { |
1414 | out_width = min(interm_width, icd->user_width); | 1448 | out_width = min(interm_width, icd->user_width); |
1415 | out_height = min(interm_height, icd->user_height); | 1449 | out_height = min(interm_height, icd->user_height); |
@@ -1725,6 +1759,63 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd, | |||
1725 | return ret; | 1759 | return ret; |
1726 | } | 1760 | } |
1727 | 1761 | ||
1762 | static int sh_mobile_ceu_set_livecrop(struct soc_camera_device *icd, | ||
1763 | struct v4l2_crop *a) | ||
1764 | { | ||
1765 | struct v4l2_subdev *sd = soc_camera_to_subdev(icd); | ||
1766 | struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); | ||
1767 | struct sh_mobile_ceu_dev *pcdev = ici->priv; | ||
1768 | u32 out_width = icd->user_width, out_height = icd->user_height; | ||
1769 | int ret; | ||
1770 | |||
1771 | /* Freeze queue */ | ||
1772 | pcdev->frozen = 1; | ||
1773 | /* Wait for frame */ | ||
1774 | ret = wait_for_completion_interruptible(&pcdev->complete); | ||
1775 | /* Stop the client */ | ||
1776 | ret = v4l2_subdev_call(sd, video, s_stream, 0); | ||
1777 | if (ret < 0) | ||
1778 | dev_warn(icd->dev.parent, | ||
1779 | "Client failed to stop the stream: %d\n", ret); | ||
1780 | else | ||
1781 | /* Do the crop, if it fails, there's nothing more we can do */ | ||
1782 | sh_mobile_ceu_set_crop(icd, a); | ||
1783 | |||
1784 | dev_geo(icd->dev.parent, "Output after crop: %ux%u\n", icd->user_width, icd->user_height); | ||
1785 | |||
1786 | if (icd->user_width != out_width || icd->user_height != out_height) { | ||
1787 | struct v4l2_format f = { | ||
1788 | .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, | ||
1789 | .fmt.pix = { | ||
1790 | .width = out_width, | ||
1791 | .height = out_height, | ||
1792 | .pixelformat = icd->current_fmt->host_fmt->fourcc, | ||
1793 | .field = pcdev->field, | ||
1794 | .colorspace = icd->colorspace, | ||
1795 | }, | ||
1796 | }; | ||
1797 | ret = sh_mobile_ceu_set_fmt(icd, &f); | ||
1798 | if (!ret && (out_width != f.fmt.pix.width || | ||
1799 | out_height != f.fmt.pix.height)) | ||
1800 | ret = -EINVAL; | ||
1801 | if (!ret) { | ||
1802 | icd->user_width = out_width; | ||
1803 | icd->user_height = out_height; | ||
1804 | ret = sh_mobile_ceu_set_bus_param(icd, | ||
1805 | icd->current_fmt->host_fmt->fourcc); | ||
1806 | } | ||
1807 | } | ||
1808 | |||
1809 | /* Thaw the queue */ | ||
1810 | pcdev->frozen = 0; | ||
1811 | spin_lock_irq(&pcdev->lock); | ||
1812 | sh_mobile_ceu_capture(pcdev); | ||
1813 | spin_unlock_irq(&pcdev->lock); | ||
1814 | /* Start the client */ | ||
1815 | ret = v4l2_subdev_call(sd, video, s_stream, 1); | ||
1816 | return ret; | ||
1817 | } | ||
1818 | |||
1728 | static unsigned int sh_mobile_ceu_poll(struct file *file, poll_table *pt) | 1819 | static unsigned int sh_mobile_ceu_poll(struct file *file, poll_table *pt) |
1729 | { | 1820 | { |
1730 | struct soc_camera_device *icd = file->private_data; | 1821 | struct soc_camera_device *icd = file->private_data; |
@@ -1811,6 +1902,7 @@ static struct soc_camera_host_ops sh_mobile_ceu_host_ops = { | |||
1811 | .put_formats = sh_mobile_ceu_put_formats, | 1902 | .put_formats = sh_mobile_ceu_put_formats, |
1812 | .get_crop = sh_mobile_ceu_get_crop, | 1903 | .get_crop = sh_mobile_ceu_get_crop, |
1813 | .set_crop = sh_mobile_ceu_set_crop, | 1904 | .set_crop = sh_mobile_ceu_set_crop, |
1905 | .set_livecrop = sh_mobile_ceu_set_livecrop, | ||
1814 | .set_fmt = sh_mobile_ceu_set_fmt, | 1906 | .set_fmt = sh_mobile_ceu_set_fmt, |
1815 | .try_fmt = sh_mobile_ceu_try_fmt, | 1907 | .try_fmt = sh_mobile_ceu_try_fmt, |
1816 | .set_ctrl = sh_mobile_ceu_set_ctrl, | 1908 | .set_ctrl = sh_mobile_ceu_set_ctrl, |
@@ -1877,6 +1969,7 @@ static int __devinit sh_mobile_ceu_probe(struct platform_device *pdev) | |||
1877 | 1969 | ||
1878 | INIT_LIST_HEAD(&pcdev->capture); | 1970 | INIT_LIST_HEAD(&pcdev->capture); |
1879 | spin_lock_init(&pcdev->lock); | 1971 | spin_lock_init(&pcdev->lock); |
1972 | init_completion(&pcdev->complete); | ||
1880 | 1973 | ||
1881 | pcdev->pdata = pdev->dev.platform_data; | 1974 | pcdev->pdata = pdev->dev.platform_data; |
1882 | if (!pcdev->pdata) { | 1975 | if (!pcdev->pdata) { |