diff options
Diffstat (limited to 'drivers/media/video/sh_mobile_ceu_camera.c')
-rw-r--r-- | drivers/media/video/sh_mobile_ceu_camera.c | 148 |
1 files changed, 131 insertions, 17 deletions
diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c index 134e86bf6d9..3ae5c9c58cb 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) |
@@ -427,6 +434,25 @@ static int sh_mobile_ceu_videobuf_init(struct vb2_buffer *vb) | |||
427 | return 0; | 434 | return 0; |
428 | } | 435 | } |
429 | 436 | ||
437 | static int sh_mobile_ceu_stop_streaming(struct vb2_queue *q) | ||
438 | { | ||
439 | struct soc_camera_device *icd = container_of(q, struct soc_camera_device, vb2_vidq); | ||
440 | struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); | ||
441 | struct sh_mobile_ceu_dev *pcdev = ici->priv; | ||
442 | struct list_head *buf_head, *tmp; | ||
443 | |||
444 | spin_lock_irq(&pcdev->lock); | ||
445 | |||
446 | pcdev->active = NULL; | ||
447 | |||
448 | list_for_each_safe(buf_head, tmp, &pcdev->capture) | ||
449 | list_del_init(buf_head); | ||
450 | |||
451 | spin_unlock_irq(&pcdev->lock); | ||
452 | |||
453 | return sh_mobile_ceu_soft_reset(pcdev); | ||
454 | } | ||
455 | |||
430 | static struct vb2_ops sh_mobile_ceu_videobuf_ops = { | 456 | static struct vb2_ops sh_mobile_ceu_videobuf_ops = { |
431 | .queue_setup = sh_mobile_ceu_videobuf_setup, | 457 | .queue_setup = sh_mobile_ceu_videobuf_setup, |
432 | .buf_prepare = sh_mobile_ceu_videobuf_prepare, | 458 | .buf_prepare = sh_mobile_ceu_videobuf_prepare, |
@@ -435,6 +461,7 @@ static struct vb2_ops sh_mobile_ceu_videobuf_ops = { | |||
435 | .buf_init = sh_mobile_ceu_videobuf_init, | 461 | .buf_init = sh_mobile_ceu_videobuf_init, |
436 | .wait_prepare = soc_camera_unlock, | 462 | .wait_prepare = soc_camera_unlock, |
437 | .wait_finish = soc_camera_lock, | 463 | .wait_finish = soc_camera_lock, |
464 | .stop_streaming = sh_mobile_ceu_stop_streaming, | ||
438 | }; | 465 | }; |
439 | 466 | ||
440 | static irqreturn_t sh_mobile_ceu_irq(int irq, void *data) | 467 | static irqreturn_t sh_mobile_ceu_irq(int irq, void *data) |
@@ -500,7 +527,6 @@ static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd) | |||
500 | { | 527 | { |
501 | 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); |
502 | struct sh_mobile_ceu_dev *pcdev = ici->priv; | 529 | struct sh_mobile_ceu_dev *pcdev = ici->priv; |
503 | unsigned long flags; | ||
504 | 530 | ||
505 | BUG_ON(icd != pcdev->icd); | 531 | BUG_ON(icd != pcdev->icd); |
506 | 532 | ||
@@ -509,13 +535,13 @@ static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd) | |||
509 | sh_mobile_ceu_soft_reset(pcdev); | 535 | sh_mobile_ceu_soft_reset(pcdev); |
510 | 536 | ||
511 | /* make sure active buffer is canceled */ | 537 | /* make sure active buffer is canceled */ |
512 | spin_lock_irqsave(&pcdev->lock, flags); | 538 | spin_lock_irq(&pcdev->lock); |
513 | if (pcdev->active) { | 539 | if (pcdev->active) { |
514 | list_del_init(&to_ceu_vb(pcdev->active)->queue); | 540 | list_del_init(&to_ceu_vb(pcdev->active)->queue); |
515 | vb2_buffer_done(pcdev->active, VB2_BUF_STATE_ERROR); | 541 | vb2_buffer_done(pcdev->active, VB2_BUF_STATE_ERROR); |
516 | pcdev->active = NULL; | 542 | pcdev->active = NULL; |
517 | } | 543 | } |
518 | spin_unlock_irqrestore(&pcdev->lock, flags); | 544 | spin_unlock_irq(&pcdev->lock); |
519 | 545 | ||
520 | pm_runtime_put_sync(ici->v4l2_dev.dev); | 546 | pm_runtime_put_sync(ici->v4l2_dev.dev); |
521 | 547 | ||
@@ -891,8 +917,8 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int | |||
891 | 917 | ||
892 | fmt = soc_mbus_get_fmtdesc(code); | 918 | fmt = soc_mbus_get_fmtdesc(code); |
893 | if (!fmt) { | 919 | if (!fmt) { |
894 | dev_err(dev, "Invalid format code #%u: %d\n", idx, code); | 920 | dev_warn(dev, "unsupported format code #%u: %d\n", idx, code); |
895 | return -EINVAL; | 921 | return 0; |
896 | } | 922 | } |
897 | 923 | ||
898 | if (!pcdev->pdata->csi2_dev) { | 924 | if (!pcdev->pdata->csi2_dev) { |
@@ -1330,7 +1356,7 @@ static int client_scale(struct soc_camera_device *icd, | |||
1330 | /* | 1356 | /* |
1331 | * 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 |
1332 | * framerate by always requesting the maximum image from the client. See | 1358 | * framerate by always requesting the maximum image from the client. See |
1333 | * Documentation/video4linux/sh_mobile_camera_ceu.txt for a description of | 1359 | * Documentation/video4linux/sh_mobile_ceu_camera.txt for a description of |
1334 | * 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. |
1335 | */ | 1361 | */ |
1336 | 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, |
@@ -1377,10 +1403,6 @@ static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd, | |||
1377 | if (mf.width > 2560 || mf.height > 1920) | 1403 | if (mf.width > 2560 || mf.height > 1920) |
1378 | return -EINVAL; | 1404 | return -EINVAL; |
1379 | 1405 | ||
1380 | /* Cache camera output window */ | ||
1381 | cam->width = mf.width; | ||
1382 | cam->height = mf.height; | ||
1383 | |||
1384 | /* 4. Calculate camera scales */ | 1406 | /* 4. Calculate camera scales */ |
1385 | scale_cam_h = calc_generic_scale(cam_rect->width, mf.width); | 1407 | scale_cam_h = calc_generic_scale(cam_rect->width, mf.width); |
1386 | scale_cam_v = calc_generic_scale(cam_rect->height, mf.height); | 1408 | scale_cam_v = calc_generic_scale(cam_rect->height, mf.height); |
@@ -1389,6 +1411,39 @@ static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd, | |||
1389 | interm_width = scale_down(rect->width, scale_cam_h); | 1411 | interm_width = scale_down(rect->width, scale_cam_h); |
1390 | interm_height = scale_down(rect->height, scale_cam_v); | 1412 | interm_height = scale_down(rect->height, scale_cam_v); |
1391 | 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 | |||
1392 | if (pcdev->image_mode) { | 1447 | if (pcdev->image_mode) { |
1393 | out_width = min(interm_width, icd->user_width); | 1448 | out_width = min(interm_width, icd->user_width); |
1394 | out_height = min(interm_height, icd->user_height); | 1449 | out_height = min(interm_height, icd->user_height); |
@@ -1704,6 +1759,63 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd, | |||
1704 | return ret; | 1759 | return ret; |
1705 | } | 1760 | } |
1706 | 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 | |||
1707 | 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) |
1708 | { | 1820 | { |
1709 | struct soc_camera_device *icd = file->private_data; | 1821 | struct soc_camera_device *icd = file->private_data; |
@@ -1790,6 +1902,7 @@ static struct soc_camera_host_ops sh_mobile_ceu_host_ops = { | |||
1790 | .put_formats = sh_mobile_ceu_put_formats, | 1902 | .put_formats = sh_mobile_ceu_put_formats, |
1791 | .get_crop = sh_mobile_ceu_get_crop, | 1903 | .get_crop = sh_mobile_ceu_get_crop, |
1792 | .set_crop = sh_mobile_ceu_set_crop, | 1904 | .set_crop = sh_mobile_ceu_set_crop, |
1905 | .set_livecrop = sh_mobile_ceu_set_livecrop, | ||
1793 | .set_fmt = sh_mobile_ceu_set_fmt, | 1906 | .set_fmt = sh_mobile_ceu_set_fmt, |
1794 | .try_fmt = sh_mobile_ceu_try_fmt, | 1907 | .try_fmt = sh_mobile_ceu_try_fmt, |
1795 | .set_ctrl = sh_mobile_ceu_set_ctrl, | 1908 | .set_ctrl = sh_mobile_ceu_set_ctrl, |
@@ -1856,6 +1969,7 @@ static int __devinit sh_mobile_ceu_probe(struct platform_device *pdev) | |||
1856 | 1969 | ||
1857 | INIT_LIST_HEAD(&pcdev->capture); | 1970 | INIT_LIST_HEAD(&pcdev->capture); |
1858 | spin_lock_init(&pcdev->lock); | 1971 | spin_lock_init(&pcdev->lock); |
1972 | init_completion(&pcdev->complete); | ||
1859 | 1973 | ||
1860 | pcdev->pdata = pdev->dev.platform_data; | 1974 | pcdev->pdata = pdev->dev.platform_data; |
1861 | if (!pcdev->pdata) { | 1975 | if (!pcdev->pdata) { |