From 1ecddfca9156dfbb03569a313f53bfdb539a72a3 Mon Sep 17 00:00:00 2001 From: Koen Kooi Date: Thu, 27 Jan 2011 19:23:37 +0100 Subject: linux-omap 2.6.37: add Laurents ISP patchset Signed-off-by: Koen Kooi --- .../linux/linux-omap-2.6.37/beagleboard/defconfig | 8 +- ...ode-between-video_usercopy-and-video_ioct.patch | 297 + ...-v4l-subdev-Don-t-require-core-operations.patch | 31 + ...Merge-v4l2_i2c_new_subdev_cfg-and-v4l2_i2.patch | 132 + .../0004-v4l-subdev-Add-device-node-support.patch | 615 + ...ev-Uninline-the-v4l2_subdev_init-function.patch | 103 + .../0006-v4l-subdev-Control-ioctls-support.patch | 88 + .../media/0007-v4l-subdev-Events-support.patch | 223 + .../0008-media-Media-device-node-support.patch | 500 + .../media/0009-media-Media-device.patch | 398 + .../media/0010-media-Entities-pads-and-links.patch | 690 + .../media/0011-media-Entity-graph-traversal.patch | 228 + .../media/0012-media-Entity-use-count.patch | 176 + ...0013-media-Media-device-information-query.patch | 659 + ...media-Entities-pads-and-links-enumeration.patch | 889 + .../media/0015-media-Links-setup.patch | 517 + .../0016-media-Pipelines-and-media-streams.patch | 259 + ...edia_device-pointer-to-the-v4l2_device-st.patch | 115 + ...ke-video_device-inherit-from-media_entity.patch | 234 + ...ake-v4l2_subdev-inherit-from-media_entity.patch | 265 + ...e-media-v4l2-mediabus.h-header-to-include.patch | 205 + ...-enums-with-fixed-sized-fields-in-public-.patch | 50 + ...V4L2_MBUS_FMT_GREY8_1X8-to-V4L2_MBUS_FMT_.patch | 154 + ...edia-bus-pixel-codes-by-types-and-sort-th.patch | 112 + ...-Create-v4l2-subdev-file-handle-structure.patch | 243 + ...4l-subdev-Add-a-new-file-operations-class.patch | 93 + ...0026-v4l-v4l2_subdev-pad-level-operations.patch | 49 + ...0028-v4l-v4l2_subdev-userspace-format-API.patch | 3546 +++ ...-v4l2_subdev-userspace-frame-interval-API.patch | 479 + .../0030-v4l-v4l2_subdev-userspace-crop-API.patch | 350 + .../0031-v4l-subdev-Generic-ioctl-support.patch | 46 + ...Add-subdev-sensor-g_skip_frames-operation.patch | 35 + ...e-linux-videodev2.h-in-media-v4l2-ctrls.h.patch | 27 + ...a-use-before-set-in-the-control-framework.patch | 32 + ...it-YUYV-on-16-bit-bus-and-SGRBG10-media-b.patch | 60 + ...aining-RAW10-patterns-w-DPCM-pixel-code-v.patch | 48 + ...d-missing-12-bits-bayer-media-bus-formats.patch | 105 + .../0038-v4l-Add-12-bits-bayer-pixel-formats.patch | 35 + ...pdate-Camera-ISP-definitions-for-OMAP3630.patch | 99 + ...0-omap3-Remove-unusued-ISP-CBUFF-resource.patch | 32 + ...unction-to-register-omap3isp-platform-dev.patch | 91 + ...-omap2-Fix-camera-resources-for-multiomap.patch | 70 + .../media/0043-OMAP3-ISP-driver.patch | 21513 +++++++++++++++++++ recipes/linux/linux-omap_2.6.37.bb | 43 + 44 files changed, 33943 insertions(+), 1 deletion(-) create mode 100644 recipes/linux/linux-omap-2.6.37/media/0001-v4l-Share-code-between-video_usercopy-and-video_ioct.patch create mode 100644 recipes/linux/linux-omap-2.6.37/media/0002-v4l-subdev-Don-t-require-core-operations.patch create mode 100644 recipes/linux/linux-omap-2.6.37/media/0003-v4l-subdev-Merge-v4l2_i2c_new_subdev_cfg-and-v4l2_i2.patch create mode 100644 recipes/linux/linux-omap-2.6.37/media/0004-v4l-subdev-Add-device-node-support.patch create mode 100644 recipes/linux/linux-omap-2.6.37/media/0005-v4l-subdev-Uninline-the-v4l2_subdev_init-function.patch create mode 100644 recipes/linux/linux-omap-2.6.37/media/0006-v4l-subdev-Control-ioctls-support.patch create mode 100644 recipes/linux/linux-omap-2.6.37/media/0007-v4l-subdev-Events-support.patch create mode 100644 recipes/linux/linux-omap-2.6.37/media/0008-media-Media-device-node-support.patch create mode 100644 recipes/linux/linux-omap-2.6.37/media/0009-media-Media-device.patch create mode 100644 recipes/linux/linux-omap-2.6.37/media/0010-media-Entities-pads-and-links.patch create mode 100644 recipes/linux/linux-omap-2.6.37/media/0011-media-Entity-graph-traversal.patch create mode 100644 recipes/linux/linux-omap-2.6.37/media/0012-media-Entity-use-count.patch create mode 100644 recipes/linux/linux-omap-2.6.37/media/0013-media-Media-device-information-query.patch create mode 100644 recipes/linux/linux-omap-2.6.37/media/0014-media-Entities-pads-and-links-enumeration.patch create mode 100644 recipes/linux/linux-omap-2.6.37/media/0015-media-Links-setup.patch create mode 100644 recipes/linux/linux-omap-2.6.37/media/0016-media-Pipelines-and-media-streams.patch create mode 100644 recipes/linux/linux-omap-2.6.37/media/0017-v4l-Add-a-media_device-pointer-to-the-v4l2_device-st.patch create mode 100644 recipes/linux/linux-omap-2.6.37/media/0018-v4l-Make-video_device-inherit-from-media_entity.patch create mode 100644 recipes/linux/linux-omap-2.6.37/media/0019-v4l-Make-v4l2_subdev-inherit-from-media_entity.patch create mode 100644 recipes/linux/linux-omap-2.6.37/media/0020-v4l-Move-the-media-v4l2-mediabus.h-header-to-include.patch create mode 100644 recipes/linux/linux-omap-2.6.37/media/0021-v4l-Replace-enums-with-fixed-sized-fields-in-public-.patch create mode 100644 recipes/linux/linux-omap-2.6.37/media/0022-v4l-Rename-V4L2_MBUS_FMT_GREY8_1X8-to-V4L2_MBUS_FMT_.patch create mode 100644 recipes/linux/linux-omap-2.6.37/media/0023-v4l-Group-media-bus-pixel-codes-by-types-and-sort-th.patch create mode 100644 recipes/linux/linux-omap-2.6.37/media/0024-v4l-Create-v4l2-subdev-file-handle-structure.patch create mode 100644 recipes/linux/linux-omap-2.6.37/media/0025-v4l-subdev-Add-a-new-file-operations-class.patch create mode 100644 recipes/linux/linux-omap-2.6.37/media/0026-v4l-v4l2_subdev-pad-level-operations.patch create mode 100644 recipes/linux/linux-omap-2.6.37/media/0028-v4l-v4l2_subdev-userspace-format-API.patch create mode 100644 recipes/linux/linux-omap-2.6.37/media/0029-v4l-v4l2_subdev-userspace-frame-interval-API.patch create mode 100644 recipes/linux/linux-omap-2.6.37/media/0030-v4l-v4l2_subdev-userspace-crop-API.patch create mode 100644 recipes/linux/linux-omap-2.6.37/media/0031-v4l-subdev-Generic-ioctl-support.patch create mode 100644 recipes/linux/linux-omap-2.6.37/media/0032-v4l-Add-subdev-sensor-g_skip_frames-operation.patch create mode 100644 recipes/linux/linux-omap-2.6.37/media/0033-v4l-Include-linux-videodev2.h-in-media-v4l2-ctrls.h.patch create mode 100644 recipes/linux/linux-omap-2.6.37/media/0034-v4l-Fix-a-use-before-set-in-the-control-framework.patch create mode 100644 recipes/linux/linux-omap-2.6.37/media/0035-v4l-Add-8-bit-YUYV-on-16-bit-bus-and-SGRBG10-media-b.patch create mode 100644 recipes/linux/linux-omap-2.6.37/media/0036-v4l-Add-remaining-RAW10-patterns-w-DPCM-pixel-code-v.patch create mode 100644 recipes/linux/linux-omap-2.6.37/media/0037-v4l-Add-missing-12-bits-bayer-media-bus-formats.patch create mode 100644 recipes/linux/linux-omap-2.6.37/media/0038-v4l-Add-12-bits-bayer-pixel-formats.patch create mode 100644 recipes/linux/linux-omap-2.6.37/media/0039-ARM-OMAP3-Update-Camera-ISP-definitions-for-OMAP3630.patch create mode 100644 recipes/linux/linux-omap-2.6.37/media/0040-omap3-Remove-unusued-ISP-CBUFF-resource.patch create mode 100644 recipes/linux/linux-omap-2.6.37/media/0041-omap3-Add-function-to-register-omap3isp-platform-dev.patch create mode 100644 recipes/linux/linux-omap-2.6.37/media/0042-omap2-Fix-camera-resources-for-multiomap.patch create mode 100644 recipes/linux/linux-omap-2.6.37/media/0043-OMAP3-ISP-driver.patch diff --git a/recipes/linux/linux-omap-2.6.37/beagleboard/defconfig b/recipes/linux/linux-omap-2.6.37/beagleboard/defconfig index cb21bec1f5..738d894964 100644 --- a/recipes/linux/linux-omap-2.6.37/beagleboard/defconfig +++ b/recipes/linux/linux-omap-2.6.37/beagleboard/defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux/arm 2.6.37 Kernel Configuration -# Sun Jan 23 11:58:18 2011 +# Thu Jan 27 16:18:14 2011 # CONFIG_ARM=y CONFIG_HAVE_PWM=y @@ -286,6 +286,8 @@ CONFIG_OMAP_RESET_CLOCKS=y CONFIG_OMAP_MCBSP=y CONFIG_OMAP_MBOX_FWK=m CONFIG_OMAP_MBOX_KFIFO_SIZE=256 +CONFIG_OMAP_IOMMU=m +CONFIG_OMAP_IOMMU_DEBUG=m # CONFIG_OMAP_MPU_TIMER is not set CONFIG_OMAP_32K_TIMER=y # CONFIG_OMAP3_L2_AUX_SECURE_SAVE_RESTORE is not set @@ -1846,10 +1848,12 @@ CONFIG_MEDIA_SUPPORT=y # # Multimedia core support # +CONFIG_MEDIA_CONTROLLER=y CONFIG_VIDEO_DEV=y CONFIG_VIDEO_V4L2_COMMON=y CONFIG_VIDEO_ALLOW_V4L1=y CONFIG_VIDEO_V4L1_COMPAT=y +CONFIG_VIDEO_V4L2_SUBDEV_API=y CONFIG_DVB_CORE=m CONFIG_VIDEO_MEDIA=m @@ -1991,6 +1995,8 @@ CONFIG_VIDEO_OMAP2_VOUT=y # CONFIG_VIDEO_CPIA2 is not set # CONFIG_VIDEO_AU0828 is not set CONFIG_VIDEO_SR030PC30=m +CONFIG_VIDEO_OMAP3=y +# CONFIG_VIDEO_OMAP3_DEBUG is not set # CONFIG_SOC_CAMERA is not set CONFIG_V4L_USB_DRIVERS=y CONFIG_USB_VIDEO_CLASS=m diff --git a/recipes/linux/linux-omap-2.6.37/media/0001-v4l-Share-code-between-video_usercopy-and-video_ioct.patch b/recipes/linux/linux-omap-2.6.37/media/0001-v4l-Share-code-between-video_usercopy-and-video_ioct.patch new file mode 100644 index 0000000000..f212020188 --- /dev/null +++ b/recipes/linux/linux-omap-2.6.37/media/0001-v4l-Share-code-between-video_usercopy-and-video_ioct.patch @@ -0,0 +1,297 @@ +From d71c2e533be956a95e4ddde8b87f657ada3c9de3 Mon Sep 17 00:00:00 2001 +From: Laurent Pinchart +Date: Mon, 12 Jul 2010 16:09:41 +0200 +Subject: [PATCH 01/43] v4l: Share code between video_usercopy and video_ioctl2 + +The two functions are mostly identical. They handle the copy_from_user +and copy_to_user operations related with V4L2 ioctls and call the real +ioctl handler. + +Create a __video_usercopy function that implements the core of +video_usercopy and video_ioctl2, and call that function from both. + +Signed-off-by: Laurent Pinchart +--- + drivers/media/video/v4l2-ioctl.c | 218 ++++++++++++------------------------- + 1 files changed, 71 insertions(+), 147 deletions(-) + +diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c +index dd9283f..1e01554 100644 +--- a/drivers/media/video/v4l2-ioctl.c ++++ b/drivers/media/video/v4l2-ioctl.c +@@ -374,35 +374,62 @@ video_fix_command(unsigned int cmd) + } + #endif + +-/* +- * Obsolete usercopy function - Should be removed soon +- */ +-long +-video_usercopy(struct file *file, unsigned int cmd, unsigned long arg, ++/* In some cases, only a few fields are used as input, i.e. when the app sets ++ * "index" and then the driver fills in the rest of the structure for the thing ++ * with that index. We only need to copy up the first non-input field. */ ++static unsigned long cmd_input_size(unsigned int cmd) ++{ ++ /* Size of structure up to and including 'field' */ ++#define CMDINSIZE(cmd, type, field) \ ++ case VIDIOC_##cmd: \ ++ return offsetof(struct v4l2_##type, field) + \ ++ sizeof(((struct v4l2_##type *)0)->field); ++ ++ switch (cmd) { ++ CMDINSIZE(ENUM_FMT, fmtdesc, type); ++ CMDINSIZE(G_FMT, format, type); ++ CMDINSIZE(QUERYBUF, buffer, type); ++ CMDINSIZE(G_PARM, streamparm, type); ++ CMDINSIZE(ENUMSTD, standard, index); ++ CMDINSIZE(ENUMINPUT, input, index); ++ CMDINSIZE(G_CTRL, control, id); ++ CMDINSIZE(G_TUNER, tuner, index); ++ CMDINSIZE(QUERYCTRL, queryctrl, id); ++ CMDINSIZE(QUERYMENU, querymenu, index); ++ CMDINSIZE(ENUMOUTPUT, output, index); ++ CMDINSIZE(G_MODULATOR, modulator, index); ++ CMDINSIZE(G_FREQUENCY, frequency, tuner); ++ CMDINSIZE(CROPCAP, cropcap, type); ++ CMDINSIZE(G_CROP, crop, type); ++ CMDINSIZE(ENUMAUDIO, audio, index); ++ CMDINSIZE(ENUMAUDOUT, audioout, index); ++ CMDINSIZE(ENCODER_CMD, encoder_cmd, flags); ++ CMDINSIZE(TRY_ENCODER_CMD, encoder_cmd, flags); ++ CMDINSIZE(G_SLICED_VBI_CAP, sliced_vbi_cap, type); ++ CMDINSIZE(ENUM_FRAMESIZES, frmsizeenum, pixel_format); ++ CMDINSIZE(ENUM_FRAMEINTERVALS, frmivalenum, height); ++ default: ++ return _IOC_SIZE(cmd); ++ } ++} ++ ++static long ++__video_usercopy(struct file *file, unsigned int cmd, unsigned long arg, + v4l2_kioctl func) + { + char sbuf[128]; + void *mbuf = NULL; +- void *parg = NULL; ++ void *parg = (void *)arg; + long err = -EINVAL; + int is_ext_ctrl; + size_t ctrls_size = 0; + void __user *user_ptr = NULL; + +-#ifdef __OLD_VIDIOC_ +- cmd = video_fix_command(cmd); +-#endif + is_ext_ctrl = (cmd == VIDIOC_S_EXT_CTRLS || cmd == VIDIOC_G_EXT_CTRLS || + cmd == VIDIOC_TRY_EXT_CTRLS); + + /* Copy arguments into temp kernel buffer */ +- switch (_IOC_DIR(cmd)) { +- case _IOC_NONE: +- parg = NULL; +- break; +- case _IOC_READ: +- case _IOC_WRITE: +- case (_IOC_WRITE | _IOC_READ): ++ if (_IOC_DIR(cmd) != _IOC_NONE) { + if (_IOC_SIZE(cmd) <= sizeof(sbuf)) { + parg = sbuf; + } else { +@@ -414,11 +441,21 @@ video_usercopy(struct file *file, unsigned int cmd, unsigned long arg, + } + + err = -EFAULT; +- if (_IOC_DIR(cmd) & _IOC_WRITE) +- if (copy_from_user(parg, (void __user *)arg, _IOC_SIZE(cmd))) ++ if (_IOC_DIR(cmd) & _IOC_WRITE) { ++ unsigned long n = cmd_input_size(cmd); ++ ++ if (copy_from_user(parg, (void __user *)arg, n)) + goto out; +- break; ++ ++ /* zero out anything we don't copy from userspace */ ++ if (n < _IOC_SIZE(cmd)) ++ memset((u8 *)parg + n, 0, _IOC_SIZE(cmd) - n); ++ } else { ++ /* read-only ioctl */ ++ memset(parg, 0, _IOC_SIZE(cmd)); ++ } + } ++ + if (is_ext_ctrl) { + struct v4l2_ext_controls *p = parg; + +@@ -440,7 +477,7 @@ video_usercopy(struct file *file, unsigned int cmd, unsigned long arg, + } + } + +- /* call driver */ ++ /* Handles IOCTL */ + err = func(file, cmd, parg); + if (err == -ENOIOCTLCMD) + err = -EINVAL; +@@ -469,6 +506,19 @@ out: + kfree(mbuf); + return err; + } ++ ++/* ++ * Obsolete usercopy function - Should be removed soon ++ */ ++long ++video_usercopy(struct file *file, unsigned int cmd, unsigned long arg, ++ v4l2_kioctl func) ++{ ++#ifdef __OLD_VIDIOC_ ++ cmd = video_fix_command(cmd); ++#endif ++ return __video_usercopy(file, cmd, arg, func); ++} + EXPORT_SYMBOL(video_usercopy); + + static void dbgbuf(unsigned int cmd, struct video_device *vfd, +@@ -2041,138 +2091,12 @@ static long __video_do_ioctl(struct file *file, + return ret; + } + +-/* In some cases, only a few fields are used as input, i.e. when the app sets +- * "index" and then the driver fills in the rest of the structure for the thing +- * with that index. We only need to copy up the first non-input field. */ +-static unsigned long cmd_input_size(unsigned int cmd) +-{ +- /* Size of structure up to and including 'field' */ +-#define CMDINSIZE(cmd, type, field) \ +- case VIDIOC_##cmd: \ +- return offsetof(struct v4l2_##type, field) + \ +- sizeof(((struct v4l2_##type *)0)->field); +- +- switch (cmd) { +- CMDINSIZE(ENUM_FMT, fmtdesc, type); +- CMDINSIZE(G_FMT, format, type); +- CMDINSIZE(QUERYBUF, buffer, type); +- CMDINSIZE(G_PARM, streamparm, type); +- CMDINSIZE(ENUMSTD, standard, index); +- CMDINSIZE(ENUMINPUT, input, index); +- CMDINSIZE(G_CTRL, control, id); +- CMDINSIZE(G_TUNER, tuner, index); +- CMDINSIZE(QUERYCTRL, queryctrl, id); +- CMDINSIZE(QUERYMENU, querymenu, index); +- CMDINSIZE(ENUMOUTPUT, output, index); +- CMDINSIZE(G_MODULATOR, modulator, index); +- CMDINSIZE(G_FREQUENCY, frequency, tuner); +- CMDINSIZE(CROPCAP, cropcap, type); +- CMDINSIZE(G_CROP, crop, type); +- CMDINSIZE(ENUMAUDIO, audio, index); +- CMDINSIZE(ENUMAUDOUT, audioout, index); +- CMDINSIZE(ENCODER_CMD, encoder_cmd, flags); +- CMDINSIZE(TRY_ENCODER_CMD, encoder_cmd, flags); +- CMDINSIZE(G_SLICED_VBI_CAP, sliced_vbi_cap, type); +- CMDINSIZE(ENUM_FRAMESIZES, frmsizeenum, pixel_format); +- CMDINSIZE(ENUM_FRAMEINTERVALS, frmivalenum, height); +- default: +- return _IOC_SIZE(cmd); +- } +-} +- + long video_ioctl2(struct file *file, + unsigned int cmd, unsigned long arg) + { +- char sbuf[128]; +- void *mbuf = NULL; +- void *parg = (void *)arg; +- long err = -EINVAL; +- int is_ext_ctrl; +- size_t ctrls_size = 0; +- void __user *user_ptr = NULL; +- + #ifdef __OLD_VIDIOC_ + cmd = video_fix_command(cmd); + #endif +- is_ext_ctrl = (cmd == VIDIOC_S_EXT_CTRLS || cmd == VIDIOC_G_EXT_CTRLS || +- cmd == VIDIOC_TRY_EXT_CTRLS); +- +- /* Copy arguments into temp kernel buffer */ +- if (_IOC_DIR(cmd) != _IOC_NONE) { +- if (_IOC_SIZE(cmd) <= sizeof(sbuf)) { +- parg = sbuf; +- } else { +- /* too big to allocate from stack */ +- mbuf = kmalloc(_IOC_SIZE(cmd), GFP_KERNEL); +- if (NULL == mbuf) +- return -ENOMEM; +- parg = mbuf; +- } +- +- err = -EFAULT; +- if (_IOC_DIR(cmd) & _IOC_WRITE) { +- unsigned long n = cmd_input_size(cmd); +- +- if (copy_from_user(parg, (void __user *)arg, n)) +- goto out; +- +- /* zero out anything we don't copy from userspace */ +- if (n < _IOC_SIZE(cmd)) +- memset((u8 *)parg + n, 0, _IOC_SIZE(cmd) - n); +- } else { +- /* read-only ioctl */ +- memset(parg, 0, _IOC_SIZE(cmd)); +- } +- } +- +- if (is_ext_ctrl) { +- struct v4l2_ext_controls *p = parg; +- +- /* In case of an error, tell the caller that it wasn't +- a specific control that caused it. */ +- p->error_idx = p->count; +- user_ptr = (void __user *)p->controls; +- if (p->count) { +- ctrls_size = sizeof(struct v4l2_ext_control) * p->count; +- /* Note: v4l2_ext_controls fits in sbuf[] so mbuf is still NULL. */ +- mbuf = kmalloc(ctrls_size, GFP_KERNEL); +- err = -ENOMEM; +- if (NULL == mbuf) +- goto out_ext_ctrl; +- err = -EFAULT; +- if (copy_from_user(mbuf, user_ptr, ctrls_size)) +- goto out_ext_ctrl; +- p->controls = mbuf; +- } +- } +- +- /* Handles IOCTL */ +- err = __video_do_ioctl(file, cmd, parg); +- if (err == -ENOIOCTLCMD) +- err = -EINVAL; +- if (is_ext_ctrl) { +- struct v4l2_ext_controls *p = parg; +- +- p->controls = (void *)user_ptr; +- if (p->count && err == 0 && copy_to_user(user_ptr, mbuf, ctrls_size)) +- err = -EFAULT; +- goto out_ext_ctrl; +- } +- if (err < 0) +- goto out; +- +-out_ext_ctrl: +- /* Copy results into user buffer */ +- switch (_IOC_DIR(cmd)) { +- case _IOC_READ: +- case (_IOC_WRITE | _IOC_READ): +- if (copy_to_user((void __user *)arg, parg, _IOC_SIZE(cmd))) +- err = -EFAULT; +- break; +- } +- +-out: +- kfree(mbuf); +- return err; ++ return __video_usercopy(file, cmd, arg, __video_do_ioctl); + } + EXPORT_SYMBOL(video_ioctl2); +-- +1.6.6.1 + diff --git a/recipes/linux/linux-omap-2.6.37/media/0002-v4l-subdev-Don-t-require-core-operations.patch b/recipes/linux/linux-omap-2.6.37/media/0002-v4l-subdev-Don-t-require-core-operations.patch new file mode 100644 index 0000000000..c53c18e479 --- /dev/null +++ b/recipes/linux/linux-omap-2.6.37/media/0002-v4l-subdev-Don-t-require-core-operations.patch @@ -0,0 +1,31 @@ +From e501e49dfa290479eaf23fcc5bd0623102220e0c Mon Sep 17 00:00:00 2001 +From: Laurent Pinchart +Date: Mon, 31 May 2010 11:33:06 +0300 +Subject: [PATCH 02/43] v4l: subdev: Don't require core operations + +There's no reason to require subdevices to implement the core +operations. Remove the check for non-NULL core operations when +initializing the subdev. + +Signed-off-by: Laurent Pinchart +--- + include/media/v4l2-subdev.h | 3 +-- + 1 files changed, 1 insertions(+), 2 deletions(-) + +diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h +index b0316a7..b636444 100644 +--- a/include/media/v4l2-subdev.h ++++ b/include/media/v4l2-subdev.h +@@ -466,8 +466,7 @@ static inline void v4l2_subdev_init(struct v4l2_subdev *sd, + const struct v4l2_subdev_ops *ops) + { + INIT_LIST_HEAD(&sd->list); +- /* ops->core MUST be set */ +- BUG_ON(!ops || !ops->core); ++ BUG_ON(!ops); + sd->ops = ops; + sd->v4l2_dev = NULL; + sd->flags = 0; +-- +1.6.6.1 + diff --git a/recipes/linux/linux-omap-2.6.37/media/0003-v4l-subdev-Merge-v4l2_i2c_new_subdev_cfg-and-v4l2_i2.patch b/recipes/linux/linux-omap-2.6.37/media/0003-v4l-subdev-Merge-v4l2_i2c_new_subdev_cfg-and-v4l2_i2.patch new file mode 100644 index 0000000000..6cca7eaa6c --- /dev/null +++ b/recipes/linux/linux-omap-2.6.37/media/0003-v4l-subdev-Merge-v4l2_i2c_new_subdev_cfg-and-v4l2_i2.patch @@ -0,0 +1,132 @@ +From 2c7009851d70caeb91ac806b133b7d77c5c2ca19 Mon Sep 17 00:00:00 2001 +From: Laurent Pinchart +Date: Thu, 8 Jul 2010 12:01:09 +0200 +Subject: [PATCH 03/43] v4l: subdev: Merge v4l2_i2c_new_subdev_cfg and v4l2_i2c_new_subdev + +v4l2_i2c_new_subdev is a thin wrapper around v4l2_i2c_new_subdev_cfg, +which is itself a wrapper around v4l2_i2c_new_subdev_board. + +The intermediate v4l2_i2c_new_subdev_cfg function is called directly by +the ivtv and cafe-ccic drivers only. Merge it with v4l2_i2c_new_subdev +and use v4l2_i2c_new_subdev_board in the ivtv and cafe-ccic drivers. + +Signed-off-by: Laurent Pinchart +--- + drivers/media/video/cafe_ccic.c | 11 +++++++++-- + drivers/media/video/ivtv/ivtv-i2c.c | 11 +++++++++-- + drivers/media/video/v4l2-common.c | 7 ++----- + include/media/v4l2-common.h | 13 +------------ + 4 files changed, 21 insertions(+), 21 deletions(-) + +diff --git a/drivers/media/video/cafe_ccic.c b/drivers/media/video/cafe_ccic.c +index 0dfff50..6e23add 100644 +--- a/drivers/media/video/cafe_ccic.c ++++ b/drivers/media/video/cafe_ccic.c +@@ -1992,6 +1992,7 @@ static int cafe_pci_probe(struct pci_dev *pdev, + { + int ret; + struct cafe_camera *cam; ++ struct i2c_board_info info; + struct ov7670_config sensor_cfg = { + /* This controller only does SMBUS */ + .use_smbus = true, +@@ -2065,8 +2066,14 @@ static int cafe_pci_probe(struct pci_dev *pdev, + sensor_cfg.clock_speed = 45; + + cam->sensor_addr = 0x42; +- cam->sensor = v4l2_i2c_new_subdev_cfg(&cam->v4l2_dev, &cam->i2c_adapter, +- "ov7670", 0, &sensor_cfg, cam->sensor_addr, NULL); ++ ++ memset(&info, 0, sizeof(info)); ++ strlcpy(info.type, "ov7670", sizeof(info.type)); ++ info.addr = cam->sensor_addr; ++ info.platform_data = &sensor_cfg; ++ ++ cam->sensor = v4l2_i2c_new_subdev_board(&cam->v4l2_dev, ++ &cam->i2c_adapter, &info, NULL); + if (cam->sensor == NULL) { + ret = -ENODEV; + goto out_smbus; +diff --git a/drivers/media/video/ivtv/ivtv-i2c.c b/drivers/media/video/ivtv/ivtv-i2c.c +index 665191c..6651a6c 100644 +--- a/drivers/media/video/ivtv/ivtv-i2c.c ++++ b/drivers/media/video/ivtv/ivtv-i2c.c +@@ -267,10 +267,17 @@ int ivtv_i2c_register(struct ivtv *itv, unsigned idx) + adap, type, 0, I2C_ADDRS(hw_addrs[idx])); + } else if (hw == IVTV_HW_CX25840) { + struct cx25840_platform_data pdata; ++ struct i2c_board_info info; + + pdata.pvr150_workaround = itv->pvr150_workaround; +- sd = v4l2_i2c_new_subdev_cfg(&itv->v4l2_dev, +- adap, type, 0, &pdata, hw_addrs[idx], NULL); ++ ++ memset(&info, 0, sizeof(info)); ++ strlcpy(info.type, type, sizeof(info.type)); ++ info.addr = hw_addrs[idx]; ++ info.platform_data = &pdata; ++ ++ sd = v4l2_i2c_new_subdev_board(&itv->v4l2_dev, adap, &info, ++ NULL); + } else { + sd = v4l2_i2c_new_subdev(&itv->v4l2_dev, + adap, type, hw_addrs[idx], NULL); +diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c +index b5eb1f3..e007e61 100644 +--- a/drivers/media/video/v4l2-common.c ++++ b/drivers/media/video/v4l2-common.c +@@ -428,9 +428,8 @@ error: + } + EXPORT_SYMBOL_GPL(v4l2_i2c_new_subdev_board); + +-struct v4l2_subdev *v4l2_i2c_new_subdev_cfg(struct v4l2_device *v4l2_dev, ++struct v4l2_subdev *v4l2_i2c_new_subdev(struct v4l2_device *v4l2_dev, + struct i2c_adapter *adapter, const char *client_type, +- int irq, void *platform_data, + u8 addr, const unsigned short *probe_addrs) + { + struct i2c_board_info info; +@@ -440,12 +439,10 @@ struct v4l2_subdev *v4l2_i2c_new_subdev_cfg(struct v4l2_device *v4l2_dev, + memset(&info, 0, sizeof(info)); + strlcpy(info.type, client_type, sizeof(info.type)); + info.addr = addr; +- info.irq = irq; +- info.platform_data = platform_data; + + return v4l2_i2c_new_subdev_board(v4l2_dev, adapter, &info, probe_addrs); + } +-EXPORT_SYMBOL_GPL(v4l2_i2c_new_subdev_cfg); ++EXPORT_SYMBOL_GPL(v4l2_i2c_new_subdev); + + /* Return i2c client address of v4l2_subdev. */ + unsigned short v4l2_i2c_subdev_addr(struct v4l2_subdev *sd) +diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h +index 239125a..565fb32 100644 +--- a/include/media/v4l2-common.h ++++ b/include/media/v4l2-common.h +@@ -138,21 +138,10 @@ struct v4l2_subdev_ops; + + /* Load an i2c module and return an initialized v4l2_subdev struct. + The client_type argument is the name of the chip that's on the adapter. */ +-struct v4l2_subdev *v4l2_i2c_new_subdev_cfg(struct v4l2_device *v4l2_dev, ++struct v4l2_subdev *v4l2_i2c_new_subdev(struct v4l2_device *v4l2_dev, + struct i2c_adapter *adapter, const char *client_type, +- int irq, void *platform_data, + u8 addr, const unsigned short *probe_addrs); + +-/* Load an i2c module and return an initialized v4l2_subdev struct. +- The client_type argument is the name of the chip that's on the adapter. */ +-static inline struct v4l2_subdev *v4l2_i2c_new_subdev(struct v4l2_device *v4l2_dev, +- struct i2c_adapter *adapter, const char *client_type, +- u8 addr, const unsigned short *probe_addrs) +-{ +- return v4l2_i2c_new_subdev_cfg(v4l2_dev, adapter, client_type, 0, NULL, +- addr, probe_addrs); +-} +- + struct i2c_board_info; + + struct v4l2_subdev *v4l2_i2c_new_subdev_board(struct v4l2_device *v4l2_dev, +-- +1.6.6.1 + diff --git a/recipes/linux/linux-omap-2.6.37/media/0004-v4l-subdev-Add-device-node-support.patch b/recipes/linux/linux-omap-2.6.37/media/0004-v4l-subdev-Add-device-node-support.patch new file mode 100644 index 0000000000..8fe2a9d62d --- /dev/null +++ b/recipes/linux/linux-omap-2.6.37/media/0004-v4l-subdev-Add-device-node-support.patch @@ -0,0 +1,615 @@ +From e5b8af4e36ca5e922dd2b881d6c215e9d4d30a6f Mon Sep 17 00:00:00 2001 +From: Laurent Pinchart +Date: Wed, 9 Dec 2009 12:38:49 +0100 +Subject: [PATCH 04/43] v4l: subdev: Add device node support + +Create a device node named subdevX for every registered subdev. + +As the device node is registered before the subdev core::s_config +function is called, return -EGAIN on open until initialization +completes. + +Signed-off-by: Laurent Pinchart +Signed-off-by: Vimarsh Zutshi +--- + Documentation/video4linux/v4l2-framework.txt | 18 +++++++ + drivers/media/radio/radio-si4713.c | 2 +- + drivers/media/video/Makefile | 2 +- + drivers/media/video/cafe_ccic.c | 2 +- + drivers/media/video/davinci/vpfe_capture.c | 2 +- + drivers/media/video/davinci/vpif_capture.c | 2 +- + drivers/media/video/davinci/vpif_display.c | 2 +- + drivers/media/video/ivtv/ivtv-i2c.c | 2 +- + drivers/media/video/s5p-fimc/fimc-capture.c | 2 +- + drivers/media/video/sh_vou.c | 2 +- + drivers/media/video/soc_camera.c | 2 +- + drivers/media/video/v4l2-common.c | 15 +++++- + drivers/media/video/v4l2-dev.c | 27 ++++------ + drivers/media/video/v4l2-device.c | 24 +++++++++- + drivers/media/video/v4l2-ioctl.c | 2 +- + drivers/media/video/v4l2-subdev.c | 66 ++++++++++++++++++++++++++ + include/media/v4l2-common.h | 5 +- + include/media/v4l2-dev.h | 18 ++++++- + include/media/v4l2-ioctl.h | 3 + + include/media/v4l2-subdev.h | 16 ++++++- + 20 files changed, 176 insertions(+), 38 deletions(-) + create mode 100644 drivers/media/video/v4l2-subdev.c + +diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt +index f22f35c..4c9185a 100644 +--- a/Documentation/video4linux/v4l2-framework.txt ++++ b/Documentation/video4linux/v4l2-framework.txt +@@ -319,6 +319,24 @@ controlled through GPIO pins. This distinction is only relevant when setting + up the device, but once the subdev is registered it is completely transparent. + + ++V4L2 sub-device userspace API ++----------------------------- ++ ++Beside exposing a kernel API through the v4l2_subdev_ops structure, V4L2 ++sub-devices can also be controlled directly by userspace applications. ++ ++When a sub-device is registered, a device node named v4l-subdevX can be created ++in /dev. If the sub-device supports direct userspace configuration it must set ++the V4L2_SUBDEV_FL_HAS_DEVNODE flag before being registered. ++ ++For I2C and SPI sub-devices, the v4l2_device driver can disable registration of ++the device node if it wants to control the sub-device on its own. In that case ++it must set the v4l2_i2c_new_subdev_board or v4l2_spi_new_subdev enable_devnode ++argument to 0. Setting the argument to 1 will only enable device node ++registration if the sub-device driver has set the V4L2_SUBDEV_FL_HAS_DEVNODE ++flag. ++ ++ + I2C sub-device drivers + ---------------------- + +diff --git a/drivers/media/radio/radio-si4713.c b/drivers/media/radio/radio-si4713.c +index 726d367..f7c942f 100644 +--- a/drivers/media/radio/radio-si4713.c ++++ b/drivers/media/radio/radio-si4713.c +@@ -293,7 +293,7 @@ static int radio_si4713_pdriver_probe(struct platform_device *pdev) + } + + sd = v4l2_i2c_new_subdev_board(&rsdev->v4l2_dev, adapter, +- pdata->subdev_board_info, NULL); ++ pdata->subdev_board_info, NULL, 0); + if (!sd) { + dev_err(&pdev->dev, "Cannot get v4l2 subdevice\n"); + rval = -ENODEV; +diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile +index af79d47..adc1bd5 100644 +--- a/drivers/media/video/Makefile ++++ b/drivers/media/video/Makefile +@@ -11,7 +11,7 @@ stkwebcam-objs := stk-webcam.o stk-sensor.o + omap2cam-objs := omap24xxcam.o omap24xxcam-dma.o + + videodev-objs := v4l2-dev.o v4l2-ioctl.o v4l2-device.o v4l2-fh.o \ +- v4l2-event.o v4l2-ctrls.o ++ v4l2-event.o v4l2-ctrls.o v4l2-subdev.o + + # V4L2 core modules + +diff --git a/drivers/media/video/cafe_ccic.c b/drivers/media/video/cafe_ccic.c +index 6e23add..f932da1 100644 +--- a/drivers/media/video/cafe_ccic.c ++++ b/drivers/media/video/cafe_ccic.c +@@ -2073,7 +2073,7 @@ static int cafe_pci_probe(struct pci_dev *pdev, + info.platform_data = &sensor_cfg; + + cam->sensor = v4l2_i2c_new_subdev_board(&cam->v4l2_dev, +- &cam->i2c_adapter, &info, NULL); ++ &cam->i2c_adapter, &info, NULL, 0); + if (cam->sensor == NULL) { + ret = -ENODEV; + goto out_smbus; +diff --git a/drivers/media/video/davinci/vpfe_capture.c b/drivers/media/video/davinci/vpfe_capture.c +index 7333a9b..bfc2a47 100644 +--- a/drivers/media/video/davinci/vpfe_capture.c ++++ b/drivers/media/video/davinci/vpfe_capture.c +@@ -1987,7 +1987,7 @@ static __init int vpfe_probe(struct platform_device *pdev) + v4l2_i2c_new_subdev_board(&vpfe_dev->v4l2_dev, + i2c_adap, + &sdinfo->board_info, +- NULL); ++ NULL, 0); + if (vpfe_dev->sd[i]) { + v4l2_info(&vpfe_dev->v4l2_dev, + "v4l2 sub device %s registered\n", +diff --git a/drivers/media/video/davinci/vpif_capture.c b/drivers/media/video/davinci/vpif_capture.c +index 193abab..d2228e0 100644 +--- a/drivers/media/video/davinci/vpif_capture.c ++++ b/drivers/media/video/davinci/vpif_capture.c +@@ -2014,7 +2014,7 @@ static __init int vpif_probe(struct platform_device *pdev) + v4l2_i2c_new_subdev_board(&vpif_obj.v4l2_dev, + i2c_adap, + &subdevdata->board_info, +- NULL); ++ NULL, 0); + + if (!vpif_obj.sd[i]) { + vpif_err("Error registering v4l2 subdevice\n"); +diff --git a/drivers/media/video/davinci/vpif_display.c b/drivers/media/video/davinci/vpif_display.c +index 412c65d..060c049 100644 +--- a/drivers/media/video/davinci/vpif_display.c ++++ b/drivers/media/video/davinci/vpif_display.c +@@ -1555,7 +1555,7 @@ static __init int vpif_probe(struct platform_device *pdev) + vpif_obj.sd[i] = v4l2_i2c_new_subdev_board(&vpif_obj.v4l2_dev, + i2c_adap, + &subdevdata[i].board_info, +- NULL); ++ NULL, 0); + if (!vpif_obj.sd[i]) { + vpif_err("Error registering v4l2 subdevice\n"); + goto probe_subdev_out; +diff --git a/drivers/media/video/ivtv/ivtv-i2c.c b/drivers/media/video/ivtv/ivtv-i2c.c +index 6651a6c..3d3b62d 100644 +--- a/drivers/media/video/ivtv/ivtv-i2c.c ++++ b/drivers/media/video/ivtv/ivtv-i2c.c +@@ -277,7 +277,7 @@ int ivtv_i2c_register(struct ivtv *itv, unsigned idx) + info.platform_data = &pdata; + + sd = v4l2_i2c_new_subdev_board(&itv->v4l2_dev, adap, &info, +- NULL); ++ NULL, 0); + } else { + sd = v4l2_i2c_new_subdev(&itv->v4l2_dev, + adap, type, hw_addrs[idx], NULL); +diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c +index 2f50080..b237daa 100644 +--- a/drivers/media/video/s5p-fimc/fimc-capture.c ++++ b/drivers/media/video/s5p-fimc/fimc-capture.c +@@ -44,7 +44,7 @@ static struct v4l2_subdev *fimc_subdev_register(struct fimc_dev *fimc, + return ERR_PTR(-ENOMEM); + + sd = v4l2_i2c_new_subdev_board(&vid_cap->v4l2_dev, i2c_adap, +- isp_info->board_info, NULL); ++ isp_info->board_info, NULL, 0); + if (!sd) { + v4l2_err(&vid_cap->v4l2_dev, "failed to acquire subdev\n"); + return NULL; +diff --git a/drivers/media/video/sh_vou.c b/drivers/media/video/sh_vou.c +index 07cf0c6..c50f0f5 100644 +--- a/drivers/media/video/sh_vou.c ++++ b/drivers/media/video/sh_vou.c +@@ -1409,7 +1409,7 @@ static int __devinit sh_vou_probe(struct platform_device *pdev) + goto ereset; + + subdev = v4l2_i2c_new_subdev_board(&vou_dev->v4l2_dev, i2c_adap, +- vou_pdata->board_info, NULL); ++ vou_pdata->board_info, NULL, 0); + if (!subdev) { + ret = -ENOMEM; + goto ei2cnd; +diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c +index 052bd6d..5afb601 100644 +--- a/drivers/media/video/soc_camera.c ++++ b/drivers/media/video/soc_camera.c +@@ -896,7 +896,7 @@ static int soc_camera_init_i2c(struct soc_camera_device *icd, + icl->board_info->platform_data = icd; + + subdev = v4l2_i2c_new_subdev_board(&ici->v4l2_dev, adap, +- icl->board_info, NULL); ++ icl->board_info, NULL, 0); + if (!subdev) + goto ei2cnd; + +diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c +index e007e61..ffee794 100644 +--- a/drivers/media/video/v4l2-common.c ++++ b/drivers/media/video/v4l2-common.c +@@ -369,7 +369,7 @@ EXPORT_SYMBOL_GPL(v4l2_i2c_subdev_init); + /* Load an i2c sub-device. */ + struct v4l2_subdev *v4l2_i2c_new_subdev_board(struct v4l2_device *v4l2_dev, + struct i2c_adapter *adapter, struct i2c_board_info *info, +- const unsigned short *probe_addrs) ++ const unsigned short *probe_addrs, int enable_devnode) + { + struct v4l2_subdev *sd = NULL; + struct i2c_client *client; +@@ -399,9 +399,12 @@ struct v4l2_subdev *v4l2_i2c_new_subdev_board(struct v4l2_device *v4l2_dev, + if (!try_module_get(client->driver->driver.owner)) + goto error; + sd = i2c_get_clientdata(client); ++ if (!enable_devnode) ++ sd->flags &= ~V4L2_SUBDEV_FL_HAS_DEVNODE; + + /* Register with the v4l2_device which increases the module's + use count as well. */ ++ sd->initialized = 0; + if (v4l2_device_register_subdev(v4l2_dev, sd)) + sd = NULL; + /* Decrease the module use count to match the first try_module_get. */ +@@ -416,6 +419,8 @@ struct v4l2_subdev *v4l2_i2c_new_subdev_board(struct v4l2_device *v4l2_dev, + if (err && err != -ENOIOCTLCMD) { + v4l2_device_unregister_subdev(sd); + sd = NULL; ++ } else { ++ sd->initialized = 1; + } + } + +@@ -440,7 +445,8 @@ struct v4l2_subdev *v4l2_i2c_new_subdev(struct v4l2_device *v4l2_dev, + strlcpy(info.type, client_type, sizeof(info.type)); + info.addr = addr; + +- return v4l2_i2c_new_subdev_board(v4l2_dev, adapter, &info, probe_addrs); ++ return v4l2_i2c_new_subdev_board(v4l2_dev, adapter, &info, probe_addrs, ++ 0); + } + EXPORT_SYMBOL_GPL(v4l2_i2c_new_subdev); + +@@ -510,7 +516,8 @@ void v4l2_spi_subdev_init(struct v4l2_subdev *sd, struct spi_device *spi, + EXPORT_SYMBOL_GPL(v4l2_spi_subdev_init); + + struct v4l2_subdev *v4l2_spi_new_subdev(struct v4l2_device *v4l2_dev, +- struct spi_master *master, struct spi_board_info *info) ++ struct spi_master *master, struct spi_board_info *info, ++ int enable_devnode) + { + struct v4l2_subdev *sd = NULL; + struct spi_device *spi = NULL; +@@ -529,6 +536,8 @@ struct v4l2_subdev *v4l2_spi_new_subdev(struct v4l2_device *v4l2_dev, + goto error; + + sd = spi_get_drvdata(spi); ++ if (!enable_devnode) ++ sd->flags &= ~V4L2_SUBDEV_FL_HAS_DEVNODE; + + /* Register with the v4l2_device which increases the module's + use count as well. */ +diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c +index 359e232..f22bd41 100644 +--- a/drivers/media/video/v4l2-dev.c ++++ b/drivers/media/video/v4l2-dev.c +@@ -408,13 +408,14 @@ static int get_index(struct video_device *vdev) + } + + /** +- * video_register_device - register video4linux devices ++ * __video_register_device - register video4linux devices + * @vdev: video device structure we want to register + * @type: type of device to register + * @nr: which device node number (0 == /dev/video0, 1 == /dev/video1, ... + * -1 == first free) + * @warn_if_nr_in_use: warn if the desired device node number + * was already in use and another number was chosen instead. ++ * @owner: module that owns the video device node + * + * The registration code assigns minor numbers and device node numbers + * based on the requested type and registers the new device node with +@@ -431,9 +432,11 @@ static int get_index(struct video_device *vdev) + * %VFL_TYPE_VBI - Vertical blank data (undecoded) + * + * %VFL_TYPE_RADIO - A radio card ++ * ++ * %VFL_TYPE_SUBDEV - A subdevice + */ +-static int __video_register_device(struct video_device *vdev, int type, int nr, +- int warn_if_nr_in_use) ++int __video_register_device(struct video_device *vdev, int type, int nr, ++ int warn_if_nr_in_use, struct module *owner) + { + int i = 0; + int ret; +@@ -466,6 +469,9 @@ static int __video_register_device(struct video_device *vdev, int type, int nr, + case VFL_TYPE_RADIO: + name_base = "radio"; + break; ++ case VFL_TYPE_SUBDEV: ++ name_base = "v4l-subdev"; ++ break; + default: + printk(KERN_ERR "%s called with unknown type: %d\n", + __func__, type); +@@ -549,7 +555,7 @@ static int __video_register_device(struct video_device *vdev, int type, int nr, + goto cleanup; + } + vdev->cdev->ops = &v4l2_fops; +- vdev->cdev->owner = vdev->fops->owner; ++ vdev->cdev->owner = owner; + ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1); + if (ret < 0) { + printk(KERN_ERR "%s: cdev_add failed\n", __func__); +@@ -598,18 +604,7 @@ cleanup: + vdev->minor = -1; + return ret; + } +- +-int video_register_device(struct video_device *vdev, int type, int nr) +-{ +- return __video_register_device(vdev, type, nr, 1); +-} +-EXPORT_SYMBOL(video_register_device); +- +-int video_register_device_no_warn(struct video_device *vdev, int type, int nr) +-{ +- return __video_register_device(vdev, type, nr, 0); +-} +-EXPORT_SYMBOL(video_register_device_no_warn); ++EXPORT_SYMBOL(__video_register_device); + + /** + * video_unregister_device - unregister a video4linux device +diff --git a/drivers/media/video/v4l2-device.c b/drivers/media/video/v4l2-device.c +index 7fe6f92..97e84df 100644 +--- a/drivers/media/video/v4l2-device.c ++++ b/drivers/media/video/v4l2-device.c +@@ -117,24 +117,43 @@ EXPORT_SYMBOL_GPL(v4l2_device_unregister); + int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev, + struct v4l2_subdev *sd) + { ++ struct video_device *vdev; + int err; + + /* Check for valid input */ + if (v4l2_dev == NULL || sd == NULL || !sd->name[0]) + return -EINVAL; ++ + /* Warn if we apparently re-register a subdev */ + WARN_ON(sd->v4l2_dev != NULL); ++ + if (!try_module_get(sd->owner)) + return -ENODEV; ++ + /* This just returns 0 if either of the two args is NULL */ + err = v4l2_ctrl_add_handler(v4l2_dev->ctrl_handler, sd->ctrl_handler); + if (err) + return err; ++ + sd->v4l2_dev = v4l2_dev; + spin_lock(&v4l2_dev->lock); + list_add_tail(&sd->list, &v4l2_dev->subdevs); + spin_unlock(&v4l2_dev->lock); +- return 0; ++ ++ /* Register the device node. */ ++ vdev = &sd->devnode; ++ strlcpy(vdev->name, sd->name, sizeof(vdev->name)); ++ vdev->parent = v4l2_dev->dev; ++ vdev->fops = &v4l2_subdev_fops; ++ vdev->release = video_device_release_empty; ++ if (sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE) { ++ err = __video_register_device(vdev, VFL_TYPE_SUBDEV, -1, 1, ++ sd->owner); ++ if (err < 0) ++ v4l2_device_unregister_subdev(sd); ++ } ++ ++ return err; + } + EXPORT_SYMBOL_GPL(v4l2_device_register_subdev); + +@@ -143,10 +162,13 @@ void v4l2_device_unregister_subdev(struct v4l2_subdev *sd) + /* return if it isn't registered */ + if (sd == NULL || sd->v4l2_dev == NULL) + return; ++ + spin_lock(&sd->v4l2_dev->lock); + list_del(&sd->list); + spin_unlock(&sd->v4l2_dev->lock); + sd->v4l2_dev = NULL; ++ + module_put(sd->owner); ++ video_unregister_device(&sd->devnode); + } + EXPORT_SYMBOL_GPL(v4l2_device_unregister_subdev); +diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c +index 1e01554..4137e4c 100644 +--- a/drivers/media/video/v4l2-ioctl.c ++++ b/drivers/media/video/v4l2-ioctl.c +@@ -413,7 +413,7 @@ static unsigned long cmd_input_size(unsigned int cmd) + } + } + +-static long ++long + __video_usercopy(struct file *file, unsigned int cmd, unsigned long arg, + v4l2_kioctl func) + { +diff --git a/drivers/media/video/v4l2-subdev.c b/drivers/media/video/v4l2-subdev.c +new file mode 100644 +index 0000000..00bd4b1 +--- /dev/null ++++ b/drivers/media/video/v4l2-subdev.c +@@ -0,0 +1,66 @@ ++/* ++ * V4L2 subdevice support. ++ * ++ * Copyright (C) 2010 Nokia Corporation ++ * ++ * Contact: Laurent Pinchart ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#include ++#include ++#include ++ ++#include ++#include ++ ++static int subdev_open(struct file *file) ++{ ++ struct video_device *vdev = video_devdata(file); ++ struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev); ++ ++ if (!sd->initialized) ++ return -EAGAIN; ++ ++ return 0; ++} ++ ++static int subdev_close(struct file *file) ++{ ++ return 0; ++} ++ ++static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) ++{ ++ switch (cmd) { ++ default: ++ return -ENOIOCTLCMD; ++ } ++ ++ return 0; ++} ++ ++static long subdev_ioctl(struct file *file, unsigned int cmd, ++ unsigned long arg) ++{ ++ return __video_usercopy(file, cmd, arg, subdev_do_ioctl); ++} ++ ++const struct v4l2_file_operations v4l2_subdev_fops = { ++ .owner = THIS_MODULE, ++ .open = subdev_open, ++ .unlocked_ioctl = subdev_ioctl, ++ .release = subdev_close, ++}; +diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h +index 565fb32..ef8965d 100644 +--- a/include/media/v4l2-common.h ++++ b/include/media/v4l2-common.h +@@ -146,7 +146,7 @@ struct i2c_board_info; + + struct v4l2_subdev *v4l2_i2c_new_subdev_board(struct v4l2_device *v4l2_dev, + struct i2c_adapter *adapter, struct i2c_board_info *info, +- const unsigned short *probe_addrs); ++ const unsigned short *probe_addrs, int enable_devnode); + + /* Initialize an v4l2_subdev with data from an i2c_client struct */ + void v4l2_i2c_subdev_init(struct v4l2_subdev *sd, struct i2c_client *client, +@@ -179,7 +179,8 @@ struct spi_device; + /* Load an spi module and return an initialized v4l2_subdev struct. + The client_type argument is the name of the chip that's on the adapter. */ + struct v4l2_subdev *v4l2_spi_new_subdev(struct v4l2_device *v4l2_dev, +- struct spi_master *master, struct spi_board_info *info); ++ struct spi_master *master, struct spi_board_info *info, ++ int enable_devnode); + + /* Initialize an v4l2_subdev with data from an spi_device struct */ + void v4l2_spi_subdev_init(struct v4l2_subdev *sd, struct spi_device *spi, +diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h +index 15802a0..4fe6831 100644 +--- a/include/media/v4l2-dev.h ++++ b/include/media/v4l2-dev.h +@@ -21,7 +21,8 @@ + #define VFL_TYPE_GRABBER 0 + #define VFL_TYPE_VBI 1 + #define VFL_TYPE_RADIO 2 +-#define VFL_TYPE_MAX 3 ++#define VFL_TYPE_SUBDEV 3 ++#define VFL_TYPE_MAX 4 + + struct v4l2_ioctl_callbacks; + struct video_device; +@@ -102,15 +103,26 @@ struct video_device + /* dev to video-device */ + #define to_video_device(cd) container_of(cd, struct video_device, dev) + ++int __must_check __video_register_device(struct video_device *vdev, int type, ++ int nr, int warn_if_nr_in_use, struct module *owner); ++ + /* Register video devices. Note that if video_register_device fails, + the release() callback of the video_device structure is *not* called, so + the caller is responsible for freeing any data. Usually that means that + you call video_device_release() on failure. */ +-int __must_check video_register_device(struct video_device *vdev, int type, int nr); ++static inline int __must_check video_register_device(struct video_device *vdev, ++ int type, int nr) ++{ ++ return __video_register_device(vdev, type, nr, 1, vdev->fops->owner); ++} + + /* Same as video_register_device, but no warning is issued if the desired + device node number was already in use. */ +-int __must_check video_register_device_no_warn(struct video_device *vdev, int type, int nr); ++static inline int __must_check video_register_device_no_warn( ++ struct video_device *vdev, int type, int nr) ++{ ++ return __video_register_device(vdev, type, nr, 0, vdev->fops->owner); ++} + + /* Unregister video devices. Will do nothing if vdev == NULL or + video_is_registered() returns false. */ +diff --git a/include/media/v4l2-ioctl.h b/include/media/v4l2-ioctl.h +index 06daa6e..abb64d0 100644 +--- a/include/media/v4l2-ioctl.h ++++ b/include/media/v4l2-ioctl.h +@@ -316,6 +316,9 @@ extern long v4l2_compat_ioctl32(struct file *file, unsigned int cmd, + unsigned long arg); + #endif + ++extern long __video_usercopy(struct file *file, unsigned int cmd, ++ unsigned long arg, v4l2_kioctl func); ++ + /* Include support for obsoleted stuff */ + extern long video_usercopy(struct file *file, unsigned int cmd, + unsigned long arg, v4l2_kioctl func); +diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h +index b636444..de181db 100644 +--- a/include/media/v4l2-subdev.h ++++ b/include/media/v4l2-subdev.h +@@ -22,6 +22,7 @@ + #define _V4L2_SUBDEV_H + + #include ++#include + #include + + /* generic v4l2_device notify callback notification values */ +@@ -418,9 +419,11 @@ struct v4l2_subdev_ops { + #define V4L2_SUBDEV_NAME_SIZE 32 + + /* Set this flag if this subdev is a i2c device. */ +-#define V4L2_SUBDEV_FL_IS_I2C (1U << 0) ++#define V4L2_SUBDEV_FL_IS_I2C (1U << 0) + /* Set this flag if this subdev is a spi device. */ +-#define V4L2_SUBDEV_FL_IS_SPI (1U << 1) ++#define V4L2_SUBDEV_FL_IS_SPI (1U << 1) ++/* Set this flag if this subdev needs a device node. */ ++#define V4L2_SUBDEV_FL_HAS_DEVNODE (1U << 2) + + /* Each instance of a subdev driver should create this struct, either + stand-alone or embedded in a larger struct. +@@ -440,8 +443,16 @@ struct v4l2_subdev { + /* pointer to private data */ + void *dev_priv; + void *host_priv; ++ /* subdev device node */ ++ struct video_device devnode; ++ unsigned int initialized; + }; + ++#define vdev_to_v4l2_subdev(vdev) \ ++ container_of(vdev, struct v4l2_subdev, devnode) ++ ++extern const struct v4l2_file_operations v4l2_subdev_fops; ++ + static inline void v4l2_set_subdevdata(struct v4l2_subdev *sd, void *p) + { + sd->dev_priv = p; +@@ -474,6 +485,7 @@ static inline void v4l2_subdev_init(struct v4l2_subdev *sd, + sd->grp_id = 0; + sd->dev_priv = NULL; + sd->host_priv = NULL; ++ sd->initialized = 1; + } + + /* Call an ops of a v4l2_subdev, doing the right checks against +-- +1.6.6.1 + diff --git a/recipes/linux/linux-omap-2.6.37/media/0005-v4l-subdev-Uninline-the-v4l2_subdev_init-function.patch b/recipes/linux/linux-omap-2.6.37/media/0005-v4l-subdev-Uninline-the-v4l2_subdev_init-function.patch new file mode 100644 index 0000000000..a4e45e6124 --- /dev/null +++ b/recipes/linux/linux-omap-2.6.37/media/0005-v4l-subdev-Uninline-the-v4l2_subdev_init-function.patch @@ -0,0 +1,103 @@ +From 7acd77b0cdf013213a6513a75ee5bc2c3e92e1a1 Mon Sep 17 00:00:00 2001 +From: Laurent Pinchart +Date: Wed, 9 Dec 2009 12:38:52 +0100 +Subject: [PATCH 05/43] v4l: subdev: Uninline the v4l2_subdev_init function + +The function isn't small or performance sensitive enough to be inlined. + +Signed-off-by: Laurent Pinchart +--- + drivers/media/video/v4l2-subdev.c | 42 +++++++++++++++++++++++++----------- + include/media/v4l2-subdev.h | 16 +------------ + 2 files changed, 31 insertions(+), 27 deletions(-) + +diff --git a/drivers/media/video/v4l2-subdev.c b/drivers/media/video/v4l2-subdev.c +index 00bd4b1..0deff78 100644 +--- a/drivers/media/video/v4l2-subdev.c ++++ b/drivers/media/video/v4l2-subdev.c +@@ -1,22 +1,23 @@ + /* +- * V4L2 subdevice support. ++ * V4L2 sub-device + * +- * Copyright (C) 2010 Nokia Corporation ++ * Copyright (C) 2010 Nokia Corporation + * +- * Contact: Laurent Pinchart ++ * Contact: Laurent Pinchart ++ * Sakari Ailus + * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation. ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. + * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. + * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + #include +@@ -64,3 +65,18 @@ const struct v4l2_file_operations v4l2_subdev_fops = { + .unlocked_ioctl = subdev_ioctl, + .release = subdev_close, + }; ++ ++void v4l2_subdev_init(struct v4l2_subdev *sd, const struct v4l2_subdev_ops *ops) ++{ ++ INIT_LIST_HEAD(&sd->list); ++ BUG_ON(!ops); ++ sd->ops = ops; ++ sd->v4l2_dev = NULL; ++ sd->flags = 0; ++ sd->name[0] = '\0'; ++ sd->grp_id = 0; ++ sd->dev_priv = NULL; ++ sd->host_priv = NULL; ++ sd->initialized = 1; ++} ++EXPORT_SYMBOL(v4l2_subdev_init); +diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h +index de181db..90022f5 100644 +--- a/include/media/v4l2-subdev.h ++++ b/include/media/v4l2-subdev.h +@@ -473,20 +473,8 @@ static inline void *v4l2_get_subdev_hostdata(const struct v4l2_subdev *sd) + return sd->host_priv; + } + +-static inline void v4l2_subdev_init(struct v4l2_subdev *sd, +- const struct v4l2_subdev_ops *ops) +-{ +- INIT_LIST_HEAD(&sd->list); +- BUG_ON(!ops); +- sd->ops = ops; +- sd->v4l2_dev = NULL; +- sd->flags = 0; +- sd->name[0] = '\0'; +- sd->grp_id = 0; +- sd->dev_priv = NULL; +- sd->host_priv = NULL; +- sd->initialized = 1; +-} ++void v4l2_subdev_init(struct v4l2_subdev *sd, ++ const struct v4l2_subdev_ops *ops); + + /* Call an ops of a v4l2_subdev, doing the right checks against + NULL pointers. +-- +1.6.6.1 + diff --git a/recipes/linux/linux-omap-2.6.37/media/0006-v4l-subdev-Control-ioctls-support.patch b/recipes/linux/linux-omap-2.6.37/media/0006-v4l-subdev-Control-ioctls-support.patch new file mode 100644 index 0000000000..218f346975 --- /dev/null +++ b/recipes/linux/linux-omap-2.6.37/media/0006-v4l-subdev-Control-ioctls-support.patch @@ -0,0 +1,88 @@ +From dd0b366441249eb10daa2275e968431507f8d0d5 Mon Sep 17 00:00:00 2001 +From: Laurent Pinchart +Date: Wed, 9 Dec 2009 12:39:54 +0100 +Subject: [PATCH 06/43] v4l: subdev: Control ioctls support + +Pass the control-related ioctls to the subdev driver through the control +framework. + +Signed-off-by: Laurent Pinchart +--- + Documentation/video4linux/v4l2-framework.txt | 16 ++++++++++++++++ + drivers/media/video/v4l2-subdev.c | 25 +++++++++++++++++++++++++ + 2 files changed, 41 insertions(+), 0 deletions(-) + +diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt +index 4c9185a..f683f63 100644 +--- a/Documentation/video4linux/v4l2-framework.txt ++++ b/Documentation/video4linux/v4l2-framework.txt +@@ -336,6 +336,22 @@ argument to 0. Setting the argument to 1 will only enable device node + registration if the sub-device driver has set the V4L2_SUBDEV_FL_HAS_DEVNODE + flag. + ++The device node handles a subset of the V4L2 API. ++ ++VIDIOC_QUERYCTRL ++VIDIOC_QUERYMENU ++VIDIOC_G_CTRL ++VIDIOC_S_CTRL ++VIDIOC_G_EXT_CTRLS ++VIDIOC_S_EXT_CTRLS ++VIDIOC_TRY_EXT_CTRLS ++ ++ The controls ioctls are identical to the ones defined in V4L2. They ++ behave identically, with the only exception that they deal only with ++ controls implemented in the sub-device. Depending on the driver, those ++ controls can be also be accessed through one (or several) V4L2 device ++ nodes. ++ + + I2C sub-device drivers + ---------------------- +diff --git a/drivers/media/video/v4l2-subdev.c b/drivers/media/video/v4l2-subdev.c +index 0deff78..fc57ce7 100644 +--- a/drivers/media/video/v4l2-subdev.c ++++ b/drivers/media/video/v4l2-subdev.c +@@ -24,6 +24,7 @@ + #include + #include + ++#include + #include + #include + +@@ -45,7 +46,31 @@ static int subdev_close(struct file *file) + + static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) + { ++ struct video_device *vdev = video_devdata(file); ++ struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev); ++ + switch (cmd) { ++ case VIDIOC_QUERYCTRL: ++ return v4l2_subdev_queryctrl(sd, arg); ++ ++ case VIDIOC_QUERYMENU: ++ return v4l2_subdev_querymenu(sd, arg); ++ ++ case VIDIOC_G_CTRL: ++ return v4l2_subdev_g_ctrl(sd, arg); ++ ++ case VIDIOC_S_CTRL: ++ return v4l2_subdev_s_ctrl(sd, arg); ++ ++ case VIDIOC_G_EXT_CTRLS: ++ return v4l2_subdev_g_ext_ctrls(sd, arg); ++ ++ case VIDIOC_S_EXT_CTRLS: ++ return v4l2_subdev_s_ext_ctrls(sd, arg); ++ ++ case VIDIOC_TRY_EXT_CTRLS: ++ return v4l2_subdev_try_ext_ctrls(sd, arg); ++ + default: + return -ENOIOCTLCMD; + } +-- +1.6.6.1 + diff --git a/recipes/linux/linux-omap-2.6.37/media/0007-v4l-subdev-Events-support.patch b/recipes/linux/linux-omap-2.6.37/media/0007-v4l-subdev-Events-support.patch new file mode 100644 index 0000000000..cb02f49867 --- /dev/null +++ b/recipes/linux/linux-omap-2.6.37/media/0007-v4l-subdev-Events-support.patch @@ -0,0 +1,223 @@ +From 127fac73175e73c509ba203717be618a611294cd Mon Sep 17 00:00:00 2001 +From: Sakari Ailus +Date: Wed, 3 Mar 2010 17:49:38 +0200 +Subject: [PATCH 07/43] v4l: subdev: Events support + +Provide v4l2_subdevs with v4l2_event support. Subdev drivers only need very +little to support events. + +Signed-off-by: Sakari Ailus +Signed-off-by: David Cohen +Signed-off-by: Laurent Pinchart +--- + Documentation/video4linux/v4l2-framework.txt | 18 ++++++ + drivers/media/video/v4l2-subdev.c | 75 +++++++++++++++++++++++++- + include/media/v4l2-subdev.h | 10 ++++ + 3 files changed, 102 insertions(+), 1 deletions(-) + +diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt +index f683f63..4db1def 100644 +--- a/Documentation/video4linux/v4l2-framework.txt ++++ b/Documentation/video4linux/v4l2-framework.txt +@@ -352,6 +352,24 @@ VIDIOC_TRY_EXT_CTRLS + controls can be also be accessed through one (or several) V4L2 device + nodes. + ++VIDIOC_DQEVENT ++VIDIOC_SUBSCRIBE_EVENT ++VIDIOC_UNSUBSCRIBE_EVENT ++ ++ The events ioctls are identical to the ones defined in V4L2. They ++ behave identically, with the only exception that they deal only with ++ events generated by the sub-device. Depending on the driver, those ++ events can also be reported by one (or several) V4L2 device nodes. ++ ++ Sub-device drivers that want to use events need to set the ++ V4L2_SUBDEV_USES_EVENTS v4l2_subdev::flags and initialize ++ v4l2_subdev::nevents to events queue depth before registering the ++ sub-device. After registration events can be queued as usual on the ++ v4l2_subdev::devnode device node. ++ ++ To properly support events, the poll() file operation is also ++ implemented. ++ + + I2C sub-device drivers + ---------------------- +diff --git a/drivers/media/video/v4l2-subdev.c b/drivers/media/video/v4l2-subdev.c +index fc57ce7..fbccefd 100644 +--- a/drivers/media/video/v4l2-subdev.c ++++ b/drivers/media/video/v4l2-subdev.c +@@ -20,27 +20,69 @@ + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +-#include + #include ++#include ++#include + #include + + #include + #include + #include ++#include ++#include + + static int subdev_open(struct file *file) + { + struct video_device *vdev = video_devdata(file); + struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev); ++ struct v4l2_fh *vfh; ++ int ret; + + if (!sd->initialized) + return -EAGAIN; + ++ if (sd->flags & V4L2_SUBDEV_FL_HAS_EVENTS) { ++ vfh = kzalloc(sizeof(*vfh), GFP_KERNEL); ++ if (vfh == NULL) ++ return -ENOMEM; ++ ++ ret = v4l2_fh_init(vfh, vdev); ++ if (ret) ++ goto err; ++ ++ ret = v4l2_event_init(vfh); ++ if (ret) ++ goto err; ++ ++ ret = v4l2_event_alloc(vfh, sd->nevents); ++ if (ret) ++ goto err; ++ ++ v4l2_fh_add(vfh); ++ file->private_data = vfh; ++ } ++ + return 0; ++ ++err: ++ if (vfh != NULL) { ++ v4l2_fh_exit(vfh); ++ kfree(vfh); ++ } ++ ++ return ret; + } + + static int subdev_close(struct file *file) + { ++ struct v4l2_fh *vfh = file->private_data; ++ ++ if (vfh != NULL) { ++ v4l2_fh_del(vfh); ++ v4l2_fh_exit(vfh); ++ kfree(vfh); ++ } ++ + return 0; + } + +@@ -48,6 +90,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) + { + struct video_device *vdev = video_devdata(file); + struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev); ++ struct v4l2_fh *fh = file->private_data; + + switch (cmd) { + case VIDIOC_QUERYCTRL: +@@ -71,6 +114,18 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) + case VIDIOC_TRY_EXT_CTRLS: + return v4l2_subdev_try_ext_ctrls(sd, arg); + ++ case VIDIOC_DQEVENT: ++ if (!(sd->flags & V4L2_SUBDEV_FL_HAS_EVENTS)) ++ return -ENOIOCTLCMD; ++ ++ return v4l2_event_dequeue(fh, arg, file->f_flags & O_NONBLOCK); ++ ++ case VIDIOC_SUBSCRIBE_EVENT: ++ return v4l2_subdev_call(sd, core, subscribe_event, fh, arg); ++ ++ case VIDIOC_UNSUBSCRIBE_EVENT: ++ return v4l2_subdev_call(sd, core, unsubscribe_event, fh, arg); ++ + default: + return -ENOIOCTLCMD; + } +@@ -84,11 +139,29 @@ static long subdev_ioctl(struct file *file, unsigned int cmd, + return __video_usercopy(file, cmd, arg, subdev_do_ioctl); + } + ++static unsigned int subdev_poll(struct file *file, poll_table *wait) ++{ ++ struct video_device *vdev = video_devdata(file); ++ struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev); ++ struct v4l2_fh *fh = file->private_data; ++ ++ if (!(sd->flags & V4L2_SUBDEV_FL_HAS_EVENTS)) ++ return POLLERR; ++ ++ poll_wait(file, &fh->events->wait, wait); ++ ++ if (v4l2_event_pending(fh)) ++ return POLLPRI; ++ ++ return 0; ++} ++ + const struct v4l2_file_operations v4l2_subdev_fops = { + .owner = THIS_MODULE, + .open = subdev_open, + .unlocked_ioctl = subdev_ioctl, + .release = subdev_close, ++ .poll = subdev_poll, + }; + + void v4l2_subdev_init(struct v4l2_subdev *sd, const struct v4l2_subdev_ops *ops) +diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h +index 90022f5..68cbe48 100644 +--- a/include/media/v4l2-subdev.h ++++ b/include/media/v4l2-subdev.h +@@ -37,6 +37,8 @@ + + struct v4l2_device; + struct v4l2_ctrl_handler; ++struct v4l2_event_subscription; ++struct v4l2_fh; + struct v4l2_subdev; + struct tuner_setup; + +@@ -165,6 +167,10 @@ struct v4l2_subdev_core_ops { + int (*s_power)(struct v4l2_subdev *sd, int on); + int (*interrupt_service_routine)(struct v4l2_subdev *sd, + u32 status, bool *handled); ++ int (*subscribe_event)(struct v4l2_subdev *sd, struct v4l2_fh *fh, ++ struct v4l2_event_subscription *sub); ++ int (*unsubscribe_event)(struct v4l2_subdev *sd, struct v4l2_fh *fh, ++ struct v4l2_event_subscription *sub); + }; + + /* s_mode: switch the tuner to a specific tuner mode. Replacement of s_radio. +@@ -424,6 +430,8 @@ struct v4l2_subdev_ops { + #define V4L2_SUBDEV_FL_IS_SPI (1U << 1) + /* Set this flag if this subdev needs a device node. */ + #define V4L2_SUBDEV_FL_HAS_DEVNODE (1U << 2) ++/* Set this flag if this subdev generates events. */ ++#define V4L2_SUBDEV_FL_HAS_EVENTS (1U << 3) + + /* Each instance of a subdev driver should create this struct, either + stand-alone or embedded in a larger struct. +@@ -446,6 +454,8 @@ struct v4l2_subdev { + /* subdev device node */ + struct video_device devnode; + unsigned int initialized; ++ /* number of events to be allocated on open */ ++ unsigned int nevents; + }; + + #define vdev_to_v4l2_subdev(vdev) \ +-- +1.6.6.1 + diff --git a/recipes/linux/linux-omap-2.6.37/media/0008-media-Media-device-node-support.patch b/recipes/linux/linux-omap-2.6.37/media/0008-media-Media-device-node-support.patch new file mode 100644 index 0000000000..fd8b4952d6 --- /dev/null +++ b/recipes/linux/linux-omap-2.6.37/media/0008-media-Media-device-node-support.patch @@ -0,0 +1,500 @@ +From 27c789f3ae1d24212355d10857efb2d406d0fedd Mon Sep 17 00:00:00 2001 +From: Laurent Pinchart +Date: Wed, 9 Dec 2009 12:39:56 +0100 +Subject: [PATCH 08/43] media: Media device node support + +The media_devnode structure provides support for registering and +unregistering character devices using a dynamic major number. Reference +counting is handled internally, making device drivers easier to write +without having to solve the open/disconnect race condition issue over +and over again. + +The code is based on video/v4l2-dev.c. + +Signed-off-by: Laurent Pinchart +--- + drivers/media/Kconfig | 13 ++ + drivers/media/Makefile | 10 +- + drivers/media/media-devnode.c | 321 +++++++++++++++++++++++++++++++++++++++++ + include/media/media-devnode.h | 97 +++++++++++++ + 4 files changed, 439 insertions(+), 2 deletions(-) + create mode 100644 drivers/media/media-devnode.c + create mode 100644 include/media/media-devnode.h + +diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig +index a28541b..6b946e6 100644 +--- a/drivers/media/Kconfig ++++ b/drivers/media/Kconfig +@@ -14,6 +14,19 @@ if MEDIA_SUPPORT + comment "Multimedia core support" + + # ++# Media controller ++# ++ ++config MEDIA_CONTROLLER ++ bool "Media Controller API (EXPERIMENTAL)" ++ depends on EXPERIMENTAL ++ ---help--- ++ Enable the media controller API used to query media devices internal ++ topology and configure it dynamically. ++ ++ This API is mostly used by camera interfaces in embedded platforms. ++ ++# + # V4L core and enabled API's + # + +diff --git a/drivers/media/Makefile b/drivers/media/Makefile +index 499b081..3a08991 100644 +--- a/drivers/media/Makefile ++++ b/drivers/media/Makefile +@@ -2,7 +2,13 @@ + # Makefile for the kernel multimedia device drivers. + # + ++media-objs := media-devnode.o ++ ++ifeq ($(CONFIG_MEDIA_CONTROLLER),y) ++ obj-$(CONFIG_MEDIA_SUPPORT) += media.o ++endif ++ + obj-y += common/ IR/ video/ + +-obj-$(CONFIG_VIDEO_DEV) += radio/ +-obj-$(CONFIG_DVB_CORE) += dvb/ ++obj-$(CONFIG_VIDEO_DEV) += radio/ ++obj-$(CONFIG_DVB_CORE) += dvb/ +diff --git a/drivers/media/media-devnode.c b/drivers/media/media-devnode.c +new file mode 100644 +index 0000000..7804b70 +--- /dev/null ++++ b/drivers/media/media-devnode.c +@@ -0,0 +1,321 @@ ++/* ++ * Media device node ++ * ++ * Copyright (C) 2010 Nokia Corporation ++ * ++ * Based on drivers/media/video/v4l2_dev.c code authored by ++ * Mauro Carvalho Chehab (version 2) ++ * Alan Cox, (version 1) ++ * ++ * Contacts: Laurent Pinchart ++ * Sakari Ailus ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ * -- ++ * ++ * Generic media device node infrastructure to register and unregister ++ * character devices using a dynamic major number and proper reference ++ * counting. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#define MEDIA_NUM_DEVICES 256 ++#define MEDIA_NAME "media" ++ ++static dev_t media_dev_t; ++ ++/* ++ * Active devices ++ */ ++static DEFINE_MUTEX(media_devnode_lock); ++static DECLARE_BITMAP(media_devnode_nums, MEDIA_NUM_DEVICES); ++ ++/* Called when the last user of the media device exits. */ ++static void media_devnode_release(struct device *cd) ++{ ++ struct media_devnode *mdev = to_media_devnode(cd); ++ ++ mutex_lock(&media_devnode_lock); ++ ++ /* Delete the cdev on this minor as well */ ++ cdev_del(&mdev->cdev); ++ ++ /* Mark device node number as free */ ++ clear_bit(mdev->minor, media_devnode_nums); ++ ++ mutex_unlock(&media_devnode_lock); ++ ++ /* Release media_devnode and perform other cleanups as needed. */ ++ if (mdev->release) ++ mdev->release(mdev); ++} ++ ++static struct bus_type media_bus_type = { ++ .name = MEDIA_NAME, ++}; ++ ++static ssize_t media_read(struct file *filp, char __user *buf, ++ size_t sz, loff_t *off) ++{ ++ struct media_devnode *mdev = media_devnode_data(filp); ++ ++ if (!mdev->fops->read) ++ return -EINVAL; ++ if (!media_devnode_is_registered(mdev)) ++ return -EIO; ++ return mdev->fops->read(filp, buf, sz, off); ++} ++ ++static ssize_t media_write(struct file *filp, const char __user *buf, ++ size_t sz, loff_t *off) ++{ ++ struct media_devnode *mdev = media_devnode_data(filp); ++ ++ if (!mdev->fops->write) ++ return -EINVAL; ++ if (!media_devnode_is_registered(mdev)) ++ return -EIO; ++ return mdev->fops->write(filp, buf, sz, off); ++} ++ ++static unsigned int media_poll(struct file *filp, ++ struct poll_table_struct *poll) ++{ ++ struct media_devnode *mdev = media_devnode_data(filp); ++ ++ if (!media_devnode_is_registered(mdev)) ++ return POLLERR | POLLHUP; ++ if (!mdev->fops->poll) ++ return DEFAULT_POLLMASK; ++ return mdev->fops->poll(filp, poll); ++} ++ ++static long media_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) ++{ ++ struct media_devnode *mdev = media_devnode_data(filp); ++ ++ if (!mdev->fops->ioctl) ++ return -ENOTTY; ++ ++ if (!media_devnode_is_registered(mdev)) ++ return -EIO; ++ ++ return mdev->fops->ioctl(filp, cmd, arg); ++} ++ ++/* Override for the open function */ ++static int media_open(struct inode *inode, struct file *filp) ++{ ++ struct media_devnode *mdev; ++ int ret; ++ ++ /* Check if the media device is available. This needs to be done with ++ * the media_devnode_lock held to prevent an open/unregister race: ++ * without the lock, the device could be unregistered and freed between ++ * the media_devnode_is_registered() and get_device() calls, leading to ++ * a crash. ++ */ ++ mutex_lock(&media_devnode_lock); ++ mdev = container_of(inode->i_cdev, struct media_devnode, cdev); ++ /* return ENXIO if the media device has been removed ++ already or if it is not registered anymore. */ ++ if (!media_devnode_is_registered(mdev)) { ++ mutex_unlock(&media_devnode_lock); ++ return -ENXIO; ++ } ++ /* and increase the device refcount */ ++ get_device(&mdev->dev); ++ mutex_unlock(&media_devnode_lock); ++ ++ filp->private_data = mdev; ++ ++ if (mdev->fops->open) { ++ ret = mdev->fops->open(filp); ++ if (ret) { ++ put_device(&mdev->dev); ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++ ++/* Override for the release function */ ++static int media_release(struct inode *inode, struct file *filp) ++{ ++ struct media_devnode *mdev = media_devnode_data(filp); ++ int ret = 0; ++ ++ if (mdev->fops->release) ++ mdev->fops->release(filp); ++ ++ /* decrease the refcount unconditionally since the release() ++ return value is ignored. */ ++ put_device(&mdev->dev); ++ filp->private_data = NULL; ++ return ret; ++} ++ ++static const struct file_operations media_devnode_fops = { ++ .owner = THIS_MODULE, ++ .read = media_read, ++ .write = media_write, ++ .open = media_open, ++ .unlocked_ioctl = media_ioctl, ++ .release = media_release, ++ .poll = media_poll, ++ .llseek = no_llseek, ++}; ++ ++/** ++ * media_devnode_register - register a media device node ++ * @mdev: media device node structure we want to register ++ * ++ * The registration code assigns minor numbers and registers the new device node ++ * with the kernel. An error is returned if no free minor number can be found, ++ * or if the registration of the device node fails. ++ * ++ * Zero is returned on success. ++ * ++ * Note that if the media_devnode_register call fails, the release() callback of ++ * the media_devnode structure is *not* called, so the caller is responsible for ++ * freeing any data. ++ */ ++int __must_check media_devnode_register(struct media_devnode *mdev) ++{ ++ int minor; ++ int ret; ++ ++ /* Part 1: Find a free minor number */ ++ mutex_lock(&media_devnode_lock); ++ minor = find_next_zero_bit(media_devnode_nums, 0, MEDIA_NUM_DEVICES); ++ if (minor == MEDIA_NUM_DEVICES) { ++ mutex_unlock(&media_devnode_lock); ++ printk(KERN_ERR "could not get a free minor\n"); ++ return -ENFILE; ++ } ++ ++ set_bit(mdev->minor, media_devnode_nums); ++ mutex_unlock(&media_devnode_lock); ++ ++ mdev->minor = minor; ++ ++ /* Part 2: Initialize and register the character device */ ++ cdev_init(&mdev->cdev, &media_devnode_fops); ++ mdev->cdev.owner = mdev->fops->owner; ++ ++ ret = cdev_add(&mdev->cdev, MKDEV(MAJOR(media_dev_t), mdev->minor), 1); ++ if (ret < 0) { ++ printk(KERN_ERR "%s: cdev_add failed\n", __func__); ++ goto error; ++ } ++ ++ /* Part 3: Register the media device */ ++ mdev->dev.bus = &media_bus_type; ++ mdev->dev.devt = MKDEV(MAJOR(media_dev_t), mdev->minor); ++ mdev->dev.release = media_devnode_release; ++ if (mdev->parent) ++ mdev->dev.parent = mdev->parent; ++ dev_set_name(&mdev->dev, "media%d", mdev->minor); ++ ret = device_register(&mdev->dev); ++ if (ret < 0) { ++ printk(KERN_ERR "%s: device_register failed\n", __func__); ++ goto error; ++ } ++ ++ /* Part 4: Activate this minor. The char device can now be used. */ ++ set_bit(MEDIA_FLAG_REGISTERED, &mdev->flags); ++ ++ return 0; ++ ++error: ++ cdev_del(&mdev->cdev); ++ clear_bit(mdev->minor, media_devnode_nums); ++ return ret; ++} ++ ++/** ++ * media_devnode_unregister - unregister a media device node ++ * @mdev: the device node to unregister ++ * ++ * This unregisters the passed device. Future open calls will be met with ++ * errors. ++ * ++ * This function can safely be called if the device node has never been ++ * registered or has already been unregistered. ++ */ ++void media_devnode_unregister(struct media_devnode *mdev) ++{ ++ /* Check if mdev was ever registered at all */ ++ if (!media_devnode_is_registered(mdev)) ++ return; ++ ++ mutex_lock(&media_devnode_lock); ++ clear_bit(MEDIA_FLAG_REGISTERED, &mdev->flags); ++ mutex_unlock(&media_devnode_lock); ++ device_unregister(&mdev->dev); ++} ++ ++/* ++ * Initialise media for linux ++ */ ++static int __init media_devnode_init(void) ++{ ++ int ret; ++ ++ printk(KERN_INFO "Linux media interface: v0.10\n"); ++ ret = alloc_chrdev_region(&media_dev_t, 0, MEDIA_NUM_DEVICES, ++ MEDIA_NAME); ++ if (ret < 0) { ++ printk(KERN_WARNING "media: unable to allocate major\n"); ++ return ret; ++ } ++ ++ ret = bus_register(&media_bus_type); ++ if (ret < 0) { ++ unregister_chrdev_region(media_dev_t, MEDIA_NUM_DEVICES); ++ printk(KERN_WARNING "media: bus_register failed\n"); ++ return -EIO; ++ } ++ ++ return 0; ++} ++ ++static void __exit media_devnode_exit(void) ++{ ++ bus_unregister(&media_bus_type); ++ unregister_chrdev_region(media_dev_t, MEDIA_NUM_DEVICES); ++} ++ ++module_init(media_devnode_init) ++module_exit(media_devnode_exit) ++ ++MODULE_AUTHOR("Laurent Pinchart "); ++MODULE_DESCRIPTION("Device node registration for media drivers"); ++MODULE_LICENSE("GPL"); +diff --git a/include/media/media-devnode.h b/include/media/media-devnode.h +new file mode 100644 +index 0000000..01cd034 +--- /dev/null ++++ b/include/media/media-devnode.h +@@ -0,0 +1,97 @@ ++/* ++ * Media device node ++ * ++ * Copyright (C) 2010 Nokia Corporation ++ * ++ * Contacts: Laurent Pinchart ++ * Sakari Ailus ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ * -- ++ * ++ * Common functions for media-related drivers to register and unregister media ++ * device nodes. ++ */ ++ ++#ifndef _MEDIA_DEVNODE_H ++#define _MEDIA_DEVNODE_H ++ ++#include ++#include ++#include ++#include ++ ++/* ++ * Flag to mark the media_devnode struct as registered. Drivers must not touch ++ * this flag directly, it will be set and cleared by media_devnode_register and ++ * media_devnode_unregister. ++ */ ++#define MEDIA_FLAG_REGISTERED 0 ++ ++struct media_file_operations { ++ struct module *owner; ++ ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); ++ ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); ++ unsigned int (*poll) (struct file *, struct poll_table_struct *); ++ long (*ioctl) (struct file *, unsigned int, unsigned long); ++ int (*open) (struct file *); ++ int (*release) (struct file *); ++}; ++ ++/** ++ * struct media_devnode - Media device node ++ * @parent: parent device ++ * @minor: device node minor number ++ * @flags: flags, combination of the MEDIA_FLAG_* constants ++ * ++ * This structure represents a media-related device node. ++ * ++ * The @parent is a physical device. It must be set by core or device drivers ++ * before registering the node. ++ */ ++struct media_devnode { ++ /* device ops */ ++ const struct media_file_operations *fops; ++ ++ /* sysfs */ ++ struct device dev; /* media device */ ++ struct cdev cdev; /* character device */ ++ struct device *parent; /* device parent */ ++ ++ /* device info */ ++ int minor; ++ unsigned long flags; /* Use bitops to access flags */ ++ ++ /* callbacks */ ++ void (*release)(struct media_devnode *mdev); ++}; ++ ++/* dev to media_devnode */ ++#define to_media_devnode(cd) container_of(cd, struct media_devnode, dev) ++ ++int __must_check media_devnode_register(struct media_devnode *mdev); ++void media_devnode_unregister(struct media_devnode *mdev); ++ ++static inline struct media_devnode *media_devnode_data(struct file *filp) ++{ ++ return filp->private_data; ++} ++ ++static inline int media_devnode_is_registered(struct media_devnode *mdev) ++{ ++ return test_bit(MEDIA_FLAG_REGISTERED, &mdev->flags); ++} ++ ++#endif /* _MEDIA_DEVNODE_H */ +-- +1.6.6.1 + diff --git a/recipes/linux/linux-omap-2.6.37/media/0009-media-Media-device.patch b/recipes/linux/linux-omap-2.6.37/media/0009-media-Media-device.patch new file mode 100644 index 0000000000..d82c798ed0 --- /dev/null +++ b/recipes/linux/linux-omap-2.6.37/media/0009-media-Media-device.patch @@ -0,0 +1,398 @@ +From 6bfbc237b86be01ad23b836ba047e76e23cc7a00 Mon Sep 17 00:00:00 2001 +From: Laurent Pinchart +Date: Wed, 9 Dec 2009 12:39:58 +0100 +Subject: [PATCH 09/43] media: Media device + +The media_device structure abstracts functions common to all kind of +media devices (v4l2, dvb, alsa, ...). It manages media entities and +offers a userspace API to discover and configure the media device +internal topology. + +Signed-off-by: Laurent Pinchart +--- + Documentation/ABI/testing/sysfs-bus-media | 6 ++ + Documentation/DocBook/media-entities.tmpl | 2 + + Documentation/DocBook/media.tmpl | 3 + + Documentation/DocBook/v4l/media-controller.xml | 56 +++++++++++++ + Documentation/media-framework.txt | 67 ++++++++++++++++ + drivers/media/Makefile | 2 +- + drivers/media/media-device.c | 100 ++++++++++++++++++++++++ + include/media/media-device.h | 69 ++++++++++++++++ + 8 files changed, 304 insertions(+), 1 deletions(-) + create mode 100644 Documentation/ABI/testing/sysfs-bus-media + create mode 100644 Documentation/DocBook/v4l/media-controller.xml + create mode 100644 Documentation/media-framework.txt + create mode 100644 drivers/media/media-device.c + create mode 100644 include/media/media-device.h + +diff --git a/Documentation/ABI/testing/sysfs-bus-media b/Documentation/ABI/testing/sysfs-bus-media +new file mode 100644 +index 0000000..7057e57 +--- /dev/null ++++ b/Documentation/ABI/testing/sysfs-bus-media +@@ -0,0 +1,6 @@ ++What: /sys/bus/media/devices/.../model ++Date: January 2011 ++Contact: Laurent Pinchart ++ linux-media@vger.kernel.org ++Description: Contains the device model name in UTF-8. The device version is ++ is not be appended to the model name. +diff --git a/Documentation/DocBook/media-entities.tmpl b/Documentation/DocBook/media-entities.tmpl +index be34dcb..61d6f11 100644 +--- a/Documentation/DocBook/media-entities.tmpl ++++ b/Documentation/DocBook/media-entities.tmpl +@@ -321,6 +321,8 @@ + + + ++ ++ + + + +diff --git a/Documentation/DocBook/media.tmpl b/Documentation/DocBook/media.tmpl +index f11048d..73464b0 100644 +--- a/Documentation/DocBook/media.tmpl ++++ b/Documentation/DocBook/media.tmpl +@@ -106,6 +106,9 @@ Foundation. A copy of the license is included in the chapter entitled + &sub-remote_controllers; + + ++ ++&sub-media-controller; ++ + + &sub-fdl-appendix; + +diff --git a/Documentation/DocBook/v4l/media-controller.xml b/Documentation/DocBook/v4l/media-controller.xml +new file mode 100644 +index 0000000..253ddb4 +--- /dev/null ++++ b/Documentation/DocBook/v4l/media-controller.xml +@@ -0,0 +1,56 @@ ++ ++ ++ ++ Laurent ++ Pinchart ++
laurent.pinchart@ideasonboard.com
++ Initial version. ++
++
++ ++ 2010 ++ Laurent Pinchart ++ ++ ++ ++ ++ ++ 1.0.0 ++ 2010-11-10 ++ lp ++ Initial revision ++ ++ ++
++ ++Media Controller API ++ ++ ++ Media Controller ++ ++
++ Introduction ++ Media devices increasingly handle multiple related functions. Many USB ++ cameras include microphones, video capture hardware can also output video, ++ or SoC camera interfaces also perform memory-to-memory operations similar to ++ video codecs. ++ Independent functions, even when implemented in the same hardware, can ++ be modelled as separate devices. A USB camera with a microphone will be ++ presented to userspace applications as V4L2 and ALSA capture devices. The ++ devices' relationships (when using a webcam, end-users shouldn't have to ++ manually select the associated USB microphone), while not made available ++ directly to applications by the drivers, can usually be retrieved from ++ sysfs. ++ With more and more advanced SoC devices being introduced, the current ++ approach will not scale. Device topologies are getting increasingly complex ++ and can't always be represented by a tree structure. Hardware blocks are ++ shared between different functions, creating dependencies between seemingly ++ unrelated devices. ++ Kernel abstraction APIs such as V4L2 and ALSA provide means for ++ applications to access hardware parameters. As newer hardware expose an ++ increasingly high number of those parameters, drivers need to guess what ++ applications really require based on limited information, thereby ++ implementing policies that belong to userspace. ++ The media controller API aims at solving those problems. ++
++
+diff --git a/Documentation/media-framework.txt b/Documentation/media-framework.txt +new file mode 100644 +index 0000000..1844c3f +--- /dev/null ++++ b/Documentation/media-framework.txt +@@ -0,0 +1,67 @@ ++Linux kernel media framework ++============================ ++ ++This document describes the Linux kernel media framework, its data structures, ++functions and their usage. ++ ++ ++Introduction ++------------ ++ ++The media controller API is documented in DocBook format in ++Documentation/DocBook/v4l/media-controller.xml. This document will focus on ++the kernel-side implementation of the media framework. ++ ++ ++Media device ++------------ ++ ++A media device is represented by a struct media_device instance, defined in ++include/media/media-device.h. Allocation of the structure is handled by the ++media device driver, usually by embedding the media_device instance in a ++larger driver-specific structure. ++ ++Drivers register media device instances by calling ++ ++ media_device_register(struct media_device *mdev); ++ ++The caller is responsible for initializing the media_device structure before ++registration. The following fields must be set: ++ ++ - dev must point to the parent device (usually a pci_dev, usb_interface or ++ platform_device instance). ++ ++ - model must be filled with the device model name as a NUL-terminated UTF-8 ++ string. The device/model revision must not be stored in this field. ++ ++The following fields are optional: ++ ++ - serial is a unique serial number stored as a NUL-terminated ASCII string. ++ The field is big enough to store a GUID in text form. If the hardware ++ doesn't provide a unique serial number this field must be left empty. ++ ++ - bus_info represents the location of the device in the system as a ++ NUL-terminated ASCII string. For PCI/PCIe devices bus_info must be set to ++ "PCI:" (or "PCIe:") followed by the value of pci_name(). For USB devices, ++ the usb_make_path() function must be used. This field is used by ++ applications to distinguish between otherwise identical devices that don't ++ provide a serial number. ++ ++ - hw_revision is the hardware device revision in a driver-specific format. ++ When possible the revision should be formatted with the KERNEL_VERSION ++ macro. ++ ++ - driver_version is formatted with the KERNEL_VERSION macro. The version ++ minor must be incremented when new features are added to the userspace API ++ without breaking binary compatibility. The version major must be ++ incremented when binary compatibility is broken. ++ ++Upon successful registration a character device named media[0-9]+ is created. ++The device major and minor numbers are dynamic. The model name is exported as ++a sysfs attribute. ++ ++Drivers unregister media device instances by calling ++ ++ media_device_unregister(struct media_device *mdev); ++ ++Unregistering a media device that hasn't been registered is *NOT* safe. +diff --git a/drivers/media/Makefile b/drivers/media/Makefile +index 3a08991..019d3e0 100644 +--- a/drivers/media/Makefile ++++ b/drivers/media/Makefile +@@ -2,7 +2,7 @@ + # Makefile for the kernel multimedia device drivers. + # + +-media-objs := media-devnode.o ++media-objs := media-device.o media-devnode.o + + ifeq ($(CONFIG_MEDIA_CONTROLLER),y) + obj-$(CONFIG_MEDIA_SUPPORT) += media.o +diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c +new file mode 100644 +index 0000000..57a9c6b +--- /dev/null ++++ b/drivers/media/media-device.c +@@ -0,0 +1,100 @@ ++/* ++ * Media device ++ * ++ * Copyright (C) 2010 Nokia Corporation ++ * ++ * Contacts: Laurent Pinchart ++ * Sakari Ailus ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#include ++#include ++ ++#include ++#include ++ ++static const struct media_file_operations media_device_fops = { ++ .owner = THIS_MODULE, ++}; ++ ++/* ----------------------------------------------------------------------------- ++ * sysfs ++ */ ++ ++static ssize_t show_model(struct device *cd, ++ struct device_attribute *attr, char *buf) ++{ ++ struct media_device *mdev = to_media_device(to_media_devnode(cd)); ++ ++ return sprintf(buf, "%.*s\n", (int)sizeof(mdev->model), mdev->model); ++} ++ ++static DEVICE_ATTR(model, S_IRUGO, show_model, NULL); ++ ++/* ----------------------------------------------------------------------------- ++ * Registration/unregistration ++ */ ++ ++static void media_device_release(struct media_devnode *mdev) ++{ ++} ++ ++/** ++ * media_device_register - register a media device ++ * @mdev: The media device ++ * ++ * The caller is responsible for initializing the media device before ++ * registration. The following fields must be set: ++ * ++ * - dev must point to the parent device ++ * - model must be filled with the device model name ++ */ ++int __must_check media_device_register(struct media_device *mdev) ++{ ++ int ret; ++ ++ if (WARN_ON(mdev->dev == NULL || mdev->model[0] == 0)) ++ return -EINVAL; ++ ++ /* Register the device node. */ ++ mdev->devnode.fops = &media_device_fops; ++ mdev->devnode.parent = mdev->dev; ++ mdev->devnode.release = media_device_release; ++ ret = media_devnode_register(&mdev->devnode); ++ if (ret < 0) ++ return ret; ++ ++ ret = device_create_file(&mdev->devnode.dev, &dev_attr_model); ++ if (ret < 0) { ++ media_devnode_unregister(&mdev->devnode); ++ return ret; ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(media_device_register); ++ ++/** ++ * media_device_unregister - unregister a media device ++ * @mdev: The media device ++ * ++ */ ++void media_device_unregister(struct media_device *mdev) ++{ ++ device_remove_file(&mdev->devnode.dev, &dev_attr_model); ++ media_devnode_unregister(&mdev->devnode); ++} ++EXPORT_SYMBOL_GPL(media_device_unregister); +diff --git a/include/media/media-device.h b/include/media/media-device.h +new file mode 100644 +index 0000000..e11f01a +--- /dev/null ++++ b/include/media/media-device.h +@@ -0,0 +1,69 @@ ++/* ++ * Media device ++ * ++ * Copyright (C) 2010 Nokia Corporation ++ * ++ * Contacts: Laurent Pinchart ++ * Sakari Ailus ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef _MEDIA_DEVICE_H ++#define _MEDIA_DEVICE_H ++ ++#include ++#include ++ ++#include ++ ++/** ++ * struct media_device - Media device ++ * @dev: Parent device ++ * @devnode: Media device node ++ * @model: Device model name ++ * @serial: Device serial number (optional) ++ * @bus_info: Unique and stable device location identifier ++ * @hw_revision: Hardware device revision ++ * @driver_version: Device driver version ++ * ++ * This structure represents an abstract high-level media device. It allows easy ++ * access to entities and provides basic media device-level support. The ++ * structure can be allocated directly or embedded in a larger structure. ++ * ++ * The parent @dev is a physical device. It must be set before registering the ++ * media device. ++ * ++ * @model is a descriptive model name exported through sysfs. It doesn't have to ++ * be unique. ++ */ ++struct media_device { ++ /* dev->driver_data points to this struct. */ ++ struct device *dev; ++ struct media_devnode devnode; ++ ++ char model[32]; ++ char serial[40]; ++ char bus_info[32]; ++ u32 hw_revision; ++ u32 driver_version; ++}; ++ ++/* media_devnode to media_device */ ++#define to_media_device(node) container_of(node, struct media_device, devnode) ++ ++int __must_check media_device_register(struct media_device *mdev); ++void media_device_unregister(struct media_device *mdev); ++ ++#endif +-- +1.6.6.1 + diff --git a/recipes/linux/linux-omap-2.6.37/media/0010-media-Entities-pads-and-links.patch b/recipes/linux/linux-omap-2.6.37/media/0010-media-Entities-pads-and-links.patch new file mode 100644 index 0000000000..be762331c9 --- /dev/null +++ b/recipes/linux/linux-omap-2.6.37/media/0010-media-Entities-pads-and-links.patch @@ -0,0 +1,690 @@ +From b4697e5a8ad1e564ea378d435c2ce190318c1027 Mon Sep 17 00:00:00 2001 +From: Laurent Pinchart +Date: Wed, 9 Dec 2009 12:40:00 +0100 +Subject: [PATCH 10/43] media: Entities, pads and links + +As video hardware pipelines become increasingly complex and +configurable, the current hardware description through v4l2 subdevices +reaches its limits. In addition to enumerating and configuring +subdevices, video camera drivers need a way to discover and modify at +runtime how those subdevices are connected. This is done through new +elements called entities, pads and links. + +An entity is a basic media hardware building block. It can correspond to +a large variety of logical blocks such as physical hardware devices +(CMOS sensor for instance), logical hardware devices (a building block +in a System-on-Chip image processing pipeline), DMA channels or physical +connectors. + +A pad is a connection endpoint through which an entity can interact with +other entities. Data (not restricted to video) produced by an entity +flows from the entity's output to one or more entity inputs. Pads should +not be confused with physical pins at chip boundaries. + +A link is a point-to-point oriented connection between two pads, either +on the same entity or on different entities. Data flows from a source +pad to a sink pad. + +Links are stored in the source entity. To make backwards graph walk +faster, a copy of all links is also stored in the sink entity. The copy +is known as a backlink and is only used to help graph traversal. + +The entity API is made of three functions: + +- media_entity_init() initializes an entity. The caller must provide an +array of pads as well as an estimated number of links. The links array +is allocated dynamically and will be reallocated if it grows beyond the +initial estimate. + +- media_entity_cleanup() frees resources allocated for an entity. It +must be called during the cleanup phase after unregistering the entity +and before freeing it. + +- media_entity_create_link() creates a link between two entities. An +entry in the link array of each entity is allocated and stores pointers +to source and sink pads. + +When a media device is unregistered, all its entities are unregistered +automatically. + +The code is based on Hans Verkuil initial work. + +Signed-off-by: Laurent Pinchart +Signed-off-by: Sakari Ailus +--- + Documentation/DocBook/v4l/media-controller.xml | 20 +++ + Documentation/media-framework.txt | 151 ++++++++++++++++++++++++ + drivers/media/Makefile | 2 +- + drivers/media/media-device.c | 56 +++++++++ + drivers/media/media-entity.c | 147 +++++++++++++++++++++++ + include/media/media-device.h | 19 +++ + include/media/media-entity.h | 122 +++++++++++++++++++ + 7 files changed, 516 insertions(+), 1 deletions(-) + create mode 100644 drivers/media/media-entity.c + create mode 100644 include/media/media-entity.h + +diff --git a/Documentation/DocBook/v4l/media-controller.xml b/Documentation/DocBook/v4l/media-controller.xml +index 253ddb4..f89228d 100644 +--- a/Documentation/DocBook/v4l/media-controller.xml ++++ b/Documentation/DocBook/v4l/media-controller.xml +@@ -53,4 +53,24 @@ + implementing policies that belong to userspace. + The media controller API aims at solving those problems. + ++ ++
++ Media device model ++ Discovering a device internal topology, and configuring it at runtime, ++ is one of the goals of the media controller API. To achieve this, hardware ++ devices are modelled as an oriented graph of building blocks called entities ++ connected through pads. ++ An entity is a basic media hardware or software building block. It can ++ correspond to a large variety of logical blocks such as physical hardware ++ devices (CMOS sensor for instance), logical hardware devices (a building ++ block in a System-on-Chip image processing pipeline), DMA channels or ++ physical connectors. ++ A pad is a connection endpoint through which an entity can interact ++ with other entities. Data (not restricted to video) produced by an entity ++ flows from the entity's output to one or more entity inputs. Pads should not ++ be confused with physical pins at chip boundaries. ++ A link is a point-to-point oriented connection between two pads, ++ either on the same entity or on different entities. Data flows from a source ++ pad to a sink pad. ++
+ +diff --git a/Documentation/media-framework.txt b/Documentation/media-framework.txt +index 1844c3f..b252cf9 100644 +--- a/Documentation/media-framework.txt ++++ b/Documentation/media-framework.txt +@@ -13,6 +13,30 @@ Documentation/DocBook/v4l/media-controller.xml. This document will focus on + the kernel-side implementation of the media framework. + + ++Abstract media device model ++--------------------------- ++ ++Discovering a device internal topology, and configuring it at runtime, is one ++of the goals of the media framework. To achieve this, hardware devices are ++modeled as an oriented graph of building blocks called entities connected ++through pads. ++ ++An entity is a basic media hardware building block. It can correspond to ++a large variety of logical blocks such as physical hardware devices ++(CMOS sensor for instance), logical hardware devices (a building block ++in a System-on-Chip image processing pipeline), DMA channels or physical ++connectors. ++ ++A pad is a connection endpoint through which an entity can interact with ++other entities. Data (not restricted to video) produced by an entity ++flows from the entity's output to one or more entity inputs. Pads should ++not be confused with physical pins at chip boundaries. ++ ++A link is a point-to-point oriented connection between two pads, either ++on the same entity or on different entities. Data flows from a source ++pad to a sink pad. ++ ++ + Media device + ------------ + +@@ -65,3 +89,130 @@ Drivers unregister media device instances by calling + media_device_unregister(struct media_device *mdev); + + Unregistering a media device that hasn't been registered is *NOT* safe. ++ ++ ++Entities, pads and links ++------------------------ ++ ++- Entities ++ ++Entities are represented by a struct media_entity instance, defined in ++include/media/media-entity.h. The structure is usually embedded into a ++higher-level structure, such as a v4l2_subdev or video_device instance, ++although drivers can allocate entities directly. ++ ++Drivers initialize entities by calling ++ ++ media_entity_init(struct media_entity *entity, u16 num_pads, ++ struct media_pad *pads, u16 extra_links); ++ ++The media_entity name, type, flags, revision and group_id fields can be ++initialized before or after calling media_entity_init. Entities embedded in ++higher-level standard structures can have some of those fields set by the ++higher-level framework. ++ ++As the number of pads is known in advance, the pads array is not allocated ++dynamically but is managed by the entity driver. Most drivers will embed the ++pads array in a driver-specific structure, avoiding dynamic allocation. ++ ++Drivers must set the direction of every pad in the pads array before calling ++media_entity_init. The function will initialize the other pads fields. ++ ++Unlike the number of pads, the total number of links isn't always known in ++advance by the entity driver. As an initial estimate, media_entity_init ++pre-allocates a number of links equal to the number of pads plus an optional ++number of extra links. The links array will be reallocated if it grows beyond ++the initial estimate. ++ ++Drivers register entities with a media device by calling ++ ++ media_device_register_entity(struct media_device *mdev, ++ struct media_entity *entity); ++ ++Entities are identified by a unique positive integer ID. Drivers can provide an ++ID by filling the media_entity id field prior to registration, or request the ++media controller framework to assign an ID automatically. Drivers that provide ++IDs manually must ensure that all IDs are unique. IDs are not guaranteed to be ++contiguous even when they are all assigned automatically by the framework. ++ ++Drivers unregister entities by calling ++ ++ media_device_unregister_entity(struct media_entity *entity); ++ ++Unregistering an entity will not change the IDs of the other entities, and the ++ID will never be reused for a newly registered entity. ++ ++When a media device is unregistered, all its entities are unregistered ++automatically. No manual entities unregistration is then required. ++ ++Drivers free resources associated with an entity by calling ++ ++ media_entity_cleanup(struct media_entity *entity); ++ ++This function must be called during the cleanup phase after unregistering the ++entity. Note that the media_entity instance itself must be freed explicitly by ++the driver if required. ++ ++Entities have flags that describe the entity capabilities and state. ++ ++ MEDIA_ENT_FL_DEFAULT indicates the default entity for a given type. ++ This can be used to report the default audio and video devices or the ++ default camera sensor. ++ ++Logical entity groups can be defined by setting the group ID of all member ++entities to the same non-zero value. An entity group serves no purpose in the ++kernel, but is reported to userspace during entities enumeration. The group_id ++field belongs to the media device driver and must not by touched by entity ++drivers. ++ ++Media device drivers should define groups if several entities are logically ++bound together. Example usages include reporting ++ ++ - ALSA, VBI and video nodes that carry the same media stream ++ - lens and flash controllers associated with a sensor ++ ++- Pads ++ ++Pads are represented by a struct media_pad instance, defined in ++include/media/media-entity.h. Each entity stores its pads in a pads array ++managed by the entity driver. Drivers usually embed the array in a ++driver-specific structure. ++ ++Pads are identified by their entity and their 0-based index in the pads array. ++Both information are stored in the media_pad structure, making the media_pad ++pointer the canonical way to store and pass link references. ++ ++Pads have flags that describe the pad capabilities and state. ++ ++ MEDIA_PAD_FL_INPUT indicates that the pad supports sinking data. ++ MEDIA_PAD_FL_OUTPUT indicates that the pad supports sourcing data. ++ ++One and only one of MEDIA_PAD_FL_INPUT and MEDIA_PAD_FL_OUTPUT must be set for ++each pad. ++ ++- Links ++ ++Links are represented by a struct media_link instance, defined in ++include/media/media-entity.h. Each entity stores all links originating at or ++targetting any of its pads in a links array. A given link is thus stored ++twice, once in the source entity and once in the target entity. The array is ++pre-allocated and grows dynamically as needed. ++ ++Drivers create links by calling ++ ++ media_entity_create_link(struct media_entity *source, u16 source_pad, ++ struct media_entity *sink, u16 sink_pad, ++ u32 flags); ++ ++An entry in the link array of each entity is allocated and stores pointers ++to source and sink pads. ++ ++Links have flags that describe the link capabilities and state. ++ ++ MEDIA_LNK_FL_ENABLED indicates that the link is enabled and can be used ++ to transfer media data. When two or more links target a sink pad, only ++ one of them can be enabled at a time. ++ MEDIA_LNK_FL_IMMUTABLE indicates that the link enabled state can't be ++ modified at runtime. If MEDIA_LNK_FL_IMMUTABLE is set, then ++ MEDIA_LNK_FL_ENABLED must also be set since an immutable link is always ++ enabled. +diff --git a/drivers/media/Makefile b/drivers/media/Makefile +index 019d3e0..b890248 100644 +--- a/drivers/media/Makefile ++++ b/drivers/media/Makefile +@@ -2,7 +2,7 @@ + # Makefile for the kernel multimedia device drivers. + # + +-media-objs := media-device.o media-devnode.o ++media-objs := media-device.o media-devnode.o media-entity.o + + ifeq ($(CONFIG_MEDIA_CONTROLLER),y) + obj-$(CONFIG_MEDIA_SUPPORT) += media.o +diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c +index 57a9c6b..b8a3ace 100644 +--- a/drivers/media/media-device.c ++++ b/drivers/media/media-device.c +@@ -25,6 +25,7 @@ + + #include + #include ++#include + + static const struct media_file_operations media_device_fops = { + .owner = THIS_MODULE, +@@ -69,6 +70,10 @@ int __must_check media_device_register(struct media_device *mdev) + if (WARN_ON(mdev->dev == NULL || mdev->model[0] == 0)) + return -EINVAL; + ++ mdev->entity_id = 1; ++ INIT_LIST_HEAD(&mdev->entities); ++ spin_lock_init(&mdev->lock); ++ + /* Register the device node. */ + mdev->devnode.fops = &media_device_fops; + mdev->devnode.parent = mdev->dev; +@@ -94,7 +99,58 @@ EXPORT_SYMBOL_GPL(media_device_register); + */ + void media_device_unregister(struct media_device *mdev) + { ++ struct media_entity *entity; ++ struct media_entity *next; ++ ++ list_for_each_entry_safe(entity, next, &mdev->entities, list) ++ media_device_unregister_entity(entity); ++ + device_remove_file(&mdev->devnode.dev, &dev_attr_model); + media_devnode_unregister(&mdev->devnode); + } + EXPORT_SYMBOL_GPL(media_device_unregister); ++ ++/** ++ * media_device_register_entity - Register an entity with a media device ++ * @mdev: The media device ++ * @entity: The entity ++ */ ++int __must_check media_device_register_entity(struct media_device *mdev, ++ struct media_entity *entity) ++{ ++ /* Warn if we apparently re-register an entity */ ++ WARN_ON(entity->parent != NULL); ++ entity->parent = mdev; ++ ++ spin_lock(&mdev->lock); ++ if (entity->id == 0) ++ entity->id = mdev->entity_id++; ++ else ++ mdev->entity_id = max(entity->id + 1, mdev->entity_id); ++ list_add_tail(&entity->list, &mdev->entities); ++ spin_unlock(&mdev->lock); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(media_device_register_entity); ++ ++/** ++ * media_device_unregister_entity - Unregister an entity ++ * @entity: The entity ++ * ++ * If the entity has never been registered this function will return ++ * immediately. ++ */ ++void media_device_unregister_entity(struct media_entity *entity) ++{ ++ struct media_device *mdev = entity->parent; ++ ++ if (mdev == NULL) ++ return; ++ ++ spin_lock(&mdev->lock); ++ list_del(&entity->list); ++ spin_unlock(&mdev->lock); ++ entity->parent = NULL; ++} ++EXPORT_SYMBOL_GPL(media_device_unregister_entity); +diff --git a/drivers/media/media-entity.c b/drivers/media/media-entity.c +new file mode 100644 +index 0000000..e4ba2bc +--- /dev/null ++++ b/drivers/media/media-entity.c +@@ -0,0 +1,147 @@ ++/* ++ * Media entity ++ * ++ * Copyright (C) 2010 Nokia Corporation ++ * ++ * Contacts: Laurent Pinchart ++ * Sakari Ailus ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#include ++#include ++#include ++ ++/** ++ * media_entity_init - Initialize a media entity ++ * ++ * @num_pads: Total number of input and output pads. ++ * @extra_links: Initial estimate of the number of extra links. ++ * @pads: Array of 'num_pads' pads. ++ * ++ * The total number of pads is an intrinsic property of entities known by the ++ * entity driver, while the total number of links depends on hardware design ++ * and is an extrinsic property unknown to the entity driver. However, in most ++ * use cases the entity driver can guess the number of links which can safely ++ * be assumed to be equal to or larger than the number of pads. ++ * ++ * For those reasons the links array can be preallocated based on the entity ++ * driver guess and will be reallocated later if extra links need to be ++ * created. ++ * ++ * This function allocates a links array with enough space to hold at least ++ * 'num_pads' + 'extra_links' elements. The media_entity::max_links field will ++ * be set to the number of allocated elements. ++ * ++ * The pads array is managed by the entity driver and passed to ++ * media_entity_init() where its pointer will be stored in the entity structure. ++ */ ++int ++media_entity_init(struct media_entity *entity, u16 num_pads, ++ struct media_pad *pads, u16 extra_links) ++{ ++ struct media_link *links; ++ unsigned int max_links = num_pads + extra_links; ++ unsigned int i; ++ ++ links = kzalloc(max_links * sizeof(links[0]), GFP_KERNEL); ++ if (links == NULL) ++ return -ENOMEM; ++ ++ entity->group_id = 0; ++ entity->max_links = max_links; ++ entity->num_links = 0; ++ entity->num_backlinks = 0; ++ entity->num_pads = num_pads; ++ entity->pads = pads; ++ entity->links = links; ++ ++ for (i = 0; i < num_pads; i++) { ++ pads[i].entity = entity; ++ pads[i].index = i; ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(media_entity_init); ++ ++void ++media_entity_cleanup(struct media_entity *entity) ++{ ++ kfree(entity->links); ++} ++EXPORT_SYMBOL_GPL(media_entity_cleanup); ++ ++static struct media_link *media_entity_add_link(struct media_entity *entity) ++{ ++ if (entity->num_links >= entity->max_links) { ++ struct media_link *links = entity->links; ++ unsigned int max_links = entity->max_links + 2; ++ unsigned int i; ++ ++ links = krealloc(links, max_links * sizeof(*links), GFP_KERNEL); ++ if (links == NULL) ++ return NULL; ++ ++ for (i = 0; i < entity->num_links; i++) ++ links[i].reverse->reverse = &links[i]; ++ ++ entity->max_links = max_links; ++ entity->links = links; ++ } ++ ++ return &entity->links[entity->num_links++]; ++} ++ ++int ++media_entity_create_link(struct media_entity *source, u16 source_pad, ++ struct media_entity *sink, u16 sink_pad, u32 flags) ++{ ++ struct media_link *link; ++ struct media_link *backlink; ++ ++ BUG_ON(source == NULL || sink == NULL); ++ BUG_ON(source_pad >= source->num_pads); ++ BUG_ON(sink_pad >= sink->num_pads); ++ ++ link = media_entity_add_link(source); ++ if (link == NULL) ++ return -ENOMEM; ++ ++ link->source = &source->pads[source_pad]; ++ link->sink = &sink->pads[sink_pad]; ++ link->flags = flags; ++ ++ /* Create the backlink. Backlinks are used to help graph traversal and ++ * are not reported to userspace. ++ */ ++ backlink = media_entity_add_link(sink); ++ if (backlink == NULL) { ++ source->num_links--; ++ return -ENOMEM; ++ } ++ ++ backlink->source = &source->pads[source_pad]; ++ backlink->sink = &sink->pads[sink_pad]; ++ backlink->flags = flags; ++ ++ link->reverse = backlink; ++ backlink->reverse = link; ++ ++ sink->num_backlinks++; ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(media_entity_create_link); +diff --git a/include/media/media-device.h b/include/media/media-device.h +index e11f01a..0b1ecf5 100644 +--- a/include/media/media-device.h ++++ b/include/media/media-device.h +@@ -25,8 +25,10 @@ + + #include + #include ++#include + + #include ++#include + + /** + * struct media_device - Media device +@@ -37,6 +39,9 @@ + * @bus_info: Unique and stable device location identifier + * @hw_revision: Hardware device revision + * @driver_version: Device driver version ++ * @entity_id: ID of the next entity to be registered ++ * @entities: List of registered entities ++ * @lock: Entities list lock + * + * This structure represents an abstract high-level media device. It allows easy + * access to entities and provides basic media device-level support. The +@@ -58,6 +63,12 @@ struct media_device { + char bus_info[32]; + u32 hw_revision; + u32 driver_version; ++ ++ u32 entity_id; ++ struct list_head entities; ++ ++ /* Protects the entities list */ ++ spinlock_t lock; + }; + + /* media_devnode to media_device */ +@@ -66,4 +77,12 @@ struct media_device { + int __must_check media_device_register(struct media_device *mdev); + void media_device_unregister(struct media_device *mdev); + ++int __must_check media_device_register_entity(struct media_device *mdev, ++ struct media_entity *entity); ++void media_device_unregister_entity(struct media_entity *entity); ++ ++/* Iterate over all entities. */ ++#define media_device_for_each_entity(entity, mdev) \ ++ list_for_each_entry(entity, &(mdev)->entities, list) ++ + #endif +diff --git a/include/media/media-entity.h b/include/media/media-entity.h +new file mode 100644 +index 0000000..7cf9135 +--- /dev/null ++++ b/include/media/media-entity.h +@@ -0,0 +1,122 @@ ++/* ++ * Media entity ++ * ++ * Copyright (C) 2010 Nokia Corporation ++ * ++ * Contacts: Laurent Pinchart ++ * Sakari Ailus ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef _MEDIA_ENTITY_H ++#define _MEDIA_ENTITY_H ++ ++#include ++ ++#define MEDIA_ENT_TYPE_SHIFT 16 ++#define MEDIA_ENT_TYPE_MASK 0x00ff0000 ++#define MEDIA_ENT_SUBTYPE_MASK 0x0000ffff ++ ++#define MEDIA_ENT_T_DEVNODE (1 << MEDIA_ENTITY_TYPE_SHIFT) ++#define MEDIA_ENT_T_DEVNODE_V4L (MEDIA_ENTITY_T_DEVNODE + 1) ++#define MEDIA_ENT_T_DEVNODE_FB (MEDIA_ENTITY_T_DEVNODE + 2) ++#define MEDIA_ENT_T_DEVNODE_ALSA (MEDIA_ENTITY_T_DEVNODE + 3) ++#define MEDIA_ENT_T_DEVNODE_DVB (MEDIA_ENTITY_T_DEVNODE + 4) ++ ++#define MEDIA_ENT_T_V4L2_SUBDEV (2 << MEDIA_ENTITY_TYPE_SHIFT) ++#define MEDIA_ENT_T_V4L2_SUBDEV_SENSOR (MEDIA_ENTITY_T_V4L2_SUBDEV + 1) ++#define MEDIA_ENT_T_V4L2_SUBDEV_FLASH (MEDIA_ENTITY_T_V4L2_SUBDEV + 2) ++#define MEDIA_ENT_T_V4L2_SUBDEV_LENS (MEDIA_ENTITY_T_V4L2_SUBDEV + 3) ++ ++#define MEDIA_ENT_FL_DEFAULT (1 << 0) ++ ++#define MEDIA_LNK_FL_ENABLED (1 << 0) ++#define MEDIA_LNK_FL_IMMUTABLE (1 << 1) ++ ++#define MEDIA_PAD_FL_INPUT (1 << 0) ++#define MEDIA_PAD_FL_OUTPUT (1 << 1) ++ ++struct media_link { ++ struct media_pad *source; /* Source pad */ ++ struct media_pad *sink; /* Sink pad */ ++ struct media_link *reverse; /* Link in the reverse direction */ ++ unsigned long flags; /* Link flags (MEDIA_LNK_FL_*) */ ++}; ++ ++struct media_pad { ++ struct media_entity *entity; /* Entity this pad belongs to */ ++ u16 index; /* Pad index in the entity pads array */ ++ unsigned long flags; /* Pad flags (MEDIA_PAD_FL_*) */ ++}; ++ ++struct media_entity { ++ struct list_head list; ++ struct media_device *parent; /* Media device this entity belongs to*/ ++ u32 id; /* Entity ID, unique in the parent media ++ * device context */ ++ const char *name; /* Entity name */ ++ u32 type; /* Entity type (MEDIA_ENT_T_*) */ ++ u32 revision; /* Entity revision, driver specific */ ++ unsigned long flags; /* Entity flags (MEDIA_ENT_FL_*) */ ++ u32 group_id; /* Entity group ID */ ++ ++ u16 num_pads; /* Number of input and output pads */ ++ u16 num_links; /* Number of existing links, both ++ * enabled and disabled */ ++ u16 num_backlinks; /* Number of backlinks */ ++ u16 max_links; /* Maximum number of links */ ++ ++ struct media_pad *pads; /* Pads array (num_pads elements) */ ++ struct media_link *links; /* Links array (max_links elements)*/ ++ ++ union { ++ /* Node specifications */ ++ struct { ++ u32 major; ++ u32 minor; ++ } v4l; ++ struct { ++ u32 major; ++ u32 minor; ++ } fb; ++ struct { ++ u32 card; ++ u32 device; ++ u32 subdevice; ++ } alsa; ++ int dvb; ++ ++ /* Sub-device specifications */ ++ /* Nothing needed yet */ ++ }; ++}; ++ ++static inline u32 media_entity_type(struct media_entity *entity) ++{ ++ return entity->type & MEDIA_ENT_TYPE_MASK; ++} ++ ++static inline u32 media_entity_subtype(struct media_entity *entity) ++{ ++ return entity->type & MEDIA_ENT_SUBTYPE_MASK; ++} ++ ++int media_entity_init(struct media_entity *entity, u16 num_pads, ++ struct media_pad *pads, u16 extra_links); ++void media_entity_cleanup(struct media_entity *entity); ++int media_entity_create_link(struct media_entity *source, u16 source_pad, ++ struct media_entity *sink, u16 sink_pad, u32 flags); ++ ++#endif +-- +1.6.6.1 + diff --git a/recipes/linux/linux-omap-2.6.37/media/0011-media-Entity-graph-traversal.patch b/recipes/linux/linux-omap-2.6.37/media/0011-media-Entity-graph-traversal.patch new file mode 100644 index 0000000000..15fc612304 --- /dev/null +++ b/recipes/linux/linux-omap-2.6.37/media/0011-media-Entity-graph-traversal.patch @@ -0,0 +1,228 @@ +From 5b45472e8a692e6acea3cb6d601b44c17ea8d59e Mon Sep 17 00:00:00 2001 +From: Sakari Ailus +Date: Sun, 7 Mar 2010 21:14:14 +0200 +Subject: [PATCH 11/43] media: Entity graph traversal + +Add media entity graph traversal. The traversal follows enabled links by +depth first. Traversing graph backwards is prevented by comparing the next +possible entity in the graph with the previous one. Multiply connected +graphs are thus not supported. + +Signed-off-by: Sakari Ailus +Signed-off-by: Laurent Pinchart +Signed-off-by: Vimarsh Zutshi +--- + Documentation/media-framework.txt | 42 +++++++++++++ + drivers/media/media-entity.c | 115 +++++++++++++++++++++++++++++++++++++ + include/media/media-entity.h | 15 +++++ + 3 files changed, 172 insertions(+), 0 deletions(-) + +diff --git a/Documentation/media-framework.txt b/Documentation/media-framework.txt +index b252cf9..88fe379 100644 +--- a/Documentation/media-framework.txt ++++ b/Documentation/media-framework.txt +@@ -216,3 +216,45 @@ Links have flags that describe the link capabilities and state. + modified at runtime. If MEDIA_LNK_FL_IMMUTABLE is set, then + MEDIA_LNK_FL_ENABLED must also be set since an immutable link is always + enabled. ++ ++ ++Graph traversal ++--------------- ++ ++The media framework provides APIs to iterate over entities in a graph. ++ ++To iterate over all entities belonging to a media device, drivers can use the ++media_device_for_each_entity macro, defined in include/media/media-device.h. ++ ++ struct media_entity *entity; ++ ++ media_device_for_each_entity(entity, mdev) { ++ /* entity will point to each entity in turn */ ++ ... ++ } ++ ++Drivers might also need to iterate over all entities in a graph that can be ++reached only through enabled links starting at a given entity. The media ++framework provides a depth-first graph traversal API for that purpose. ++ ++Note that graphs with cycles (whether directed or undirected) are *NOT* ++supported by the graph traversal API. To prevent infinite loops, the graph ++traversal code limits the maximum depth to MEDIA_ENTITY_ENUM_MAX_DEPTH, ++currently defined as 16. ++ ++Drivers initiate a graph traversal by calling ++ ++ media_entity_graph_walk_start(struct media_entity_graph *graph, ++ struct media_entity *entity); ++ ++The graph structure, provided by the caller, is initialized to start graph ++traversal at the given entity. ++ ++Drivers can then retrieve the next entity by calling ++ ++ media_entity_graph_walk_next(struct media_entity_graph *graph); ++ ++When the graph traversal is complete the function will return NULL. ++ ++Graph traversal can be interrupted at any moment. No cleanup function call is ++required and the graph structure can be freed normally. +diff --git a/drivers/media/media-entity.c b/drivers/media/media-entity.c +index e4ba2bc..a805f20 100644 +--- a/drivers/media/media-entity.c ++++ b/drivers/media/media-entity.c +@@ -84,6 +84,121 @@ media_entity_cleanup(struct media_entity *entity) + } + EXPORT_SYMBOL_GPL(media_entity_cleanup); + ++/* ----------------------------------------------------------------------------- ++ * Graph traversal ++ */ ++ ++static struct media_entity * ++media_entity_other(struct media_entity *entity, struct media_link *link) ++{ ++ if (link->source->entity == entity) ++ return link->sink->entity; ++ else ++ return link->source->entity; ++} ++ ++/* push an entity to traversal stack */ ++static void stack_push(struct media_entity_graph *graph, ++ struct media_entity *entity) ++{ ++ if (graph->top == MEDIA_ENTITY_ENUM_MAX_DEPTH - 1) { ++ WARN_ON(1); ++ return; ++ } ++ graph->top++; ++ graph->stack[graph->top].link = 0; ++ graph->stack[graph->top].entity = entity; ++} ++ ++static struct media_entity *stack_pop(struct media_entity_graph *graph) ++{ ++ struct media_entity *entity; ++ ++ entity = graph->stack[graph->top].entity; ++ graph->top--; ++ ++ return entity; ++} ++ ++#define stack_peek(en) ((en)->stack[(en)->top - 1].entity) ++#define link_top(en) ((en)->stack[(en)->top].link) ++#define stack_top(en) ((en)->stack[(en)->top].entity) ++ ++/** ++ * media_entity_graph_walk_start - Start walking the media graph at a given entity ++ * @graph: Media graph structure that will be used to walk the graph ++ * @entity: Starting entity ++ * ++ * This function initializes the graph traversal structure to walk the entities ++ * graph starting at the given entity. The traversal structure must not be ++ * modified by the caller during graph traversal. When done the structure can ++ * safely be freed. ++ */ ++void media_entity_graph_walk_start(struct media_entity_graph *graph, ++ struct media_entity *entity) ++{ ++ graph->top = 0; ++ graph->stack[graph->top].entity = NULL; ++ stack_push(graph, entity); ++} ++EXPORT_SYMBOL_GPL(media_entity_graph_walk_start); ++ ++/** ++ * media_entity_graph_walk_next - Get the next entity in the graph ++ * @graph: Media graph structure ++ * ++ * Perform a depth-first traversal of the given media entities graph. ++ * ++ * The graph structure must have been previously initialized with a call to ++ * media_entity_graph_walk_start(). ++ * ++ * Return the next entity in the graph or NULL if the whole graph have been ++ * traversed. ++ */ ++struct media_entity * ++media_entity_graph_walk_next(struct media_entity_graph *graph) ++{ ++ if (stack_top(graph) == NULL) ++ return NULL; ++ ++ /* ++ * Depth first search. Push entity to stack and continue from ++ * top of the stack until no more entities on the level can be ++ * found. ++ */ ++ while (link_top(graph) < stack_top(graph)->num_links) { ++ struct media_entity *entity = stack_top(graph); ++ struct media_link *link = &entity->links[link_top(graph)]; ++ struct media_entity *next; ++ ++ /* The link is not enabled so we do not follow. */ ++ if (!(link->flags & MEDIA_LNK_FL_ENABLED)) { ++ link_top(graph)++; ++ continue; ++ } ++ ++ /* Get the entity in the other end of the link . */ ++ next = media_entity_other(entity, link); ++ ++ /* Was it the entity we came here from? */ ++ if (next == stack_peek(graph)) { ++ link_top(graph)++; ++ continue; ++ } ++ ++ /* Push the new entity to stack and start over. */ ++ link_top(graph)++; ++ stack_push(graph, next); ++ } ++ ++ return stack_pop(graph); ++} ++EXPORT_SYMBOL_GPL(media_entity_graph_walk_next); ++ ++/* ----------------------------------------------------------------------------- ++ * Links management ++ */ ++ + static struct media_link *media_entity_add_link(struct media_entity *entity) + { + if (entity->num_links >= entity->max_links) { +diff --git a/include/media/media-entity.h b/include/media/media-entity.h +index 7cf9135..b82f824 100644 +--- a/include/media/media-entity.h ++++ b/include/media/media-entity.h +@@ -113,10 +113,25 @@ static inline u32 media_entity_subtype(struct media_entity *entity) + return entity->type & MEDIA_ENT_SUBTYPE_MASK; + } + ++#define MEDIA_ENTITY_ENUM_MAX_DEPTH 16 ++ ++struct media_entity_graph { ++ struct { ++ struct media_entity *entity; ++ int link; ++ } stack[MEDIA_ENTITY_ENUM_MAX_DEPTH]; ++ int top; ++}; ++ + int media_entity_init(struct media_entity *entity, u16 num_pads, + struct media_pad *pads, u16 extra_links); + void media_entity_cleanup(struct media_entity *entity); + int media_entity_create_link(struct media_entity *source, u16 source_pad, + struct media_entity *sink, u16 sink_pad, u32 flags); + ++void media_entity_graph_walk_start(struct media_entity_graph *graph, ++ struct media_entity *entity); ++struct media_entity * ++media_entity_graph_walk_next(struct media_entity_graph *graph); ++ + #endif +-- +1.6.6.1 + diff --git a/recipes/linux/linux-omap-2.6.37/media/0012-media-Entity-use-count.patch b/recipes/linux/linux-omap-2.6.37/media/0012-media-Entity-use-count.patch new file mode 100644 index 0000000000..bc850e44a3 --- /dev/null +++ b/recipes/linux/linux-omap-2.6.37/media/0012-media-Entity-use-count.patch @@ -0,0 +1,176 @@ +From 3be6a2d10ff0cad0b240c65054da28395b014f82 Mon Sep 17 00:00:00 2001 +From: Laurent Pinchart +Date: Sun, 7 Mar 2010 20:04:59 +0200 +Subject: [PATCH 12/43] media: Entity use count + +Due to the wide differences between drivers regarding power management +needs, the media controller does not implement power management. +However, the media_entity structure includes a use_count field that +media drivers can use to track the number of users of every entity for +power management needs. + +The use_count field is owned by media drivers and must not be touched by +entity drivers. Access to the field must be protected by the media +device graph_mutex lock. + +Signed-off-by: Laurent Pinchart +--- + Documentation/media-framework.txt | 13 ++++++++++ + drivers/media/media-device.c | 1 + + drivers/media/media-entity.c | 46 +++++++++++++++++++++++++++++++++++++ + include/media/media-device.h | 4 +++ + include/media/media-entity.h | 5 ++++ + 5 files changed, 69 insertions(+), 0 deletions(-) + +diff --git a/Documentation/media-framework.txt b/Documentation/media-framework.txt +index 88fe379..9017a41 100644 +--- a/Documentation/media-framework.txt ++++ b/Documentation/media-framework.txt +@@ -258,3 +258,16 @@ When the graph traversal is complete the function will return NULL. + + Graph traversal can be interrupted at any moment. No cleanup function call is + required and the graph structure can be freed normally. ++ ++ ++Use count and power handling ++---------------------------- ++ ++Due to the wide differences between drivers regarding power management needs, ++the media controller does not implement power management. However, the ++media_entity structure includes a use_count field that media drivers can use to ++track the number of users of every entity for power management needs. ++ ++The use_count field is owned by media drivers and must not be touched by entity ++drivers. Access to the field must be protected by the media device graph_mutex ++lock. +diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c +index b8a3ace..e4c2157 100644 +--- a/drivers/media/media-device.c ++++ b/drivers/media/media-device.c +@@ -73,6 +73,7 @@ int __must_check media_device_register(struct media_device *mdev) + mdev->entity_id = 1; + INIT_LIST_HEAD(&mdev->entities); + spin_lock_init(&mdev->lock); ++ mutex_init(&mdev->graph_mutex); + + /* Register the device node. */ + mdev->devnode.fops = &media_device_fops; +diff --git a/drivers/media/media-entity.c b/drivers/media/media-entity.c +index a805f20..fe6bfd2 100644 +--- a/drivers/media/media-entity.c ++++ b/drivers/media/media-entity.c +@@ -23,6 +23,7 @@ + #include + #include + #include ++#include + + /** + * media_entity_init - Initialize a media entity +@@ -196,6 +197,51 @@ media_entity_graph_walk_next(struct media_entity_graph *graph) + EXPORT_SYMBOL_GPL(media_entity_graph_walk_next); + + /* ----------------------------------------------------------------------------- ++ * Module use count ++ */ ++ ++/* ++ * media_entity_get - Get a reference to the parent module ++ * @entity: The entity ++ * ++ * Get a reference to the parent media device module. ++ * ++ * The function will return immediately if @entity is NULL. ++ * ++ * Return a pointer to the entity on success or NULL on failure. ++ */ ++struct media_entity *media_entity_get(struct media_entity *entity) ++{ ++ if (entity == NULL) ++ return NULL; ++ ++ if (entity->parent->dev && ++ !try_module_get(entity->parent->dev->driver->owner)) ++ return NULL; ++ ++ return entity; ++} ++EXPORT_SYMBOL_GPL(media_entity_get); ++ ++/* ++ * media_entity_put - Release the reference to the parent module ++ * @entity: The entity ++ * ++ * Release the reference count acquired by media_entity_get(). ++ * ++ * The function will return immediately if @entity is NULL. ++ */ ++void media_entity_put(struct media_entity *entity) ++{ ++ if (entity == NULL) ++ return; ++ ++ if (entity->parent->dev) ++ module_put(entity->parent->dev->driver->owner); ++} ++EXPORT_SYMBOL_GPL(media_entity_put); ++ ++/* ----------------------------------------------------------------------------- + * Links management + */ + +diff --git a/include/media/media-device.h b/include/media/media-device.h +index 0b1ecf5..260d59c 100644 +--- a/include/media/media-device.h ++++ b/include/media/media-device.h +@@ -25,6 +25,7 @@ + + #include + #include ++#include + #include + + #include +@@ -42,6 +43,7 @@ + * @entity_id: ID of the next entity to be registered + * @entities: List of registered entities + * @lock: Entities list lock ++ * @graph_mutex: Entities graph operation lock + * + * This structure represents an abstract high-level media device. It allows easy + * access to entities and provides basic media device-level support. The +@@ -69,6 +71,8 @@ struct media_device { + + /* Protects the entities list */ + spinlock_t lock; ++ /* Serializes graph operations. */ ++ struct mutex graph_mutex; + }; + + /* media_devnode to media_device */ +diff --git a/include/media/media-entity.h b/include/media/media-entity.h +index b82f824..114541a 100644 +--- a/include/media/media-entity.h ++++ b/include/media/media-entity.h +@@ -81,6 +81,8 @@ struct media_entity { + struct media_pad *pads; /* Pads array (num_pads elements) */ + struct media_link *links; /* Links array (max_links elements)*/ + ++ int use_count; /* Use count for the entity. */ ++ + union { + /* Node specifications */ + struct { +@@ -129,6 +131,9 @@ void media_entity_cleanup(struct media_entity *entity); + int media_entity_create_link(struct media_entity *source, u16 source_pad, + struct media_entity *sink, u16 sink_pad, u32 flags); + ++struct media_entity *media_entity_get(struct media_entity *entity); ++void media_entity_put(struct media_entity *entity); ++ + void media_entity_graph_walk_start(struct media_entity_graph *graph, + struct media_entity *entity); + struct media_entity * +-- +1.6.6.1 + diff --git a/recipes/linux/linux-omap-2.6.37/media/0013-media-Media-device-information-query.patch b/recipes/linux/linux-omap-2.6.37/media/0013-media-Media-device-information-query.patch new file mode 100644 index 0000000000..bf9fcd9787 --- /dev/null +++ b/recipes/linux/linux-omap-2.6.37/media/0013-media-Media-device-information-query.patch @@ -0,0 +1,659 @@ +From cb6936ced565e168ac7f9be06dc3320733aac17f Mon Sep 17 00:00:00 2001 +From: Laurent Pinchart +Date: Wed, 18 Aug 2010 16:41:22 +0200 +Subject: [PATCH 13/43] media: Media device information query + +Create the following ioctl and implement it at the media device level to +query device information. + +- MEDIA_IOC_DEVICE_INFO: Query media device information + +The ioctl and its data structure are defined in the new kernel header +linux/media.h available to userspace applications. + +Signed-off-by: Laurent Pinchart +--- + Documentation/DocBook/media-entities.tmpl | 12 ++ + Documentation/DocBook/v4l/media-controller.xml | 10 ++ + Documentation/DocBook/v4l/media-func-close.xml | 59 +++++++++ + Documentation/DocBook/v4l/media-func-ioctl.xml | 116 +++++++++++++++++ + Documentation/DocBook/v4l/media-func-open.xml | 94 ++++++++++++++ + .../DocBook/v4l/media-ioc-device-info.xml | 132 ++++++++++++++++++++ + drivers/media/media-device.c | 57 +++++++++ + include/linux/Kbuild | 1 + + include/linux/media.h | 45 +++++++ + 9 files changed, 526 insertions(+), 0 deletions(-) + create mode 100644 Documentation/DocBook/v4l/media-func-close.xml + create mode 100644 Documentation/DocBook/v4l/media-func-ioctl.xml + create mode 100644 Documentation/DocBook/v4l/media-func-open.xml + create mode 100644 Documentation/DocBook/v4l/media-ioc-device-info.xml + create mode 100644 include/linux/media.h + +diff --git a/Documentation/DocBook/media-entities.tmpl b/Documentation/DocBook/media-entities.tmpl +index 61d6f11..6af3375 100644 +--- a/Documentation/DocBook/media-entities.tmpl ++++ b/Documentation/DocBook/media-entities.tmpl +@@ -11,6 +11,10 @@ + select()"> + write()"> + ++close()"> ++ioctl()"> ++open()"> ++ + + VIDIOC_CROPCAP"> + VIDIOC_DBG_G_CHIP_IDENT"> +@@ -87,6 +91,8 @@ + VIDIOC_TRY_FMT"> + VIDIOC_UNSUBSCRIBE_EVENT"> + ++MEDIA_IOC_DEVICE_INFO"> ++ + + v4l2_std_id"> + +@@ -181,6 +187,8 @@ + v4l2_vbi_format"> + v4l2_window"> + ++media_device_info"> ++ + + EACCES error code"> + EAGAIN error code"> +@@ -322,6 +330,10 @@ + + + ++ ++ ++ ++ + + + +diff --git a/Documentation/DocBook/v4l/media-controller.xml b/Documentation/DocBook/v4l/media-controller.xml +index f89228d..a46b786 100644 +--- a/Documentation/DocBook/v4l/media-controller.xml ++++ b/Documentation/DocBook/v4l/media-controller.xml +@@ -74,3 +74,13 @@ + pad to a sink pad. + + ++ ++ ++ Function Reference ++ ++ &sub-media-open; ++ &sub-media-close; ++ &sub-media-ioctl; ++ ++ &sub-media-ioc-device-info; ++ +diff --git a/Documentation/DocBook/v4l/media-func-close.xml b/Documentation/DocBook/v4l/media-func-close.xml +new file mode 100644 +index 0000000..be149c8 +--- /dev/null ++++ b/Documentation/DocBook/v4l/media-func-close.xml +@@ -0,0 +1,59 @@ ++ ++ ++ media close() ++ &manvol; ++ ++ ++ ++ media-close ++ Close a media device ++ ++ ++ ++ ++ #include <unistd.h> ++ ++ int close ++ int fd ++ ++ ++ ++ ++ ++ Arguments ++ ++ ++ ++ fd ++ ++ &fd; ++ ++ ++ ++ ++ ++ ++ Description ++ ++ Closes the media device. Resources associated with the file descriptor ++ are freed. The device configuration remain unchanged. ++ ++ ++ ++ Return Value ++ ++ close returns 0 on success. On error, -1 is ++ returned, and errno is set appropriately. Possible error ++ codes are: ++ ++ ++ ++ EBADF ++ ++ fd is not a valid open file descriptor. ++ ++ ++ ++ ++ ++ +diff --git a/Documentation/DocBook/v4l/media-func-ioctl.xml b/Documentation/DocBook/v4l/media-func-ioctl.xml +new file mode 100644 +index 0000000..bda8604 +--- /dev/null ++++ b/Documentation/DocBook/v4l/media-func-ioctl.xml +@@ -0,0 +1,116 @@ ++ ++ ++ media ioctl() ++ &manvol; ++ ++ ++ ++ media-ioctl ++ Control a media device ++ ++ ++ ++ ++ #include <sys/ioctl.h> ++ ++ int ioctl ++ int fd ++ int request ++ void *argp ++ ++ ++ ++ ++ ++ Arguments ++ ++ ++ ++ fd ++ ++ &fd; ++ ++ ++ ++ request ++ ++ Media ioctl request code as defined in the media.h header file, ++ for example MEDIA_IOC_SETUP_LINK. ++ ++ ++ ++ argp ++ ++ Pointer to a request-specific structure. ++ ++ ++ ++ ++ ++ ++ Description ++ The ioctl() function manipulates media device ++ parameters. The argument fd must be an open file ++ descriptor. ++ The ioctl request code specifies the media ++ function to be called. It has encoded in it whether the argument is an ++ input, output or read/write parameter, and the size of the argument ++ argp in bytes. ++ Macros and structures definitions specifying media ioctl requests and ++ their parameters are located in the media.h header file. All media ioctl ++ requests, their respective function and parameters are specified in ++ . ++ ++ ++ ++ Return Value ++ ++ ioctl() returns 0 on ++ success. On failure, -1 is returned, and the ++ errno variable is set appropriately. Generic error codes ++ are listed below, and request-specific error codes are listed in the ++ individual requests descriptions. ++ When an ioctl that takes an output or read/write parameter fails, ++ the parameter remains unmodified. ++ ++ ++ ++ EBADF ++ ++ fd is not a valid open file descriptor. ++ ++ ++ ++ ++ EFAULT ++ ++ argp references an inaccessible memory ++ area. ++ ++ ++ ++ EINVAL ++ ++ The request or the data pointed to by ++ argp is not valid. This is a very common error ++ code, see the individual ioctl requests listed in ++ for actual causes. ++ ++ ++ ++ ENOMEM ++ ++ Insufficient kernel memory was available to complete the ++ request. ++ ++ ++ ++ ENOTTY ++ ++ fd is not associated with a character ++ special device. ++ ++ ++ ++ ++ +diff --git a/Documentation/DocBook/v4l/media-func-open.xml b/Documentation/DocBook/v4l/media-func-open.xml +new file mode 100644 +index 0000000..f7df034 +--- /dev/null ++++ b/Documentation/DocBook/v4l/media-func-open.xml +@@ -0,0 +1,94 @@ ++ ++ ++ media open() ++ &manvol; ++ ++ ++ ++ media-open ++ Open a media device ++ ++ ++ ++ ++ #include <fcntl.h> ++ ++ int open ++ const char *device_name ++ int flags ++ ++ ++ ++ ++ ++ Arguments ++ ++ ++ ++ device_name ++ ++ Device to be opened. ++ ++ ++ ++ flags ++ ++ Open flags. Access mode must be either O_RDONLY ++ or O_RDWR. Other flags have no effect. ++ ++ ++ ++ ++ ++ Description ++ To open a media device applications call open() ++ with the desired device name. The function has no side effects; the device ++ configuration remain unchanged. ++ When the device is opened in read-only mode, attemps to modify its ++ configuration will result in an error, and errno will be ++ set to EBADF. ++ ++ ++ Return Value ++ ++ open returns the new file descriptor on success. ++ On error, -1 is returned, and errno is set appropriately. ++ Possible error codes are: ++ ++ ++ ++ EACCES ++ ++ The requested access to the file is not allowed. ++ ++ ++ ++ EMFILE ++ ++ The process already has the maximum number of files open. ++ ++ ++ ++ ++ ENFILE ++ ++ The system limit on the total number of open files has been ++ reached. ++ ++ ++ ++ ENOMEM ++ ++ Insufficient kernel memory was available. ++ ++ ++ ++ ENXIO ++ ++ No device corresponding to this device special file exists. ++ ++ ++ ++ ++ ++ +diff --git a/Documentation/DocBook/v4l/media-ioc-device-info.xml b/Documentation/DocBook/v4l/media-ioc-device-info.xml +new file mode 100644 +index 0000000..278a312 +--- /dev/null ++++ b/Documentation/DocBook/v4l/media-ioc-device-info.xml +@@ -0,0 +1,132 @@ ++ ++ ++ ioctl MEDIA_IOC_DEVICE_INFO ++ &manvol; ++ ++ ++ ++ MEDIA_IOC_DEVICE_INFO ++ Query device information ++ ++ ++ ++ ++ ++ int ioctl ++ int fd ++ int request ++ struct media_device_info *argp ++ ++ ++ ++ ++ ++ Arguments ++ ++ ++ ++ fd ++ ++ &fd; ++ ++ ++ ++ request ++ ++ MEDIA_IOC_DEVICE_INFO ++ ++ ++ ++ argp ++ ++ ++ ++ ++ ++ ++ ++ ++ Description ++ ++ All media devices must support the MEDIA_IOC_DEVICE_INFO ++ ioctl. To query device information, applications call the ioctl with a ++ pointer to a &media-device-info;. The driver fills the structure and returns ++ the information to the application. ++ The ioctl never fails. ++ ++ ++ struct <structname>media_device_info</structname> ++ ++ &cs-str; ++ ++ ++ char ++ driver[16] ++ Name of the driver implementing the media API as a ++ NUL-terminated ASCII string. The driver version is stored in the ++ driver_version field. ++ Driver specific applications can use this information to ++ verify the driver identity. It is also useful to work around ++ known bugs, or to identify drivers in error reports. ++ ++ ++ char ++ model[32] ++ Device model name as a NUL-terminated UTF-8 string. The ++ device version is stored in the device_version ++ field and is not be appended to the model name. ++ ++ ++ char ++ serial[40] ++ Serial number as a NUL-terminated ASCII string. ++ ++ ++ char ++ bus_info[32] ++ Location of the device in the system as a NUL-terminated ++ ASCII string. This includes the bus type name (PCI, USB, ...) and a ++ bus-specific identifier. ++ ++ ++ __u32 ++ media_version ++ Media API version, formatted with the ++ KERNEL_VERSION() macro. ++ ++ ++ __u32 ++ hw_revision ++ Hardware device revision in a driver-specific format. ++ ++ ++ __u32 ++ media_version ++ Media device driver version, formatted with the ++ KERNEL_VERSION() macro. Together with the ++ driver field this identifies a particular ++ driver. ++ ++ ++ __u32 ++ reserved[31] ++ Reserved for future extensions. Drivers and applications must ++ set this array to zero. ++ ++ ++ ++
++ The serial and bus_info ++ fields can be used to distinguish between multiple instances of otherwise ++ identical hardware. The serial number takes precedence when provided and can ++ be assumed to be unique. If the serial number is an empty string, the ++ bus_info field can be used instead. The ++ bus_info field is guaranteed to be unique, but ++ can vary across reboots or device unplug/replug. ++
++ ++ ++ Return value ++ This function doesn't return specific error codes. ++ ++
+diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c +index e4c2157..5c745be 100644 +--- a/drivers/media/media-device.c ++++ b/drivers/media/media-device.c +@@ -22,13 +22,70 @@ + + #include + #include ++#include + + #include + #include + #include + ++/* ----------------------------------------------------------------------------- ++ * Userspace API ++ */ ++ ++static int media_device_open(struct file *filp) ++{ ++ return 0; ++} ++ ++static int media_device_close(struct file *filp) ++{ ++ return 0; ++} ++ ++static int media_device_get_info(struct media_device *dev, ++ struct media_device_info __user *__info) ++{ ++ struct media_device_info info; ++ ++ memset(&info, 0, sizeof(info)); ++ ++ strlcpy(info.driver, dev->dev->driver->name, sizeof(info.driver)); ++ strlcpy(info.model, dev->model, sizeof(info.model)); ++ strlcpy(info.serial, dev->serial, sizeof(info.serial)); ++ strlcpy(info.bus_info, dev->bus_info, sizeof(info.bus_info)); ++ ++ info.media_version = MEDIA_API_VERSION; ++ info.hw_revision = dev->hw_revision; ++ info.driver_version = dev->driver_version; ++ ++ return copy_to_user(__info, &info, sizeof(*__info)); ++} ++ ++static long media_device_ioctl(struct file *filp, unsigned int cmd, ++ unsigned long arg) ++{ ++ struct media_devnode *devnode = media_devnode_data(filp); ++ struct media_device *dev = to_media_device(devnode); ++ long ret; ++ ++ switch (cmd) { ++ case MEDIA_IOC_DEVICE_INFO: ++ ret = media_device_get_info(dev, ++ (struct media_device_info __user *)arg); ++ break; ++ ++ default: ++ ret = -ENOIOCTLCMD; ++ } ++ ++ return ret; ++} ++ + static const struct media_file_operations media_device_fops = { + .owner = THIS_MODULE, ++ .open = media_device_open, ++ .ioctl = media_device_ioctl, ++ .release = media_device_close, + }; + + /* ----------------------------------------------------------------------------- +diff --git a/include/linux/Kbuild b/include/linux/Kbuild +index 97319a8..26e0a7f 100644 +--- a/include/linux/Kbuild ++++ b/include/linux/Kbuild +@@ -228,6 +228,7 @@ header-y += magic.h + header-y += major.h + header-y += map_to_7segment.h + header-y += matroxfb.h ++header-y += media.h + header-y += mempolicy.h + header-y += meye.h + header-y += mii.h +diff --git a/include/linux/media.h b/include/linux/media.h +new file mode 100644 +index 0000000..4c52f08 +--- /dev/null ++++ b/include/linux/media.h +@@ -0,0 +1,45 @@ ++/* ++ * Multimedia device API ++ * ++ * Copyright (C) 2010 Nokia Corporation ++ * ++ * Contacts: Laurent Pinchart ++ * Sakari Ailus ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef __LINUX_MEDIA_H ++#define __LINUX_MEDIA_H ++ ++#include ++#include ++#include ++ ++#define MEDIA_API_VERSION KERNEL_VERSION(0, 1, 0) ++ ++struct media_device_info { ++ char driver[16]; ++ char model[32]; ++ char serial[40]; ++ char bus_info[32]; ++ __u32 media_version; ++ __u32 hw_revision; ++ __u32 driver_version; ++ __u32 reserved[31]; ++}; ++ ++#define MEDIA_IOC_DEVICE_INFO _IOWR('M', 1, struct media_device_info) ++ ++#endif /* __LINUX_MEDIA_H */ +-- +1.6.6.1 + diff --git a/recipes/linux/linux-omap-2.6.37/media/0014-media-Entities-pads-and-links-enumeration.patch b/recipes/linux/linux-omap-2.6.37/media/0014-media-Entities-pads-and-links-enumeration.patch new file mode 100644 index 0000000000..cc9e87614e --- /dev/null +++ b/recipes/linux/linux-omap-2.6.37/media/0014-media-Entities-pads-and-links-enumeration.patch @@ -0,0 +1,889 @@ +From d7784ca094970b836c99e5f2a6344811625753a3 Mon Sep 17 00:00:00 2001 +From: Laurent Pinchart +Date: Wed, 9 Dec 2009 12:40:01 +0100 +Subject: [PATCH 14/43] media: Entities, pads and links enumeration + +Create the following two ioctls and implement them at the media device +level to enumerate entities, pads and links. + +- MEDIA_IOC_ENUM_ENTITIES: Enumerate entities and their properties +- MEDIA_IOC_ENUM_LINKS: Enumerate all pads and links for a given entity + +Entity IDs can be non-contiguous. Userspace applications should +enumerate entities using the MEDIA_ENT_ID_FLAG_NEXT flag. When the flag +is set in the entity ID, the MEDIA_IOC_ENUM_ENTITIES will return the +next entity with an ID bigger than the requested one. + +Only forward links that originate at one of the entity's source pads are +returned during the enumeration process. + +Signed-off-by: Laurent Pinchart +Signed-off-by: Sakari Ailus +--- + Documentation/DocBook/media-entities.tmpl | 8 + + Documentation/DocBook/v4l/media-controller.xml | 2 + + .../DocBook/v4l/media-ioc-device-info.xml | 3 +- + .../DocBook/v4l/media-ioc-enum-entities.xml | 308 ++++++++++++++++++++ + Documentation/DocBook/v4l/media-ioc-enum-links.xml | 202 +++++++++++++ + drivers/media/media-device.c | 123 ++++++++ + include/linux/media.h | 85 ++++++ + include/media/media-entity.h | 24 +-- + 8 files changed, 731 insertions(+), 24 deletions(-) + create mode 100644 Documentation/DocBook/v4l/media-ioc-enum-entities.xml + create mode 100644 Documentation/DocBook/v4l/media-ioc-enum-links.xml + +diff --git a/Documentation/DocBook/media-entities.tmpl b/Documentation/DocBook/media-entities.tmpl +index 6af3375..6e7dae4 100644 +--- a/Documentation/DocBook/media-entities.tmpl ++++ b/Documentation/DocBook/media-entities.tmpl +@@ -92,6 +92,8 @@ + VIDIOC_UNSUBSCRIBE_EVENT"> + + MEDIA_IOC_DEVICE_INFO"> ++MEDIA_IOC_ENUM_ENTITIES"> ++MEDIA_IOC_ENUM_LINKS"> + + + v4l2_std_id"> +@@ -188,6 +190,10 @@ + v4l2_window"> + + media_device_info"> ++media_entity_desc"> ++media_links_enum"> ++media_pad_desc"> ++media_link_desc"> + + + EACCES error code"> +@@ -334,6 +340,8 @@ + + + ++ ++ + + + +diff --git a/Documentation/DocBook/v4l/media-controller.xml b/Documentation/DocBook/v4l/media-controller.xml +index a46b786..2c4fd2b 100644 +--- a/Documentation/DocBook/v4l/media-controller.xml ++++ b/Documentation/DocBook/v4l/media-controller.xml +@@ -83,4 +83,6 @@ + &sub-media-ioctl; + + &sub-media-ioc-device-info; ++ &sub-media-ioc-enum-entities; ++ &sub-media-ioc-enum-links; + +diff --git a/Documentation/DocBook/v4l/media-ioc-device-info.xml b/Documentation/DocBook/v4l/media-ioc-device-info.xml +index 278a312..1f32373 100644 +--- a/Documentation/DocBook/v4l/media-ioc-device-info.xml ++++ b/Documentation/DocBook/v4l/media-ioc-device-info.xml +@@ -27,7 +27,8 @@ + + fd + +- &fd; ++ File descriptor returned by ++ open(). + + + +diff --git a/Documentation/DocBook/v4l/media-ioc-enum-entities.xml b/Documentation/DocBook/v4l/media-ioc-enum-entities.xml +new file mode 100644 +index 0000000..13d0cc4 +--- /dev/null ++++ b/Documentation/DocBook/v4l/media-ioc-enum-entities.xml +@@ -0,0 +1,308 @@ ++ ++ ++ ioctl MEDIA_IOC_ENUM_ENTITIES ++ &manvol; ++ ++ ++ ++ MEDIA_IOC_ENUM_ENTITIES ++ Enumerate entities and their properties ++ ++ ++ ++ ++ ++ int ioctl ++ int fd ++ int request ++ struct media_entity_desc *argp ++ ++ ++ ++ ++ ++ Arguments ++ ++ ++ ++ fd ++ ++ File descriptor returned by ++ open(). ++ ++ ++ ++ request ++ ++ MEDIA_IOC_ENUM_ENTITIES ++ ++ ++ ++ argp ++ ++ ++ ++ ++ ++ ++ ++ ++ Description ++ To query the attributes of an entity, applications set the id field ++ of a &media-entity-desc; structure and call the MEDIA_IOC_ENUM_ENTITIES ++ ioctl with a pointer to this structure. The driver fills the rest of the ++ structure or returns an &EINVAL; when the id is invalid. ++ Entities can be enumerated by or'ing the id with the ++ MEDIA_ENT_ID_FLAG_NEXT flag. The driver will return ++ information about the entity with the smallest id strictly larger than the ++ requested one ('next entity'), or the &EINVAL; if there is none. ++ Entity IDs can be non-contiguous. Applications must ++ not try to enumerate entities by calling ++ MEDIA_IOC_ENUM_ENTITIES with increasing id's until they get an error. ++ Two or more entities that share a common non-zero ++ group_id value are considered as logically ++ grouped. Groups are used to report ++ ++ ALSA, VBI and video nodes that carry the same media ++ stream ++ lens and flash controllers associated with a sensor ++ ++ ++ ++ ++ struct <structname>media_entity_desc</structname> ++ ++ ++ ++ ++ ++ ++ ++ ++ __u32 ++ id ++ ++ ++ Entity id, set by the application. When the id is or'ed with ++ MEDIA_ENT_ID_FLAG_NEXT, the driver clears the ++ flag and returns the first entity with a larger id. ++ ++ ++ char ++ name[32] ++ ++ ++ Entity name as an UTF-8 NULL-terminated string. ++ ++ ++ __u32 ++ type ++ ++ ++ Entity type, see for details. ++ ++ ++ __u32 ++ revision ++ ++ ++ Entity revision in a driver/hardware specific format. ++ ++ ++ __u32 ++ flags ++ ++ ++ Entity flags, see for details. ++ ++ ++ __u32 ++ group_id ++ ++ ++ Entity group ID ++ ++ ++ __u16 ++ pads ++ ++ ++ Number of pads ++ ++ ++ __u16 ++ links ++ ++ ++ Total number of outbound links. Inbound links are not counted ++ in this field. ++ ++ ++ union ++ ++ ++ ++ struct ++ v4l ++ ++ Valid for V4L sub-devices and nodes only. ++ ++ ++ ++ ++ __u32 ++ major ++ V4L device node major number. For V4L sub-devices with no ++ device node, set by the driver to 0. ++ ++ ++ ++ ++ __u32 ++ minor ++ V4L device node minor number. For V4L sub-devices with no ++ device node, set by the driver to 0. ++ ++ ++ ++ struct ++ fb ++ ++ Valid for frame buffer nodes only. ++ ++ ++ ++ ++ __u32 ++ major ++ Frame buffer device node major number. ++ ++ ++ ++ ++ __u32 ++ minor ++ Frame buffer device node minor number. ++ ++ ++ ++ struct ++ alsa ++ ++ Valid for ALSA devices only. ++ ++ ++ ++ ++ __u32 ++ card ++ ALSA card number ++ ++ ++ ++ ++ __u32 ++ device ++ ALSA device number ++ ++ ++ ++ ++ __u32 ++ subdevice ++ ALSA sub-device number ++ ++ ++ ++ int ++ dvb ++ ++ DVB card number ++ ++ ++ ++ __u8 ++ raw[180] ++ ++ ++ ++ ++ ++
++ ++ ++ Media entity types ++ ++ ++ ++ ++ ++ MEDIA_ENT_T_DEVNODE ++ Unknown device node ++ ++ ++ MEDIA_ENT_T_DEVNODE_V4L ++ V4L video, radio or vbi device node ++ ++ ++ MEDIA_ENT_T_DEVNODE_FB ++ Frame buffer device node ++ ++ ++ MEDIA_ENT_T_DEVNODE_ALSA ++ ALSA card ++ ++ ++ MEDIA_ENT_T_DEVNODE_DVB ++ DVB card ++ ++ ++ MEDIA_ENT_T_V4L2_SUBDEV ++ Unknown V4L sub-device ++ ++ ++ MEDIA_ENT_T_V4L2_SUBDEV_SENSOR ++ Video sensor ++ ++ ++ MEDIA_ENT_T_V4L2_SUBDEV_FLASH ++ Flash controller ++ ++ ++ MEDIA_ENT_T_V4L2_SUBDEV_LENS ++ Lens controller ++ ++ ++ ++
++ ++ ++ Media entity flags ++ ++ ++ ++ ++ ++ MEDIA_ENT_FL_DEFAULT ++ Default entity for its type. Used to discover the default ++ audio, VBI and video devices, the default camera sensor, ... ++ ++ ++ ++
++
++ ++ ++ &return-value; ++ ++ ++ ++ EINVAL ++ ++ The &media-entity-desc; id references ++ a non-existing entity. ++ ++ ++ ++ ++
+diff --git a/Documentation/DocBook/v4l/media-ioc-enum-links.xml b/Documentation/DocBook/v4l/media-ioc-enum-links.xml +new file mode 100644 +index 0000000..daf0360 +--- /dev/null ++++ b/Documentation/DocBook/v4l/media-ioc-enum-links.xml +@@ -0,0 +1,202 @@ ++ ++ ++ ioctl MEDIA_IOC_ENUM_LINKS ++ &manvol; ++ ++ ++ ++ MEDIA_IOC_ENUM_LINKS ++ Enumerate all pads and links for a given entity ++ ++ ++ ++ ++ ++ int ioctl ++ int fd ++ int request ++ struct media_links_enum *argp ++ ++ ++ ++ ++ ++ Arguments ++ ++ ++ ++ fd ++ ++ File descriptor returned by ++ open(). ++ ++ ++ ++ request ++ ++ MEDIA_IOC_ENUM_LINKS ++ ++ ++ ++ argp ++ ++ ++ ++ ++ ++ ++ ++ ++ Description ++ ++ To enumerate pads and/or links for a given entity, applications set ++ the entity field of a &media-links-enum; structure and initialize the ++ &media-pad-desc; and &media-link-desc; structure arrays pointed by the ++ pads and links fields. ++ They then call the MEDIA_IOC_ENUM_LINKS ioctl with a pointer to this ++ structure. ++ If the pads field is not NULL, the driver ++ fills the pads array with information about the ++ entity's pads. The array must have enough room to store all the entity's ++ pads. The number of pads can be retrieved with the &MEDIA-IOC-ENUM-ENTITIES; ++ ioctl. ++ If the links field is not NULL, the driver ++ fills the links array with information about the ++ entity's outbound links. The array must have enough room to store all the ++ entity's outbound links. The number of outbound links can be retrieved with ++ the &MEDIA-IOC-ENUM-ENTITIES; ioctl. ++ Only forward links that originate at one of the entity's source pads ++ are returned during the enumeration process. ++ ++ ++ struct <structname>media_links_enum</structname> ++ ++ &cs-str; ++ ++ ++ __u32 ++ entity ++ Entity id, set by the application. ++ ++ ++ struct &media-pad-desc; ++ *pads ++ Pointer to a pads array allocated by the application. Ignored ++ if NULL. ++ ++ ++ struct &media-link-desc; ++ *links ++ Pointer to a links array allocated by the application. Ignored ++ if NULL. ++ ++ ++ ++ ++ ++ ++ struct <structname>media_pad_desc</structname> ++ ++ &cs-str; ++ ++ ++ __u32 ++ entity ++ ID of the entity this pad belongs to. ++ ++ ++ __u16 ++ index ++ 0-based pad index. ++ ++ ++ __u32 ++ flags ++ Pad flags, see for more details. ++ ++ ++ ++
++ ++ ++ Media pad flags ++ ++ ++ ++ ++ ++ MEDIA_PAD_FL_INPUT ++ Input pad, relative to the entity. Input pads sink data and ++ are targets of links. ++ ++ ++ MEDIA_PAD_FL_OUTPUT ++ Output pad, relative to the entity. Output pads source data ++ and are origins of links. ++ ++ ++ ++
++ ++ ++ struct <structname>media_links_enum</structname> ++ ++ &cs-str; ++ ++ ++ struct &media-pad-desc; ++ source ++ Pad at the origin of this link. ++ ++ ++ struct &media-pad-desc; ++ sink ++ Pad at the target of this link. ++ ++ ++ __u32 ++ flags ++ Link flags, see for more details. ++ ++ ++ ++ ++ ++ ++ Media link flags ++ ++ ++ ++ ++ ++ MEDIA_LNK_FL_ENABLED ++ The link is enabled and can be used to transfer media data. ++ When two or more links target a sink pad, only one of them can be ++ enabled at a time. ++ ++ ++ MEDIA_LNK_FL_IMMUTABLE ++ The link enabled state can't be modified at runtime. An ++ immutable link is always enabled. ++ ++ ++ ++ ++ One and only one of MEDIA_PAD_FL_INPUT and ++ MEDIA_PAD_FL_OUTPUT must be set for every pad. ++
++ ++ ++ &return-value; ++ ++ ++ ++ EINVAL ++ ++ The &media-links-enum; id references ++ a non-existing entity. ++ ++ ++ ++ ++
+diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c +index 5c745be..1f46acb 100644 +--- a/drivers/media/media-device.c ++++ b/drivers/media/media-device.c +@@ -61,6 +61,117 @@ static int media_device_get_info(struct media_device *dev, + return copy_to_user(__info, &info, sizeof(*__info)); + } + ++static struct media_entity *find_entity(struct media_device *mdev, u32 id) ++{ ++ struct media_entity *entity; ++ int next = id & MEDIA_ENT_ID_FLAG_NEXT; ++ ++ id &= ~MEDIA_ENT_ID_FLAG_NEXT; ++ ++ spin_lock(&mdev->lock); ++ ++ media_device_for_each_entity(entity, mdev) { ++ if ((entity->id == id && !next) || ++ (entity->id > id && next)) { ++ spin_unlock(&mdev->lock); ++ return entity; ++ } ++ } ++ ++ spin_unlock(&mdev->lock); ++ ++ return NULL; ++} ++ ++static long media_device_enum_entities(struct media_device *mdev, ++ struct media_entity_desc __user *uent) ++{ ++ struct media_entity *ent; ++ struct media_entity_desc u_ent; ++ ++ if (copy_from_user(&u_ent.id, &uent->id, sizeof(u_ent.id))) ++ return -EFAULT; ++ ++ ent = find_entity(mdev, u_ent.id); ++ ++ if (ent == NULL) ++ return -EINVAL; ++ ++ u_ent.id = ent->id; ++ u_ent.name[0] = '\0'; ++ if (ent->name) ++ strlcpy(u_ent.name, ent->name, sizeof(u_ent.name)); ++ u_ent.type = ent->type; ++ u_ent.revision = ent->revision; ++ u_ent.flags = ent->flags; ++ u_ent.group_id = ent->group_id; ++ u_ent.pads = ent->num_pads; ++ u_ent.links = ent->num_links - ent->num_backlinks; ++ u_ent.v4l.major = ent->v4l.major; ++ u_ent.v4l.minor = ent->v4l.minor; ++ if (copy_to_user(uent, &u_ent, sizeof(u_ent))) ++ return -EFAULT; ++ return 0; ++} ++ ++static void media_device_kpad_to_upad(const struct media_pad *kpad, ++ struct media_pad_desc *upad) ++{ ++ upad->entity = kpad->entity->id; ++ upad->index = kpad->index; ++ upad->flags = kpad->flags; ++} ++ ++static long media_device_enum_links(struct media_device *mdev, ++ struct media_links_enum __user *ulinks) ++{ ++ struct media_entity *entity; ++ struct media_links_enum links; ++ ++ if (copy_from_user(&links, ulinks, sizeof(links))) ++ return -EFAULT; ++ ++ entity = find_entity(mdev, links.entity); ++ if (entity == NULL) ++ return -EINVAL; ++ ++ if (links.pads) { ++ unsigned int p; ++ ++ for (p = 0; p < entity->num_pads; p++) { ++ struct media_pad_desc pad; ++ media_device_kpad_to_upad(&entity->pads[p], &pad); ++ if (copy_to_user(&links.pads[p], &pad, sizeof(pad))) ++ return -EFAULT; ++ } ++ } ++ ++ if (links.links) { ++ struct media_link_desc __user *ulink; ++ unsigned int l; ++ ++ for (l = 0, ulink = links.links; l < entity->num_links; l++) { ++ struct media_link_desc link; ++ ++ /* Ignore backlinks. */ ++ if (entity->links[l].source->entity != entity) ++ continue; ++ ++ media_device_kpad_to_upad(entity->links[l].source, ++ &link.source); ++ media_device_kpad_to_upad(entity->links[l].sink, ++ &link.sink); ++ link.flags = entity->links[l].flags; ++ if (copy_to_user(ulink, &link, sizeof(*ulink))) ++ return -EFAULT; ++ ulink++; ++ } ++ } ++ if (copy_to_user(ulinks, &links, sizeof(*ulinks))) ++ return -EFAULT; ++ return 0; ++} ++ + static long media_device_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) + { +@@ -74,6 +185,18 @@ static long media_device_ioctl(struct file *filp, unsigned int cmd, + (struct media_device_info __user *)arg); + break; + ++ case MEDIA_IOC_ENUM_ENTITIES: ++ ret = media_device_enum_entities(dev, ++ (struct media_entity_desc __user *)arg); ++ break; ++ ++ case MEDIA_IOC_ENUM_LINKS: ++ mutex_lock(&dev->graph_mutex); ++ ret = media_device_enum_links(dev, ++ (struct media_links_enum __user *)arg); ++ mutex_unlock(&dev->graph_mutex); ++ break; ++ + default: + ret = -ENOIOCTLCMD; + } +diff --git a/include/linux/media.h b/include/linux/media.h +index 4c52f08..64c0313 100644 +--- a/include/linux/media.h ++++ b/include/linux/media.h +@@ -40,6 +40,91 @@ struct media_device_info { + __u32 reserved[31]; + }; + ++#define MEDIA_ENT_ID_FLAG_NEXT (1 << 31) ++ ++#define MEDIA_ENT_TYPE_SHIFT 16 ++#define MEDIA_ENT_TYPE_MASK 0x00ff0000 ++#define MEDIA_ENT_SUBTYPE_MASK 0x0000ffff ++ ++#define MEDIA_ENT_T_DEVNODE (1 << MEDIA_ENT_TYPE_SHIFT) ++#define MEDIA_ENT_T_DEVNODE_V4L (MEDIA_ENT_T_DEVNODE + 1) ++#define MEDIA_ENT_T_DEVNODE_FB (MEDIA_ENT_T_DEVNODE + 2) ++#define MEDIA_ENT_T_DEVNODE_ALSA (MEDIA_ENT_T_DEVNODE + 3) ++#define MEDIA_ENT_T_DEVNODE_DVB (MEDIA_ENT_T_DEVNODE + 4) ++ ++#define MEDIA_ENT_T_V4L2_SUBDEV (2 << MEDIA_ENT_TYPE_SHIFT) ++#define MEDIA_ENT_T_V4L2_SUBDEV_SENSOR (MEDIA_ENT_T_V4L2_SUBDEV + 1) ++#define MEDIA_ENT_T_V4L2_SUBDEV_FLASH (MEDIA_ENT_T_V4L2_SUBDEV + 2) ++#define MEDIA_ENT_T_V4L2_SUBDEV_LENS (MEDIA_ENT_T_V4L2_SUBDEV + 3) ++ ++#define MEDIA_ENT_FL_DEFAULT (1 << 0) ++ ++struct media_entity_desc { ++ __u32 id; ++ char name[32]; ++ __u32 type; ++ __u32 revision; ++ __u32 flags; ++ __u32 group_id; ++ __u16 pads; ++ __u16 links; ++ ++ __u32 reserved[4]; ++ ++ union { ++ /* Node specifications */ ++ struct { ++ __u32 major; ++ __u32 minor; ++ } v4l; ++ struct { ++ __u32 major; ++ __u32 minor; ++ } fb; ++ struct { ++ __u32 card; ++ __u32 device; ++ __u32 subdevice; ++ } alsa; ++ int dvb; ++ ++ /* Sub-device specifications */ ++ /* Nothing needed yet */ ++ __u8 raw[184]; ++ }; ++}; ++ ++#define MEDIA_PAD_FL_INPUT (1 << 0) ++#define MEDIA_PAD_FL_OUTPUT (1 << 1) ++ ++struct media_pad_desc { ++ __u32 entity; /* entity ID */ ++ __u16 index; /* pad index */ ++ __u32 flags; /* pad flags */ ++ __u32 reserved[2]; ++}; ++ ++#define MEDIA_LNK_FL_ENABLED (1 << 0) ++#define MEDIA_LNK_FL_IMMUTABLE (1 << 1) ++ ++struct media_link_desc { ++ struct media_pad_desc source; ++ struct media_pad_desc sink; ++ __u32 flags; ++ __u32 reserved[2]; ++}; ++ ++struct media_links_enum { ++ __u32 entity; ++ /* Should have enough room for pads elements */ ++ struct media_pad_desc __user *pads; ++ /* Should have enough room for links elements */ ++ struct media_link_desc __user *links; ++ __u32 reserved[4]; ++}; ++ + #define MEDIA_IOC_DEVICE_INFO _IOWR('M', 1, struct media_device_info) ++#define MEDIA_IOC_ENUM_ENTITIES _IOWR('M', 2, struct media_entity_desc) ++#define MEDIA_IOC_ENUM_LINKS _IOWR('M', 3, struct media_links_enum) + + #endif /* __LINUX_MEDIA_H */ +diff --git a/include/media/media-entity.h b/include/media/media-entity.h +index 114541a..0954490 100644 +--- a/include/media/media-entity.h ++++ b/include/media/media-entity.h +@@ -24,29 +24,7 @@ + #define _MEDIA_ENTITY_H + + #include +- +-#define MEDIA_ENT_TYPE_SHIFT 16 +-#define MEDIA_ENT_TYPE_MASK 0x00ff0000 +-#define MEDIA_ENT_SUBTYPE_MASK 0x0000ffff +- +-#define MEDIA_ENT_T_DEVNODE (1 << MEDIA_ENTITY_TYPE_SHIFT) +-#define MEDIA_ENT_T_DEVNODE_V4L (MEDIA_ENTITY_T_DEVNODE + 1) +-#define MEDIA_ENT_T_DEVNODE_FB (MEDIA_ENTITY_T_DEVNODE + 2) +-#define MEDIA_ENT_T_DEVNODE_ALSA (MEDIA_ENTITY_T_DEVNODE + 3) +-#define MEDIA_ENT_T_DEVNODE_DVB (MEDIA_ENTITY_T_DEVNODE + 4) +- +-#define MEDIA_ENT_T_V4L2_SUBDEV (2 << MEDIA_ENTITY_TYPE_SHIFT) +-#define MEDIA_ENT_T_V4L2_SUBDEV_SENSOR (MEDIA_ENTITY_T_V4L2_SUBDEV + 1) +-#define MEDIA_ENT_T_V4L2_SUBDEV_FLASH (MEDIA_ENTITY_T_V4L2_SUBDEV + 2) +-#define MEDIA_ENT_T_V4L2_SUBDEV_LENS (MEDIA_ENTITY_T_V4L2_SUBDEV + 3) +- +-#define MEDIA_ENT_FL_DEFAULT (1 << 0) +- +-#define MEDIA_LNK_FL_ENABLED (1 << 0) +-#define MEDIA_LNK_FL_IMMUTABLE (1 << 1) +- +-#define MEDIA_PAD_FL_INPUT (1 << 0) +-#define MEDIA_PAD_FL_OUTPUT (1 << 1) ++#include + + struct media_link { + struct media_pad *source; /* Source pad */ +-- +1.6.6.1 + diff --git a/recipes/linux/linux-omap-2.6.37/media/0015-media-Links-setup.patch b/recipes/linux/linux-omap-2.6.37/media/0015-media-Links-setup.patch new file mode 100644 index 0000000000..4ea39740e0 --- /dev/null +++ b/recipes/linux/linux-omap-2.6.37/media/0015-media-Links-setup.patch @@ -0,0 +1,517 @@ +From 9991c219079532183cc33f16064f86680b80237c Mon Sep 17 00:00:00 2001 +From: Laurent Pinchart +Date: Wed, 9 Dec 2009 12:40:03 +0100 +Subject: [PATCH 15/43] media: Links setup + +Create the following ioctl and implement it at the media device level to +setup links. + +- MEDIA_IOC_SETUP_LINK: Modify the properties of a given link + +The only property that can currently be modified is the ENABLED link +flag to enable/disable a link. Links marked with the IMMUTABLE link flag +can not be enabled or disabled. + +Enabling or disabling a link has effects on entities' use count. Those +changes are automatically propagated through the graph. + +Signed-off-by: Laurent Pinchart +Signed-off-by: Stanimir Varbanov +Signed-off-by: Sakari Ailus +--- + Documentation/DocBook/media-entities.tmpl | 2 + + Documentation/DocBook/v4l/media-controller.xml | 1 + + Documentation/DocBook/v4l/media-ioc-setup-link.xml | 90 +++++++++++ + Documentation/media-framework.txt | 42 ++++++ + drivers/media/media-device.c | 45 ++++++ + drivers/media/media-entity.c | 155 ++++++++++++++++++++ + include/linux/media.h | 1 + + include/media/media-device.h | 3 + + include/media/media-entity.h | 17 ++ + 9 files changed, 356 insertions(+), 0 deletions(-) + create mode 100644 Documentation/DocBook/v4l/media-ioc-setup-link.xml + +diff --git a/Documentation/DocBook/media-entities.tmpl b/Documentation/DocBook/media-entities.tmpl +index 6e7dae4..679c585 100644 +--- a/Documentation/DocBook/media-entities.tmpl ++++ b/Documentation/DocBook/media-entities.tmpl +@@ -94,6 +94,7 @@ + MEDIA_IOC_DEVICE_INFO"> + MEDIA_IOC_ENUM_ENTITIES"> + MEDIA_IOC_ENUM_LINKS"> ++MEDIA_IOC_SETUP_LINK"> + + + v4l2_std_id"> +@@ -342,6 +343,7 @@ + + + ++ + + + +diff --git a/Documentation/DocBook/v4l/media-controller.xml b/Documentation/DocBook/v4l/media-controller.xml +index 2c4fd2b..2dc25e1 100644 +--- a/Documentation/DocBook/v4l/media-controller.xml ++++ b/Documentation/DocBook/v4l/media-controller.xml +@@ -85,4 +85,5 @@ + &sub-media-ioc-device-info; + &sub-media-ioc-enum-entities; + &sub-media-ioc-enum-links; ++ &sub-media-ioc-setup-link; + +diff --git a/Documentation/DocBook/v4l/media-ioc-setup-link.xml b/Documentation/DocBook/v4l/media-ioc-setup-link.xml +new file mode 100644 +index 0000000..09ab3d2 +--- /dev/null ++++ b/Documentation/DocBook/v4l/media-ioc-setup-link.xml +@@ -0,0 +1,90 @@ ++ ++ ++ ioctl MEDIA_IOC_SETUP_LINK ++ &manvol; ++ ++ ++ ++ MEDIA_IOC_SETUP_LINK ++ Modify the properties of a link ++ ++ ++ ++ ++ ++ int ioctl ++ int fd ++ int request ++ struct media_link_desc *argp ++ ++ ++ ++ ++ ++ Arguments ++ ++ ++ ++ fd ++ ++ File descriptor returned by ++ open(). ++ ++ ++ ++ request ++ ++ MEDIA_IOC_ENUM_LINKS ++ ++ ++ ++ argp ++ ++ ++ ++ ++ ++ ++ ++ ++ Description ++ ++ To change link properties applications fill a &media-link-desc; with ++ link identification information (source and sink pad) and the new requested ++ link flags. They then call the MEDIA_IOC_SETUP_LINK ioctl with a pointer to ++ that structure. ++ The only configurable property is the ENABLED ++ link flag to enable/disable a link. Links marked with the ++ IMMUTABLE link flag can not be enabled or disabled. ++ ++ Link configuration has no side effect on other links. If an enabled ++ link at the sink pad prevents the link from being enabled, the driver ++ returns with an &EBUSY;. ++ If the specified link can't be found the driver returns with an ++ &EINVAL;. ++ ++ ++ ++ &return-value; ++ ++ ++ ++ EBUSY ++ ++ The link properties can't be changed because the link is ++ currently busy. This can be caused, for instance, by an active media ++ stream (audio or video) on the link. The ioctl shouldn't be retried if ++ no other action is performed before to fix the problem. ++ ++ ++ ++ EINVAL ++ ++ The &media-link-desc; references a non-existing link, or the ++ link is immutable and an attempt to modify its configuration was made. ++ ++ ++ ++ ++ ++ +diff --git a/Documentation/media-framework.txt b/Documentation/media-framework.txt +index 9017a41..634845e 100644 +--- a/Documentation/media-framework.txt ++++ b/Documentation/media-framework.txt +@@ -259,6 +259,16 @@ When the graph traversal is complete the function will return NULL. + Graph traversal can be interrupted at any moment. No cleanup function call is + required and the graph structure can be freed normally. + ++Helper functions can be used to find a link between two given pads, or a pad ++connected to another pad through an enabled link ++ ++ media_entity_find_link(struct media_pad *source, ++ struct media_pad *sink); ++ ++ media_entity_remote_source(struct media_pad *pad); ++ ++Refer to the kerneldoc documentation for more information. ++ + + Use count and power handling + ---------------------------- +@@ -271,3 +281,35 @@ track the number of users of every entity for power management needs. + The use_count field is owned by media drivers and must not be touched by entity + drivers. Access to the field must be protected by the media device graph_mutex + lock. ++ ++ ++Links setup ++----------- ++ ++Link properties can be modified at runtime by calling ++ ++ media_entity_setup_link(struct media_link *link, u32 flags); ++ ++The flags argument contains the requested new link flags. ++ ++The only configurable property is the ENABLED link flag to enable/disable a ++link. Links marked with the IMMUTABLE link flag can not be enabled or disabled. ++ ++When a link is enabled or disabled, the media framework calls the ++link_setup operation for the two entities at the source and sink of the link, ++in that order. If the second link_setup call fails, another link_setup call is ++made on the first entity to restore the original link flags. ++ ++Media device drivers can be notified of link setup operations by setting the ++media_device::link_notify pointer to a callback function. If provided, the ++notification callback will be called before enabling and after disabling ++links. ++ ++Entity drivers must implement the link_setup operation if any of their links ++is non-immutable. The operation must either configure the hardware or store ++the configuration information to be applied later. ++ ++Link configuration must not have any side effect on other links. If an enabled ++link at a sink pad prevents another link at the same pad from being disabled, ++the link_setup operation must return -EBUSY and can't implicitly disable the ++first enabled link. +diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c +index 1f46acb..719deba 100644 +--- a/drivers/media/media-device.c ++++ b/drivers/media/media-device.c +@@ -172,6 +172,44 @@ static long media_device_enum_links(struct media_device *mdev, + return 0; + } + ++static long media_device_setup_link(struct media_device *mdev, ++ struct media_link_desc __user *_ulink) ++{ ++ struct media_link *link = NULL; ++ struct media_link_desc ulink; ++ struct media_entity *source; ++ struct media_entity *sink; ++ int ret; ++ ++ if (copy_from_user(&ulink, _ulink, sizeof(ulink))) ++ return -EFAULT; ++ ++ /* Find the source and sink entities and link. ++ */ ++ source = find_entity(mdev, ulink.source.entity); ++ sink = find_entity(mdev, ulink.sink.entity); ++ ++ if (source == NULL || sink == NULL) ++ return -EINVAL; ++ ++ if (ulink.source.index >= source->num_pads || ++ ulink.sink.index >= sink->num_pads) ++ return -EINVAL; ++ ++ link = media_entity_find_link(&source->pads[ulink.source.index], ++ &sink->pads[ulink.sink.index]); ++ if (link == NULL) ++ return -EINVAL; ++ ++ /* Setup the link on both entities. */ ++ ret = __media_entity_setup_link(link, ulink.flags); ++ ++ if (copy_to_user(_ulink, &ulink, sizeof(ulink))) ++ return -EFAULT; ++ ++ return ret; ++} ++ + static long media_device_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) + { +@@ -197,6 +235,13 @@ static long media_device_ioctl(struct file *filp, unsigned int cmd, + mutex_unlock(&dev->graph_mutex); + break; + ++ case MEDIA_IOC_SETUP_LINK: ++ mutex_lock(&dev->graph_mutex); ++ ret = media_device_setup_link(dev, ++ (struct media_link_desc __user *)arg); ++ mutex_unlock(&dev->graph_mutex); ++ break; ++ + default: + ret = -ENOIOCTLCMD; + } +diff --git a/drivers/media/media-entity.c b/drivers/media/media-entity.c +index fe6bfd2..d703ce8 100644 +--- a/drivers/media/media-entity.c ++++ b/drivers/media/media-entity.c +@@ -306,3 +306,158 @@ media_entity_create_link(struct media_entity *source, u16 source_pad, + return 0; + } + EXPORT_SYMBOL_GPL(media_entity_create_link); ++ ++static int __media_entity_setup_link_notify(struct media_link *link, u32 flags) ++{ ++ const u32 mask = MEDIA_LNK_FL_ENABLED; ++ int ret; ++ ++ /* Notify both entities. */ ++ ret = media_entity_call(link->source->entity, link_setup, ++ link->source, link->sink, flags); ++ if (ret < 0 && ret != -ENOIOCTLCMD) ++ return ret; ++ ++ ret = media_entity_call(link->sink->entity, link_setup, ++ link->sink, link->source, flags); ++ if (ret < 0 && ret != -ENOIOCTLCMD) { ++ media_entity_call(link->source->entity, link_setup, ++ link->source, link->sink, link->flags); ++ return ret; ++ } ++ ++ link->flags = (link->flags & ~mask) | (flags & mask); ++ link->reverse->flags = link->flags; ++ ++ return 0; ++} ++ ++/** ++ * __media_entity_setup_link - Configure a media link ++ * @link: The link being configured ++ * @flags: Link configuration flags ++ * ++ * The bulk of link setup is handled by the two entities connected through the ++ * link. This function notifies both entities of the link configuration change. ++ * ++ * If the link is immutable or if the current and new configuration are ++ * identical, return immediately. ++ * ++ * The user is expected to hold link->source->parent->mutex. If not, ++ * media_entity_setup_link() should be used instead. ++ */ ++int __media_entity_setup_link(struct media_link *link, u32 flags) ++{ ++ struct media_device *mdev; ++ struct media_entity *source, *sink; ++ int ret = -EBUSY; ++ ++ if (link == NULL) ++ return -EINVAL; ++ ++ if (link->flags & MEDIA_LNK_FL_IMMUTABLE) ++ return link->flags == flags ? 0 : -EINVAL; ++ ++ if (link->flags == flags) ++ return 0; ++ ++ source = link->source->entity; ++ sink = link->sink->entity; ++ ++ mdev = source->parent; ++ ++ if ((flags & MEDIA_LNK_FL_ENABLED) && mdev->link_notify) { ++ ret = mdev->link_notify(link->source, link->sink, ++ MEDIA_LNK_FL_ENABLED); ++ if (ret < 0) ++ return ret; ++ } ++ ++ ret = __media_entity_setup_link_notify(link, flags); ++ if (ret < 0) ++ goto err; ++ ++ if (!(flags & MEDIA_LNK_FL_ENABLED) && mdev->link_notify) ++ mdev->link_notify(link->source, link->sink, 0); ++ ++ return 0; ++ ++err: ++ if ((flags & MEDIA_LNK_FL_ENABLED) && mdev->link_notify) ++ mdev->link_notify(link->source, link->sink, 0); ++ ++ return ret; ++} ++ ++int media_entity_setup_link(struct media_link *link, u32 flags) ++{ ++ int ret; ++ ++ mutex_lock(&link->source->entity->parent->graph_mutex); ++ ret = __media_entity_setup_link(link, flags); ++ mutex_unlock(&link->source->entity->parent->graph_mutex); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(media_entity_setup_link); ++ ++/** ++ * media_entity_find_link - Find a link between two pads ++ * @source: Source pad ++ * @sink: Sink pad ++ * ++ * Return a pointer to the link between the two entities. If no such link ++ * exists, return NULL. ++ */ ++struct media_link * ++media_entity_find_link(struct media_pad *source, struct media_pad *sink) ++{ ++ struct media_link *link; ++ unsigned int i; ++ ++ for (i = 0; i < source->entity->num_links; ++i) { ++ link = &source->entity->links[i]; ++ ++ if (link->source->entity == source->entity && ++ link->source->index == source->index && ++ link->sink->entity == sink->entity && ++ link->sink->index == sink->index) ++ return link; ++ } ++ ++ return NULL; ++} ++EXPORT_SYMBOL_GPL(media_entity_find_link); ++ ++/** ++ * media_entity_remote_source - Find the source pad at the remote end of a link ++ * @pad: Sink pad at the local end of the link ++ * ++ * Search for a remote source pad connected to the given sink pad by iterating ++ * over all links originating or terminating at that pad until an enabled link ++ * is found. ++ * ++ * Return a pointer to the pad at the remote end of the first found enabled ++ * link, or NULL if no enabled link has been found. ++ */ ++struct media_pad *media_entity_remote_source(struct media_pad *pad) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < pad->entity->num_links; i++) { ++ struct media_link *link = &pad->entity->links[i]; ++ ++ if (!(link->flags & MEDIA_LNK_FL_ENABLED)) ++ continue; ++ ++ if (link->source == pad) ++ return link->sink; ++ ++ if (link->sink == pad) ++ return link->source; ++ } ++ ++ return NULL; ++ ++} ++EXPORT_SYMBOL_GPL(media_entity_remote_source); +diff --git a/include/linux/media.h b/include/linux/media.h +index 64c0313..2f67ed2 100644 +--- a/include/linux/media.h ++++ b/include/linux/media.h +@@ -126,5 +126,6 @@ struct media_links_enum { + #define MEDIA_IOC_DEVICE_INFO _IOWR('M', 1, struct media_device_info) + #define MEDIA_IOC_ENUM_ENTITIES _IOWR('M', 2, struct media_entity_desc) + #define MEDIA_IOC_ENUM_LINKS _IOWR('M', 3, struct media_links_enum) ++#define MEDIA_IOC_SETUP_LINK _IOWR('M', 4, struct media_link_desc) + + #endif /* __LINUX_MEDIA_H */ +diff --git a/include/media/media-device.h b/include/media/media-device.h +index 260d59c..ad93e66 100644 +--- a/include/media/media-device.h ++++ b/include/media/media-device.h +@@ -73,6 +73,9 @@ struct media_device { + spinlock_t lock; + /* Serializes graph operations. */ + struct mutex graph_mutex; ++ ++ int (*link_notify)(struct media_pad *source, ++ struct media_pad *sink, u32 flags); + }; + + /* media_devnode to media_device */ +diff --git a/include/media/media-entity.h b/include/media/media-entity.h +index 0954490..60fc7bd 100644 +--- a/include/media/media-entity.h ++++ b/include/media/media-entity.h +@@ -39,6 +39,12 @@ struct media_pad { + unsigned long flags; /* Pad flags (MEDIA_PAD_FL_*) */ + }; + ++struct media_entity_operations { ++ int (*link_setup)(struct media_entity *entity, ++ const struct media_pad *local, ++ const struct media_pad *remote, u32 flags); ++}; ++ + struct media_entity { + struct list_head list; + struct media_device *parent; /* Media device this entity belongs to*/ +@@ -59,6 +65,8 @@ struct media_entity { + struct media_pad *pads; /* Pads array (num_pads elements) */ + struct media_link *links; /* Links array (max_links elements)*/ + ++ const struct media_entity_operations *ops; /* Entity operations */ ++ + int use_count; /* Use count for the entity. */ + + union { +@@ -108,6 +116,11 @@ int media_entity_init(struct media_entity *entity, u16 num_pads, + void media_entity_cleanup(struct media_entity *entity); + int media_entity_create_link(struct media_entity *source, u16 source_pad, + struct media_entity *sink, u16 sink_pad, u32 flags); ++int __media_entity_setup_link(struct media_link *link, u32 flags); ++int media_entity_setup_link(struct media_link *link, u32 flags); ++struct media_link *media_entity_find_link(struct media_pad *source, ++ struct media_pad *sink); ++struct media_pad *media_entity_remote_source(struct media_pad *pad); + + struct media_entity *media_entity_get(struct media_entity *entity); + void media_entity_put(struct media_entity *entity); +@@ -117,4 +130,8 @@ void media_entity_graph_walk_start(struct media_entity_graph *graph, + struct media_entity * + media_entity_graph_walk_next(struct media_entity_graph *graph); + ++#define media_entity_call(entity, operation, args...) \ ++ (((entity)->ops && (entity)->ops->operation) ? \ ++ (entity)->ops->operation((entity) , ##args) : -ENOIOCTLCMD) ++ + #endif +-- +1.6.6.1 + diff --git a/recipes/linux/linux-omap-2.6.37/media/0016-media-Pipelines-and-media-streams.patch b/recipes/linux/linux-omap-2.6.37/media/0016-media-Pipelines-and-media-streams.patch new file mode 100644 index 0000000000..969162f8ec --- /dev/null +++ b/recipes/linux/linux-omap-2.6.37/media/0016-media-Pipelines-and-media-streams.patch @@ -0,0 +1,259 @@ +From 4e07e9ada1b3baaec6d4948eccf3c0499e3228df Mon Sep 17 00:00:00 2001 +From: Laurent Pinchart +Date: Wed, 25 Aug 2010 15:00:41 +0300 +Subject: [PATCH 16/43] media: Pipelines and media streams + +Drivers often need to associate pipeline objects to entities, and to +take stream state into account when configuring entities and links. The +pipeline API helps drivers manage that information. + +When starting streaming, drivers call media_entity_pipeline_start(). The +function marks all entities connected to the given entity through +enabled links, either directly or indirectly, as streaming. Similarly, +when stopping the stream, drivers call media_entity_pipeline_stop(). + +The media_entity_pipeline_start() function takes a pointer to a media +pipeline and stores it in every entity in the graph. Drivers should +embed the media_pipeline structure in higher-level pipeline structures +and can then access the pipeline through the media_entity structure. + +Link configuration will fail with -EBUSY by default if either end of the +link is a streaming entity, unless the link is marked with the +MEDIA_LNK_FL_DYNAMIC flag. + +Signed-off-by: Laurent Pinchart +--- + Documentation/DocBook/v4l/media-ioc-enum-links.xml | 5 ++ + Documentation/DocBook/v4l/media-ioc-setup-link.xml | 3 + + Documentation/media-framework.txt | 38 ++++++++++ + drivers/media/media-entity.c | 73 ++++++++++++++++++++ + include/linux/media.h | 1 + + include/media/media-entity.h | 10 +++ + 6 files changed, 130 insertions(+), 0 deletions(-) + +diff --git a/Documentation/DocBook/v4l/media-ioc-enum-links.xml b/Documentation/DocBook/v4l/media-ioc-enum-links.xml +index daf0360..b204bfb 100644 +--- a/Documentation/DocBook/v4l/media-ioc-enum-links.xml ++++ b/Documentation/DocBook/v4l/media-ioc-enum-links.xml +@@ -179,6 +179,11 @@ + The link enabled state can't be modified at runtime. An + immutable link is always enabled. + ++ ++ MEDIA_LNK_FL_DYNAMIC ++ The link enabled state can be modified during streaming. This ++ flag is set by drivers and is read-only for applications. ++ + + + +diff --git a/Documentation/DocBook/v4l/media-ioc-setup-link.xml b/Documentation/DocBook/v4l/media-ioc-setup-link.xml +index 09ab3d2..2331e76 100644 +--- a/Documentation/DocBook/v4l/media-ioc-setup-link.xml ++++ b/Documentation/DocBook/v4l/media-ioc-setup-link.xml +@@ -60,6 +60,9 @@ + Link configuration has no side effect on other links. If an enabled + link at the sink pad prevents the link from being enabled, the driver + returns with an &EBUSY;. ++ Only links marked with the DYNAMIC link flag can ++ be enabled/disabled while streaming media data. Attempting to enable or ++ disable a streaming non-dynamic link will return an &EBUSY;. + If the specified link can't be found the driver returns with an + &EINVAL;. + +diff --git a/Documentation/media-framework.txt b/Documentation/media-framework.txt +index 634845e..435d0c4 100644 +--- a/Documentation/media-framework.txt ++++ b/Documentation/media-framework.txt +@@ -313,3 +313,41 @@ Link configuration must not have any side effect on other links. If an enabled + link at a sink pad prevents another link at the same pad from being disabled, + the link_setup operation must return -EBUSY and can't implicitly disable the + first enabled link. ++ ++ ++Pipelines and media streams ++--------------------------- ++ ++When starting streaming, drivers must notify all entities in the pipeline to ++prevent link states from being modified during streaming by calling ++ ++ media_entity_pipeline_start(struct media_entity *entity, ++ struct media_pipeline *pipe); ++ ++The function will mark all entities connected to the given entity through ++enabled links, either directly or indirectly, as streaming. ++ ++The media_pipeline instance pointed to by the pipe argument will be stored in ++every entity in the pipeline. Drivers should embed the media_pipeline structure ++in higher-level pipeline structures and can then access the pipeline through ++the media_entity pipe field. ++ ++Calls to media_entity_pipeline_start() can be nested. The pipeline pointer must ++be identical for all nested calls to the function. ++ ++When stopping the stream, drivers must notify the entities with ++ ++ media_entity_pipeline_stop(struct media_entity *entity); ++ ++If multiple calls to media_entity_pipeline_start() have been made the same ++number of media_entity_pipeline_stop() calls are required to stop streaming. The ++media_entity pipe field is reset to NULL on the last nested stop call. ++ ++Link configuration will fail with -EBUSY by default if either end of the link is ++a streaming entity. Links that can be modified while streaming must be marked ++with the MEDIA_LNK_FL_DYNAMIC flag. ++ ++If other operations need to be disallowed on streaming entities (such as ++changing entities configuration parameters) drivers can explictly check the ++media_entity stream_count field to find out if an entity is streaming. This ++operation must be done with the media_device graph_mutex held. +diff --git a/drivers/media/media-entity.c b/drivers/media/media-entity.c +index d703ce8..e63e089 100644 +--- a/drivers/media/media-entity.c ++++ b/drivers/media/media-entity.c +@@ -197,6 +197,75 @@ media_entity_graph_walk_next(struct media_entity_graph *graph) + EXPORT_SYMBOL_GPL(media_entity_graph_walk_next); + + /* ----------------------------------------------------------------------------- ++ * Pipeline management ++ */ ++ ++/** ++ * media_entity_pipeline_start - Mark a pipeline as streaming ++ * @entity: Starting entity ++ * @pipe: Media pipeline to be assigned to all entities in the pipeline. ++ * ++ * Mark all entities connected to a given entity through enabled links, either ++ * directly or indirectly, as streaming. The given pipeline object is assigned to ++ * every entity in the pipeline and stored in the media_entity pipe field. ++ * ++ * Calls to this function can be nested, in which case the same number of ++ * media_entity_pipeline_stop() calls will be required to stop streaming. The ++ * pipeline pointer must be identical for all nested calls to ++ * media_entity_pipeline_start(). ++ */ ++void media_entity_pipeline_start(struct media_entity *entity, ++ struct media_pipeline *pipe) ++{ ++ struct media_device *mdev = entity->parent; ++ struct media_entity_graph graph; ++ ++ mutex_lock(&mdev->graph_mutex); ++ ++ media_entity_graph_walk_start(&graph, entity); ++ ++ while ((entity = media_entity_graph_walk_next(&graph))) { ++ entity->stream_count++; ++ WARN_ON(entity->pipe && entity->pipe != pipe); ++ entity->pipe = pipe; ++ } ++ ++ mutex_unlock(&mdev->graph_mutex); ++} ++EXPORT_SYMBOL_GPL(media_entity_pipeline_start); ++ ++/** ++ * media_entity_pipeline_stop - Mark a pipeline as not streaming ++ * @entity: Starting entity ++ * ++ * Mark all entities connected to a given entity through enabled links, either ++ * directly or indirectly, as not streaming. The media_entity pipe field is ++ * reset to NULL. ++ * ++ * If multiple calls to media_entity_pipeline_start() have been made, the same ++ * number of calls to this function are required to mark the pipeline as not ++ * streaming. ++ */ ++void media_entity_pipeline_stop(struct media_entity *entity) ++{ ++ struct media_device *mdev = entity->parent; ++ struct media_entity_graph graph; ++ ++ mutex_lock(&mdev->graph_mutex); ++ ++ media_entity_graph_walk_start(&graph, entity); ++ ++ while ((entity = media_entity_graph_walk_next(&graph))) { ++ entity->stream_count--; ++ if (entity->stream_count == 0) ++ entity->pipe = NULL; ++ } ++ ++ mutex_unlock(&mdev->graph_mutex); ++} ++EXPORT_SYMBOL_GPL(media_entity_pipeline_stop); ++ ++/* ----------------------------------------------------------------------------- + * Module use count + */ + +@@ -364,6 +433,10 @@ int __media_entity_setup_link(struct media_link *link, u32 flags) + source = link->source->entity; + sink = link->sink->entity; + ++ if (!(link->flags & MEDIA_LNK_FL_DYNAMIC) && ++ (source->stream_count || sink->stream_count)) ++ return -EBUSY; ++ + mdev = source->parent; + + if ((flags & MEDIA_LNK_FL_ENABLED) && mdev->link_notify) { +diff --git a/include/linux/media.h b/include/linux/media.h +index 2f67ed2..29039e8 100644 +--- a/include/linux/media.h ++++ b/include/linux/media.h +@@ -106,6 +106,7 @@ struct media_pad_desc { + + #define MEDIA_LNK_FL_ENABLED (1 << 0) + #define MEDIA_LNK_FL_IMMUTABLE (1 << 1) ++#define MEDIA_LNK_FL_DYNAMIC (1 << 2) + + struct media_link_desc { + struct media_pad_desc source; +diff --git a/include/media/media-entity.h b/include/media/media-entity.h +index 60fc7bd..450ba12 100644 +--- a/include/media/media-entity.h ++++ b/include/media/media-entity.h +@@ -26,6 +26,9 @@ + #include + #include + ++struct media_pipeline { ++}; ++ + struct media_link { + struct media_pad *source; /* Source pad */ + struct media_pad *sink; /* Sink pad */ +@@ -67,8 +70,11 @@ struct media_entity { + + const struct media_entity_operations *ops; /* Entity operations */ + ++ int stream_count; /* Stream count for the entity. */ + int use_count; /* Use count for the entity. */ + ++ struct media_pipeline *pipe; /* Pipeline this entity belongs to. */ ++ + union { + /* Node specifications */ + struct { +@@ -114,6 +120,7 @@ struct media_entity_graph { + int media_entity_init(struct media_entity *entity, u16 num_pads, + struct media_pad *pads, u16 extra_links); + void media_entity_cleanup(struct media_entity *entity); ++ + int media_entity_create_link(struct media_entity *source, u16 source_pad, + struct media_entity *sink, u16 sink_pad, u32 flags); + int __media_entity_setup_link(struct media_link *link, u32 flags); +@@ -129,6 +136,9 @@ void media_entity_graph_walk_start(struct media_entity_graph *graph, + struct media_entity *entity); + struct media_entity * + media_entity_graph_walk_next(struct media_entity_graph *graph); ++void media_entity_pipeline_start(struct media_entity *entity, ++ struct media_pipeline *pipe); ++void media_entity_pipeline_stop(struct media_entity *entity); + + #define media_entity_call(entity, operation, args...) \ + (((entity)->ops && (entity)->ops->operation) ? \ +-- +1.6.6.1 + diff --git a/recipes/linux/linux-omap-2.6.37/media/0017-v4l-Add-a-media_device-pointer-to-the-v4l2_device-st.patch b/recipes/linux/linux-omap-2.6.37/media/0017-v4l-Add-a-media_device-pointer-to-the-v4l2_device-st.patch new file mode 100644 index 0000000000..714c5a3b63 --- /dev/null +++ b/recipes/linux/linux-omap-2.6.37/media/0017-v4l-Add-a-media_device-pointer-to-the-v4l2_device-st.patch @@ -0,0 +1,115 @@ +From 56e006c01032f98483195e572700e17fb8aaa8b1 Mon Sep 17 00:00:00 2001 +From: Laurent Pinchart +Date: Wed, 9 Dec 2009 12:40:05 +0100 +Subject: [PATCH 17/43] v4l: Add a media_device pointer to the v4l2_device structure + +The pointer will later be used to register/unregister media entities +when registering/unregistering a v4l2_subdev or a video_device. + +With the introduction of media devices, device drivers need to store a +pointer to a driver-specific structure in the device's drvdata. +v4l2_device can't claim ownership of the drvdata anymore. + +To maintain compatibility with drivers that rely on v4l2_device storing +a pointer to itself in the device's drvdata, v4l2_device_register() will +keep doing so if the drvdata is NULL. + +Signed-off-by: Laurent Pinchart +--- + Documentation/video4linux/v4l2-framework.txt | 17 ++++++++++++----- + drivers/media/video/v4l2-device.c | 13 +++++++------ + include/media/v4l2-device.h | 4 ++++ + 3 files changed, 23 insertions(+), 11 deletions(-) + +diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt +index 4db1def..aeb2a22 100644 +--- a/Documentation/video4linux/v4l2-framework.txt ++++ b/Documentation/video4linux/v4l2-framework.txt +@@ -83,11 +83,17 @@ You must register the device instance: + + v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev); + +-Registration will initialize the v4l2_device struct and link dev->driver_data +-to v4l2_dev. If v4l2_dev->name is empty then it will be set to a value derived +-from dev (driver name followed by the bus_id, to be precise). If you set it +-up before calling v4l2_device_register then it will be untouched. If dev is +-NULL, then you *must* setup v4l2_dev->name before calling v4l2_device_register. ++Registration will initialize the v4l2_device struct. If the dev->driver_data ++field is NULL, it will be linked to v4l2_dev. Drivers that use the media ++device framework in addition to the V4L2 framework need to set ++dev->driver_data manually to point to the driver-specific device structure ++that embed the struct v4l2_device instance. This is achieved by a ++dev_set_drvdata() call before registering the V4L2 device instance. ++ ++If v4l2_dev->name is empty then it will be set to a value derived from dev ++(driver name followed by the bus_id, to be precise). If you set it up before ++calling v4l2_device_register then it will be untouched. If dev is NULL, then ++you *must* setup v4l2_dev->name before calling v4l2_device_register. + + You can use v4l2_device_set_name() to set the name based on a driver name and + a driver-global atomic_t instance. This will generate names like ivtv0, ivtv1, +@@ -108,6 +114,7 @@ You unregister with: + + v4l2_device_unregister(struct v4l2_device *v4l2_dev); + ++If the dev->driver_data field points to v4l2_dev, it will be reset to NULL. + Unregistering will also automatically unregister all subdevs from the device. + + If you have a hotpluggable device (e.g. a USB device), then when a disconnect +diff --git a/drivers/media/video/v4l2-device.c b/drivers/media/video/v4l2-device.c +index 97e84df..5c16a12 100644 +--- a/drivers/media/video/v4l2-device.c ++++ b/drivers/media/video/v4l2-device.c +@@ -47,9 +47,8 @@ int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev) + if (!v4l2_dev->name[0]) + snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), "%s %s", + dev->driver->name, dev_name(dev)); +- if (dev_get_drvdata(dev)) +- v4l2_warn(v4l2_dev, "Non-NULL drvdata on register\n"); +- dev_set_drvdata(dev, v4l2_dev); ++ if (!dev_get_drvdata(dev)) ++ dev_set_drvdata(dev, v4l2_dev); + return 0; + } + EXPORT_SYMBOL_GPL(v4l2_device_register); +@@ -72,10 +71,12 @@ EXPORT_SYMBOL_GPL(v4l2_device_set_name); + + void v4l2_device_disconnect(struct v4l2_device *v4l2_dev) + { +- if (v4l2_dev->dev) { ++ if (v4l2_dev->dev == NULL) ++ return; ++ ++ if (dev_get_drvdata(v4l2_dev->dev) == v4l2_dev) + dev_set_drvdata(v4l2_dev->dev, NULL); +- v4l2_dev->dev = NULL; +- } ++ v4l2_dev->dev = NULL; + } + EXPORT_SYMBOL_GPL(v4l2_device_disconnect); + +diff --git a/include/media/v4l2-device.h b/include/media/v4l2-device.h +index b16f307..759db73 100644 +--- a/include/media/v4l2-device.h ++++ b/include/media/v4l2-device.h +@@ -21,6 +21,7 @@ + #ifndef _V4L2_DEVICE_H + #define _V4L2_DEVICE_H + ++#include + #include + + /* Each instance of a V4L2 device should create the v4l2_device struct, +@@ -39,6 +40,9 @@ struct v4l2_device { + Note: dev might be NULL if there is no parent device + as is the case with e.g. ISA devices. */ + struct device *dev; ++#if defined(CONFIG_MEDIA_CONTROLLER) ++ struct media_device *mdev; ++#endif + /* used to keep track of the registered subdevs */ + struct list_head subdevs; + /* lock this struct; can be used by the driver as well if this +-- +1.6.6.1 + diff --git a/recipes/linux/linux-omap-2.6.37/media/0018-v4l-Make-video_device-inherit-from-media_entity.patch b/recipes/linux/linux-omap-2.6.37/media/0018-v4l-Make-video_device-inherit-from-media_entity.patch new file mode 100644 index 0000000000..6417d1dbde --- /dev/null +++ b/recipes/linux/linux-omap-2.6.37/media/0018-v4l-Make-video_device-inherit-from-media_entity.patch @@ -0,0 +1,234 @@ +From e31cb57c733341b49256a47f086fa4cc1c1c56ac Mon Sep 17 00:00:00 2001 +From: Laurent Pinchart +Date: Wed, 9 Dec 2009 12:40:10 +0100 +Subject: [PATCH 18/43] v4l: Make video_device inherit from media_entity + +V4L2 devices are media entities. As such they need to inherit from +(include) the media_entity structure. + +When registering/unregistering the device, the media entity is +automatically registered/unregistered. The entity is acquired on device +open and released on device close. + +Signed-off-by: Laurent Pinchart +Signed-off-by: Sakari Ailus +--- + Documentation/video4linux/v4l2-framework.txt | 38 ++++++++++++++++++-- + drivers/media/video/v4l2-dev.c | 49 +++++++++++++++++++++++-- + include/media/v4l2-dev.h | 7 ++++ + 3 files changed, 87 insertions(+), 7 deletions(-) + +diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt +index aeb2a22..f231bc2 100644 +--- a/Documentation/video4linux/v4l2-framework.txt ++++ b/Documentation/video4linux/v4l2-framework.txt +@@ -71,6 +71,10 @@ sub-device instances, the video_device struct stores V4L2 device node data + and in the future a v4l2_fh struct will keep track of filehandle instances + (this is not yet implemented). + ++The V4L2 framework also optionally integrates with the media framework. If a ++driver sets the struct v4l2_device mdev field, sub-devices and video nodes ++will automatically appear in the media framework as entities. ++ + + struct v4l2_device + ------------------ +@@ -84,11 +88,14 @@ You must register the device instance: + v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev); + + Registration will initialize the v4l2_device struct. If the dev->driver_data +-field is NULL, it will be linked to v4l2_dev. Drivers that use the media +-device framework in addition to the V4L2 framework need to set ++field is NULL, it will be linked to v4l2_dev. ++ ++Drivers that want integration with the media device framework need to set + dev->driver_data manually to point to the driver-specific device structure + that embed the struct v4l2_device instance. This is achieved by a +-dev_set_drvdata() call before registering the V4L2 device instance. ++dev_set_drvdata() call before registering the V4L2 device instance. They must ++also set the struct v4l2_device mdev field to point to a properly initialized ++and registered media_device instance. + + If v4l2_dev->name is empty then it will be set to a value derived from dev + (driver name followed by the bus_id, to be precise). If you set it up before +@@ -532,6 +539,21 @@ If you use v4l2_ioctl_ops, then you should set either .unlocked_ioctl or + The v4l2_file_operations struct is a subset of file_operations. The main + difference is that the inode argument is omitted since it is never used. + ++If integration with the media framework is needed, you must initialize the ++media_entity struct embedded in the video_device struct (entity field) by ++calling media_entity_init(): ++ ++ struct media_pad *pad = &my_vdev->pad; ++ int err; ++ ++ err = media_entity_init(&vdev->entity, 1, pad, 0); ++ ++The pads array must have been previously initialized. There is no need to ++manually set the struct media_entity type and name fields. ++ ++A reference to the entity will be automatically acquired/released when the ++video device is opened/closed. ++ + v4l2_file_operations and locking + -------------------------------- + +@@ -561,6 +583,9 @@ for you. + return err; + } + ++If the v4l2_device parent device has a non-NULL mdev field, the video device ++entity will be automatically registered with the media device. ++ + Which device is registered depends on the type argument. The following + types exist: + +@@ -636,6 +661,13 @@ release, of course) will return an error as well. + When the last user of the video device node exits, then the vdev->release() + callback is called and you can do the final cleanup there. + ++Don't forget to cleanup the media entity associated with the video device if ++it has been initialized: ++ ++ media_entity_cleanup(&vdev->entity); ++ ++This can be done from the release callback. ++ + + video_device helper functions + ----------------------------- +diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c +index f22bd41..f91348f 100644 +--- a/drivers/media/video/v4l2-dev.c ++++ b/drivers/media/video/v4l2-dev.c +@@ -303,6 +303,9 @@ static int v4l2_mmap(struct file *filp, struct vm_area_struct *vm) + static int v4l2_open(struct inode *inode, struct file *filp) + { + struct video_device *vdev; ++#if defined(CONFIG_MEDIA_CONTROLLER) ++ struct media_entity *entity = NULL; ++#endif + int ret = 0; + + /* Check if the video device is available */ +@@ -316,6 +319,16 @@ static int v4l2_open(struct inode *inode, struct file *filp) + /* and increase the device refcount */ + video_get(vdev); + mutex_unlock(&videodev_lock); ++#if defined(CONFIG_MEDIA_CONTROLLER) ++ if (vdev->v4l2_dev && vdev->v4l2_dev->mdev) { ++ entity = media_entity_get(&vdev->entity); ++ if (!entity) { ++ ret = -EBUSY; ++ video_put(vdev); ++ return ret; ++ } ++ } ++#endif + if (vdev->fops->open) { + if (vdev->lock && mutex_lock_interruptible(vdev->lock)) { + ret = -ERESTARTSYS; +@@ -331,8 +344,13 @@ static int v4l2_open(struct inode *inode, struct file *filp) + + err: + /* decrease the refcount in case of an error */ +- if (ret) ++ if (ret) { ++#if defined(CONFIG_MEDIA_CONTROLLER) ++ if (vdev->v4l2_dev && vdev->v4l2_dev->mdev) ++ media_entity_put(entity); ++#endif + video_put(vdev); ++ } + return ret; + } + +@@ -349,7 +367,10 @@ static int v4l2_release(struct inode *inode, struct file *filp) + if (vdev->lock) + mutex_unlock(vdev->lock); + } +- ++#if defined(CONFIG_MEDIA_CONTROLLER) ++ if (vdev->v4l2_dev && vdev->v4l2_dev->mdev) ++ media_entity_put(&vdev->entity); ++#endif + /* decrease the refcount unconditionally since the release() + return value is ignored. */ + video_put(vdev); +@@ -586,12 +607,27 @@ int __video_register_device(struct video_device *vdev, int type, int nr, + if (nr != -1 && nr != vdev->num && warn_if_nr_in_use) + printk(KERN_WARNING "%s: requested %s%d, got %s\n", __func__, + name_base, nr, video_device_node_name(vdev)); +- +- /* Part 5: Activate this minor. The char device can now be used. */ ++#if defined(CONFIG_MEDIA_CONTROLLER) ++ /* Part 5: Register the entity. */ ++ if (vdev->v4l2_dev && vdev->v4l2_dev->mdev) { ++ vdev->entity.type = MEDIA_ENT_T_DEVNODE_V4L; ++ vdev->entity.name = vdev->name; ++ vdev->entity.v4l.major = VIDEO_MAJOR; ++ vdev->entity.v4l.minor = vdev->minor; ++ ret = media_device_register_entity(vdev->v4l2_dev->mdev, ++ &vdev->entity); ++ if (ret < 0) ++ printk(KERN_WARNING ++ "%s: media_device_register_entity failed\n", ++ __func__); ++ } ++#endif ++ /* Part 6: Activate this minor. The char device can now be used. */ + set_bit(V4L2_FL_REGISTERED, &vdev->flags); + mutex_lock(&videodev_lock); + video_device[vdev->minor] = vdev; + mutex_unlock(&videodev_lock); ++ + return 0; + + cleanup: +@@ -619,6 +655,11 @@ void video_unregister_device(struct video_device *vdev) + if (!vdev || !video_is_registered(vdev)) + return; + ++#if defined(CONFIG_MEDIA_CONTROLLER) ++ if (vdev->v4l2_dev && vdev->v4l2_dev->mdev) ++ media_device_unregister_entity(&vdev->entity); ++#endif ++ + mutex_lock(&videodev_lock); + /* This must be in a critical section to prevent a race with v4l2_open. + * Once this bit has been cleared video_get may never be called again. +diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h +index 4fe6831..51b2c51 100644 +--- a/include/media/v4l2-dev.h ++++ b/include/media/v4l2-dev.h +@@ -16,6 +16,8 @@ + #include + #include + ++#include ++ + #define VIDEO_MAJOR 81 + + #define VFL_TYPE_GRABBER 0 +@@ -55,6 +57,9 @@ struct v4l2_file_operations { + + struct video_device + { ++#if defined(CONFIG_MEDIA_CONTROLLER) ++ struct media_entity entity; ++#endif + /* device ops */ + const struct v4l2_file_operations *fops; + +@@ -100,6 +105,8 @@ struct video_device + struct mutex *lock; + }; + ++#define media_entity_to_video_device(entity) \ ++ container_of(entity, struct video_device, entity) + /* dev to video-device */ + #define to_video_device(cd) container_of(cd, struct video_device, dev) + +-- +1.6.6.1 + diff --git a/recipes/linux/linux-omap-2.6.37/media/0019-v4l-Make-v4l2_subdev-inherit-from-media_entity.patch b/recipes/linux/linux-omap-2.6.37/media/0019-v4l-Make-v4l2_subdev-inherit-from-media_entity.patch new file mode 100644 index 0000000000..c7ae882a08 --- /dev/null +++ b/recipes/linux/linux-omap-2.6.37/media/0019-v4l-Make-v4l2_subdev-inherit-from-media_entity.patch @@ -0,0 +1,265 @@ +From ab4bf9e43078f79ba2b287e6dd6d6871901d0341 Mon Sep 17 00:00:00 2001 +From: Laurent Pinchart +Date: Wed, 9 Dec 2009 12:40:08 +0100 +Subject: [PATCH 19/43] v4l: Make v4l2_subdev inherit from media_entity + +V4L2 subdevices are media entities. As such they need to inherit from +(include) the media_entity structure. + +When registering/unregistering the subdevice, the media entity is +automatically registered/unregistered. The entity is acquired on device +open and released on device close. + +Signed-off-by: Laurent Pinchart +Signed-off-by: Sakari Ailus +--- + Documentation/video4linux/v4l2-framework.txt | 23 ++++++++++++++ + drivers/media/video/v4l2-device.c | 39 ++++++++++++++++++++---- + drivers/media/video/v4l2-subdev.c | 41 ++++++++++++++++++++++++- + include/media/v4l2-subdev.h | 10 ++++++ + 4 files changed, 104 insertions(+), 9 deletions(-) + +diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt +index f231bc2..d0fb880 100644 +--- a/Documentation/video4linux/v4l2-framework.txt ++++ b/Documentation/video4linux/v4l2-framework.txt +@@ -268,6 +268,26 @@ A sub-device driver initializes the v4l2_subdev struct using: + Afterwards you need to initialize subdev->name with a unique name and set the + module owner. This is done for you if you use the i2c helper functions. + ++If integration with the media framework is needed, you must initialize the ++media_entity struct embedded in the v4l2_subdev struct (entity field) by ++calling media_entity_init(): ++ ++ struct media_pad *pads = &my_sd->pads; ++ int err; ++ ++ err = media_entity_init(&sd->entity, npads, pads, 0); ++ ++The pads array must have been previously initialized. There is no need to ++manually set the struct media_entity type and name fields, but the revision ++field must be initialized if needed. ++ ++A reference to the entity will be automatically acquired/released when the ++subdev device node (if any) is opened/closed. ++ ++Don't forget to cleanup the media entity before the sub-device is destroyed: ++ ++ media_entity_cleanup(&sd->entity); ++ + A device (bridge) driver needs to register the v4l2_subdev with the + v4l2_device: + +@@ -277,6 +297,9 @@ This can fail if the subdev module disappeared before it could be registered. + After this function was called successfully the subdev->dev field points to + the v4l2_device. + ++If the v4l2_device parent device has a non-NULL mdev field, the sub-device ++entity will be automatically registered with the media device. ++ + You can unregister a sub-device using: + + v4l2_device_unregister_subdev(sd); +diff --git a/drivers/media/video/v4l2-device.c b/drivers/media/video/v4l2-device.c +index 5c16a12..69cb429 100644 +--- a/drivers/media/video/v4l2-device.c ++++ b/drivers/media/video/v4l2-device.c +@@ -116,8 +116,11 @@ void v4l2_device_unregister(struct v4l2_device *v4l2_dev) + EXPORT_SYMBOL_GPL(v4l2_device_unregister); + + int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev, +- struct v4l2_subdev *sd) ++ struct v4l2_subdev *sd) + { ++#if defined(CONFIG_MEDIA_CONTROLLER) ++ struct media_entity *entity = &sd->entity; ++#endif + struct video_device *vdev; + int err; + +@@ -135,7 +138,16 @@ int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev, + err = v4l2_ctrl_add_handler(v4l2_dev->ctrl_handler, sd->ctrl_handler); + if (err) + return err; +- ++#if defined(CONFIG_MEDIA_CONTROLLER) ++ /* Register the entity. */ ++ if (v4l2_dev->mdev) { ++ err = media_device_register_entity(v4l2_dev->mdev, entity); ++ if (err < 0) { ++ module_put(sd->owner); ++ return err; ++ } ++ } ++#endif + sd->v4l2_dev = v4l2_dev; + spin_lock(&v4l2_dev->lock); + list_add_tail(&sd->list, &v4l2_dev->subdevs); +@@ -150,26 +162,39 @@ int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev, + if (sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE) { + err = __video_register_device(vdev, VFL_TYPE_SUBDEV, -1, 1, + sd->owner); +- if (err < 0) ++ if (err < 0) { + v4l2_device_unregister_subdev(sd); ++ return err; ++ } + } +- +- return err; ++#if defined(CONFIG_MEDIA_CONTROLLER) ++ entity->v4l.major = VIDEO_MAJOR; ++ entity->v4l.minor = vdev->minor; ++#endif ++ return 0; + } + EXPORT_SYMBOL_GPL(v4l2_device_register_subdev); + + void v4l2_device_unregister_subdev(struct v4l2_subdev *sd) + { ++ struct v4l2_device *v4l2_dev; ++ + /* return if it isn't registered */ + if (sd == NULL || sd->v4l2_dev == NULL) + return; + +- spin_lock(&sd->v4l2_dev->lock); ++ v4l2_dev = sd->v4l2_dev; ++ ++ spin_lock(&v4l2_dev->lock); + list_del(&sd->list); +- spin_unlock(&sd->v4l2_dev->lock); ++ spin_unlock(&v4l2_dev->lock); + sd->v4l2_dev = NULL; + + module_put(sd->owner); ++#if defined(CONFIG_MEDIA_CONTROLLER) ++ if (v4l2_dev->mdev) ++ media_device_unregister_entity(&sd->entity); ++#endif + video_unregister_device(&sd->devnode); + } + EXPORT_SYMBOL_GPL(v4l2_device_unregister_subdev); +diff --git a/drivers/media/video/v4l2-subdev.c b/drivers/media/video/v4l2-subdev.c +index fbccefd..a49856a 100644 +--- a/drivers/media/video/v4l2-subdev.c ++++ b/drivers/media/video/v4l2-subdev.c +@@ -35,7 +35,10 @@ static int subdev_open(struct file *file) + { + struct video_device *vdev = video_devdata(file); + struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev); +- struct v4l2_fh *vfh; ++#if defined(CONFIG_MEDIA_CONTROLLER) ++ struct media_entity *entity; ++#endif ++ struct v4l2_fh *vfh = NULL; + int ret; + + if (!sd->initialized) +@@ -61,11 +64,20 @@ static int subdev_open(struct file *file) + v4l2_fh_add(vfh); + file->private_data = vfh; + } +- ++#if defined(CONFIG_MEDIA_CONTROLLER) ++ if (sd->v4l2_dev->mdev) { ++ entity = media_entity_get(&sd->entity); ++ if (!entity) { ++ ret = -EBUSY; ++ goto err; ++ } ++ } ++#endif + return 0; + + err: + if (vfh != NULL) { ++ v4l2_fh_del(vfh); + v4l2_fh_exit(vfh); + kfree(vfh); + } +@@ -75,8 +87,16 @@ err: + + static int subdev_close(struct file *file) + { ++#if defined(CONFIG_MEDIA_CONTROLLER) ++ struct video_device *vdev = video_devdata(file); ++ struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev); ++#endif + struct v4l2_fh *vfh = file->private_data; + ++#if defined(CONFIG_MEDIA_CONTROLLER) ++ if (sd->v4l2_dev->mdev) ++ media_entity_put(&sd->entity); ++#endif + if (vfh != NULL) { + v4l2_fh_del(vfh); + v4l2_fh_exit(vfh); +@@ -176,5 +196,22 @@ void v4l2_subdev_init(struct v4l2_subdev *sd, const struct v4l2_subdev_ops *ops) + sd->dev_priv = NULL; + sd->host_priv = NULL; + sd->initialized = 1; ++#if defined(CONFIG_MEDIA_CONTROLLER) ++ sd->entity.name = sd->name; ++ sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV; ++#endif + } + EXPORT_SYMBOL(v4l2_subdev_init); ++ ++#if defined(CONFIG_MEDIA_CONTROLLER) ++int v4l2_subdev_set_power(struct media_entity *entity, int power) ++{ ++ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); ++ ++ dev_dbg(entity->parent->dev, ++ "%s power%s\n", entity->name, power ? "on" : "off"); ++ ++ return v4l2_subdev_call(sd, core, s_power, power); ++} ++EXPORT_SYMBOL_GPL(v4l2_subdev_set_power); ++#endif +diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h +index 68cbe48..7d55b0c 100644 +--- a/include/media/v4l2-subdev.h ++++ b/include/media/v4l2-subdev.h +@@ -21,6 +21,7 @@ + #ifndef _V4L2_SUBDEV_H + #define _V4L2_SUBDEV_H + ++#include + #include + #include + #include +@@ -437,6 +438,9 @@ struct v4l2_subdev_ops { + stand-alone or embedded in a larger struct. + */ + struct v4l2_subdev { ++#if defined(CONFIG_MEDIA_CONTROLLER) ++ struct media_entity entity; ++#endif + struct list_head list; + struct module *owner; + u32 flags; +@@ -458,6 +462,8 @@ struct v4l2_subdev { + unsigned int nevents; + }; + ++#define media_entity_to_v4l2_subdev(ent) \ ++ container_of(ent, struct v4l2_subdev, entity) + #define vdev_to_v4l2_subdev(vdev) \ + container_of(vdev, struct v4l2_subdev, devnode) + +@@ -486,6 +492,10 @@ static inline void *v4l2_get_subdev_hostdata(const struct v4l2_subdev *sd) + void v4l2_subdev_init(struct v4l2_subdev *sd, + const struct v4l2_subdev_ops *ops); + ++#if defined(CONFIG_MEDIA_CONTROLLER) ++int v4l2_subdev_set_power(struct media_entity *entity, int power); ++#endif ++ + /* Call an ops of a v4l2_subdev, doing the right checks against + NULL pointers. + +-- +1.6.6.1 + diff --git a/recipes/linux/linux-omap-2.6.37/media/0020-v4l-Move-the-media-v4l2-mediabus.h-header-to-include.patch b/recipes/linux/linux-omap-2.6.37/media/0020-v4l-Move-the-media-v4l2-mediabus.h-header-to-include.patch new file mode 100644 index 0000000000..302fe53261 --- /dev/null +++ b/recipes/linux/linux-omap-2.6.37/media/0020-v4l-Move-the-media-v4l2-mediabus.h-header-to-include.patch @@ -0,0 +1,205 @@ +From 0d2a2247733eca8f357f5a93fcc357edbb941ec1 Mon Sep 17 00:00:00 2001 +From: Laurent Pinchart +Date: Mon, 15 Mar 2010 23:33:31 +0100 +Subject: [PATCH 20/43] v4l: Move the media/v4l2-mediabus.h header to include/linux + +The header defines the v4l2_mbus_framefmt structure which will be used +by the V4L2 subdevs userspace API. + +Change the type of the v4l2_mbus_framefmt::code field to __u32, as enum +sizes can differ between different ABIs on the same architectures. + +Signed-off-by: Laurent Pinchart +--- + include/linux/Kbuild | 1 + + include/linux/v4l2-mediabus.h | 78 +++++++++++++++++++++++++++++++++++++++++ + include/media/soc_mediabus.h | 3 +- + include/media/v4l2-mediabus.h | 61 +------------------------------- + 4 files changed, 81 insertions(+), 62 deletions(-) + create mode 100644 include/linux/v4l2-mediabus.h + +diff --git a/include/linux/Kbuild b/include/linux/Kbuild +index 26e0a7f..796e1d8 100644 +--- a/include/linux/Kbuild ++++ b/include/linux/Kbuild +@@ -366,6 +366,7 @@ header-y += unistd.h + header-y += usbdevice_fs.h + header-y += utime.h + header-y += utsname.h ++header-y += v4l2-mediabus.h + header-y += veth.h + header-y += vhost.h + header-y += videodev.h +diff --git a/include/linux/v4l2-mediabus.h b/include/linux/v4l2-mediabus.h +new file mode 100644 +index 0000000..a62cd64 +--- /dev/null ++++ b/include/linux/v4l2-mediabus.h +@@ -0,0 +1,78 @@ ++/* ++ * Media Bus API header ++ * ++ * Copyright (C) 2009, Guennadi Liakhovetski ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#ifndef __LINUX_V4L2_MEDIABUS_H ++#define __LINUX_V4L2_MEDIABUS_H ++ ++#include ++#include ++ ++/* ++ * These pixel codes uniquely identify data formats on the media bus. Mostly ++ * they correspond to similarly named V4L2_PIX_FMT_* formats, format 0 is ++ * reserved, V4L2_MBUS_FMT_FIXED shall be used by host-client pairs, where the ++ * data format is fixed. Additionally, "2X8" means that one pixel is transferred ++ * in two 8-bit samples, "BE" or "LE" specify in which order those samples are ++ * transferred over the bus: "LE" means that the least significant bits are ++ * transferred first, "BE" means that the most significant bits are transferred ++ * first, and "PADHI" and "PADLO" define which bits - low or high, in the ++ * incomplete high byte, are filled with padding bits. ++ */ ++enum v4l2_mbus_pixelcode { ++ V4L2_MBUS_FMT_FIXED = 1, ++ V4L2_MBUS_FMT_YUYV8_2X8, ++ V4L2_MBUS_FMT_YVYU8_2X8, ++ V4L2_MBUS_FMT_UYVY8_2X8, ++ V4L2_MBUS_FMT_VYUY8_2X8, ++ V4L2_MBUS_FMT_YVYU10_2X10, ++ V4L2_MBUS_FMT_YUYV10_2X10, ++ V4L2_MBUS_FMT_YVYU10_1X20, ++ V4L2_MBUS_FMT_YUYV10_1X20, ++ V4L2_MBUS_FMT_RGB444_2X8_PADHI_LE, ++ V4L2_MBUS_FMT_RGB444_2X8_PADHI_BE, ++ V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE, ++ V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE, ++ V4L2_MBUS_FMT_RGB565_2X8_LE, ++ V4L2_MBUS_FMT_RGB565_2X8_BE, ++ V4L2_MBUS_FMT_BGR565_2X8_LE, ++ V4L2_MBUS_FMT_BGR565_2X8_BE, ++ V4L2_MBUS_FMT_SBGGR8_1X8, ++ V4L2_MBUS_FMT_SBGGR10_1X10, ++ V4L2_MBUS_FMT_GREY8_1X8, ++ V4L2_MBUS_FMT_Y10_1X10, ++ V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE, ++ V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_LE, ++ V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_BE, ++ V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_BE, ++ V4L2_MBUS_FMT_SGRBG8_1X8, ++ V4L2_MBUS_FMT_SBGGR12_1X12, ++ V4L2_MBUS_FMT_YUYV8_1_5X8, ++ V4L2_MBUS_FMT_YVYU8_1_5X8, ++ V4L2_MBUS_FMT_UYVY8_1_5X8, ++ V4L2_MBUS_FMT_VYUY8_1_5X8, ++}; ++ ++/** ++ * struct v4l2_mbus_framefmt - frame format on the media bus ++ * @width: frame width ++ * @height: frame height ++ * @code: data format code ++ * @field: used interlacing type ++ * @colorspace: colorspace of the data ++ */ ++struct v4l2_mbus_framefmt { ++ __u32 width; ++ __u32 height; ++ __u32 code; ++ enum v4l2_field field; ++ enum v4l2_colorspace colorspace; ++}; ++ ++#endif +diff --git a/include/media/soc_mediabus.h b/include/media/soc_mediabus.h +index 037cd7b..6243147 100644 +--- a/include/media/soc_mediabus.h ++++ b/include/media/soc_mediabus.h +@@ -12,8 +12,7 @@ + #define SOC_MEDIABUS_H + + #include +- +-#include ++#include + + /** + * enum soc_mbus_packing - data packing types on the media-bus +diff --git a/include/media/v4l2-mediabus.h b/include/media/v4l2-mediabus.h +index 8e65598..971c7fa 100644 +--- a/include/media/v4l2-mediabus.h ++++ b/include/media/v4l2-mediabus.h +@@ -11,66 +11,7 @@ + #ifndef V4L2_MEDIABUS_H + #define V4L2_MEDIABUS_H + +-/* +- * These pixel codes uniquely identify data formats on the media bus. Mostly +- * they correspond to similarly named V4L2_PIX_FMT_* formats, format 0 is +- * reserved, V4L2_MBUS_FMT_FIXED shall be used by host-client pairs, where the +- * data format is fixed. Additionally, "2X8" means that one pixel is transferred +- * in two 8-bit samples, "BE" or "LE" specify in which order those samples are +- * transferred over the bus: "LE" means that the least significant bits are +- * transferred first, "BE" means that the most significant bits are transferred +- * first, and "PADHI" and "PADLO" define which bits - low or high, in the +- * incomplete high byte, are filled with padding bits. +- */ +-enum v4l2_mbus_pixelcode { +- V4L2_MBUS_FMT_FIXED = 1, +- V4L2_MBUS_FMT_YUYV8_2X8, +- V4L2_MBUS_FMT_YVYU8_2X8, +- V4L2_MBUS_FMT_UYVY8_2X8, +- V4L2_MBUS_FMT_VYUY8_2X8, +- V4L2_MBUS_FMT_YVYU10_2X10, +- V4L2_MBUS_FMT_YUYV10_2X10, +- V4L2_MBUS_FMT_YVYU10_1X20, +- V4L2_MBUS_FMT_YUYV10_1X20, +- V4L2_MBUS_FMT_RGB444_2X8_PADHI_LE, +- V4L2_MBUS_FMT_RGB444_2X8_PADHI_BE, +- V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE, +- V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE, +- V4L2_MBUS_FMT_RGB565_2X8_LE, +- V4L2_MBUS_FMT_RGB565_2X8_BE, +- V4L2_MBUS_FMT_BGR565_2X8_LE, +- V4L2_MBUS_FMT_BGR565_2X8_BE, +- V4L2_MBUS_FMT_SBGGR8_1X8, +- V4L2_MBUS_FMT_SBGGR10_1X10, +- V4L2_MBUS_FMT_GREY8_1X8, +- V4L2_MBUS_FMT_Y10_1X10, +- V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE, +- V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_LE, +- V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_BE, +- V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_BE, +- V4L2_MBUS_FMT_SGRBG8_1X8, +- V4L2_MBUS_FMT_SBGGR12_1X12, +- V4L2_MBUS_FMT_YUYV8_1_5X8, +- V4L2_MBUS_FMT_YVYU8_1_5X8, +- V4L2_MBUS_FMT_UYVY8_1_5X8, +- V4L2_MBUS_FMT_VYUY8_1_5X8, +-}; +- +-/** +- * struct v4l2_mbus_framefmt - frame format on the media bus +- * @width: frame width +- * @height: frame height +- * @code: data format code +- * @field: used interlacing type +- * @colorspace: colorspace of the data +- */ +-struct v4l2_mbus_framefmt { +- __u32 width; +- __u32 height; +- enum v4l2_mbus_pixelcode code; +- enum v4l2_field field; +- enum v4l2_colorspace colorspace; +-}; ++#include + + static inline void v4l2_fill_pix_format(struct v4l2_pix_format *pix_fmt, + const struct v4l2_mbus_framefmt *mbus_fmt) +-- +1.6.6.1 + diff --git a/recipes/linux/linux-omap-2.6.37/media/0021-v4l-Replace-enums-with-fixed-sized-fields-in-public-.patch b/recipes/linux/linux-omap-2.6.37/media/0021-v4l-Replace-enums-with-fixed-sized-fields-in-public-.patch new file mode 100644 index 0000000000..e04f4e2add --- /dev/null +++ b/recipes/linux/linux-omap-2.6.37/media/0021-v4l-Replace-enums-with-fixed-sized-fields-in-public-.patch @@ -0,0 +1,50 @@ +From fb1156d3125e36952f884b09afb9d0815ddeafd7 Mon Sep 17 00:00:00 2001 +From: Laurent Pinchart +Date: Wed, 6 Oct 2010 08:30:26 +0200 +Subject: [PATCH 21/43] v4l: Replace enums with fixed-sized fields in public structure + +The v4l2_mbus_framefmt structure will be part of the public userspace +API and used (albeit indirectly) as an ioctl argument. As such, its size +must be fixed across userspace ABIs. + +Replace the v4l2_field and v4l2_colorspace enums by __u32 fields and add +padding for future enhancements. + +Signed-off-by: Laurent Pinchart +--- + include/linux/v4l2-mediabus.h | 17 +++++++++-------- + 1 files changed, 9 insertions(+), 8 deletions(-) + +diff --git a/include/linux/v4l2-mediabus.h b/include/linux/v4l2-mediabus.h +index a62cd64..feeb88c 100644 +--- a/include/linux/v4l2-mediabus.h ++++ b/include/linux/v4l2-mediabus.h +@@ -63,16 +63,17 @@ enum v4l2_mbus_pixelcode { + * struct v4l2_mbus_framefmt - frame format on the media bus + * @width: frame width + * @height: frame height +- * @code: data format code +- * @field: used interlacing type +- * @colorspace: colorspace of the data ++ * @code: data format code (from enum v4l2_mbus_pixelcode) ++ * @field: used interlacing type (from enum v4l2_field) ++ * @colorspace: colorspace of the data (from enum v4l2_colorspace) + */ + struct v4l2_mbus_framefmt { +- __u32 width; +- __u32 height; +- __u32 code; +- enum v4l2_field field; +- enum v4l2_colorspace colorspace; ++ __u32 width; ++ __u32 height; ++ __u32 code; ++ __u32 field; ++ __u32 colorspace; ++ __u32 reserved[7]; + }; + + #endif +-- +1.6.6.1 + diff --git a/recipes/linux/linux-omap-2.6.37/media/0022-v4l-Rename-V4L2_MBUS_FMT_GREY8_1X8-to-V4L2_MBUS_FMT_.patch b/recipes/linux/linux-omap-2.6.37/media/0022-v4l-Rename-V4L2_MBUS_FMT_GREY8_1X8-to-V4L2_MBUS_FMT_.patch new file mode 100644 index 0000000000..ffffd26a62 --- /dev/null +++ b/recipes/linux/linux-omap-2.6.37/media/0022-v4l-Rename-V4L2_MBUS_FMT_GREY8_1X8-to-V4L2_MBUS_FMT_.patch @@ -0,0 +1,154 @@ +From 0be9c8b998cef9ce650e1e53d12bb5a6d772d151 Mon Sep 17 00:00:00 2001 +From: Laurent Pinchart +Date: Tue, 28 Sep 2010 12:01:44 +0200 +Subject: [PATCH 22/43] v4l: Rename V4L2_MBUS_FMT_GREY8_1X8 to V4L2_MBUS_FMT_Y8_1X8 + +For consistency with the V4L2_MBUS_FMT_Y10_1X10 format. + +Signed-off-by: Laurent Pinchart +--- + drivers/media/video/mt9m001.c | 2 +- + drivers/media/video/mt9v022.c | 4 ++-- + drivers/media/video/ov6650.c | 10 +++++----- + drivers/media/video/sh_mobile_csi2.c | 6 +++--- + drivers/media/video/soc_mediabus.c | 2 +- + include/linux/v4l2-mediabus.h | 2 +- + 6 files changed, 13 insertions(+), 13 deletions(-) + +diff --git a/drivers/media/video/mt9m001.c b/drivers/media/video/mt9m001.c +index fcb4cd9..3aaedf6 100644 +--- a/drivers/media/video/mt9m001.c ++++ b/drivers/media/video/mt9m001.c +@@ -79,7 +79,7 @@ static const struct mt9m001_datafmt mt9m001_colour_fmts[] = { + static const struct mt9m001_datafmt mt9m001_monochrome_fmts[] = { + /* Order important - see above */ + {V4L2_MBUS_FMT_Y10_1X10, V4L2_COLORSPACE_JPEG}, +- {V4L2_MBUS_FMT_GREY8_1X8, V4L2_COLORSPACE_JPEG}, ++ {V4L2_MBUS_FMT_Y8_1X8, V4L2_COLORSPACE_JPEG}, + }; + + struct mt9m001 { +diff --git a/drivers/media/video/mt9v022.c b/drivers/media/video/mt9v022.c +index b96171c..56dd4fc 100644 +--- a/drivers/media/video/mt9v022.c ++++ b/drivers/media/video/mt9v022.c +@@ -95,7 +95,7 @@ static const struct mt9v022_datafmt mt9v022_colour_fmts[] = { + static const struct mt9v022_datafmt mt9v022_monochrome_fmts[] = { + /* Order important - see above */ + {V4L2_MBUS_FMT_Y10_1X10, V4L2_COLORSPACE_JPEG}, +- {V4L2_MBUS_FMT_GREY8_1X8, V4L2_COLORSPACE_JPEG}, ++ {V4L2_MBUS_FMT_Y8_1X8, V4L2_COLORSPACE_JPEG}, + }; + + struct mt9v022 { +@@ -392,7 +392,7 @@ static int mt9v022_s_fmt(struct v4l2_subdev *sd, + * icd->try_fmt(), datawidth is from our supported format list + */ + switch (mf->code) { +- case V4L2_MBUS_FMT_GREY8_1X8: ++ case V4L2_MBUS_FMT_Y8_1X8: + case V4L2_MBUS_FMT_Y10_1X10: + if (mt9v022->model != V4L2_IDENT_MT9V022IX7ATM) + return -EINVAL; +diff --git a/drivers/media/video/ov6650.c b/drivers/media/video/ov6650.c +index cf93de9..fe8e3eb 100644 +--- a/drivers/media/video/ov6650.c ++++ b/drivers/media/video/ov6650.c +@@ -207,7 +207,7 @@ static enum v4l2_mbus_pixelcode ov6650_codes[] = { + V4L2_MBUS_FMT_YVYU8_2X8, + V4L2_MBUS_FMT_VYUY8_2X8, + V4L2_MBUS_FMT_SBGGR8_1X8, +- V4L2_MBUS_FMT_GREY8_1X8, ++ V4L2_MBUS_FMT_Y8_1X8, + }; + + static const struct v4l2_queryctrl ov6650_controls[] = { +@@ -800,7 +800,7 @@ static int ov6650_s_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf) + + /* select color matrix configuration for given color encoding */ + switch (code) { +- case V4L2_MBUS_FMT_GREY8_1X8: ++ case V4L2_MBUS_FMT_Y8_1X8: + dev_dbg(&client->dev, "pixel format GREY8_1X8\n"); + coma_mask |= COMA_RGB | COMA_WORD_SWAP | COMA_BYTE_SWAP; + coma_set |= COMA_BW; +@@ -846,7 +846,7 @@ static int ov6650_s_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf) + } + priv->code = code; + +- if (code == V4L2_MBUS_FMT_GREY8_1X8 || ++ if (code == V4L2_MBUS_FMT_Y8_1X8 || + code == V4L2_MBUS_FMT_SBGGR8_1X8) { + coml_mask = COML_ONE_CHANNEL; + coml_set = 0; +@@ -936,8 +936,8 @@ static int ov6650_try_fmt(struct v4l2_subdev *sd, + + switch (mf->code) { + case V4L2_MBUS_FMT_Y10_1X10: +- mf->code = V4L2_MBUS_FMT_GREY8_1X8; +- case V4L2_MBUS_FMT_GREY8_1X8: ++ mf->code = V4L2_MBUS_FMT_Y8_1X8; ++ case V4L2_MBUS_FMT_Y8_1X8: + case V4L2_MBUS_FMT_YVYU8_2X8: + case V4L2_MBUS_FMT_YUYV8_2X8: + case V4L2_MBUS_FMT_VYUY8_2X8: +diff --git a/drivers/media/video/sh_mobile_csi2.c b/drivers/media/video/sh_mobile_csi2.c +index 84a6468..dd1b81b 100644 +--- a/drivers/media/video/sh_mobile_csi2.c ++++ b/drivers/media/video/sh_mobile_csi2.c +@@ -56,7 +56,7 @@ static int sh_csi2_try_fmt(struct v4l2_subdev *sd, + switch (mf->code) { + case V4L2_MBUS_FMT_UYVY8_2X8: /* YUV422 */ + case V4L2_MBUS_FMT_YUYV8_1_5X8: /* YUV420 */ +- case V4L2_MBUS_FMT_GREY8_1X8: /* RAW8 */ ++ case V4L2_MBUS_FMT_Y8_1X8: /* RAW8 */ + case V4L2_MBUS_FMT_SBGGR8_1X8: + case V4L2_MBUS_FMT_SGRBG8_1X8: + break; +@@ -67,7 +67,7 @@ static int sh_csi2_try_fmt(struct v4l2_subdev *sd, + break; + case SH_CSI2I: + switch (mf->code) { +- case V4L2_MBUS_FMT_GREY8_1X8: /* RAW8 */ ++ case V4L2_MBUS_FMT_Y8_1X8: /* RAW8 */ + case V4L2_MBUS_FMT_SBGGR8_1X8: + case V4L2_MBUS_FMT_SGRBG8_1X8: + case V4L2_MBUS_FMT_SBGGR10_1X10: /* RAW10 */ +@@ -111,7 +111,7 @@ static int sh_csi2_s_fmt(struct v4l2_subdev *sd, + case V4L2_MBUS_FMT_RGB565_2X8_BE: + tmp |= 0x22; /* RGB565 */ + break; +- case V4L2_MBUS_FMT_GREY8_1X8: ++ case V4L2_MBUS_FMT_Y8_1X8: + case V4L2_MBUS_FMT_SBGGR8_1X8: + case V4L2_MBUS_FMT_SGRBG8_1X8: + tmp |= 0x2a; /* RAW8 */ +diff --git a/drivers/media/video/soc_mediabus.c b/drivers/media/video/soc_mediabus.c +index 9139121..d9c297d 100644 +--- a/drivers/media/video/soc_mediabus.c ++++ b/drivers/media/video/soc_mediabus.c +@@ -88,7 +88,7 @@ static const struct soc_mbus_pixelfmt mbus_fmt[] = { + .packing = SOC_MBUS_PACKING_EXTEND16, + .order = SOC_MBUS_ORDER_LE, + }, +- [MBUS_IDX(GREY8_1X8)] = { ++ [MBUS_IDX(Y8_1X8)] = { + .fourcc = V4L2_PIX_FMT_GREY, + .name = "Grey", + .bits_per_sample = 8, +diff --git a/include/linux/v4l2-mediabus.h b/include/linux/v4l2-mediabus.h +index feeb88c..dc1d5c0 100644 +--- a/include/linux/v4l2-mediabus.h ++++ b/include/linux/v4l2-mediabus.h +@@ -45,7 +45,7 @@ enum v4l2_mbus_pixelcode { + V4L2_MBUS_FMT_BGR565_2X8_BE, + V4L2_MBUS_FMT_SBGGR8_1X8, + V4L2_MBUS_FMT_SBGGR10_1X10, +- V4L2_MBUS_FMT_GREY8_1X8, ++ V4L2_MBUS_FMT_Y8_1X8, + V4L2_MBUS_FMT_Y10_1X10, + V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE, + V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_LE, +-- +1.6.6.1 + diff --git a/recipes/linux/linux-omap-2.6.37/media/0023-v4l-Group-media-bus-pixel-codes-by-types-and-sort-th.patch b/recipes/linux/linux-omap-2.6.37/media/0023-v4l-Group-media-bus-pixel-codes-by-types-and-sort-th.patch new file mode 100644 index 0000000000..aee5444a9d --- /dev/null +++ b/recipes/linux/linux-omap-2.6.37/media/0023-v4l-Group-media-bus-pixel-codes-by-types-and-sort-th.patch @@ -0,0 +1,112 @@ +From 9a13751e47503b4c966538e194a5027e5e7d9c5d Mon Sep 17 00:00:00 2001 +From: Laurent Pinchart +Date: Wed, 1 Sep 2010 17:58:22 +0200 +Subject: [PATCH 23/43] v4l: Group media bus pixel codes by types and sort them alphabetically + +Adding new pixel codes at the end of the enumeration will soon create a +mess, so group the pixel codes by type and sort them by bus_width, bits +per component, samples per pixel and order of subsamples. + +As the codes are part of the kernel ABI their value can't change when a +new code is inserted in the enumeration, so they are given an explicit +numerical value. When inserting a new pixel code developers must use and +update the next free value. + +Signed-off-by: Laurent Pinchart +--- + include/linux/v4l2-mediabus.h | 77 ++++++++++++++++++++++++---------------- + 1 files changed, 46 insertions(+), 31 deletions(-) + +diff --git a/include/linux/v4l2-mediabus.h b/include/linux/v4l2-mediabus.h +index dc1d5c0..cccfa34 100644 +--- a/include/linux/v4l2-mediabus.h ++++ b/include/linux/v4l2-mediabus.h +@@ -24,39 +24,54 @@ + * transferred first, "BE" means that the most significant bits are transferred + * first, and "PADHI" and "PADLO" define which bits - low or high, in the + * incomplete high byte, are filled with padding bits. ++ * ++ * The pixel codes are grouped by type, bus_width, bits per component, samples ++ * per pixel and order of subsamples. Numerical values are sorted using generic ++ * numerical sort order (8 thus comes before 10). ++ * ++ * As their value can't change when a new pixel code is inserted in the ++ * enumeration, the pixel codes are explicitly given a numerical value. The next ++ * free values for each category are listed below, update them when inserting ++ * new pixel codes. + */ + enum v4l2_mbus_pixelcode { +- V4L2_MBUS_FMT_FIXED = 1, +- V4L2_MBUS_FMT_YUYV8_2X8, +- V4L2_MBUS_FMT_YVYU8_2X8, +- V4L2_MBUS_FMT_UYVY8_2X8, +- V4L2_MBUS_FMT_VYUY8_2X8, +- V4L2_MBUS_FMT_YVYU10_2X10, +- V4L2_MBUS_FMT_YUYV10_2X10, +- V4L2_MBUS_FMT_YVYU10_1X20, +- V4L2_MBUS_FMT_YUYV10_1X20, +- V4L2_MBUS_FMT_RGB444_2X8_PADHI_LE, +- V4L2_MBUS_FMT_RGB444_2X8_PADHI_BE, +- V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE, +- V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE, +- V4L2_MBUS_FMT_RGB565_2X8_LE, +- V4L2_MBUS_FMT_RGB565_2X8_BE, +- V4L2_MBUS_FMT_BGR565_2X8_LE, +- V4L2_MBUS_FMT_BGR565_2X8_BE, +- V4L2_MBUS_FMT_SBGGR8_1X8, +- V4L2_MBUS_FMT_SBGGR10_1X10, +- V4L2_MBUS_FMT_Y8_1X8, +- V4L2_MBUS_FMT_Y10_1X10, +- V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE, +- V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_LE, +- V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_BE, +- V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_BE, +- V4L2_MBUS_FMT_SGRBG8_1X8, +- V4L2_MBUS_FMT_SBGGR12_1X12, +- V4L2_MBUS_FMT_YUYV8_1_5X8, +- V4L2_MBUS_FMT_YVYU8_1_5X8, +- V4L2_MBUS_FMT_UYVY8_1_5X8, +- V4L2_MBUS_FMT_VYUY8_1_5X8, ++ V4L2_MBUS_FMT_FIXED = 0x0001, ++ ++ /* RGB - next is 0x1009 */ ++ V4L2_MBUS_FMT_RGB444_2X8_PADHI_BE = 0x1001, ++ V4L2_MBUS_FMT_RGB444_2X8_PADHI_LE = 0x1002, ++ V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE = 0x1003, ++ V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE = 0x1004, ++ V4L2_MBUS_FMT_BGR565_2X8_BE = 0x1005, ++ V4L2_MBUS_FMT_BGR565_2X8_LE = 0x1006, ++ V4L2_MBUS_FMT_RGB565_2X8_BE = 0x1007, ++ V4L2_MBUS_FMT_RGB565_2X8_LE = 0x1008, ++ ++ /* YUV (including grey) - next is 0x200f */ ++ V4L2_MBUS_FMT_Y8_1X8 = 0x2001, ++ V4L2_MBUS_FMT_UYVY8_1_5X8 = 0x2002, ++ V4L2_MBUS_FMT_VYUY8_1_5X8 = 0x2003, ++ V4L2_MBUS_FMT_YUYV8_1_5X8 = 0x2004, ++ V4L2_MBUS_FMT_YVYU8_1_5X8 = 0x2005, ++ V4L2_MBUS_FMT_UYVY8_2X8 = 0x2006, ++ V4L2_MBUS_FMT_VYUY8_2X8 = 0x2007, ++ V4L2_MBUS_FMT_YUYV8_2X8 = 0x2008, ++ V4L2_MBUS_FMT_YVYU8_2X8 = 0x2009, ++ V4L2_MBUS_FMT_Y10_1X10 = 0x200a, ++ V4L2_MBUS_FMT_YUYV10_2X10 = 0x200b, ++ V4L2_MBUS_FMT_YVYU10_2X10 = 0x200c, ++ V4L2_MBUS_FMT_YUYV10_1X20 = 0x200d, ++ V4L2_MBUS_FMT_YVYU10_1X20 = 0x200e, ++ ++ /* Bayer - next is 0x3009 */ ++ V4L2_MBUS_FMT_SBGGR8_1X8 = 0x3001, ++ V4L2_MBUS_FMT_SGRBG8_1X8 = 0x3002, ++ V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_BE = 0x3003, ++ V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE = 0x3004, ++ V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_BE = 0x3005, ++ V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_LE = 0x3006, ++ V4L2_MBUS_FMT_SBGGR10_1X10 = 0x3007, ++ V4L2_MBUS_FMT_SBGGR12_1X12 = 0x3008, + }; + + /** +-- +1.6.6.1 + diff --git a/recipes/linux/linux-omap-2.6.37/media/0024-v4l-Create-v4l2-subdev-file-handle-structure.patch b/recipes/linux/linux-omap-2.6.37/media/0024-v4l-Create-v4l2-subdev-file-handle-structure.patch new file mode 100644 index 0000000000..726af5d0b6 --- /dev/null +++ b/recipes/linux/linux-omap-2.6.37/media/0024-v4l-Create-v4l2-subdev-file-handle-structure.patch @@ -0,0 +1,243 @@ +From 47f7677adda05f6d85a35047c4aac940c46a123c Mon Sep 17 00:00:00 2001 +From: Stanimir Varbanov +Date: Fri, 21 May 2010 12:04:24 +0300 +Subject: [PATCH 24/43] v4l: Create v4l2 subdev file handle structure + +Used for storing subdev information per file handle and hold V4L2 file +handle. + +Signed-off-by: Stanimir Varbanov +Signed-off-by: Antti Koskipaa +Signed-off-by: Laurent Pinchart +--- + drivers/media/Kconfig | 9 ++++ + drivers/media/video/v4l2-subdev.c | 85 +++++++++++++++++++++++++------------ + include/media/v4l2-subdev.h | 29 +++++++++++++ + 3 files changed, 96 insertions(+), 27 deletions(-) + +diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig +index 6b946e6..eaf4734 100644 +--- a/drivers/media/Kconfig ++++ b/drivers/media/Kconfig +@@ -82,6 +82,15 @@ config VIDEO_V4L1_COMPAT + + If you are unsure as to whether this is required, answer Y. + ++config VIDEO_V4L2_SUBDEV_API ++ bool "V4L2 sub-device userspace API (EXPERIMENTAL)" ++ depends on VIDEO_DEV && MEDIA_CONTROLLER && EXPERIMENTAL ++ ---help--- ++ Enables the V4L2 sub-device pad-level userspace API used to configure ++ video format, size and frame rate between hardware blocks. ++ ++ This API is mostly used by camera interfaces in embedded platforms. ++ + # + # DVB Core + # +diff --git a/drivers/media/video/v4l2-subdev.c b/drivers/media/video/v4l2-subdev.c +index a49856a..15449fc 100644 +--- a/drivers/media/video/v4l2-subdev.c ++++ b/drivers/media/video/v4l2-subdev.c +@@ -31,39 +31,69 @@ + #include + #include + ++static int subdev_fh_init(struct v4l2_subdev_fh *fh, struct v4l2_subdev *sd) ++{ ++#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) ++ /* Allocate try format and crop in the same memory block */ ++ fh->try_fmt = kzalloc((sizeof(*fh->try_fmt) + sizeof(*fh->try_crop)) ++ * sd->entity.num_pads, GFP_KERNEL); ++ if (fh->try_fmt == NULL) ++ return -ENOMEM; ++ ++ fh->try_crop = (struct v4l2_rect *) ++ (fh->try_fmt + sd->entity.num_pads); ++#endif ++ return 0; ++} ++ ++static void subdev_fh_free(struct v4l2_subdev_fh *fh) ++{ ++#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) ++ kfree(fh->try_fmt); ++ fh->try_fmt = NULL; ++ fh->try_crop = NULL; ++#endif ++} ++ + static int subdev_open(struct file *file) + { + struct video_device *vdev = video_devdata(file); + struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev); ++ struct v4l2_subdev_fh *subdev_fh; + #if defined(CONFIG_MEDIA_CONTROLLER) + struct media_entity *entity; + #endif +- struct v4l2_fh *vfh = NULL; + int ret; + + if (!sd->initialized) + return -EAGAIN; + +- if (sd->flags & V4L2_SUBDEV_FL_HAS_EVENTS) { +- vfh = kzalloc(sizeof(*vfh), GFP_KERNEL); +- if (vfh == NULL) +- return -ENOMEM; ++ subdev_fh = kzalloc(sizeof(*subdev_fh), GFP_KERNEL); ++ if (subdev_fh == NULL) ++ return -ENOMEM; + +- ret = v4l2_fh_init(vfh, vdev); +- if (ret) +- goto err; ++ ret = subdev_fh_init(subdev_fh, sd); ++ if (ret) { ++ kfree(subdev_fh); ++ return ret; ++ } ++ ++ ret = v4l2_fh_init(&subdev_fh->vfh, vdev); ++ if (ret) ++ goto err; + +- ret = v4l2_event_init(vfh); ++ if (sd->flags & V4L2_SUBDEV_FL_HAS_EVENTS) { ++ ret = v4l2_event_init(&subdev_fh->vfh); + if (ret) + goto err; + +- ret = v4l2_event_alloc(vfh, sd->nevents); ++ ret = v4l2_event_alloc(&subdev_fh->vfh, sd->nevents); + if (ret) + goto err; +- +- v4l2_fh_add(vfh); +- file->private_data = vfh; + } ++ ++ v4l2_fh_add(&subdev_fh->vfh); ++ file->private_data = &subdev_fh->vfh; + #if defined(CONFIG_MEDIA_CONTROLLER) + if (sd->v4l2_dev->mdev) { + entity = media_entity_get(&sd->entity); +@@ -73,14 +103,14 @@ static int subdev_open(struct file *file) + } + } + #endif ++ + return 0; + + err: +- if (vfh != NULL) { +- v4l2_fh_del(vfh); +- v4l2_fh_exit(vfh); +- kfree(vfh); +- } ++ v4l2_fh_del(&subdev_fh->vfh); ++ v4l2_fh_exit(&subdev_fh->vfh); ++ subdev_fh_free(subdev_fh); ++ kfree(subdev_fh); + + return ret; + } +@@ -92,16 +122,17 @@ static int subdev_close(struct file *file) + struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev); + #endif + struct v4l2_fh *vfh = file->private_data; ++ struct v4l2_subdev_fh *subdev_fh = to_v4l2_subdev_fh(vfh); + + #if defined(CONFIG_MEDIA_CONTROLLER) + if (sd->v4l2_dev->mdev) + media_entity_put(&sd->entity); + #endif +- if (vfh != NULL) { +- v4l2_fh_del(vfh); +- v4l2_fh_exit(vfh); +- kfree(vfh); +- } ++ v4l2_fh_del(vfh); ++ v4l2_fh_exit(vfh); ++ subdev_fh_free(subdev_fh); ++ kfree(subdev_fh); ++ file->private_data = NULL; + + return 0; + } +@@ -110,7 +141,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) + { + struct video_device *vdev = video_devdata(file); + struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev); +- struct v4l2_fh *fh = file->private_data; ++ struct v4l2_fh *vfh = file->private_data; + + switch (cmd) { + case VIDIOC_QUERYCTRL: +@@ -138,13 +169,13 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) + if (!(sd->flags & V4L2_SUBDEV_FL_HAS_EVENTS)) + return -ENOIOCTLCMD; + +- return v4l2_event_dequeue(fh, arg, file->f_flags & O_NONBLOCK); ++ return v4l2_event_dequeue(vfh, arg, file->f_flags & O_NONBLOCK); + + case VIDIOC_SUBSCRIBE_EVENT: +- return v4l2_subdev_call(sd, core, subscribe_event, fh, arg); ++ return v4l2_subdev_call(sd, core, subscribe_event, vfh, arg); + + case VIDIOC_UNSUBSCRIBE_EVENT: +- return v4l2_subdev_call(sd, core, unsubscribe_event, fh, arg); ++ return v4l2_subdev_call(sd, core, unsubscribe_event, vfh, arg); + + default: + return -ENOIOCTLCMD; +diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h +index 7d55b0c..f8704ff 100644 +--- a/include/media/v4l2-subdev.h ++++ b/include/media/v4l2-subdev.h +@@ -24,6 +24,7 @@ + #include + #include + #include ++#include + #include + + /* generic v4l2_device notify callback notification values */ +@@ -467,6 +468,34 @@ struct v4l2_subdev { + #define vdev_to_v4l2_subdev(vdev) \ + container_of(vdev, struct v4l2_subdev, devnode) + ++/* ++ * Used for storing subdev information per file handle ++ */ ++struct v4l2_subdev_fh { ++ struct v4l2_fh vfh; ++#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) ++ struct v4l2_mbus_framefmt *try_fmt; ++ struct v4l2_rect *try_crop; ++#endif ++}; ++ ++#define to_v4l2_subdev_fh(fh) \ ++ container_of(fh, struct v4l2_subdev_fh, vfh) ++ ++#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) ++static inline struct v4l2_mbus_framefmt * ++v4l2_subdev_get_try_format(struct v4l2_subdev_fh *fh, unsigned int pad) ++{ ++ return &fh->try_fmt[pad]; ++} ++ ++static inline struct v4l2_rect * ++v4l2_subdev_get_try_crop(struct v4l2_subdev_fh *fh, unsigned int pad) ++{ ++ return &fh->try_crop[pad]; ++} ++#endif ++ + extern const struct v4l2_file_operations v4l2_subdev_fops; + + static inline void v4l2_set_subdevdata(struct v4l2_subdev *sd, void *p) +-- +1.6.6.1 + diff --git a/recipes/linux/linux-omap-2.6.37/media/0025-v4l-subdev-Add-a-new-file-operations-class.patch b/recipes/linux/linux-omap-2.6.37/media/0025-v4l-subdev-Add-a-new-file-operations-class.patch new file mode 100644 index 0000000000..cf631c8887 --- /dev/null +++ b/recipes/linux/linux-omap-2.6.37/media/0025-v4l-subdev-Add-a-new-file-operations-class.patch @@ -0,0 +1,93 @@ +From 4dc43ce10d8b66537a680635d4f2dbe0a1daa1d9 Mon Sep 17 00:00:00 2001 +From: Laurent Pinchart +Date: Mon, 2 Aug 2010 00:05:09 +0200 +Subject: [PATCH 25/43] v4l: subdev: Add a new file operations class + +V4L2 sub-devices store pad formats and crop settings in the file handle. +To let drivers initialize those settings properly, add a file::open +operation that is called when the subdev is opened as well as a +corresponding file::close operation. + +Signed-off-by: Laurent Pinchart +--- + drivers/media/video/v4l2-subdev.c | 13 ++++++++++--- + include/media/v4l2-subdev.h | 10 ++++++++++ + 2 files changed, 20 insertions(+), 3 deletions(-) + +diff --git a/drivers/media/video/v4l2-subdev.c b/drivers/media/video/v4l2-subdev.c +index 15449fc..0f904e2 100644 +--- a/drivers/media/video/v4l2-subdev.c ++++ b/drivers/media/video/v4l2-subdev.c +@@ -61,7 +61,7 @@ static int subdev_open(struct file *file) + struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev); + struct v4l2_subdev_fh *subdev_fh; + #if defined(CONFIG_MEDIA_CONTROLLER) +- struct media_entity *entity; ++ struct media_entity *entity = NULL; + #endif + int ret; + +@@ -104,9 +104,17 @@ static int subdev_open(struct file *file) + } + #endif + ++ ret = v4l2_subdev_call(sd, file, open, subdev_fh); ++ if (ret < 0 && ret != -ENOIOCTLCMD) ++ goto err; ++ + return 0; + + err: ++#if defined(CONFIG_MEDIA_CONTROLLER) ++ if (entity) ++ media_entity_put(entity); ++#endif + v4l2_fh_del(&subdev_fh->vfh); + v4l2_fh_exit(&subdev_fh->vfh); + subdev_fh_free(subdev_fh); +@@ -117,13 +125,12 @@ err: + + static int subdev_close(struct file *file) + { +-#if defined(CONFIG_MEDIA_CONTROLLER) + struct video_device *vdev = video_devdata(file); + struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev); +-#endif + struct v4l2_fh *vfh = file->private_data; + struct v4l2_subdev_fh *subdev_fh = to_v4l2_subdev_fh(vfh); + ++ v4l2_subdev_call(sd, file, close, subdev_fh); + #if defined(CONFIG_MEDIA_CONTROLLER) + if (sd->v4l2_dev->mdev) + media_entity_put(&sd->entity); +diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h +index f8704ff..af704df 100644 +--- a/include/media/v4l2-subdev.h ++++ b/include/media/v4l2-subdev.h +@@ -175,6 +175,15 @@ struct v4l2_subdev_core_ops { + struct v4l2_event_subscription *sub); + }; + ++/* open: called when the subdev device node is opened by an application. ++ ++ close: called when the subdev device node is close. ++ */ ++struct v4l2_subdev_file_ops { ++ int (*open)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh); ++ int (*close)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh); ++}; ++ + /* s_mode: switch the tuner to a specific tuner mode. Replacement of s_radio. + + s_radio: v4l device was opened in Radio mode, to be replaced by s_mode. +@@ -416,6 +425,7 @@ struct v4l2_subdev_ir_ops { + + struct v4l2_subdev_ops { + const struct v4l2_subdev_core_ops *core; ++ const struct v4l2_subdev_file_ops *file; + const struct v4l2_subdev_tuner_ops *tuner; + const struct v4l2_subdev_audio_ops *audio; + const struct v4l2_subdev_video_ops *video; +-- +1.6.6.1 + diff --git a/recipes/linux/linux-omap-2.6.37/media/0026-v4l-v4l2_subdev-pad-level-operations.patch b/recipes/linux/linux-omap-2.6.37/media/0026-v4l-v4l2_subdev-pad-level-operations.patch new file mode 100644 index 0000000000..6376245189 --- /dev/null +++ b/recipes/linux/linux-omap-2.6.37/media/0026-v4l-v4l2_subdev-pad-level-operations.patch @@ -0,0 +1,49 @@ +From 7a089b741d5c2ca3881d61e81971a1a0e464aa27 Mon Sep 17 00:00:00 2001 +From: Laurent Pinchart +Date: Wed, 9 Dec 2009 12:39:52 +0100 +Subject: [PATCH 26/43] v4l: v4l2_subdev pad-level operations + +Add a v4l2_subdev_pad_ops structure for the operations that need to be +performed at the pad level such as format-related operations. + +Pad format-related operations use v4l2_mbus_framefmt instead of +v4l2_format. + +Signed-off-by: Laurent Pinchart +--- + include/media/v4l2-subdev.h | 5 +++++ + 1 files changed, 5 insertions(+), 0 deletions(-) + +diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h +index af704df..4f6ddba 100644 +--- a/include/media/v4l2-subdev.h ++++ b/include/media/v4l2-subdev.h +@@ -42,6 +42,7 @@ struct v4l2_ctrl_handler; + struct v4l2_event_subscription; + struct v4l2_fh; + struct v4l2_subdev; ++struct v4l2_subdev_fh; + struct tuner_setup; + + /* decode_vbi_line */ +@@ -423,6 +424,9 @@ struct v4l2_subdev_ir_ops { + struct v4l2_subdev_ir_parameters *params); + }; + ++struct v4l2_subdev_pad_ops { ++}; ++ + struct v4l2_subdev_ops { + const struct v4l2_subdev_core_ops *core; + const struct v4l2_subdev_file_ops *file; +@@ -432,6 +436,7 @@ struct v4l2_subdev_ops { + const struct v4l2_subdev_vbi_ops *vbi; + const struct v4l2_subdev_ir_ops *ir; + const struct v4l2_subdev_sensor_ops *sensor; ++ const struct v4l2_subdev_pad_ops *pad; + }; + + #define V4L2_SUBDEV_NAME_SIZE 32 +-- +1.6.6.1 + diff --git a/recipes/linux/linux-omap-2.6.37/media/0028-v4l-v4l2_subdev-userspace-format-API.patch b/recipes/linux/linux-omap-2.6.37/media/0028-v4l-v4l2_subdev-userspace-format-API.patch new file mode 100644 index 0000000000..2b851d6f8f --- /dev/null +++ b/recipes/linux/linux-omap-2.6.37/media/0028-v4l-v4l2_subdev-userspace-format-API.patch @@ -0,0 +1,3546 @@ +From 58fa3ca8af541e6704ac11703fc3091d856e0700 Mon Sep 17 00:00:00 2001 +From: Laurent Pinchart +Date: Tue, 16 Mar 2010 00:26:04 +0100 +Subject: [PATCH 28/43] v4l: v4l2_subdev userspace format API + +Add a userspace API to get, set and enumerate the media format on a +subdev pad. + +The format at the output of a subdev usually depends on the format at +its input(s). The try format operation is thus not suitable for probing +format at individual pads, as it can't modify the device state and thus +can't remember the format tried at the input to compute the output +format. + +To fix the problem, pass an extra argument to the get/set format +operations to select the 'try' or 'active' format. + +The try format is used when probing the subdev. Setting the try format +must not change the device configuration but can store data for later +reuse. Data storage is provided at the file-handle level so applications +probing the subdev concurently won't interfere with each other. + +The active format is used when configuring the subdev. It's identical to +the format handled by the usual get/set operations. + +Signed-off-by: Laurent Pinchart +Signed-off-by: Stanimir Varbanov +Signed-off-by: Sakari Ailus +--- + Documentation/DocBook/Makefile | 5 +- + Documentation/DocBook/media-entities.tmpl | 16 + + Documentation/DocBook/v4l/dev-subdev.xml | 274 +++ + Documentation/DocBook/v4l/subdev-formats.xml | 2416 ++++++++++++++++++++ + Documentation/DocBook/v4l/v4l2.xml | 4 + + Documentation/DocBook/v4l/vidioc-streamon.xml | 9 + + .../DocBook/v4l/vidioc-subdev-enum-frame-size.xml | 148 ++ + .../DocBook/v4l/vidioc-subdev-enum-mbus-code.xml | 113 + + Documentation/DocBook/v4l/vidioc-subdev-g-fmt.xml | 174 ++ + drivers/media/video/v4l2-subdev.c | 49 + + include/linux/Kbuild | 1 + + include/linux/v4l2-subdev.h | 90 + + include/media/v4l2-subdev.h | 10 + + 13 files changed, 3308 insertions(+), 1 deletions(-) + create mode 100644 Documentation/DocBook/v4l/dev-subdev.xml + create mode 100644 Documentation/DocBook/v4l/subdev-formats.xml + create mode 100644 Documentation/DocBook/v4l/vidioc-subdev-enum-frame-size.xml + create mode 100644 Documentation/DocBook/v4l/vidioc-subdev-enum-mbus-code.xml + create mode 100644 Documentation/DocBook/v4l/vidioc-subdev-g-fmt.xml + create mode 100644 include/linux/v4l2-subdev.h + +diff --git a/Documentation/DocBook/Makefile b/Documentation/DocBook/Makefile +index 8b6e00a..2deb069 100644 +--- a/Documentation/DocBook/Makefile ++++ b/Documentation/DocBook/Makefile +@@ -53,7 +53,10 @@ MAN := $(patsubst %.xml, %.9, $(BOOKS)) + mandocs: $(MAN) + + build_images = mkdir -p $(objtree)/Documentation/DocBook/media/ && \ +- cp $(srctree)/Documentation/DocBook/dvb/*.png $(srctree)/Documentation/DocBook/v4l/*.gif $(objtree)/Documentation/DocBook/media/ ++ cp $(srctree)/Documentation/DocBook/dvb/*.png \ ++ $(srctree)/Documentation/DocBook/v4l/*.gif \ ++ $(srctree)/Documentation/DocBook/v4l/*.png \ ++ $(objtree)/Documentation/DocBook/media/ + + xmldoclinks: + ifneq ($(objtree),$(srctree)) +diff --git a/Documentation/DocBook/media-entities.tmpl b/Documentation/DocBook/media-entities.tmpl +index 679c585..538f8fe 100644 +--- a/Documentation/DocBook/media-entities.tmpl ++++ b/Documentation/DocBook/media-entities.tmpl +@@ -86,6 +86,10 @@ + VIDIOC_S_PRIORITY"> + VIDIOC_S_STD"> + VIDIOC_S_TUNER"> ++VIDIOC_SUBDEV_ENUM_FRAME_SIZE"> ++VIDIOC_SUBDEV_ENUM_MBUS_CODE"> ++VIDIOC_SUBDEV_G_FMT"> ++VIDIOC_SUBDEV_S_FMT"> + VIDIOC_TRY_ENCODER_CMD"> + VIDIOC_TRY_EXT_CTRLS"> + VIDIOC_TRY_FMT"> +@@ -107,6 +111,7 @@ + v4l2_field"> + v4l2_frmivaltypes"> + v4l2_frmsizetypes"> ++v4l2_mbus_pixelcode"> + v4l2_memory"> + v4l2_mpeg_audio_ac3_bitrate"> + v4l2_mpeg_audio_crc"> +@@ -130,6 +135,7 @@ + v4l2_mpeg_video_encoding"> + v4l2_power_line_frequency"> + v4l2_priority"> ++v4l2_subdev_format_whence"> + v4l2_tuner_type"> + v4l2_preemphasis"> + +@@ -171,6 +177,7 @@ + v4l2_hw_freq_seek"> + v4l2_input"> + v4l2_jpegcompression"> ++v4l2_mbus_framefmt"> + v4l2_modulator"> + v4l2_mpeg_vbi_fmt_ivtv"> + v4l2_output"> +@@ -183,6 +190,9 @@ + v4l2_sliced_vbi_cap"> + v4l2_sliced_vbi_data"> + v4l2_sliced_vbi_format"> ++v4l2_subdev_frame_size_enum"> ++v4l2_subdev_format"> ++v4l2_subdev_mbus_code_enum"> + v4l2_standard"> + v4l2_streamparm"> + v4l2_timecode"> +@@ -212,6 +222,7 @@ + ENXIO error code"> + EMFILE error code"> + EPERM error code"> ++EPIPE error code"> + ERANGE error code"> + + +@@ -230,6 +241,7 @@ + + + ++ + + + +@@ -313,6 +325,10 @@ + + + ++ ++ ++ ++ + + + +diff --git a/Documentation/DocBook/v4l/dev-subdev.xml b/Documentation/DocBook/v4l/dev-subdev.xml +new file mode 100644 +index 0000000..12fdca4 +--- /dev/null ++++ b/Documentation/DocBook/v4l/dev-subdev.xml +@@ -0,0 +1,274 @@ ++ Sub-device Interface ++ ++ The complex nature of V4L2 devices, where hardware is often made of ++ several integrated circuits that need to interact with each other in a ++ controlled way, leads to complex V4L2 drivers. The drivers usually reflect ++ the hardware model in software, and model the different hardware components ++ as software blocks called sub-devices. ++ ++ V4L2 sub-devices are usually kernel-only objects. If the V4L2 driver ++ implements the media device API, they will automatically inherit from media ++ entities. Applications will be able to enumerate the sub-devices and discover ++ the hardware topology using the media entities, pads and links enumeration ++ API. ++ ++ In addition to make sub-devices discoverable, drivers can also choose ++ to make them directly configurable by applications. When both the sub-device ++ driver and the V4L2 device driver support this, sub-devices will feature a ++ character device node on which ioctls can be called to ++ ++ query, read and write sub-devices controls ++ subscribe and unsubscribe to events and retrieve them ++ negotiate image formats on individual pads ++ ++ ++ ++ Sub-device character device nodes, conventionally named ++ /dev/v4l-subdev*, use major number 81. ++ ++
++ Controls ++ Most V4L2 controls are implemented by sub-device hardware. Drivers ++ usually merge all controls and expose them through video device nodes. ++ Applications can control all sub-devices through a single interface. ++ ++ Complex devices sometimes implement the same control in different ++ pieces of hardware. This situation is common in embedded platforms, where ++ both sensors and image processing hardware implement identical functions, ++ such as contrast adjustment, white balance or faulty pixels correction. As ++ the V4L2 controls API doesn't support several identical controls in a single ++ device, all but one of the identical controls are hidden. ++ ++ Applications can access those hidden controls through the sub-device ++ node with the V4L2 control API described in . The ++ ioctls behave identically as when issued on V4L2 device nodes, with the ++ exception that they deal only with controls implemented in the sub-device. ++ ++ ++ Depending on the driver, those controls might also be exposed through ++ one (or several) V4L2 device nodes. ++
++ ++
++ Events ++ V4L2 sub-devices can notify applications of events as described in ++ . The API behaves identically as when used on V4L2 ++ device nodes, with the exception that it only deals with events generated by ++ the sub-device. Depending on the driver, those events might also be reported ++ on one (or several) V4L2 device nodes. ++
++ ++
++ Pad-level Formats ++ ++ Pad-level formats are only applicable to very complex device that ++ need to expose low-level format configuration to user space. Generic V4L2 ++ applications do not need to use the API described in ++ this section. ++ ++ For the purpose of this section, the term ++ format means the combination of media bus data ++ format, frame width and frame height. ++ ++ Image formats are typically negotiated on video capture and output ++ devices using the cropping and scaling ioctls. ++ The driver is responsible for configuring every block in the video pipeline ++ according to the requested format at the pipeline input and/or ++ output. ++ ++ For complex devices, such as often found in embedded systems, ++ identical image sizes at the output of a pipeline can be achieved using ++ different hardware configurations. One such exemple is shown on ++ , where ++ image scaling can be performed on both the video sensor and the host image ++ processing hardware. ++ ++
++ Image Format Negotation on Pipelines ++ ++ ++ ++ ++ ++ ++ ++ ++ High quality and high speed pipeline configuration ++ ++ ++
++ ++ The sensor scaler is usually of less quality than the host scaler, but ++ scaling on the sensor is required to achieve higher frame rates. Depending ++ on the use case (quality vs. speed), the pipeline must be configured ++ differently. Applications need to configure the formats at every point in ++ the pipeline explicitly. ++ ++ Drivers that implement the media ++ API can expose pad-level image format configuration to applications. ++ When they do, applications can use the &VIDIOC-SUBDEV-G-FMT; and ++ &VIDIOC-SUBDEV-S-FMT; ioctls. to negotiate formats on a per-pad basis. ++ ++ Applications are responsible for configuring coherent parameters on ++ the whole pipeline and making sure that connected pads have compatible ++ formats. The pipeline is checked for formats mismatch at &VIDIOC-STREAMON; ++ time, and an &EPIPE; is then returned if the configuration is ++ invalid. ++ ++ Pad-level image format configuration support can be tested by calling ++ the &VIDIOC-SUBDEV-G-FMT; ioctl on pad 0. If the driver returns an &EINVAL; ++ pad-level format configuration is not supported by the sub-device. ++ ++
++ Format Negotiation ++ ++ Acceptable formats on pads can (and usually do) depend on a number ++ of external parameters, such as formats on other pads, active links, or ++ even controls. Finding a combination of formats on all pads in a video ++ pipeline, acceptable to both application and driver, can't rely on formats ++ enumeration only. A format negotiation mechanism is required. ++ ++ Central to the format negotiation mechanism are the get/set format ++ operations. When called with the which argument ++ set to V4L2_SUBDEV_FORMAT_TRY, the ++ &VIDIOC-SUBDEV-G-FMT; and &VIDIOC-SUBDEV-S-FMT; ioctls operate on a set of ++ formats parameters that are not connected to the hardware configuration. ++ Modifying those 'try' formats leaves the device state untouched (this ++ applies to both the software state stored in the driver and the hardware ++ state stored in the device itself). ++ ++ While not kept as part of the device state, try formats are stored ++ in the sub-device file handles. A &VIDIOC-SUBDEV-G-FMT; call will return ++ the last try format set on the same sub-device file ++ handle. Several applications querying the same sub-device at ++ the same time will thus not interact with each other. ++ ++ To find out whether a particular format is supported by the device, ++ applications use the &VIDIOC-SUBDEV-S-FMT; ioctl. Drivers verify and, if ++ needed, change the requested format based on ++ device requirements and return the possibly modified value. Applications ++ can then choose to try a different format or accept the returned value and ++ continue. ++ ++ Formats returned by the driver during a negotiation iteration are ++ guaranteed to be supported by the device. In particular, drivers guarantee ++ that a returned format will not be further changed if passed to an ++ &VIDIOC-SUBDEV-S-FMT; call as-is (as long as external parameters, such as ++ formats on other pads or links' configuration are not changed). ++ ++ Drivers automatically propagate formats inside sub-devices. When a ++ try or active format is set on a pad, corresponding formats on other pads ++ of the same sub-device can be modified by the driver. Drivers are free to ++ modify formats as required by the device. However, they should comply with ++ the following rules when possible: ++ ++ Formats should be propagated from sink pads to source pads. ++ Modifying a format on a source pad should not modify the format on any ++ sink pad. ++ Sub-devices that scale frames using variable scaling factors ++ should reset the scale factors to default values when sink pads formats ++ are modified. If the 1:1 scaling ratio is supported, this means that ++ source pads formats should be reset to the sink pads formats. ++ ++ ++ ++ Formats are not propagated across links, as that would involve ++ propagating them from one sub-device file handle to another. Applications ++ must then take care to configure both ends of every link explicitly with ++ compatible formats. Identical formats on the two ends of a link are ++ guaranteed to be compatible. Drivers are free to accept different formats ++ matching device requirements as being compatible. ++ ++ ++ shows a sample configuration sequence for the pipeline described in ++ (table ++ columns list entity names and pad numbers). ++ ++ ++ Sample Pipeline Configuration ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ Sensor/0 ++ Frontend/0 ++ Frontend/1 ++ Scaler/0 ++ Scaler/1 ++ ++ ++ ++ ++ Initial state ++ 2048x1536 ++ - ++ - ++ - ++ - ++ ++ ++ Configure frontend input ++ 2048x1536 ++ 2048x1536 ++ 2046x1534 ++ - ++ - ++ ++ ++ Configure scaler input ++ 2048x1536 ++ 2048x1536 ++ 2046x1534 ++ 2046x1534 ++ 2046x1534 ++ ++ ++ Configure scaler output ++ 2048x1536 ++ 2048x1536 ++ 2046x1534 ++ 2046x1534 ++ 1280x960 ++ ++ ++ ++
++ ++ ++ ++ Initial state. The sensor output is set to its native 3MP ++ resolution. Resolutions on the host frontend and scaler input and output ++ pads are undefined. ++ The application configures the frontend input pad resolution to ++ 2048x1536. The driver propagates the format to the frontend output pad. ++ Note that the propagated output format can be different, as in this case, ++ than the input format, as the hardware might need to crop pixels (for ++ instance when converting a Bayer filter pattern to RGB or YUV). ++ The application configures the scaler input pad resolution to ++ 2046x1534 to match the frontend output resolution. The driver propagates ++ the format to the scaler output pad. ++ The application configures the scaler output pad resolution to ++ 1280x960. ++ ++ ++ ++ When satisfied with the try results, applications can set the active ++ formats by setting the which argument to ++ V4L2_SUBDEV_FORMAT_TRY. Active formats are changed ++ exactly as try formats by drivers. To avoid modifying the hardware state ++ during format negotiation, applications should negotiate try formats first ++ and then modify the active settings using the try formats returned during ++ the last negotiation iteration. This guarantees that the active format ++ will be applied as-is by the driver without being modified. ++ ++
++ ++
++ ++ &sub-subdev-formats; +diff --git a/Documentation/DocBook/v4l/subdev-formats.xml b/Documentation/DocBook/v4l/subdev-formats.xml +new file mode 100644 +index 0000000..0cae572 +--- /dev/null ++++ b/Documentation/DocBook/v4l/subdev-formats.xml +@@ -0,0 +1,2416 @@ ++
++ Media Bus Formats ++ ++ ++ struct <structname>v4l2_mbus_framefmt</structname> ++ ++ &cs-str; ++ ++ ++ __u32 ++ width ++ Image width, in pixels. ++ ++ ++ __u32 ++ height ++ Image height, in pixels. ++ ++ ++ __u32 ++ code ++ Format code, from &v4l2-mbus-pixelcode;. ++ ++ ++ __u32 ++ field ++ Field order, from &v4l2-field;. See ++ for details. ++ ++ ++ __u32 ++ colorspace ++ Image colorspace, from &v4l2-colorspace;. See ++ for details. ++ ++ ++ __u32 ++ reserved[7] ++ Reserved for future extensions. Applications and drivers must ++ set the array to zero. ++ ++ ++ ++
++ ++
++ Media Bus Pixel Codes ++ ++ The media bus pixel codes describe image formats as flowing over ++ physical busses (both between separate physical components and inside SoC ++ devices). This should not be confused with the V4L2 pixel formats that ++ describe, using four character codes, image formats as stored in memory. ++ ++ ++ While there is a relationship between image formats on busses and ++ image formats in memory (a raw Bayer image won't be magically converted to ++ JPEG just by storing it to memory), there is no one-to-one correspondance ++ between them. ++ ++
++ Packed RGB Formats ++ ++ Those formats transfer pixel data as red, green and blue components. ++ The format code is made of the following information. ++ ++ The red, green and blue components order code, as encoded in a ++ pixel sample. Possible values are RGB and BGR. ++ The number of bits per component, for each component. The values ++ can be different for all components. Common values are 555 and 565. ++ ++ The number of bus samples per pixel. Pixels that are wider than ++ the bus width must be transferred in multiple samples. Common values are ++ 1 and 2. ++ The bus width. ++ For formats where the total number of bits per pixel is smaller ++ than the number of bus samples per pixel times the bus width, a padding ++ value stating if the bytes are padded in their most high order bits ++ (PADHI) or low order bits (PADLO). ++ For formats where the number of bus samples per pixel is larger ++ than 1, an endianness value stating if the pixel is transferred MSB first ++ (BE) or LSB first (LE). ++ ++ ++ ++ For instance, a format where pixels are encoded as 5-bits red, 5-bits ++ green and 5-bit blue values padded on the high bit, transferred as 2 8-bit ++ samples per pixel with the most significant bits (padding, red and half of ++ the green value) transferred first will be named ++ V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE. ++ ++ ++ The following tables list existing packet RGB formats. ++ ++ ++ RGB formats ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ Identifier ++ Code ++ ++ Data organization ++ ++ ++ ++ ++ Bit ++ 7 ++ 6 ++ 5 ++ 4 ++ 3 ++ 2 ++ 1 ++ 0 ++ ++ ++ ++ ++ V4L2_MBUS_FMT_RGB444_2X8_PADHI_BE ++ 0x1001 ++ ++ 0 ++ 0 ++ 0 ++ 0 ++ r3 ++ r2 ++ r1 ++ r0 ++ ++ ++ ++ ++ ++ g3 ++ g2 ++ g1 ++ g0 ++ b3 ++ b2 ++ b1 ++ b0 ++ ++ ++ V4L2_MBUS_FMT_RGB444_2X8_PADHI_LE ++ 0x1002 ++ ++ g3 ++ g2 ++ g1 ++ g0 ++ b3 ++ b2 ++ b1 ++ b0 ++ ++ ++ ++ ++ ++ 0 ++ 0 ++ 0 ++ 0 ++ r3 ++ r2 ++ r1 ++ r0 ++ ++ ++ V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE ++ 0x1003 ++ ++ 0 ++ r4 ++ r3 ++ r2 ++ r1 ++ r0 ++ g4 ++ g3 ++ ++ ++ ++ ++ ++ g2 ++ g1 ++ g0 ++ b4 ++ b3 ++ b2 ++ b1 ++ b0 ++ ++ ++ V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE ++ 0x1004 ++ ++ g2 ++ g1 ++ g0 ++ b4 ++ b3 ++ b2 ++ b1 ++ b0 ++ ++ ++ ++ ++ ++ 0 ++ r4 ++ r3 ++ r2 ++ r1 ++ r0 ++ g4 ++ g3 ++ ++ ++ V4L2_MBUS_FMT_BGR565_2X8_BE ++ 0x1005 ++ ++ b4 ++ b3 ++ b2 ++ b1 ++ b0 ++ g5 ++ g4 ++ g3 ++ ++ ++ ++ ++ ++ g2 ++ g1 ++ g0 ++ r4 ++ r3 ++ r2 ++ r1 ++ r0 ++ ++ ++ V4L2_MBUS_FMT_BGR565_2X8_LE ++ 0x1006 ++ ++ g2 ++ g1 ++ g0 ++ r4 ++ r3 ++ r2 ++ r1 ++ r0 ++ ++ ++ ++ ++ ++ b4 ++ b3 ++ b2 ++ b1 ++ b0 ++ g5 ++ g4 ++ g3 ++ ++ ++ V4L2_MBUS_FMT_RGB565_2X8_BE ++ 0x1007 ++ ++ r4 ++ r3 ++ r2 ++ r1 ++ r0 ++ g5 ++ g4 ++ g3 ++ ++ ++ ++ ++ ++ g2 ++ g1 ++ g0 ++ b4 ++ b3 ++ b2 ++ b1 ++ b0 ++ ++ ++ V4L2_MBUS_FMT_RGB565_2X8_LE ++ 0x1008 ++ ++ g2 ++ g1 ++ g0 ++ b4 ++ b3 ++ b2 ++ b1 ++ b0 ++ ++ ++ ++ ++ ++ r4 ++ r3 ++ r2 ++ r1 ++ r0 ++ g5 ++ g4 ++ g3 ++ ++ ++ ++
++
++ ++
++ Bayer Formats ++ ++ Those formats transfer pixel data as red, green and blue components. ++ The format code is made of the following information. ++ ++ The red, green and blue components order code, as encoded in a ++ pixel sample. The possible values are shown in . ++ The number of bits per pixel component. All components are ++ transferred on the same number of bits. Common values are 8, 10 and 12. ++ ++ If the pixel components are DPCM-compressed, a mention of the ++ DPCM compression and the number of bits per compressed pixel component. ++ ++ The number of bus samples per pixel. Pixels that are wider than ++ the bus width must be transferred in multiple samples. Common values are ++ 1 and 2. ++ The bus width. ++ For formats where the total number of bits per pixel is smaller ++ than the number of bus samples per pixel times the bus width, a padding ++ value stating if the bytes are padded in their most high order bits ++ (PADHI) or low order bits (PADLO). ++ For formats where the number of bus samples per pixel is larger ++ than 1, an endianness value stating if the pixel is transferred MSB first ++ (BE) or LSB first (LE). ++ ++ ++ ++ For instance, a format with uncompressed 10-bit Bayer components ++ arranged in a red, green, green, blue pattern transferred as 2 8-bit ++ samples per pixel with the least significant bits transferred first will ++ be named V4L2_MBUS_FMT_SRGGB10_2X8_PADHI_LE. ++ ++ ++
++ Bayer Patterns ++ ++ ++ ++ ++ ++ ++ ++ ++ Bayer filter color patterns ++ ++ ++
++ ++ The following table lists existing packet Bayer formats. The data ++ organization is given as an example for the first pixel only. ++ ++ ++ Bayer Formats ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ Identifier ++ Code ++ ++ Data organization ++ ++ ++ ++ ++ Bit ++ 11 ++ 10 ++ 9 ++ 8 ++ 7 ++ 6 ++ 5 ++ 4 ++ 3 ++ 2 ++ 1 ++ 0 ++ ++ ++ ++ ++ V4L2_MBUS_FMT_SBGGR8_1X8 ++ 0x3001 ++ ++ - ++ - ++ - ++ - ++ b7 ++ b6 ++ b5 ++ b4 ++ b3 ++ b2 ++ b1 ++ b0 ++ ++ ++ V4L2_MBUS_FMT_SGRBG8_1X8 ++ 0x3002 ++ ++ - ++ - ++ - ++ - ++ g7 ++ g6 ++ g5 ++ g4 ++ g3 ++ g2 ++ g1 ++ g0 ++ ++ ++ V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8 ++ 0x300b ++ ++ - ++ - ++ - ++ - ++ b7 ++ b6 ++ b5 ++ b4 ++ b3 ++ b2 ++ b1 ++ b0 ++ ++ ++ V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8 ++ 0x300c ++ ++ - ++ - ++ - ++ - ++ g7 ++ g6 ++ g5 ++ g4 ++ g3 ++ g2 ++ g1 ++ g0 ++ ++ ++ V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8 ++ 0x3009 ++ ++ - ++ - ++ - ++ - ++ g7 ++ g6 ++ g5 ++ g4 ++ g3 ++ g2 ++ g1 ++ g0 ++ ++ ++ V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8 ++ 0x300d ++ ++ - ++ - ++ - ++ - ++ r7 ++ r6 ++ r5 ++ r4 ++ r3 ++ r2 ++ r1 ++ r0 ++ ++ ++ V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_BE ++ 0x3003 ++ ++ - ++ - ++ - ++ - ++ 0 ++ 0 ++ 0 ++ 0 ++ 0 ++ 0 ++ b9 ++ b8 ++ ++ ++ ++ ++ ++ - ++ - ++ - ++ - ++ b7 ++ b6 ++ b5 ++ b4 ++ b3 ++ b2 ++ b1 ++ b0 ++ ++ ++ V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE ++ 0x3004 ++ ++ - ++ - ++ - ++ - ++ b7 ++ b6 ++ b5 ++ b4 ++ b3 ++ b2 ++ b1 ++ b0 ++ ++ ++ ++ ++ ++ - ++ - ++ - ++ - ++ 0 ++ 0 ++ 0 ++ 0 ++ 0 ++ 0 ++ b9 ++ b8 ++ ++ ++ V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_BE ++ 0x3005 ++ ++ - ++ - ++ - ++ - ++ b9 ++ b8 ++ b7 ++ b6 ++ b5 ++ b4 ++ b3 ++ b2 ++ ++ ++ ++ ++ ++ - ++ - ++ - ++ - ++ b1 ++ b0 ++ 0 ++ 0 ++ 0 ++ 0 ++ 0 ++ 0 ++ ++ ++ V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_LE ++ 0x3006 ++ ++ - ++ - ++ - ++ - ++ b1 ++ b0 ++ 0 ++ 0 ++ 0 ++ 0 ++ 0 ++ 0 ++ ++ ++ ++ ++ ++ - ++ - ++ - ++ - ++ b9 ++ b8 ++ b7 ++ b6 ++ b5 ++ b4 ++ b3 ++ b2 ++ ++ ++ V4L2_MBUS_FMT_SBGGR10_1X10 ++ 0x3007 ++ ++ - ++ - ++ b9 ++ b8 ++ b7 ++ b6 ++ b5 ++ b4 ++ b3 ++ b2 ++ b1 ++ b0 ++ ++ ++ V4L2_MBUS_FMT_SGBRG10_1X10 ++ 0x300e ++ ++ - ++ - ++ g9 ++ g8 ++ g7 ++ g6 ++ g5 ++ g4 ++ g3 ++ g2 ++ g1 ++ g0 ++ ++ ++ V4L2_MBUS_FMT_SGRBG10_1X10 ++ 0x300a ++ ++ - ++ - ++ g9 ++ g8 ++ g7 ++ g6 ++ g5 ++ g4 ++ g3 ++ g2 ++ g1 ++ g0 ++ ++ ++ V4L2_MBUS_FMT_SRGGB10_1X10 ++ 0x300f ++ ++ - ++ - ++ r9 ++ r8 ++ r7 ++ r6 ++ r5 ++ r4 ++ r3 ++ r2 ++ r1 ++ r0 ++ ++ ++ V4L2_MBUS_FMT_SBGGR12_1X12 ++ 0x3008 ++ ++ b11 ++ b10 ++ b9 ++ b8 ++ b7 ++ b6 ++ b5 ++ b4 ++ b3 ++ b2 ++ b1 ++ b0 ++ ++ ++ ++
++
++ ++
++ Packed YUV Formats ++ ++ Those data formats transfer pixel data as (possibly downsampled) Y, U ++ and V components. The format code is made of the following information. ++ ++ The Y, U and V components order code, as transferred on the ++ bus. Possible values are YUYV, UYVY, YVYU and VYUY. ++ The number of bits per pixel component. All components are ++ transferred on the same number of bits. Common values are 8, 10 and 12. ++ ++ The number of bus samples per pixel. Pixels that are wider than ++ the bus width must be transferred in multiple samples. Common values are ++ 1, 1.5 (encoded as 1_5) and 2. ++ The bus width. When the bus width is larger than the number of ++ bits per pixel component, several components are packed in a single bus ++ sample. The components are ordered as specified by the order code, with ++ components on the left of the code transferred in the high order bits. ++ Common values are 8 and 16. ++ ++ ++ ++ ++ For instance, a format where pixels are encoded as 8-bit YUV values ++ downsampled to 4:2:2 and transferred as 2 8-bit bus samples per pixel in the ++ U, Y, V, Y order will be named V4L2_MBUS_FMT_UYVY8_2X8. ++ ++ ++ The following table lisst existing packet YUV formats. ++ ++ ++ YUV Formats ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ Identifier ++ Code ++ ++ Data organization ++ ++ ++ ++ ++ Bit ++ 19 ++ 18 ++ 17 ++ 16 ++ 15 ++ 14 ++ 13 ++ 12 ++ 11 ++ 10 ++ 9 ++ 8 ++ 7 ++ 6 ++ 5 ++ 4 ++ 3 ++ 2 ++ 1 ++ 0 ++ ++ ++ ++ ++ V4L2_MBUS_FMT_Y8_1X8 ++ 0x2001 ++ ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ y7 ++ y6 ++ y5 ++ y4 ++ y3 ++ y2 ++ y1 ++ y0 ++ ++ ++ V4L2_MBUS_FMT_UYVY8_1_5X8 ++ 0x2002 ++ ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ u7 ++ u6 ++ u5 ++ u4 ++ u3 ++ u2 ++ u1 ++ u0 ++ ++ ++ ++ ++ ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ y7 ++ y6 ++ y5 ++ y4 ++ y3 ++ y2 ++ y1 ++ y0 ++ ++ ++ ++ ++ ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ y7 ++ y6 ++ y5 ++ y4 ++ y3 ++ y2 ++ y1 ++ y0 ++ ++ ++ ++ ++ ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ v7 ++ v6 ++ v5 ++ v4 ++ v3 ++ v2 ++ v1 ++ v0 ++ ++ ++ ++ ++ ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ y7 ++ y6 ++ y5 ++ y4 ++ y3 ++ y2 ++ y1 ++ y0 ++ ++ ++ ++ ++ ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ y7 ++ y6 ++ y5 ++ y4 ++ y3 ++ y2 ++ y1 ++ y0 ++ ++ ++ V4L2_MBUS_FMT_VYUY8_1_5X8 ++ 0x2003 ++ ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ v7 ++ v6 ++ v5 ++ v4 ++ v3 ++ v2 ++ v1 ++ v0 ++ ++ ++ ++ ++ ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ y7 ++ y6 ++ y5 ++ y4 ++ y3 ++ y2 ++ y1 ++ y0 ++ ++ ++ ++ ++ ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ y7 ++ y6 ++ y5 ++ y4 ++ y3 ++ y2 ++ y1 ++ y0 ++ ++ ++ ++ ++ ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ u7 ++ u6 ++ u5 ++ u4 ++ u3 ++ u2 ++ u1 ++ u0 ++ ++ ++ ++ ++ ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ y7 ++ y6 ++ y5 ++ y4 ++ y3 ++ y2 ++ y1 ++ y0 ++ ++ ++ ++ ++ ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ y7 ++ y6 ++ y5 ++ y4 ++ y3 ++ y2 ++ y1 ++ y0 ++ ++ ++ V4L2_MBUS_FMT_YUYV8_1_5X8 ++ 0x2004 ++ ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ y7 ++ y6 ++ y5 ++ y4 ++ y3 ++ y2 ++ y1 ++ y0 ++ ++ ++ ++ ++ ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ y7 ++ y6 ++ y5 ++ y4 ++ y3 ++ y2 ++ y1 ++ y0 ++ ++ ++ ++ ++ ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ u7 ++ u6 ++ u5 ++ u4 ++ u3 ++ u2 ++ u1 ++ u0 ++ ++ ++ ++ ++ ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ y7 ++ y6 ++ y5 ++ y4 ++ y3 ++ y2 ++ y1 ++ y0 ++ ++ ++ ++ ++ ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ y7 ++ y6 ++ y5 ++ y4 ++ y3 ++ y2 ++ y1 ++ y0 ++ ++ ++ ++ ++ ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ v7 ++ v6 ++ v5 ++ v4 ++ v3 ++ v2 ++ v1 ++ v0 ++ ++ ++ V4L2_MBUS_FMT_YVYU8_1_5X8 ++ 0x2005 ++ ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ y7 ++ y6 ++ y5 ++ y4 ++ y3 ++ y2 ++ y1 ++ y0 ++ ++ ++ ++ ++ ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ y7 ++ y6 ++ y5 ++ y4 ++ y3 ++ y2 ++ y1 ++ y0 ++ ++ ++ ++ ++ ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ v7 ++ v6 ++ v5 ++ v4 ++ v3 ++ v2 ++ v1 ++ v0 ++ ++ ++ ++ ++ ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ y7 ++ y6 ++ y5 ++ y4 ++ y3 ++ y2 ++ y1 ++ y0 ++ ++ ++ ++ ++ ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ y7 ++ y6 ++ y5 ++ y4 ++ y3 ++ y2 ++ y1 ++ y0 ++ ++ ++ ++ ++ ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ u7 ++ u6 ++ u5 ++ u4 ++ u3 ++ u2 ++ u1 ++ u0 ++ ++ ++ V4L2_MBUS_FMT_UYVY8_2X8 ++ 0x2006 ++ ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ u7 ++ u6 ++ u5 ++ u4 ++ u3 ++ u2 ++ u1 ++ u0 ++ ++ ++ ++ ++ ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ y7 ++ y6 ++ y5 ++ y4 ++ y3 ++ y2 ++ y1 ++ y0 ++ ++ ++ ++ ++ ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ v7 ++ v6 ++ v5 ++ v4 ++ v3 ++ v2 ++ v1 ++ v0 ++ ++ ++ ++ ++ ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ y7 ++ y6 ++ y5 ++ y4 ++ y3 ++ y2 ++ y1 ++ y0 ++ ++ ++ V4L2_MBUS_FMT_VYUY8_2X8 ++ 0x2007 ++ ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ v7 ++ v6 ++ v5 ++ v4 ++ v3 ++ v2 ++ v1 ++ v0 ++ ++ ++ ++ ++ ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ y7 ++ y6 ++ y5 ++ y4 ++ y3 ++ y2 ++ y1 ++ y0 ++ ++ ++ ++ ++ ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ u7 ++ u6 ++ u5 ++ u4 ++ u3 ++ u2 ++ u1 ++ u0 ++ ++ ++ ++ ++ ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ y7 ++ y6 ++ y5 ++ y4 ++ y3 ++ y2 ++ y1 ++ y0 ++ ++ ++ V4L2_MBUS_FMT_YUYV8_2X8 ++ 0x2008 ++ ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ y7 ++ y6 ++ y5 ++ y4 ++ y3 ++ y2 ++ y1 ++ y0 ++ ++ ++ ++ ++ ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ u7 ++ u6 ++ u5 ++ u4 ++ u3 ++ u2 ++ u1 ++ u0 ++ ++ ++ ++ ++ ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ y7 ++ y6 ++ y5 ++ y4 ++ y3 ++ y2 ++ y1 ++ y0 ++ ++ ++ ++ ++ ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ v7 ++ v6 ++ v5 ++ v4 ++ v3 ++ v2 ++ v1 ++ v0 ++ ++ ++ V4L2_MBUS_FMT_YVYU8_2X8 ++ 0x2009 ++ ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ y7 ++ y6 ++ y5 ++ y4 ++ y3 ++ y2 ++ y1 ++ y0 ++ ++ ++ ++ ++ ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ v7 ++ v6 ++ v5 ++ v4 ++ v3 ++ v2 ++ v1 ++ v0 ++ ++ ++ ++ ++ ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ y7 ++ y6 ++ y5 ++ y4 ++ y3 ++ y2 ++ y1 ++ y0 ++ ++ ++ ++ ++ ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ u7 ++ u6 ++ u5 ++ u4 ++ u3 ++ u2 ++ u1 ++ u0 ++ ++ ++ V4L2_MBUS_FMT_Y10_1X10 ++ 0x200a ++ ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ y9 ++ y8 ++ y7 ++ y6 ++ y5 ++ y4 ++ y3 ++ y2 ++ y1 ++ y0 ++ ++ ++ V4L2_MBUS_FMT_YUYV10_2X10 ++ 0x200b ++ ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ y9 ++ y8 ++ y7 ++ y6 ++ y5 ++ y4 ++ y3 ++ y2 ++ y1 ++ y0 ++ ++ ++ ++ ++ ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ u9 ++ u8 ++ u7 ++ u6 ++ u5 ++ u4 ++ u3 ++ u2 ++ u1 ++ u0 ++ ++ ++ ++ ++ ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ y9 ++ y8 ++ y7 ++ y6 ++ y5 ++ y4 ++ y3 ++ y2 ++ y1 ++ y0 ++ ++ ++ ++ ++ ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ v9 ++ v8 ++ v7 ++ v6 ++ v5 ++ v4 ++ v3 ++ v2 ++ v1 ++ v0 ++ ++ ++ V4L2_MBUS_FMT_YVYU10_2X10 ++ 0x200c ++ ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ y9 ++ y8 ++ y7 ++ y6 ++ y5 ++ y4 ++ y3 ++ y2 ++ y1 ++ y0 ++ ++ ++ ++ ++ ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ v9 ++ v8 ++ v7 ++ v6 ++ v5 ++ v4 ++ v3 ++ v2 ++ v1 ++ v0 ++ ++ ++ ++ ++ ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ y9 ++ y8 ++ y7 ++ y6 ++ y5 ++ y4 ++ y3 ++ y2 ++ y1 ++ y0 ++ ++ ++ ++ ++ ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ - ++ u9 ++ u8 ++ u7 ++ u6 ++ u5 ++ u4 ++ u3 ++ u2 ++ u1 ++ u0 ++ ++ ++ V4L2_MBUS_FMT_UYVY8_1X16 ++ 0x200f ++ ++ - ++ - ++ - ++ - ++ u7 ++ u6 ++ u5 ++ u4 ++ u3 ++ u2 ++ u1 ++ u0 ++ y7 ++ y6 ++ y5 ++ y4 ++ y3 ++ y2 ++ y1 ++ y0 ++ ++ ++ ++ ++ ++ - ++ - ++ - ++ - ++ v7 ++ v6 ++ v5 ++ v4 ++ v3 ++ v2 ++ v1 ++ v0 ++ y7 ++ y6 ++ y5 ++ y4 ++ y3 ++ y2 ++ y1 ++ y0 ++ ++ ++ V4L2_MBUS_FMT_VYUY8_1X16 ++ 0x2010 ++ ++ - ++ - ++ - ++ - ++ v7 ++ v6 ++ v5 ++ v4 ++ v3 ++ v2 ++ v1 ++ v0 ++ y7 ++ y6 ++ y5 ++ y4 ++ y3 ++ y2 ++ y1 ++ y0 ++ ++ ++ ++ ++ ++ - ++ - ++ - ++ - ++ u7 ++ u6 ++ u5 ++ u4 ++ u3 ++ u2 ++ u1 ++ u0 ++ y7 ++ y6 ++ y5 ++ y4 ++ y3 ++ y2 ++ y1 ++ y0 ++ ++ ++ V4L2_MBUS_FMT_YUYV8_1X16 ++ 0x2011 ++ ++ - ++ - ++ - ++ - ++ y7 ++ y6 ++ y5 ++ y4 ++ y3 ++ y2 ++ y1 ++ y0 ++ u7 ++ u6 ++ u5 ++ u4 ++ u3 ++ u2 ++ u1 ++ u0 ++ ++ ++ ++ ++ ++ - ++ - ++ - ++ - ++ y7 ++ y6 ++ y5 ++ y4 ++ y3 ++ y2 ++ y1 ++ y0 ++ v7 ++ v6 ++ v5 ++ v4 ++ v3 ++ v2 ++ v1 ++ v0 ++ ++ ++ V4L2_MBUS_FMT_YVYU8_1X16 ++ 0x2012 ++ ++ - ++ - ++ - ++ - ++ y7 ++ y6 ++ y5 ++ y4 ++ y3 ++ y2 ++ y1 ++ y0 ++ v7 ++ v6 ++ v5 ++ v4 ++ v3 ++ v2 ++ v1 ++ v0 ++ ++ ++ ++ ++ ++ - ++ - ++ - ++ - ++ y7 ++ y6 ++ y5 ++ y4 ++ y3 ++ y2 ++ y1 ++ y0 ++ u7 ++ u6 ++ u5 ++ u4 ++ u3 ++ u2 ++ u1 ++ u0 ++ ++ ++ V4L2_MBUS_FMT_YUYV10_1X20 ++ 0x200d ++ ++ y9 ++ y8 ++ y7 ++ y6 ++ y5 ++ y4 ++ y3 ++ y2 ++ y1 ++ y0 ++ u9 ++ u8 ++ u7 ++ u6 ++ u5 ++ u4 ++ u3 ++ u2 ++ u1 ++ u0 ++ ++ ++ ++ ++ ++ y9 ++ y8 ++ y7 ++ y6 ++ y5 ++ y4 ++ y3 ++ y2 ++ y1 ++ y0 ++ v9 ++ v8 ++ v7 ++ v6 ++ v5 ++ v4 ++ v3 ++ v2 ++ v1 ++ v0 ++ ++ ++ V4L2_MBUS_FMT_YVYU10_1X20 ++ 0x200e ++ ++ y9 ++ y8 ++ y7 ++ y6 ++ y5 ++ y4 ++ y3 ++ y2 ++ y1 ++ y0 ++ v9 ++ v8 ++ v7 ++ v6 ++ v5 ++ v4 ++ v3 ++ v2 ++ v1 ++ v0 ++ ++ ++ ++ ++ ++ y9 ++ y8 ++ y7 ++ y6 ++ y5 ++ y4 ++ y3 ++ y2 ++ y1 ++ y0 ++ u9 ++ u8 ++ u7 ++ u6 ++ u5 ++ u4 ++ u3 ++ u2 ++ u1 ++ u0 ++ ++ ++ ++
++
++
++
+diff --git a/Documentation/DocBook/v4l/v4l2.xml b/Documentation/DocBook/v4l/v4l2.xml +index 839e93e..695e3bf 100644 +--- a/Documentation/DocBook/v4l/v4l2.xml ++++ b/Documentation/DocBook/v4l/v4l2.xml +@@ -410,6 +410,7 @@ and discussions on the V4L mailing list. +
&sub-dev-radio;
+
&sub-dev-rds;
+
&sub-dev-event;
++
&sub-dev-subdev;
+ + + +@@ -477,6 +478,9 @@ and discussions on the V4L mailing list. + &sub-reqbufs; + &sub-s-hw-freq-seek; + &sub-streamon; ++ &sub-subdev-enum-frame-size; ++ &sub-subdev-enum-mbus-code; ++ &sub-subdev-g-fmt; + &sub-subscribe-event; + + &sub-mmap; +diff --git a/Documentation/DocBook/v4l/vidioc-streamon.xml b/Documentation/DocBook/v4l/vidioc-streamon.xml +index e42bff1..75ed39b 100644 +--- a/Documentation/DocBook/v4l/vidioc-streamon.xml ++++ b/Documentation/DocBook/v4l/vidioc-streamon.xml +@@ -93,6 +93,15 @@ synchronize with other events. + been allocated (memory mapping) or enqueued (output) yet. + +
++ ++ EPIPE ++ ++ The driver implements pad-level format configuration and ++ the pipeline configuration is invalid. ++ ++ ++ + + + +diff --git a/Documentation/DocBook/v4l/vidioc-subdev-enum-frame-size.xml b/Documentation/DocBook/v4l/vidioc-subdev-enum-frame-size.xml +new file mode 100644 +index 0000000..209e983 +--- /dev/null ++++ b/Documentation/DocBook/v4l/vidioc-subdev-enum-frame-size.xml +@@ -0,0 +1,148 @@ ++ ++ ++ ioctl VIDIOC_SUBDEV_ENUM_FRAME_SIZE ++ &manvol; ++ ++ ++ ++ VIDIOC_SUBDEV_ENUM_FRAME_SIZE ++ Enumerate media bus frame sizes ++ ++ ++ ++ ++ ++ int ioctl ++ int fd ++ int request ++ struct v4l2_subdev_frame_size_enum * ++ argp ++ ++ ++ ++ ++ ++ Arguments ++ ++ ++ ++ fd ++ ++ &fd; ++ ++ ++ ++ request ++ ++ VIDIOC_SUBDEV_ENUM_FRAME_SIZE ++ ++ ++ ++ argp ++ ++ ++ ++ ++ ++ ++ ++ ++ Description ++ ++ This ioctl allows applications to enumerate all frame sizes ++ supported by a sub-device on the given pad for the given media bus format. ++ Supported formats can be retrieved with the &VIDIOC-SUBDEV-ENUM-MBUS-CODE; ++ ioctl. ++ ++ To enumerate frame sizes applications initialize the ++ pad, code and ++ index fields of the ++ &v4l2-subdev-mbus-code-enum; and call the ++ VIDIOC_SUBDEV_ENUM_FRAME_SIZE ioctl with a pointer to ++ the structure. Drivers fill the minimum and maximum frame sizes or return ++ an &EINVAL; if one of the input parameters is invalid. ++ ++ Sub-devices that only support discrete frame sizes (such as most ++ sensors) will return one or more frame sizes with identical minimum and ++ maximum values. ++ ++ Not all possible sizes in given [minimum, maximum] ranges need to be ++ supported. For instance, a scaler that uses a fixed-point scaling ratio ++ might not be able to produce every frame size between the minimum and ++ maximum values. Applications must use the &VIDIOC-SUBDEV-S-FMT; ioctl to ++ try the sub-device for an exact supported frame size. ++ ++ Available frame sizes may depend on the current 'try' formats at other ++ pads of the sub-device, as well as on the current active links and the ++ current values of V4L2 controls. See &VIDIOC-SUBDEV-G-FMT; for more ++ information about try formats. ++ ++ ++ struct <structname>v4l2_subdev_frame_size_enum</structname> ++ ++ &cs-str; ++ ++ ++ __u32 ++ index ++ Number of the format in the enumeration, set by the ++ application. ++ ++ ++ __u32 ++ pad ++ Pad number as reported by the media controller API. ++ ++ ++ __u32 ++ code ++ The media bus format code, as defined in ++ . ++ ++ ++ __u32 ++ min_width ++ Minimum frame width, in pixels. ++ ++ ++ __u32 ++ max_width ++ Maximum frame width, in pixels. ++ ++ ++ __u32 ++ min_height ++ Minimum frame height, in pixels. ++ ++ ++ __u32 ++ max_height ++ Maximum frame height, in pixels. ++ ++ ++ __u32 ++ reserved[9] ++ Reserved for future extensions. Applications and drivers must ++ set the array to zero. ++ ++ ++ ++
++
++ ++ ++ &return-value; ++ ++ ++ ++ EINVAL ++ ++ The &v4l2-subdev-frame-size-enum; pad ++ references a non-existing pad, the code is ++ invalid for the given pad or the index ++ field is out of bounds. ++ ++ ++ ++ ++
+diff --git a/Documentation/DocBook/v4l/vidioc-subdev-enum-mbus-code.xml b/Documentation/DocBook/v4l/vidioc-subdev-enum-mbus-code.xml +new file mode 100644 +index 0000000..763dbc7 +--- /dev/null ++++ b/Documentation/DocBook/v4l/vidioc-subdev-enum-mbus-code.xml +@@ -0,0 +1,113 @@ ++ ++ ++ ioctl VIDIOC_SUBDEV_ENUM_MBUS_CODE ++ &manvol; ++ ++ ++ ++ VIDIOC_SUBDEV_ENUM_MBUS_CODE ++ Enumerate media bus formats ++ ++ ++ ++ ++ ++ int ioctl ++ int fd ++ int request ++ struct v4l2_subdev_mbus_code_enum * ++ argp ++ ++ ++ ++ ++ ++ Arguments ++ ++ ++ ++ fd ++ ++ &fd; ++ ++ ++ ++ request ++ ++ VIDIOC_SUBDEV_ENUM_MBUS_CODE ++ ++ ++ ++ argp ++ ++ ++ ++ ++ ++ ++ ++ ++ Description ++ ++ To enumerate media bus formats available at a given sub-device pad ++ applications initialize the pad and ++ index fields of &v4l2-subdev-mbus-code-enum; and ++ call the VIDIOC_SUBDEV_ENUM_MBUS_CODE ioctl with a ++ pointer to this structure. Drivers fill the rest of the structure or return ++ an &EINVAL; if either the pad or ++ index are invalid. All media bus formats are ++ enumerable by beginning at index zero and incrementing by one until ++ EINVAL is returned. ++ ++ Available media bus formats may depend on the current 'try' formats ++ at other pads of the sub-device, as well as on the current active links. See ++ &VIDIOC-SUBDEV-G-FMT; for more information about the try formats. ++ ++ ++ struct <structname>v4l2_subdev_mbus_code_enum</structname> ++ ++ &cs-str; ++ ++ ++ __u32 ++ pad ++ Pad number as reported by the media controller API. ++ ++ ++ __u32 ++ index ++ Number of the format in the enumeration, set by the ++ application. ++ ++ ++ __u32 ++ code ++ The media bus format code, as defined in ++ . ++ ++ ++ __u32 ++ reserved[9] ++ Reserved for future extensions. Applications and drivers must ++ set the array to zero. ++ ++ ++ ++
++
++ ++ ++ &return-value; ++ ++ ++ ++ EINVAL ++ ++ The &v4l2-subdev-mbus-code-enum; pad ++ references a non-existing pad, or the index ++ field is out of bounds. ++ ++ ++ ++ ++
+diff --git a/Documentation/DocBook/v4l/vidioc-subdev-g-fmt.xml b/Documentation/DocBook/v4l/vidioc-subdev-g-fmt.xml +new file mode 100644 +index 0000000..f06c41b +--- /dev/null ++++ b/Documentation/DocBook/v4l/vidioc-subdev-g-fmt.xml +@@ -0,0 +1,174 @@ ++ ++ ++ ioctl VIDIOC_SUBDEV_G_FMT, VIDIOC_SUBDEV_S_FMT ++ &manvol; ++ ++ ++ ++ VIDIOC_SUBDEV_G_FMT ++ VIDIOC_SUBDEV_S_FMT ++ Get or set the data format on a subdev pad ++ ++ ++ ++ ++ ++ int ioctl ++ int fd ++ int request ++ struct v4l2_subdev_format *argp ++ ++ ++ ++ ++ ++ ++ Arguments ++ ++ ++ ++ fd ++ ++ &fd; ++ ++ ++ ++ request ++ ++ VIDIOC_SUBDEV_G_FMT, VIDIOC_SUBDEV_S_FMT ++ ++ ++ ++ argp ++ ++ ++ ++ ++ ++ ++ ++ ++ Description ++ ++ These ioctls are used to negotiate the frame format at specific ++ subdev pads in the image pipeline. ++ ++ To retrieve the current format applications set the ++ pad field of a &v4l2-subdev-format; to the ++ desired pad number as reported by the media API and the ++ which field to ++ V4L2_SUBDEV_FORMAT_ACTIVE. When they call the ++ VIDIOC_SUBDEV_G_FMT ioctl with a pointer to this ++ structure the driver fills the members of the format ++ field. ++ ++ To change the current format applications set both the ++ pad and which fields ++ and all members of the format field. When they ++ call the VIDIOC_SUBDEV_S_FMT ioctl with a pointer to this ++ structure the driver verifies the requested format, adjusts it based on the ++ hardware capabilities and configures the device. Upon return the ++ &v4l2-subdev-format; contains the current format as would be returned by a ++ VIDIOC_SUBDEV_G_FMT call. ++ ++ Applications can query the device capabilities by setting the ++ which to ++ V4L2_SUBDEV_FORMAT_TRY. When set, 'try' formats are not ++ applied to the device by the driver, but are changed exactly as active ++ formats and stored in the sub-device file handle. Two applications querying ++ the same sub-device would thus not interact with each other. ++ ++ For instance, to try a format at the output pad of a sub-device, ++ applications would first set the try format at the sub-device input with the ++ VIDIOC_SUBDEV_S_FMT ioctl. They would then either ++ retrieve the default format at the output pad with the ++ VIDIOC_SUBDEV_G_FMT ioctl, or set the desired output ++ pad format with the VIDIOC_SUBDEV_S_FMT ioctl and check ++ the returned value. ++ ++ Try formats do not depend on active formats, but can depend on the ++ current links configuration or sub-device controls value. For instance, a ++ low-pass noise filter might crop pixels at the frame boundaries, modifying ++ its output frame size. ++ ++ Drivers must not return an error solely because the requested format ++ doesn't match the device capabilities. They must instead modify the format ++ to match what the hardware can provide. The modified format should be as ++ close as possible to the original request. ++ ++ ++ struct <structname>v4l2_subdev_format</structname> ++ ++ &cs-str; ++ ++ ++ __u32 ++ pad ++ Pad number as reported by the media controller API. ++ ++ ++ __u32 ++ which ++ Format to modified, from &v4l2-subdev-format-whence;. ++ ++ ++ &v4l2-mbus-framefmt; ++ format ++ Definition of an image format, see for details. ++ ++ ++ __u32 ++ reserved[8] ++ Reserved for future extensions. Applications and drivers must ++ set the array to zero. ++ ++ ++ ++
++ ++ ++ enum <structname>v4l2_subdev_format_whence</structname> ++ ++ &cs-def; ++ ++ ++ V4L2_SUBDEV_FORMAT_TRY ++ 0 ++ Try formats, used for querying device capabilities. ++ ++ ++ V4L2_SUBDEV_FORMAT_ACTIVE ++ 1 ++ Active formats, applied to the hardware. ++ ++ ++ ++
++
++ ++ ++ &return-value; ++ ++ ++ ++ EBUSY ++ ++ The format can't be changed because the pad is currently busy. ++ This can be caused, for instance, by an active video stream on the ++ pad. The ioctl must not be retried without performing another action ++ to fix the problem first. Only returned by ++ VIDIOC_SUBDEV_S_FMT ++ ++ ++ ++ EINVAL ++ ++ The &v4l2-subdev-format; pad ++ references a non-existing pad, or the which ++ field references a non-existing format. ++ ++ ++ ++ ++
+diff --git a/drivers/media/video/v4l2-subdev.c b/drivers/media/video/v4l2-subdev.c +index 0f904e2..73aae00 100644 +--- a/drivers/media/video/v4l2-subdev.c ++++ b/drivers/media/video/v4l2-subdev.c +@@ -149,6 +149,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) + struct video_device *vdev = video_devdata(file); + struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev); + struct v4l2_fh *vfh = file->private_data; ++#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) ++ struct v4l2_subdev_fh *subdev_fh = to_v4l2_subdev_fh(vfh); ++#endif + + switch (cmd) { + case VIDIOC_QUERYCTRL: +@@ -183,7 +186,53 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) + + case VIDIOC_UNSUBSCRIBE_EVENT: + return v4l2_subdev_call(sd, core, unsubscribe_event, vfh, arg); ++#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) ++ case VIDIOC_SUBDEV_G_FMT: { ++ struct v4l2_subdev_format *format = arg; ++ ++ if (format->which != V4L2_SUBDEV_FORMAT_TRY && ++ format->which != V4L2_SUBDEV_FORMAT_ACTIVE) ++ return -EINVAL; ++ ++ if (format->pad >= sd->entity.num_pads) ++ return -EINVAL; ++ ++ return v4l2_subdev_call(sd, pad, get_fmt, subdev_fh, format); ++ } ++ ++ case VIDIOC_SUBDEV_S_FMT: { ++ struct v4l2_subdev_format *format = arg; ++ ++ if (format->which != V4L2_SUBDEV_FORMAT_TRY && ++ format->which != V4L2_SUBDEV_FORMAT_ACTIVE) ++ return -EINVAL; ++ ++ if (format->pad >= sd->entity.num_pads) ++ return -EINVAL; + ++ return v4l2_subdev_call(sd, pad, set_fmt, subdev_fh, format); ++ } ++ ++ case VIDIOC_SUBDEV_ENUM_MBUS_CODE: { ++ struct v4l2_subdev_mbus_code_enum *code = arg; ++ ++ if (code->pad >= sd->entity.num_pads) ++ return -EINVAL; ++ ++ return v4l2_subdev_call(sd, pad, enum_mbus_code, subdev_fh, ++ code); ++ } ++ ++ case VIDIOC_SUBDEV_ENUM_FRAME_SIZE: { ++ struct v4l2_subdev_frame_size_enum *fse = arg; ++ ++ if (fse->pad >= sd->entity.num_pads) ++ return -EINVAL; ++ ++ return v4l2_subdev_call(sd, pad, enum_frame_size, subdev_fh, ++ fse); ++ } ++#endif + default: + return -ENOIOCTLCMD; + } +diff --git a/include/linux/Kbuild b/include/linux/Kbuild +index 796e1d8..c0db7f4 100644 +--- a/include/linux/Kbuild ++++ b/include/linux/Kbuild +@@ -367,6 +367,7 @@ header-y += usbdevice_fs.h + header-y += utime.h + header-y += utsname.h + header-y += v4l2-mediabus.h ++header-y += v4l2-subdev.h + header-y += veth.h + header-y += vhost.h + header-y += videodev.h +diff --git a/include/linux/v4l2-subdev.h b/include/linux/v4l2-subdev.h +new file mode 100644 +index 0000000..38d0eda +--- /dev/null ++++ b/include/linux/v4l2-subdev.h +@@ -0,0 +1,90 @@ ++/* ++ * V4L2 subdev userspace API ++ * ++ * Copyright (C) 2010 Nokia Corporation ++ * ++ * Contacts: Laurent Pinchart ++ * Sakari Ailus ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef __LINUX_V4L2_SUBDEV_H ++#define __LINUX_V4L2_SUBDEV_H ++ ++#include ++#include ++#include ++ ++/** ++ * enum v4l2_subdev_format_whence - Media bus format type ++ * @V4L2_SUBDEV_FORMAT_TRY: try format, for negotiation only ++ * @V4L2_SUBDEV_FORMAT_ACTIVE: active format, applied to the device ++ */ ++enum v4l2_subdev_format_whence { ++ V4L2_SUBDEV_FORMAT_TRY = 0, ++ V4L2_SUBDEV_FORMAT_ACTIVE = 1, ++}; ++ ++/** ++ * struct v4l2_subdev_format - Pad-level media bus format ++ * @which: format type (from enum v4l2_subdev_format_whence) ++ * @pad: pad number, as reported by the media API ++ * @format: media bus format (format code and frame size) ++ */ ++struct v4l2_subdev_format { ++ __u32 which; ++ __u32 pad; ++ struct v4l2_mbus_framefmt format; ++ __u32 reserved[8]; ++}; ++ ++/** ++ * struct v4l2_subdev_mbus_code_enum - Media bus format enumeration ++ * @pad: pad number, as reported by the media API ++ * @index: format index during enumeration ++ * @code: format code (from enum v4l2_mbus_pixelcode) ++ */ ++struct v4l2_subdev_mbus_code_enum { ++ __u32 pad; ++ __u32 index; ++ __u32 code; ++ __u32 reserved[9]; ++}; ++ ++/** ++ * struct v4l2_subdev_frame_size_enum - Media bus format enumeration ++ * @pad: pad number, as reported by the media API ++ * @index: format index during enumeration ++ * @code: format code (from enum v4l2_mbus_pixelcode) ++ */ ++struct v4l2_subdev_frame_size_enum { ++ __u32 index; ++ __u32 pad; ++ __u32 code; ++ __u32 min_width; ++ __u32 max_width; ++ __u32 min_height; ++ __u32 max_height; ++ __u32 reserved[9]; ++}; ++ ++#define VIDIOC_SUBDEV_G_FMT _IOWR('V', 4, struct v4l2_subdev_format) ++#define VIDIOC_SUBDEV_S_FMT _IOWR('V', 5, struct v4l2_subdev_format) ++#define VIDIOC_SUBDEV_ENUM_MBUS_CODE \ ++ _IOWR('V', 2, struct v4l2_subdev_mbus_code_enum) ++#define VIDIOC_SUBDEV_ENUM_FRAME_SIZE \ ++ _IOWR('V', 74, struct v4l2_subdev_frame_size_enum) ++ ++#endif +diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h +index 4f6ddba..f5611c2 100644 +--- a/include/media/v4l2-subdev.h ++++ b/include/media/v4l2-subdev.h +@@ -21,6 +21,7 @@ + #ifndef _V4L2_SUBDEV_H + #define _V4L2_SUBDEV_H + ++#include + #include + #include + #include +@@ -425,6 +426,15 @@ struct v4l2_subdev_ir_ops { + }; + + struct v4l2_subdev_pad_ops { ++ int (*enum_mbus_code)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_mbus_code_enum *code); ++ int (*enum_frame_size)(struct v4l2_subdev *sd, ++ struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_frame_size_enum *fse); ++ int (*get_fmt)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_format *format); ++ int (*set_fmt)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_format *format); + }; + + struct v4l2_subdev_ops { +-- +1.6.6.1 + diff --git a/recipes/linux/linux-omap-2.6.37/media/0029-v4l-v4l2_subdev-userspace-frame-interval-API.patch b/recipes/linux/linux-omap-2.6.37/media/0029-v4l-v4l2_subdev-userspace-frame-interval-API.patch new file mode 100644 index 0000000000..8de33fdb6e --- /dev/null +++ b/recipes/linux/linux-omap-2.6.37/media/0029-v4l-v4l2_subdev-userspace-frame-interval-API.patch @@ -0,0 +1,479 @@ +From b137f96a198afb39c8457e2c5d28c1c4bca129a3 Mon Sep 17 00:00:00 2001 +From: Laurent Pinchart +Date: Wed, 5 May 2010 16:38:35 +0200 +Subject: [PATCH 29/43] v4l: v4l2_subdev userspace frame interval API + +The three new ioctl VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL, +VIDIOC_SUBDEV_G_FRAME_INTERVAL and VIDIOC_SUBDEV_S_FRAME_INTERVAL can be +used to enumerate and configure a subdev's frame rate from userspace. + +Two new video::g/s_frame_interval subdev operations are introduced to +support those ioctls. The existing video::g/s_parm operations are +deprecated and shouldn't be used anymore. + +Signed-off-by: Laurent Pinchart +Signed-off-by: Sakari Ailus +--- + Documentation/DocBook/media-entities.tmpl | 6 + + Documentation/DocBook/v4l/v4l2.xml | 2 + + .../v4l/vidioc-subdev-enum-frame-interval.xml | 146 ++++++++++++++++++++ + .../DocBook/v4l/vidioc-subdev-g-frame-interval.xml | 135 ++++++++++++++++++ + drivers/media/video/v4l2-subdev.c | 16 ++ + include/linux/v4l2-subdev.h | 36 +++++ + include/media/v4l2-subdev.h | 7 + + 7 files changed, 348 insertions(+), 0 deletions(-) + create mode 100644 Documentation/DocBook/v4l/vidioc-subdev-enum-frame-interval.xml + create mode 100644 Documentation/DocBook/v4l/vidioc-subdev-g-frame-interval.xml + +diff --git a/Documentation/DocBook/media-entities.tmpl b/Documentation/DocBook/media-entities.tmpl +index 538f8fe..4af3c2e 100644 +--- a/Documentation/DocBook/media-entities.tmpl ++++ b/Documentation/DocBook/media-entities.tmpl +@@ -89,7 +89,9 @@ + VIDIOC_SUBDEV_ENUM_FRAME_SIZE"> + VIDIOC_SUBDEV_ENUM_MBUS_CODE"> + VIDIOC_SUBDEV_G_FMT"> ++VIDIOC_SUBDEV_G_FRAME_INTERVAL"> + VIDIOC_SUBDEV_S_FMT"> ++VIDIOC_SUBDEV_S_FRAME_INTERVAL"> + VIDIOC_TRY_ENCODER_CMD"> + VIDIOC_TRY_EXT_CTRLS"> + VIDIOC_TRY_FMT"> +@@ -190,6 +192,8 @@ + v4l2_sliced_vbi_cap"> + v4l2_sliced_vbi_data"> + v4l2_sliced_vbi_format"> ++v4l2_subdev_frame_interval"> ++v4l2_subdev_frame_interval_enum"> + v4l2_subdev_frame_size_enum"> + v4l2_subdev_format"> + v4l2_subdev_mbus_code_enum"> +@@ -325,10 +329,12 @@ + + + ++ + + + + ++ + + + +diff --git a/Documentation/DocBook/v4l/v4l2.xml b/Documentation/DocBook/v4l/v4l2.xml +index 695e3bf..e6225e0 100644 +--- a/Documentation/DocBook/v4l/v4l2.xml ++++ b/Documentation/DocBook/v4l/v4l2.xml +@@ -478,9 +478,11 @@ and discussions on the V4L mailing list. + &sub-reqbufs; + &sub-s-hw-freq-seek; + &sub-streamon; ++ &sub-subdev-enum-frame-interval; + &sub-subdev-enum-frame-size; + &sub-subdev-enum-mbus-code; + &sub-subdev-g-fmt; ++ &sub-subdev-g-frame-interval; + &sub-subscribe-event; + + &sub-mmap; +diff --git a/Documentation/DocBook/v4l/vidioc-subdev-enum-frame-interval.xml b/Documentation/DocBook/v4l/vidioc-subdev-enum-frame-interval.xml +new file mode 100644 +index 0000000..bcea9d4 +--- /dev/null ++++ b/Documentation/DocBook/v4l/vidioc-subdev-enum-frame-interval.xml +@@ -0,0 +1,146 @@ ++ ++ ++ ioctl VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL ++ &manvol; ++ ++ ++ ++ VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL ++ Enumerate frame intervals ++ ++ ++ ++ ++ ++ int ioctl ++ int fd ++ int request ++ struct v4l2_subdev_frame_interval_enum * ++ argp ++ ++ ++ ++ ++ ++ Arguments ++ ++ ++ ++ fd ++ ++ &fd; ++ ++ ++ ++ request ++ ++ VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL ++ ++ ++ ++ argp ++ ++ ++ ++ ++ ++ ++ ++ ++ Description ++ ++ This ioctl lets applications enumerate available frame intervals on a ++ given sub-device pad. Frame intervals only makes sense for sub-devices that ++ can control the frame period on their own. This includes, for instance, ++ image sensors and TV tuners. ++ ++ For the common use case of image sensors, the frame intervals ++ available on the sub-device output pad depend on the frame format and size ++ on the same pad. Applications must thus specify the desired format and size ++ when enumerating frame intervals. ++ ++ To enumerate frame intervals applications initialize the ++ index, pad, ++ code, width and ++ height fields of ++ &v4l2-subdev-frame-interval-enum; and call the ++ VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL ioctl with a pointer ++ to this structure. Drivers fill the rest of the structure or return ++ an &EINVAL; if one of the input fields is invalid. All frame intervals are ++ enumerable by beginning at index zero and incrementing by one until ++ EINVAL is returned. ++ ++ Available frame intervals may depend on the current 'try' formats ++ at other pads of the sub-device, as well as on the current active links. See ++ &VIDIOC-SUBDEV-G-FMT; for more information about the try formats. ++ ++ Sub-devices that support the frame interval enumeration ioctl should ++ implemented it on a single pad only. Its behaviour when supported on ++ multiple pads of the same sub-device is not defined. ++ ++ ++ struct <structname>v4l2_subdev_frame_interval_enum</structname> ++ ++ &cs-str; ++ ++ ++ __u32 ++ index ++ Number of the format in the enumeration, set by the ++ application. ++ ++ ++ __u32 ++ pad ++ Pad number as reported by the media controller API. ++ ++ ++ __u32 ++ code ++ The media bus format code, as defined in ++ . ++ ++ ++ __u32 ++ width ++ Frame width, in pixels. ++ ++ ++ __u32 ++ height ++ Frame height, in pixels. ++ ++ ++ &v4l2-fract; ++ interval ++ Period, in seconds, between consecutive video frames. ++ ++ ++ __u32 ++ reserved[9] ++ Reserved for future extensions. Applications and drivers must ++ set the array to zero. ++ ++ ++ ++
++
++ ++ ++ &return-value; ++ ++ ++ ++ EINVAL ++ ++ The &v4l2-subdev-frame-interval-enum; ++ pad references a non-existing pad, one of ++ the code, width ++ or height fields are invalid for the given ++ pad or the index field is out of bounds. ++ ++ ++ ++ ++ ++
+diff --git a/Documentation/DocBook/v4l/vidioc-subdev-g-frame-interval.xml b/Documentation/DocBook/v4l/vidioc-subdev-g-frame-interval.xml +new file mode 100644 +index 0000000..848ec78 +--- /dev/null ++++ b/Documentation/DocBook/v4l/vidioc-subdev-g-frame-interval.xml +@@ -0,0 +1,135 @@ ++ ++ ++ ioctl VIDIOC_SUBDEV_G_FRAME_INTERVAL, VIDIOC_SUBDEV_S_FRAME_INTERVAL ++ &manvol; ++ ++ ++ ++ VIDIOC_SUBDEV_G_FRAME_INTERVAL ++ VIDIOC_SUBDEV_S_FRAME_INTERVAL ++ Get or set the frame interval on a subdev pad ++ ++ ++ ++ ++ ++ int ioctl ++ int fd ++ int request ++ struct v4l2_subdev_frame_interval *argp ++ ++ ++ ++ ++ ++ ++ Arguments ++ ++ ++ ++ fd ++ ++ &fd; ++ ++ ++ ++ request ++ ++ VIDIOC_SUBDEV_G_FRAME_INTERVAL, VIDIOC_SUBDEV_S_FRAME_INTERVAL ++ ++ ++ ++ argp ++ ++ ++ ++ ++ ++ ++ ++ ++ Description ++ ++ These ioctls are used to get and set the frame interval at specific ++ subdev pads in the image pipeline. The frame interval only makes sense for ++ sub-devices that can control the frame period on their own. This includes, ++ for instance, image sensors and TV tuners. Sub-devices that don't support ++ frame intervals must not implement these ioctls. ++ ++ To retrieve the current frame interval applications set the ++ pad field of a &v4l2-subdev-frame-interval; to ++ the desired pad number as reported by the media controller API. When they ++ call the VIDIOC_SUBDEV_G_FRAME_INTERVAL ioctl with a ++ pointer to this structure the driver fills the members of the ++ interval field. ++ ++ To change the current frame interval applications set both the ++ pad field and all members of the ++ interval field. When they call the ++ VIDIOC_SUBDEV_S_FRAME_INTERVAL ioctl with a pointer to ++ this structure the driver verifies the requested interval, adjusts it based ++ on the hardware capabilities and configures the device. Upon return the ++ &v4l2-subdev-frame-interval; contains the current frame interval as would be ++ returned by a VIDIOC_SUBDEV_G_FRAME_INTERVAL call. ++ ++ ++ Drivers must not return an error solely because the requested interval ++ doesn't match the device capabilities. They must instead modify the interval ++ to match what the hardware can provide. The modified interval should be as ++ close as possible to the original request. ++ ++ Sub-devices that support the frame interval ioctls should implement ++ them on a single pad only. Their behaviour when supported on multiple pads ++ of the same sub-device is not defined. ++ ++ ++ struct <structname>v4l2_subdev_frame_interval</structname> ++ ++ &cs-str; ++ ++ ++ __u32 ++ pad ++ Pad number as reported by the media controller API. ++ ++ ++ &v4l2-fract; ++ interval ++ Period, in seconds, between consecutive video frames. ++ ++ ++ __u32 ++ reserved[9] ++ Reserved for future extensions. Applications and drivers must ++ set the array to zero. ++ ++ ++ ++
++
++ ++ ++ &return-value; ++ ++ ++ ++ EBUSY ++ ++ The frame interval can't be changed because the pad is currently ++ busy. This can be caused, for instance, by an active video stream on ++ the pad. The ioctl must not be retried without performing another ++ action to fix the problem first. Only returned by ++ VIDIOC_SUBDEV_S_FRAME_INTERVAL ++ ++ ++ ++ EINVAL ++ ++ The &v4l2-subdev-frame-interval; pad ++ references a non-existing pad, or the pad doesn't support frame ++ intervals. ++ ++ ++ ++ ++
+diff --git a/drivers/media/video/v4l2-subdev.c b/drivers/media/video/v4l2-subdev.c +index 73aae00..316a08a 100644 +--- a/drivers/media/video/v4l2-subdev.c ++++ b/drivers/media/video/v4l2-subdev.c +@@ -232,6 +232,22 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) + return v4l2_subdev_call(sd, pad, enum_frame_size, subdev_fh, + fse); + } ++ ++ case VIDIOC_SUBDEV_G_FRAME_INTERVAL: ++ return v4l2_subdev_call(sd, video, g_frame_interval, arg); ++ ++ case VIDIOC_SUBDEV_S_FRAME_INTERVAL: ++ return v4l2_subdev_call(sd, video, s_frame_interval, arg); ++ ++ case VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL: { ++ struct v4l2_subdev_frame_interval_enum *fie = arg; ++ ++ if (fie->pad >= sd->entity.num_pads) ++ return -EINVAL; ++ ++ return v4l2_subdev_call(sd, pad, enum_frame_interval, subdev_fh, ++ fie); ++ } + #endif + default: + return -ENOIOCTLCMD; +diff --git a/include/linux/v4l2-subdev.h b/include/linux/v4l2-subdev.h +index 38d0eda..bf9f3e9 100644 +--- a/include/linux/v4l2-subdev.h ++++ b/include/linux/v4l2-subdev.h +@@ -80,11 +80,47 @@ struct v4l2_subdev_frame_size_enum { + __u32 reserved[9]; + }; + ++/** ++ * struct v4l2_subdev_frame_interval - Pad-level frame rate ++ * @pad: pad number, as reported by the media API ++ * @interval: frame interval in seconds ++ */ ++struct v4l2_subdev_frame_interval { ++ __u32 pad; ++ struct v4l2_fract interval; ++ __u32 reserved[9]; ++}; ++ ++/** ++ * struct v4l2_subdev_frame_interval_enum - Frame interval enumeration ++ * @pad: pad number, as reported by the media API ++ * @index: frame interval index during enumeration ++ * @code: format code (from enum v4l2_mbus_pixelcode) ++ * @width: frame width in pixels ++ * @height: frame height in pixels ++ * @interval: frame interval in seconds ++ */ ++struct v4l2_subdev_frame_interval_enum { ++ __u32 index; ++ __u32 pad; ++ __u32 code; ++ __u32 width; ++ __u32 height; ++ struct v4l2_fract interval; ++ __u32 reserved[9]; ++}; ++ + #define VIDIOC_SUBDEV_G_FMT _IOWR('V', 4, struct v4l2_subdev_format) + #define VIDIOC_SUBDEV_S_FMT _IOWR('V', 5, struct v4l2_subdev_format) ++#define VIDIOC_SUBDEV_G_FRAME_INTERVAL \ ++ _IOWR('V', 21, struct v4l2_subdev_frame_interval) ++#define VIDIOC_SUBDEV_S_FRAME_INTERVAL \ ++ _IOWR('V', 22, struct v4l2_subdev_frame_interval) + #define VIDIOC_SUBDEV_ENUM_MBUS_CODE \ + _IOWR('V', 2, struct v4l2_subdev_mbus_code_enum) + #define VIDIOC_SUBDEV_ENUM_FRAME_SIZE \ + _IOWR('V', 74, struct v4l2_subdev_frame_size_enum) ++#define VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL \ ++ _IOWR('V', 75, struct v4l2_subdev_frame_interval_enum) + + #endif +diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h +index f5611c2..9c8bcd3 100644 +--- a/include/media/v4l2-subdev.h ++++ b/include/media/v4l2-subdev.h +@@ -281,6 +281,10 @@ struct v4l2_subdev_video_ops { + int (*s_crop)(struct v4l2_subdev *sd, struct v4l2_crop *crop); + int (*g_parm)(struct v4l2_subdev *sd, struct v4l2_streamparm *param); + int (*s_parm)(struct v4l2_subdev *sd, struct v4l2_streamparm *param); ++ int (*g_frame_interval)(struct v4l2_subdev *sd, ++ struct v4l2_subdev_frame_interval *interval); ++ int (*s_frame_interval)(struct v4l2_subdev *sd, ++ struct v4l2_subdev_frame_interval *interval); + int (*enum_framesizes)(struct v4l2_subdev *sd, struct v4l2_frmsizeenum *fsize); + int (*enum_frameintervals)(struct v4l2_subdev *sd, struct v4l2_frmivalenum *fival); + int (*enum_dv_presets) (struct v4l2_subdev *sd, +@@ -431,6 +435,9 @@ struct v4l2_subdev_pad_ops { + int (*enum_frame_size)(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse); ++ int (*enum_frame_interval)(struct v4l2_subdev *sd, ++ struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_frame_interval_enum *fie); + int (*get_fmt)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *format); + int (*set_fmt)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +-- +1.6.6.1 + diff --git a/recipes/linux/linux-omap-2.6.37/media/0030-v4l-v4l2_subdev-userspace-crop-API.patch b/recipes/linux/linux-omap-2.6.37/media/0030-v4l-v4l2_subdev-userspace-crop-API.patch new file mode 100644 index 0000000000..d1a3aae873 --- /dev/null +++ b/recipes/linux/linux-omap-2.6.37/media/0030-v4l-v4l2_subdev-userspace-crop-API.patch @@ -0,0 +1,350 @@ +From 9e87e6d59dc364ec78717fb91cbe9bad7df14223 Mon Sep 17 00:00:00 2001 +From: Antti Koskipaa +Date: Wed, 23 Jun 2010 11:03:42 +0300 +Subject: [PATCH 30/43] v4l: v4l2_subdev userspace crop API + +This patch adds the VIDIOC_SUBDEV_S_CROP and G_CROP ioctls to the +userland API. CROPCAP is not implemented because it's redundant. + +Signed-off-by: Antti Koskipaa +Signed-off-by: Laurent Pinchart +--- + Documentation/DocBook/media-entities.tmpl | 4 + + Documentation/DocBook/v4l/dev-subdev.xml | 33 +++++ + Documentation/DocBook/v4l/v4l2.xml | 1 + + Documentation/DocBook/v4l/vidioc-subdev-g-crop.xml | 149 ++++++++++++++++++++ + drivers/media/video/v4l2-subdev.c | 26 ++++ + include/linux/v4l2-subdev.h | 15 ++ + include/media/v4l2-subdev.h | 4 + + 7 files changed, 232 insertions(+), 0 deletions(-) + create mode 100644 Documentation/DocBook/v4l/vidioc-subdev-g-crop.xml + +diff --git a/Documentation/DocBook/media-entities.tmpl b/Documentation/DocBook/media-entities.tmpl +index 4af3c2e..157d147 100644 +--- a/Documentation/DocBook/media-entities.tmpl ++++ b/Documentation/DocBook/media-entities.tmpl +@@ -88,8 +88,10 @@ + VIDIOC_S_TUNER"> + VIDIOC_SUBDEV_ENUM_FRAME_SIZE"> + VIDIOC_SUBDEV_ENUM_MBUS_CODE"> ++VIDIOC_SUBDEV_G_CROP"> + VIDIOC_SUBDEV_G_FMT"> + VIDIOC_SUBDEV_G_FRAME_INTERVAL"> ++VIDIOC_SUBDEV_S_CROP"> + VIDIOC_SUBDEV_S_FMT"> + VIDIOC_SUBDEV_S_FRAME_INTERVAL"> + VIDIOC_TRY_ENCODER_CMD"> +@@ -195,6 +197,7 @@ + v4l2_subdev_frame_interval"> + v4l2_subdev_frame_interval_enum"> + v4l2_subdev_frame_size_enum"> ++v4l2_subdev_crop"> + v4l2_subdev_format"> + v4l2_subdev_mbus_code_enum"> + v4l2_standard"> +@@ -333,6 +336,7 @@ + + + ++ + + + +diff --git a/Documentation/DocBook/v4l/dev-subdev.xml b/Documentation/DocBook/v4l/dev-subdev.xml +index 12fdca4..a8da916 100644 +--- a/Documentation/DocBook/v4l/dev-subdev.xml ++++ b/Documentation/DocBook/v4l/dev-subdev.xml +@@ -269,6 +269,39 @@ + + + ++
++ Cropping and scaling ++ ++ Many sub-devices support cropping frames on their input or output ++ pads (or possible even on both). Cropping is used to select the area of ++ interest in an image, typically on a video sensor or video decoder. It can ++ also be used as part of digital zoom implementations to select the area of ++ the image that will be scaled up. ++ ++ Crop settings are defined by a crop rectangle and represented in a ++ &v4l2-rect; by the coordinates of the top left corner and the rectangle ++ size. Both the coordinates and sizes are expressed in pixels. ++ ++ The crop rectangle is retrieved and set using the ++ &VIDIOC-SUBDEV-G-CROP; and &VIDIOC-SUBDEV-S-CROP; ioctls. Like for pad ++ formats, drivers store try and active crop rectangles. The format ++ negotiation mechanism applies to crop settings as well. ++ ++ On input pads, cropping is applied relatively to the current pad ++ format. The pad format represents the image size as received by the ++ sub-device from the previous block in the pipeline, and the crop rectangle ++ represents the sub-image that will be transmitted further inside the ++ sub-device for processing. The crop rectangle be entirely containted ++ inside the input image size. ++ ++ Input crop rectangle are reset to their default value when the input ++ image format is modified. Drivers should use the input image size as the ++ crop rectangle default value, but hardware requirements may prevent this. ++ ++ ++ Cropping behaviour on output pads is not defined. ++ ++
+ + + &sub-subdev-formats; +diff --git a/Documentation/DocBook/v4l/v4l2.xml b/Documentation/DocBook/v4l/v4l2.xml +index e6225e0..5e640ca 100644 +--- a/Documentation/DocBook/v4l/v4l2.xml ++++ b/Documentation/DocBook/v4l/v4l2.xml +@@ -481,6 +481,7 @@ and discussions on the V4L mailing list. + &sub-subdev-enum-frame-interval; + &sub-subdev-enum-frame-size; + &sub-subdev-enum-mbus-code; ++ &sub-subdev-g-crop; + &sub-subdev-g-fmt; + &sub-subdev-g-frame-interval; + &sub-subscribe-event; +diff --git a/Documentation/DocBook/v4l/vidioc-subdev-g-crop.xml b/Documentation/DocBook/v4l/vidioc-subdev-g-crop.xml +new file mode 100644 +index 0000000..cef127f +--- /dev/null ++++ b/Documentation/DocBook/v4l/vidioc-subdev-g-crop.xml +@@ -0,0 +1,149 @@ ++ ++ ++ ioctl VIDIOC_SUBDEV_G_CROP, VIDIOC_SUBDEV_S_CROP ++ &manvol; ++ ++ ++ ++ VIDIOC_SUBDEV_G_CROP ++ VIDIOC_SUBDEV_S_CROP ++ Get or set the crop rectangle on a subdev pad ++ ++ ++ ++ ++ ++ int ioctl ++ int fd ++ int request ++ struct v4l2_subdev_crop *argp ++ ++ ++ ++ ++ int ioctl ++ int fd ++ int request ++ const struct v4l2_subdev_crop *argp ++ ++ ++ ++ ++ ++ Arguments ++ ++ ++ ++ fd ++ ++ &fd; ++ ++ ++ ++ request ++ ++ VIDIOC_SUBDEV_G_CROP, VIDIOC_SUBDEV_S_CROP ++ ++ ++ ++ argp ++ ++ ++ ++ ++ ++ ++ ++ ++ Description ++ ++ To retrieve the current crop rectangle applications set the ++ pad field of a &v4l2-subdev-crop; to the ++ desired pad number as reported by the media API and the ++ which field to ++ V4L2_SUBDEV_FORMAT_ACTIVE. They then call the ++ VIDIOC_SUBDEV_G_CROP ioctl with a pointer to this ++ structure. The driver fills the members of the rect ++ field or returns &EINVAL; if the input arguments are invalid, or if cropping ++ is not supported on the given pad. ++ ++ To change the current crop rectangle applications set both the ++ pad and which fields ++ and all members of the rect field. They then call ++ the VIDIOC_SUBDEV_S_CROP ioctl with a pointer to this ++ structure. The driver verifies the requested crop rectangle, adjusts it ++ based on the hardware capabilities and configures the device. Upon return ++ the &v4l2-subdev-crop; contains the current format as would be returned ++ by a VIDIOC_SUBDEV_G_CROP call. ++ ++ Applications can query the device capabilities by setting the ++ which to ++ V4L2_SUBDEV_FORMAT_TRY. When set, 'try' crop ++ rectangles are not applied to the device by the driver, but are mangled ++ exactly as active crop rectangles and stored in the sub-device file handle. ++ Two applications querying the same sub-device would thus not interact with ++ each other. ++ ++ Drivers must not return an error solely because the requested crop ++ rectangle doesn't match the device capabilities. They must instead modify ++ the rectangle to match what the hardware can provide. The modified format ++ should be as close as possible to the original request. ++ ++ ++ struct <structname>v4l2_subdev_crop</structname> ++ ++ &cs-str; ++ ++ ++ __u32 ++ pad ++ Pad number as reported by the media framework. ++ ++ ++ __u32 ++ which ++ Crop rectangle to get or set, from ++ &v4l2-subdev-format-whence;. ++ ++ ++ &v4l2-rect; ++ rect ++ Crop rectangle boundaries, in pixels. ++ ++ ++ __u32 ++ reserved[8] ++ Reserved for future extensions. Applications and drivers must ++ set the array to zero. ++ ++ ++ ++
++
++ ++ ++ &return-value; ++ ++ ++ ++ EBUSY ++ ++ The crop rectangle can't be changed because the pad is currently ++ busy. This can be caused, for instance, by an active video stream on ++ the pad. The ioctl must not be retried without performing another ++ action to fix the problem first. Only returned by ++ VIDIOC_SUBDEV_S_CROP ++ ++ ++ ++ EINVAL ++ ++ The &v4l2-subdev-crop; pad ++ references a non-existing pad, the which ++ field references a non-existing format, or cropping is not supported ++ on the given subdev pad. ++ ++ ++ ++ ++
+diff --git a/drivers/media/video/v4l2-subdev.c b/drivers/media/video/v4l2-subdev.c +index 316a08a..e706c4c 100644 +--- a/drivers/media/video/v4l2-subdev.c ++++ b/drivers/media/video/v4l2-subdev.c +@@ -213,6 +213,32 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) + return v4l2_subdev_call(sd, pad, set_fmt, subdev_fh, format); + } + ++ case VIDIOC_SUBDEV_G_CROP: { ++ struct v4l2_subdev_crop *crop = arg; ++ ++ if (crop->which != V4L2_SUBDEV_FORMAT_TRY && ++ crop->which != V4L2_SUBDEV_FORMAT_ACTIVE) ++ return -EINVAL; ++ ++ if (crop->pad >= sd->entity.num_pads) ++ return -EINVAL; ++ ++ return v4l2_subdev_call(sd, pad, get_crop, subdev_fh, crop); ++ } ++ ++ case VIDIOC_SUBDEV_S_CROP: { ++ struct v4l2_subdev_crop *crop = arg; ++ ++ if (crop->which != V4L2_SUBDEV_FORMAT_TRY && ++ crop->which != V4L2_SUBDEV_FORMAT_ACTIVE) ++ return -EINVAL; ++ ++ if (crop->pad >= sd->entity.num_pads) ++ return -EINVAL; ++ ++ return v4l2_subdev_call(sd, pad, set_crop, subdev_fh, crop); ++ } ++ + case VIDIOC_SUBDEV_ENUM_MBUS_CODE: { + struct v4l2_subdev_mbus_code_enum *code = arg; + +diff --git a/include/linux/v4l2-subdev.h b/include/linux/v4l2-subdev.h +index bf9f3e9..49ec1e0 100644 +--- a/include/linux/v4l2-subdev.h ++++ b/include/linux/v4l2-subdev.h +@@ -51,6 +51,19 @@ struct v4l2_subdev_format { + }; + + /** ++ * struct v4l2_subdev_crop - Pad-level crop settings ++ * @which: format type (from enum v4l2_subdev_format_whence) ++ * @pad: pad number, as reported by the media API ++ * @rect: pad crop rectangle boundaries ++ */ ++struct v4l2_subdev_crop { ++ __u32 which; ++ __u32 pad; ++ struct v4l2_rect rect; ++ __u32 reserved[8]; ++}; ++ ++/** + * struct v4l2_subdev_mbus_code_enum - Media bus format enumeration + * @pad: pad number, as reported by the media API + * @index: format index during enumeration +@@ -122,5 +135,7 @@ struct v4l2_subdev_frame_interval_enum { + _IOWR('V', 74, struct v4l2_subdev_frame_size_enum) + #define VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL \ + _IOWR('V', 75, struct v4l2_subdev_frame_interval_enum) ++#define VIDIOC_SUBDEV_G_CROP _IOWR('V', 59, struct v4l2_subdev_crop) ++#define VIDIOC_SUBDEV_S_CROP _IOWR('V', 60, struct v4l2_subdev_crop) + + #endif +diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h +index 9c8bcd3..a02663e 100644 +--- a/include/media/v4l2-subdev.h ++++ b/include/media/v4l2-subdev.h +@@ -442,6 +442,10 @@ struct v4l2_subdev_pad_ops { + struct v4l2_subdev_format *format); + int (*set_fmt)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *format); ++ int (*set_crop)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_crop *crop); ++ int (*get_crop)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_crop *crop); + }; + + struct v4l2_subdev_ops { +-- +1.6.6.1 + diff --git a/recipes/linux/linux-omap-2.6.37/media/0031-v4l-subdev-Generic-ioctl-support.patch b/recipes/linux/linux-omap-2.6.37/media/0031-v4l-subdev-Generic-ioctl-support.patch new file mode 100644 index 0000000000..d1bac037a1 --- /dev/null +++ b/recipes/linux/linux-omap-2.6.37/media/0031-v4l-subdev-Generic-ioctl-support.patch @@ -0,0 +1,46 @@ +From 3378e81670a983f084f6d8e6be654234b258e482 Mon Sep 17 00:00:00 2001 +From: Laurent Pinchart +Date: Fri, 26 Feb 2010 16:23:10 +0100 +Subject: [PATCH 31/43] v4l: subdev: Generic ioctl support + +Instead of returning an error when receiving an ioctl call with an +unsupported command, forward the call to the subdev core::ioctl handler. + +Signed-off-by: Laurent Pinchart +--- + Documentation/video4linux/v4l2-framework.txt | 5 +++++ + drivers/media/video/v4l2-subdev.c | 2 +- + 2 files changed, 6 insertions(+), 1 deletions(-) + +diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt +index d0fb880..1bb5f22 100644 +--- a/Documentation/video4linux/v4l2-framework.txt ++++ b/Documentation/video4linux/v4l2-framework.txt +@@ -407,6 +407,11 @@ VIDIOC_UNSUBSCRIBE_EVENT + To properly support events, the poll() file operation is also + implemented. + ++Private ioctls ++ ++ All ioctls not in the above list are passed directly to the sub-device ++ driver through the core::ioctl operation. ++ + + I2C sub-device drivers + ---------------------- +diff --git a/drivers/media/video/v4l2-subdev.c b/drivers/media/video/v4l2-subdev.c +index e706c4c..1710a64 100644 +--- a/drivers/media/video/v4l2-subdev.c ++++ b/drivers/media/video/v4l2-subdev.c +@@ -276,7 +276,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) + } + #endif + default: +- return -ENOIOCTLCMD; ++ return v4l2_subdev_call(sd, core, ioctl, cmd, arg); + } + + return 0; +-- +1.6.6.1 + diff --git a/recipes/linux/linux-omap-2.6.37/media/0032-v4l-Add-subdev-sensor-g_skip_frames-operation.patch b/recipes/linux/linux-omap-2.6.37/media/0032-v4l-Add-subdev-sensor-g_skip_frames-operation.patch new file mode 100644 index 0000000000..bbfe8470e6 --- /dev/null +++ b/recipes/linux/linux-omap-2.6.37/media/0032-v4l-Add-subdev-sensor-g_skip_frames-operation.patch @@ -0,0 +1,35 @@ +From 80c35f54b7d24b5f05e1e510f87e2ad1b94efede Mon Sep 17 00:00:00 2001 +From: Laurent Pinchart +Date: Tue, 16 Nov 2010 06:21:06 +0200 +Subject: [PATCH 32/43] v4l: Add subdev sensor g_skip_frames operation + +Some buggy sensors generate corrupt frames when the stream is started. +This new operation return the number of corrupt frames to skip when +starting the stream. + +Signed-off-by: Laurent Pinchart +--- + include/media/v4l2-subdev.h | 4 ++++ + 1 files changed, 4 insertions(+), 0 deletions(-) + +diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h +index a02663e..181de59 100644 +--- a/include/media/v4l2-subdev.h ++++ b/include/media/v4l2-subdev.h +@@ -352,9 +352,13 @@ struct v4l2_subdev_vbi_ops { + * This is needed for some sensors, which always corrupt + * several top lines of the output image, or which send their + * metadata in them. ++ * @g_skip_frames: number of frames to skip at stream start. This is needed for ++ * buggy sensors that generate faulty frames when they are ++ * turned on. + */ + struct v4l2_subdev_sensor_ops { + int (*g_skip_top_lines)(struct v4l2_subdev *sd, u32 *lines); ++ int (*g_skip_frames)(struct v4l2_subdev *sd, u32 *frames); + }; + + /* +-- +1.6.6.1 + diff --git a/recipes/linux/linux-omap-2.6.37/media/0033-v4l-Include-linux-videodev2.h-in-media-v4l2-ctrls.h.patch b/recipes/linux/linux-omap-2.6.37/media/0033-v4l-Include-linux-videodev2.h-in-media-v4l2-ctrls.h.patch new file mode 100644 index 0000000000..92dfe0d350 --- /dev/null +++ b/recipes/linux/linux-omap-2.6.37/media/0033-v4l-Include-linux-videodev2.h-in-media-v4l2-ctrls.h.patch @@ -0,0 +1,27 @@ +From 70e40e24f3da31a0c29f6f6042da9a085aa9ba7f Mon Sep 17 00:00:00 2001 +From: Laurent Pinchart +Date: Fri, 19 Nov 2010 15:20:06 +0100 +Subject: [PATCH 33/43] v4l: Include linux/videodev2.h in media/v4l2-ctrls.h + +The later makes extensive use of structures defined in the former. + +Signed-off-by: Laurent Pinchart +--- + include/media/v4l2-ctrls.h | 1 + + 1 files changed, 1 insertions(+), 0 deletions(-) + +diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h +index 9b7bea9..3b133b7 100644 +--- a/include/media/v4l2-ctrls.h ++++ b/include/media/v4l2-ctrls.h +@@ -23,6 +23,7 @@ + + #include + #include ++#include + + /* forward references */ + struct v4l2_ctrl_handler; +-- +1.6.6.1 + diff --git a/recipes/linux/linux-omap-2.6.37/media/0034-v4l-Fix-a-use-before-set-in-the-control-framework.patch b/recipes/linux/linux-omap-2.6.37/media/0034-v4l-Fix-a-use-before-set-in-the-control-framework.patch new file mode 100644 index 0000000000..adf8b4d4fe --- /dev/null +++ b/recipes/linux/linux-omap-2.6.37/media/0034-v4l-Fix-a-use-before-set-in-the-control-framework.patch @@ -0,0 +1,32 @@ +From d887b7e4224fa03f080ab6ede038eee8aac4c221 Mon Sep 17 00:00:00 2001 +From: Laurent Pinchart +Date: Tue, 7 Dec 2010 12:57:25 +0100 +Subject: [PATCH 34/43] v4l: Fix a use-before-set in the control framework + +v4l2_queryctrl sets the step value based on the control type. That would +be fine if it used the control type stored in the V4L2 kernel control +object, not the one stored in the userspace ioctl structure that has +just been memset to 0. Fix this. + +Signed-off-by: Laurent Pinchart +Acked-by: Hans Verkuil +--- + drivers/media/video/v4l2-ctrls.c | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c +index 9d2502c..5f74fec 100644 +--- a/drivers/media/video/v4l2-ctrls.c ++++ b/drivers/media/video/v4l2-ctrls.c +@@ -1338,7 +1338,7 @@ int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc) + qc->minimum = ctrl->minimum; + qc->maximum = ctrl->maximum; + qc->default_value = ctrl->default_value; +- if (qc->type == V4L2_CTRL_TYPE_MENU) ++ if (ctrl->type == V4L2_CTRL_TYPE_MENU) + qc->step = 1; + else + qc->step = ctrl->step; +-- +1.6.6.1 + diff --git a/recipes/linux/linux-omap-2.6.37/media/0035-v4l-Add-8-bit-YUYV-on-16-bit-bus-and-SGRBG10-media-b.patch b/recipes/linux/linux-omap-2.6.37/media/0035-v4l-Add-8-bit-YUYV-on-16-bit-bus-and-SGRBG10-media-b.patch new file mode 100644 index 0000000000..8660a14890 --- /dev/null +++ b/recipes/linux/linux-omap-2.6.37/media/0035-v4l-Add-8-bit-YUYV-on-16-bit-bus-and-SGRBG10-media-b.patch @@ -0,0 +1,60 @@ +From 4ad2d8ab7eef4bc2a482c228f334cfbf30d71855 Mon Sep 17 00:00:00 2001 +From: Laurent Pinchart +Date: Wed, 1 Sep 2010 17:59:36 +0200 +Subject: [PATCH 35/43] v4l: Add 8-bit YUYV on 16-bit bus and SGRBG10 media bus pixel codes + +Add the following media bus format code definitions: + +- V4L2_MBUS_FMT_SGRBG10_1X10 for 10-bit GRBG Bayer +- V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8 for 10-bit DPCM compressed GRBG Bayer +- V4L2_MBUS_FMT_YUYV16_1X16 for 8-bit YUYV on 16-bit bus +- V4L2_MBUS_FMT_UYVY16_1X16 for 8-bit UYVY on 16-bit bus +- V4L2_MBUS_FMT_YVYU16_1X16 for 8-bit YVYU on 16-bit bus +- V4L2_MBUS_FMT_VYUY16_1X16 for 8-bit VYUY on 16-bit bus + +Signed-off-by: Laurent Pinchart +--- + include/linux/v4l2-mediabus.h | 10 ++++++++-- + 1 files changed, 8 insertions(+), 2 deletions(-) + +diff --git a/include/linux/v4l2-mediabus.h b/include/linux/v4l2-mediabus.h +index cccfa34..c4caca3 100644 +--- a/include/linux/v4l2-mediabus.h ++++ b/include/linux/v4l2-mediabus.h +@@ -47,7 +47,7 @@ enum v4l2_mbus_pixelcode { + V4L2_MBUS_FMT_RGB565_2X8_BE = 0x1007, + V4L2_MBUS_FMT_RGB565_2X8_LE = 0x1008, + +- /* YUV (including grey) - next is 0x200f */ ++ /* YUV (including grey) - next is 0x2013 */ + V4L2_MBUS_FMT_Y8_1X8 = 0x2001, + V4L2_MBUS_FMT_UYVY8_1_5X8 = 0x2002, + V4L2_MBUS_FMT_VYUY8_1_5X8 = 0x2003, +@@ -60,17 +60,23 @@ enum v4l2_mbus_pixelcode { + V4L2_MBUS_FMT_Y10_1X10 = 0x200a, + V4L2_MBUS_FMT_YUYV10_2X10 = 0x200b, + V4L2_MBUS_FMT_YVYU10_2X10 = 0x200c, ++ V4L2_MBUS_FMT_UYVY8_1X16 = 0x200f, ++ V4L2_MBUS_FMT_VYUY8_1X16 = 0x2010, ++ V4L2_MBUS_FMT_YUYV8_1X16 = 0x2011, ++ V4L2_MBUS_FMT_YVYU8_1X16 = 0x2012, + V4L2_MBUS_FMT_YUYV10_1X20 = 0x200d, + V4L2_MBUS_FMT_YVYU10_1X20 = 0x200e, + +- /* Bayer - next is 0x3009 */ ++ /* Bayer - next is 0x300b */ + V4L2_MBUS_FMT_SBGGR8_1X8 = 0x3001, + V4L2_MBUS_FMT_SGRBG8_1X8 = 0x3002, ++ V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8 = 0x3009, + V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_BE = 0x3003, + V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE = 0x3004, + V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_BE = 0x3005, + V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_LE = 0x3006, + V4L2_MBUS_FMT_SBGGR10_1X10 = 0x3007, ++ V4L2_MBUS_FMT_SGRBG10_1X10 = 0x300a, + V4L2_MBUS_FMT_SBGGR12_1X12 = 0x3008, + }; + +-- +1.6.6.1 + diff --git a/recipes/linux/linux-omap-2.6.37/media/0036-v4l-Add-remaining-RAW10-patterns-w-DPCM-pixel-code-v.patch b/recipes/linux/linux-omap-2.6.37/media/0036-v4l-Add-remaining-RAW10-patterns-w-DPCM-pixel-code-v.patch new file mode 100644 index 0000000000..ec6c332f23 --- /dev/null +++ b/recipes/linux/linux-omap-2.6.37/media/0036-v4l-Add-remaining-RAW10-patterns-w-DPCM-pixel-code-v.patch @@ -0,0 +1,48 @@ +From a63be84f54298581d51efb8a4745747ca17a9d0d Mon Sep 17 00:00:00 2001 +From: Laurent Pinchart +Date: Fri, 3 Sep 2010 10:47:25 +0200 +Subject: [PATCH 36/43] v4l: Add remaining RAW10 patterns w DPCM pixel code variants + +This adds following formats: +- V4L2_MBUS_FMT_SRGGB10_1X10 +- V4L2_MBUS_FMT_SGBRG10_1X10 +- V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8 +- V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8 +- V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8 + +Signed-off-by: Sergio Aguirre +Signed-off-by: Laurent Pinchart +--- + include/linux/v4l2-mediabus.h | 7 ++++++- + 1 files changed, 6 insertions(+), 1 deletions(-) + +diff --git a/include/linux/v4l2-mediabus.h b/include/linux/v4l2-mediabus.h +index c4caca3..5c64924 100644 +--- a/include/linux/v4l2-mediabus.h ++++ b/include/linux/v4l2-mediabus.h +@@ -67,16 +67,21 @@ enum v4l2_mbus_pixelcode { + V4L2_MBUS_FMT_YUYV10_1X20 = 0x200d, + V4L2_MBUS_FMT_YVYU10_1X20 = 0x200e, + +- /* Bayer - next is 0x300b */ ++ /* Bayer - next is 0x3010 */ + V4L2_MBUS_FMT_SBGGR8_1X8 = 0x3001, + V4L2_MBUS_FMT_SGRBG8_1X8 = 0x3002, ++ V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8 = 0x300b, ++ V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8 = 0x300c, + V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8 = 0x3009, ++ V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8 = 0x300d, + V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_BE = 0x3003, + V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE = 0x3004, + V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_BE = 0x3005, + V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_LE = 0x3006, + V4L2_MBUS_FMT_SBGGR10_1X10 = 0x3007, ++ V4L2_MBUS_FMT_SGBRG10_1X10 = 0x300e, + V4L2_MBUS_FMT_SGRBG10_1X10 = 0x300a, ++ V4L2_MBUS_FMT_SRGGB10_1X10 = 0x300f, + V4L2_MBUS_FMT_SBGGR12_1X12 = 0x3008, + }; + +-- +1.6.6.1 + diff --git a/recipes/linux/linux-omap-2.6.37/media/0037-v4l-Add-missing-12-bits-bayer-media-bus-formats.patch b/recipes/linux/linux-omap-2.6.37/media/0037-v4l-Add-missing-12-bits-bayer-media-bus-formats.patch new file mode 100644 index 0000000000..5644da5f6f --- /dev/null +++ b/recipes/linux/linux-omap-2.6.37/media/0037-v4l-Add-missing-12-bits-bayer-media-bus-formats.patch @@ -0,0 +1,105 @@ +From 6585f70cdd7cbe63e6618d06a10819d31c7009fe Mon Sep 17 00:00:00 2001 +From: Laurent Pinchart +Date: Thu, 23 Dec 2010 15:14:49 +0100 +Subject: [PATCH 37/43] v4l: Add missing 12 bits bayer media bus formats + +Add codes and documentation for the following media bus formats: + +- V4L2_MBUS_FMT_SGBRG12_1X12 +- V4L2_MBUS_FMT_SGRBG12_1X12 +- V4L2_MBUS_FMT_SRGGB12_1X12 + +Signed-off-by: Laurent Pinchart +--- + Documentation/DocBook/v4l/subdev-formats.xml | 51 ++++++++++++++++++++++++++ + include/linux/v4l2-mediabus.h | 5 ++- + 2 files changed, 55 insertions(+), 1 deletions(-) + +diff --git a/Documentation/DocBook/v4l/subdev-formats.xml b/Documentation/DocBook/v4l/subdev-formats.xml +index 0cae572..2fed9be 100644 +--- a/Documentation/DocBook/v4l/subdev-formats.xml ++++ b/Documentation/DocBook/v4l/subdev-formats.xml +@@ -490,6 +490,57 @@ + b1 + b0 + ++ ++ V4L2_MBUS_FMT_SGBRG12_1X12 ++ 0x3010 ++ ++ g11 ++ g10 ++ g9 ++ g8 ++ g7 ++ g6 ++ g5 ++ g4 ++ g3 ++ g2 ++ g1 ++ g0 ++ ++ ++ V4L2_MBUS_FMT_SGRBG12_1X12 ++ 0x3011 ++ ++ g11 ++ g10 ++ g9 ++ g8 ++ g7 ++ g6 ++ g5 ++ g4 ++ g3 ++ g2 ++ g1 ++ g0 ++ ++ ++ V4L2_MBUS_FMT_SRGGB12_1X12 ++ 0x3012 ++ ++ r11 ++ r10 ++ r9 ++ r8 ++ r7 ++ r6 ++ r5 ++ r4 ++ r3 ++ r2 ++ r1 ++ r0 ++ + + V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8 + 0x300c +diff --git a/include/linux/v4l2-mediabus.h b/include/linux/v4l2-mediabus.h +index 5c64924..7054a7a 100644 +--- a/include/linux/v4l2-mediabus.h ++++ b/include/linux/v4l2-mediabus.h +@@ -67,7 +67,7 @@ enum v4l2_mbus_pixelcode { + V4L2_MBUS_FMT_YUYV10_1X20 = 0x200d, + V4L2_MBUS_FMT_YVYU10_1X20 = 0x200e, + +- /* Bayer - next is 0x3010 */ ++ /* Bayer - next is 0x3013 */ + V4L2_MBUS_FMT_SBGGR8_1X8 = 0x3001, + V4L2_MBUS_FMT_SGRBG8_1X8 = 0x3002, + V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8 = 0x300b, +@@ -83,6 +83,9 @@ enum v4l2_mbus_pixelcode { + V4L2_MBUS_FMT_SGRBG10_1X10 = 0x300a, + V4L2_MBUS_FMT_SRGGB10_1X10 = 0x300f, + V4L2_MBUS_FMT_SBGGR12_1X12 = 0x3008, ++ V4L2_MBUS_FMT_SGBRG12_1X12 = 0x3010, ++ V4L2_MBUS_FMT_SGRBG12_1X12 = 0x3011, ++ V4L2_MBUS_FMT_SRGGB12_1X12 = 0x3012, + }; + + /** +-- +1.6.6.1 + diff --git a/recipes/linux/linux-omap-2.6.37/media/0038-v4l-Add-12-bits-bayer-pixel-formats.patch b/recipes/linux/linux-omap-2.6.37/media/0038-v4l-Add-12-bits-bayer-pixel-formats.patch new file mode 100644 index 0000000000..9deb0ee304 --- /dev/null +++ b/recipes/linux/linux-omap-2.6.37/media/0038-v4l-Add-12-bits-bayer-pixel-formats.patch @@ -0,0 +1,35 @@ +From 859b5c38e30c3d41e7987a6bb46f7d062f7e02ad Mon Sep 17 00:00:00 2001 +From: Laurent Pinchart +Date: Thu, 23 Dec 2010 15:14:50 +0100 +Subject: [PATCH 38/43] v4l: Add 12 bits bayer pixel formats + +Add FCCs for the following pixel formats: + +- V4L2_PIX_FMT_SBGGR12 +- V4L2_PIX_FMT_SGBRG12 +- V4L2_PIX_FMT_SGRBG12 +- V4L2_PIX_FMT_SRGGB12 + +Signed-off-by: Laurent Pinchart +--- + include/linux/videodev2.h | 4 ++++ + 1 files changed, 4 insertions(+), 0 deletions(-) + +diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h +index 5f6f470..02da9e7 100644 +--- a/include/linux/videodev2.h ++++ b/include/linux/videodev2.h +@@ -328,6 +328,10 @@ struct v4l2_pix_format { + #define V4L2_PIX_FMT_SGBRG10 v4l2_fourcc('G', 'B', '1', '0') /* 10 GBGB.. RGRG.. */ + #define V4L2_PIX_FMT_SGRBG10 v4l2_fourcc('B', 'A', '1', '0') /* 10 GRGR.. BGBG.. */ + #define V4L2_PIX_FMT_SRGGB10 v4l2_fourcc('R', 'G', '1', '0') /* 10 RGRG.. GBGB.. */ ++#define V4L2_PIX_FMT_SBGGR12 v4l2_fourcc('B', 'G', '1', '2') /* 12 BGBG.. GRGR.. */ ++#define V4L2_PIX_FMT_SGBRG12 v4l2_fourcc('G', 'B', '1', '2') /* 12 GBGB.. RGRG.. */ ++#define V4L2_PIX_FMT_SGRBG12 v4l2_fourcc('B', 'A', '1', '2') /* 12 GRGR.. BGBG.. */ ++#define V4L2_PIX_FMT_SRGGB12 v4l2_fourcc('R', 'G', '1', '2') /* 12 RGRG.. GBGB.. */ + /* 10bit raw bayer DPCM compressed to 8 bits */ + #define V4L2_PIX_FMT_SGRBG10DPCM8 v4l2_fourcc('B', 'D', '1', '0') + /* +-- +1.6.6.1 + diff --git a/recipes/linux/linux-omap-2.6.37/media/0039-ARM-OMAP3-Update-Camera-ISP-definitions-for-OMAP3630.patch b/recipes/linux/linux-omap-2.6.37/media/0039-ARM-OMAP3-Update-Camera-ISP-definitions-for-OMAP3630.patch new file mode 100644 index 0000000000..73f40d7905 --- /dev/null +++ b/recipes/linux/linux-omap-2.6.37/media/0039-ARM-OMAP3-Update-Camera-ISP-definitions-for-OMAP3630.patch @@ -0,0 +1,99 @@ +From 0fe8d5d2b4d1e48bf2ef9b5803636dc68c91b5f2 Mon Sep 17 00:00:00 2001 +From: Tuukka Toivonen +Date: Tue, 2 Feb 2010 16:17:33 +0200 +Subject: [PATCH 39/43] ARM: OMAP3: Update Camera ISP definitions for OMAP3630 + +Add new/changed base address definitions and resources for +OMAP3630 ISP. + +The OMAP3430 CSI2PHY block is same as the OMAP3630 CSIPHY2 +block. But the later name is chosen as it gives more symmetry +to the names. + +Signed-off-by: Tuukka Toivonen +Signed-off-by: Vimarsh Zutshi +Acked-by: Tony Lindgren +--- + arch/arm/mach-omap2/devices.c | 28 ++++++++++++++++++++++++---- + arch/arm/plat-omap/include/plat/omap34xx.h | 16 ++++++++++++---- + 2 files changed, 36 insertions(+), 8 deletions(-) + +diff --git a/arch/arm/mach-omap2/devices.c b/arch/arm/mach-omap2/devices.c +index 381f4eb..40c64b9 100644 +--- a/arch/arm/mach-omap2/devices.c ++++ b/arch/arm/mach-omap2/devices.c +@@ -109,13 +109,33 @@ static struct resource omap3isp_resources[] = { + .flags = IORESOURCE_MEM, + }, + { +- .start = OMAP3430_ISP_CSI2A_BASE, +- .end = OMAP3430_ISP_CSI2A_END, ++ .start = OMAP3430_ISP_CSI2A_REGS1_BASE, ++ .end = OMAP3430_ISP_CSI2A_REGS1_END, + .flags = IORESOURCE_MEM, + }, + { +- .start = OMAP3430_ISP_CSI2PHY_BASE, +- .end = OMAP3430_ISP_CSI2PHY_END, ++ .start = OMAP3430_ISP_CSIPHY2_BASE, ++ .end = OMAP3430_ISP_CSIPHY2_END, ++ .flags = IORESOURCE_MEM, ++ }, ++ { ++ .start = OMAP3630_ISP_CSI2A_REGS2_BASE, ++ .end = OMAP3630_ISP_CSI2A_REGS2_END, ++ .flags = IORESOURCE_MEM, ++ }, ++ { ++ .start = OMAP3630_ISP_CSI2C_REGS1_BASE, ++ .end = OMAP3630_ISP_CSI2C_REGS1_END, ++ .flags = IORESOURCE_MEM, ++ }, ++ { ++ .start = OMAP3630_ISP_CSIPHY1_BASE, ++ .end = OMAP3630_ISP_CSIPHY1_END, ++ .flags = IORESOURCE_MEM, ++ }, ++ { ++ .start = OMAP3630_ISP_CSI2C_REGS2_BASE, ++ .end = OMAP3630_ISP_CSI2C_REGS2_END, + .flags = IORESOURCE_MEM, + }, + { +diff --git a/arch/arm/plat-omap/include/plat/omap34xx.h b/arch/arm/plat-omap/include/plat/omap34xx.h +index 98fc8b4..b9e8588 100644 +--- a/arch/arm/plat-omap/include/plat/omap34xx.h ++++ b/arch/arm/plat-omap/include/plat/omap34xx.h +@@ -56,8 +56,12 @@ + #define OMAP3430_ISP_RESZ_BASE (OMAP3430_ISP_BASE + 0x1000) + #define OMAP3430_ISP_SBL_BASE (OMAP3430_ISP_BASE + 0x1200) + #define OMAP3430_ISP_MMU_BASE (OMAP3430_ISP_BASE + 0x1400) +-#define OMAP3430_ISP_CSI2A_BASE (OMAP3430_ISP_BASE + 0x1800) +-#define OMAP3430_ISP_CSI2PHY_BASE (OMAP3430_ISP_BASE + 0x1970) ++#define OMAP3430_ISP_CSI2A_REGS1_BASE (OMAP3430_ISP_BASE + 0x1800) ++#define OMAP3430_ISP_CSIPHY2_BASE (OMAP3430_ISP_BASE + 0x1970) ++#define OMAP3630_ISP_CSI2A_REGS2_BASE (OMAP3430_ISP_BASE + 0x19C0) ++#define OMAP3630_ISP_CSI2C_REGS1_BASE (OMAP3430_ISP_BASE + 0x1C00) ++#define OMAP3630_ISP_CSIPHY1_BASE (OMAP3430_ISP_BASE + 0x1D70) ++#define OMAP3630_ISP_CSI2C_REGS2_BASE (OMAP3430_ISP_BASE + 0x1DC0) + + #define OMAP3430_ISP_END (OMAP3430_ISP_BASE + 0x06F) + #define OMAP3430_ISP_CBUFF_END (OMAP3430_ISP_CBUFF_BASE + 0x077) +@@ -69,8 +73,12 @@ + #define OMAP3430_ISP_RESZ_END (OMAP3430_ISP_RESZ_BASE + 0x0AB) + #define OMAP3430_ISP_SBL_END (OMAP3430_ISP_SBL_BASE + 0x0FB) + #define OMAP3430_ISP_MMU_END (OMAP3430_ISP_MMU_BASE + 0x06F) +-#define OMAP3430_ISP_CSI2A_END (OMAP3430_ISP_CSI2A_BASE + 0x16F) +-#define OMAP3430_ISP_CSI2PHY_END (OMAP3430_ISP_CSI2PHY_BASE + 0x007) ++#define OMAP3430_ISP_CSI2A_REGS1_END (OMAP3430_ISP_CSI2A_REGS1_BASE + 0x16F) ++#define OMAP3430_ISP_CSIPHY2_END (OMAP3430_ISP_CSIPHY2_BASE + 0x00B) ++#define OMAP3630_ISP_CSI2A_REGS2_END (OMAP3630_ISP_CSI2A_REGS2_BASE + 0x3F) ++#define OMAP3630_ISP_CSI2C_REGS1_END (OMAP3630_ISP_CSI2C_REGS1_BASE + 0x16F) ++#define OMAP3630_ISP_CSIPHY1_END (OMAP3630_ISP_CSIPHY1_BASE + 0x00B) ++#define OMAP3630_ISP_CSI2C_REGS2_END (OMAP3630_ISP_CSI2C_REGS2_BASE + 0x3F) + + #define OMAP34XX_HSUSB_OTG_BASE (L4_34XX_BASE + 0xAB000) + #define OMAP34XX_USBTLL_BASE (L4_34XX_BASE + 0x62000) +-- +1.6.6.1 + diff --git a/recipes/linux/linux-omap-2.6.37/media/0040-omap3-Remove-unusued-ISP-CBUFF-resource.patch b/recipes/linux/linux-omap-2.6.37/media/0040-omap3-Remove-unusued-ISP-CBUFF-resource.patch new file mode 100644 index 0000000000..d48e0e6085 --- /dev/null +++ b/recipes/linux/linux-omap-2.6.37/media/0040-omap3-Remove-unusued-ISP-CBUFF-resource.patch @@ -0,0 +1,32 @@ +From 70b39450b2de8e96504332730c9b00c663cfeaf9 Mon Sep 17 00:00:00 2001 +From: Sergio Aguirre +Date: Mon, 15 Nov 2010 08:29:56 -0600 +Subject: [PATCH 40/43] omap3: Remove unusued ISP CBUFF resource + +The ISP CBUFF module isn't use, its resource isn't needed. + +Signed-off-by: Sergio Aguirre +Acked-by: Laurent Pinchart +--- + arch/arm/mach-omap2/devices.c | 5 ----- + 1 files changed, 0 insertions(+), 5 deletions(-) + +diff --git a/arch/arm/mach-omap2/devices.c b/arch/arm/mach-omap2/devices.c +index 40c64b9..60cb86f 100644 +--- a/arch/arm/mach-omap2/devices.c ++++ b/arch/arm/mach-omap2/devices.c +@@ -69,11 +69,6 @@ static struct resource omap3isp_resources[] = { + .flags = IORESOURCE_MEM, + }, + { +- .start = OMAP3430_ISP_CBUFF_BASE, +- .end = OMAP3430_ISP_CBUFF_END, +- .flags = IORESOURCE_MEM, +- }, +- { + .start = OMAP3430_ISP_CCP2_BASE, + .end = OMAP3430_ISP_CCP2_END, + .flags = IORESOURCE_MEM, +-- +1.6.6.1 + diff --git a/recipes/linux/linux-omap-2.6.37/media/0041-omap3-Add-function-to-register-omap3isp-platform-dev.patch b/recipes/linux/linux-omap-2.6.37/media/0041-omap3-Add-function-to-register-omap3isp-platform-dev.patch new file mode 100644 index 0000000000..8bcbd73c65 --- /dev/null +++ b/recipes/linux/linux-omap-2.6.37/media/0041-omap3-Add-function-to-register-omap3isp-platform-dev.patch @@ -0,0 +1,91 @@ +From d59f7c080e1c0d35a71f788350b619e76cee5033 Mon Sep 17 00:00:00 2001 +From: Laurent Pinchart +Date: Mon, 14 Dec 2009 13:09:07 +0200 +Subject: [PATCH 41/43] omap3: Add function to register omap3isp platform device structure + +The omap3isp platform device requires platform data. Instead of +registering the device in omap2_init_devices(), export an +omap3_init_camera() function to fill the device structure with the +platform data pointer and register the device. + +Signed-off-by: Laurent Pinchart +Acked-by: Tony Lindgren +--- + arch/arm/mach-omap2/devices.c | 20 +++++++++++--------- + arch/arm/mach-omap2/devices.h | 17 +++++++++++++++++ + 2 files changed, 28 insertions(+), 9 deletions(-) + create mode 100644 arch/arm/mach-omap2/devices.h + +diff --git a/arch/arm/mach-omap2/devices.c b/arch/arm/mach-omap2/devices.c +index 60cb86f..9b243be 100644 +--- a/arch/arm/mach-omap2/devices.c ++++ b/arch/arm/mach-omap2/devices.c +@@ -34,6 +34,8 @@ + #include "mux.h" + #include "control.h" + ++#include "devices.h" ++ + #if defined(CONFIG_VIDEO_OMAP2) || defined(CONFIG_VIDEO_OMAP2_MODULE) + + static struct resource cam_resources[] = { +@@ -59,8 +61,11 @@ static inline void omap_init_camera(void) + { + platform_device_register(&omap_cam_device); + } +- +-#elif defined(CONFIG_VIDEO_OMAP3) || defined(CONFIG_VIDEO_OMAP3_MODULE) ++#else ++static inline void omap_init_camera(void) ++{ ++} ++#endif + + static struct resource omap3isp_resources[] = { + { +@@ -146,15 +151,12 @@ static struct platform_device omap3isp_device = { + .resource = omap3isp_resources, + }; + +-static inline void omap_init_camera(void) +-{ +- platform_device_register(&omap3isp_device); +-} +-#else +-static inline void omap_init_camera(void) ++int omap3_init_camera(void *pdata) + { ++ omap3isp_device.dev.platform_data = pdata; ++ return platform_device_register(&omap3isp_device); + } +-#endif ++EXPORT_SYMBOL_GPL(omap3_init_camera); + + #if defined(CONFIG_OMAP_MBOX_FWK) || defined(CONFIG_OMAP_MBOX_FWK_MODULE) + +diff --git a/arch/arm/mach-omap2/devices.h b/arch/arm/mach-omap2/devices.h +new file mode 100644 +index 0000000..12ddb8a +--- /dev/null ++++ b/arch/arm/mach-omap2/devices.h +@@ -0,0 +1,17 @@ ++/* ++ * arch/arm/mach-omap2/devices.h ++ * ++ * OMAP2 platform device setup/initialization ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ */ ++ ++#ifndef __ARCH_ARM_MACH_OMAP_DEVICES_H ++#define __ARCH_ARM_MACH_OMAP_DEVICES_H ++ ++int omap3_init_camera(void *pdata); ++ ++#endif +-- +1.6.6.1 + diff --git a/recipes/linux/linux-omap-2.6.37/media/0042-omap2-Fix-camera-resources-for-multiomap.patch b/recipes/linux/linux-omap-2.6.37/media/0042-omap2-Fix-camera-resources-for-multiomap.patch new file mode 100644 index 0000000000..273d6d05d2 --- /dev/null +++ b/recipes/linux/linux-omap-2.6.37/media/0042-omap2-Fix-camera-resources-for-multiomap.patch @@ -0,0 +1,70 @@ +From 5cc262328a97b1d048ae42234909ac33c2fc342c Mon Sep 17 00:00:00 2001 +From: Sergio Aguirre +Date: Mon, 15 Nov 2010 08:29:54 -0600 +Subject: [PATCH 42/43] omap2: Fix camera resources for multiomap + +Make sure the kernel can be compiled with both OMAP2 and OMAP3 camera +support linked in, and give public symbols proper omap2/omap3 prefixes. + +Signed-off-by: Sergio Aguirre +Acked-by: Laurent Pinchart +--- + arch/arm/mach-omap2/devices.c | 25 ++++++++++++------------- + 1 files changed, 12 insertions(+), 13 deletions(-) + +diff --git a/arch/arm/mach-omap2/devices.c b/arch/arm/mach-omap2/devices.c +index 9b243be..c132c65 100644 +--- a/arch/arm/mach-omap2/devices.c ++++ b/arch/arm/mach-omap2/devices.c +@@ -38,7 +38,7 @@ + + #if defined(CONFIG_VIDEO_OMAP2) || defined(CONFIG_VIDEO_OMAP2_MODULE) + +-static struct resource cam_resources[] = { ++static struct resource omap2cam_resources[] = { + { + .start = OMAP24XX_CAMERA_BASE, + .end = OMAP24XX_CAMERA_BASE + 0xfff, +@@ -50,21 +50,12 @@ static struct resource cam_resources[] = { + } + }; + +-static struct platform_device omap_cam_device = { ++static struct platform_device omap2cam_device = { + .name = "omap24xxcam", + .id = -1, +- .num_resources = ARRAY_SIZE(cam_resources), +- .resource = cam_resources, ++ .num_resources = ARRAY_SIZE(omap2cam_resources), ++ .resource = omap2cam_resources, + }; +- +-static inline void omap_init_camera(void) +-{ +- platform_device_register(&omap_cam_device); +-} +-#else +-static inline void omap_init_camera(void) +-{ +-} + #endif + + static struct resource omap3isp_resources[] = { +@@ -158,6 +149,14 @@ int omap3_init_camera(void *pdata) + } + EXPORT_SYMBOL_GPL(omap3_init_camera); + ++static inline void omap_init_camera(void) ++{ ++#if defined(CONFIG_VIDEO_OMAP2) || defined(CONFIG_VIDEO_OMAP2_MODULE) ++ if (cpu_is_omap24xx()) ++ platform_device_register(&omap2cam_device); ++#endif ++} ++ + #if defined(CONFIG_OMAP_MBOX_FWK) || defined(CONFIG_OMAP_MBOX_FWK_MODULE) + + #define MBOX_REG_SIZE 0x120 +-- +1.6.6.1 + diff --git a/recipes/linux/linux-omap-2.6.37/media/0043-OMAP3-ISP-driver.patch b/recipes/linux/linux-omap-2.6.37/media/0043-OMAP3-ISP-driver.patch new file mode 100644 index 0000000000..b4e9784865 --- /dev/null +++ b/recipes/linux/linux-omap-2.6.37/media/0043-OMAP3-ISP-driver.patch @@ -0,0 +1,21513 @@ +From f12978691d5189949c9296bceb43c5b272c9c03c Mon Sep 17 00:00:00 2001 +From: Laurent Pinchart +Date: Tue, 17 Feb 2009 09:23:45 -0600 +Subject: [PATCH 43/43] OMAP3 ISP driver + +Last 10 commits from upstream are + +omap3isp: Autoidle enabled for ISP +omap3isp: enable AUTOIDLE through module parameter +omap3isp: preview: Fix defect correct config function +omap3isp: video: Replace BUG with WARN_ON in case of buffer queue error +omap3isp: Add module device table +omap3isp: csi2: Print registers on stream on +v4l: OMAP3 ISP CCDC: Add support for 8bit greyscale sensors +omap3isp: ccdc: Set default DC subtract value to 0 +omap3isp: Prefix all public symbols with omap3isp_ +omap3isp: Fix dependencies and mark as experimental + +Signed-off-by: Laurent Pinchart +Signed-off-by: Sakari Ailus +Signed-off-by: David Cohen +Signed-off-by: Stanimir Varbanov +Signed-off-by: Vimarsh Zutshi +Signed-off-by: Tuukka Toivonen +Signed-off-by: Sergio Aguirre +Signed-off-by: Antti Koskipaa +Signed-off-by: Ivan T. Ivanov +Signed-off-by: RaniSuneela +Signed-off-by: Atanas Filipov +Signed-off-by: Gjorgji Rosikopulos +Signed-off-by: Hiroshi DOYU +Signed-off-by: Nayden Kanchev +Signed-off-by: Phil Carmody +Signed-off-by: Artem Bityutskiy +Signed-off-by: Dominic Curran +Signed-off-by: Ilkka Myllyperkio +Signed-off-by: Pallavi Kulkarni +Signed-off-by: Vaibhav Hiremath +--- + drivers/media/video/Kconfig | 13 + + drivers/media/video/Makefile | 2 + + drivers/media/video/isp/Makefile | 13 + + drivers/media/video/isp/cfa_coef_table.h | 601 +++++++ + drivers/media/video/isp/gamma_table.h | 90 + + drivers/media/video/isp/isp.c | 2221 +++++++++++++++++++++++++ + drivers/media/video/isp/isp.h | 427 +++++ + drivers/media/video/isp/ispccdc.c | 2280 ++++++++++++++++++++++++++ + drivers/media/video/isp/ispccdc.h | 223 +++ + drivers/media/video/isp/ispccp2.c | 1189 ++++++++++++++ + drivers/media/video/isp/ispccp2.h | 101 ++ + drivers/media/video/isp/ispcsi2.c | 1332 +++++++++++++++ + drivers/media/video/isp/ispcsi2.h | 169 ++ + drivers/media/video/isp/ispcsiphy.c | 247 +++ + drivers/media/video/isp/ispcsiphy.h | 74 + + drivers/media/video/isp/isph3a.h | 117 ++ + drivers/media/video/isp/isph3a_aewb.c | 374 +++++ + drivers/media/video/isp/isph3a_af.c | 429 +++++ + drivers/media/video/isp/isphist.c | 520 ++++++ + drivers/media/video/isp/isphist.h | 40 + + drivers/media/video/isp/isppreview.c | 2120 ++++++++++++++++++++++++ + drivers/media/video/isp/isppreview.h | 214 +++ + drivers/media/video/isp/ispqueue.c | 1136 +++++++++++++ + drivers/media/video/isp/ispqueue.h | 185 +++ + drivers/media/video/isp/ispreg.h | 1589 ++++++++++++++++++ + drivers/media/video/isp/ispresizer.c | 1710 +++++++++++++++++++ + drivers/media/video/isp/ispresizer.h | 150 ++ + drivers/media/video/isp/ispstat.c | 1100 +++++++++++++ + drivers/media/video/isp/ispstat.h | 169 ++ + drivers/media/video/isp/ispvideo.c | 1264 ++++++++++++++ + drivers/media/video/isp/ispvideo.h | 202 +++ + drivers/media/video/isp/luma_enhance_table.h | 154 ++ + drivers/media/video/isp/noise_filter_table.h | 90 + + include/linux/Kbuild | 1 + + include/linux/omap3isp.h | 631 +++++++ + 35 files changed, 21177 insertions(+), 0 deletions(-) + create mode 100644 drivers/media/video/isp/Makefile + create mode 100644 drivers/media/video/isp/cfa_coef_table.h + create mode 100644 drivers/media/video/isp/gamma_table.h + create mode 100644 drivers/media/video/isp/isp.c + create mode 100644 drivers/media/video/isp/isp.h + create mode 100644 drivers/media/video/isp/ispccdc.c + create mode 100644 drivers/media/video/isp/ispccdc.h + create mode 100644 drivers/media/video/isp/ispccp2.c + create mode 100644 drivers/media/video/isp/ispccp2.h + create mode 100644 drivers/media/video/isp/ispcsi2.c + create mode 100644 drivers/media/video/isp/ispcsi2.h + create mode 100644 drivers/media/video/isp/ispcsiphy.c + create mode 100644 drivers/media/video/isp/ispcsiphy.h + create mode 100644 drivers/media/video/isp/isph3a.h + create mode 100644 drivers/media/video/isp/isph3a_aewb.c + create mode 100644 drivers/media/video/isp/isph3a_af.c + create mode 100644 drivers/media/video/isp/isphist.c + create mode 100644 drivers/media/video/isp/isphist.h + create mode 100644 drivers/media/video/isp/isppreview.c + create mode 100644 drivers/media/video/isp/isppreview.h + create mode 100644 drivers/media/video/isp/ispqueue.c + create mode 100644 drivers/media/video/isp/ispqueue.h + create mode 100644 drivers/media/video/isp/ispreg.h + create mode 100644 drivers/media/video/isp/ispresizer.c + create mode 100644 drivers/media/video/isp/ispresizer.h + create mode 100644 drivers/media/video/isp/ispstat.c + create mode 100644 drivers/media/video/isp/ispstat.h + create mode 100644 drivers/media/video/isp/ispvideo.c + create mode 100644 drivers/media/video/isp/ispvideo.h + create mode 100644 drivers/media/video/isp/luma_enhance_table.h + create mode 100644 drivers/media/video/isp/noise_filter_table.h + create mode 100644 include/linux/omap3isp.h + +diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig +index 6830d28..60c2bf0 100644 +--- a/drivers/media/video/Kconfig ++++ b/drivers/media/video/Kconfig +@@ -722,6 +722,19 @@ config VIDEO_VIA_CAMERA + Chrome9 chipsets. Currently only tested on OLPC xo-1.5 systems + with ov7670 sensors. + ++config VIDEO_OMAP3 ++ tristate "OMAP 3 Camera support (EXPERIMENTAL)" ++ select OMAP_IOMMU ++ depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && ARCH_OMAP3 && EXPERIMENTAL ++ ---help--- ++ Driver for an OMAP 3 camera controller. ++ ++config VIDEO_OMAP3_DEBUG ++ bool "OMAP 3 Camera debug messages" ++ depends on VIDEO_OMAP3 ++ ---help--- ++ Enable debug messages on OMAP 3 camera controller driver. ++ + config SOC_CAMERA + tristate "SoC camera support" + depends on VIDEO_V4L2 && HAS_DMA && I2C +diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile +index adc1bd5..bd2f556 100644 +--- a/drivers/media/video/Makefile ++++ b/drivers/media/video/Makefile +@@ -124,6 +124,8 @@ obj-$(CONFIG_VIDEO_CAFE_CCIC) += cafe_ccic.o + + obj-$(CONFIG_VIDEO_VIA_CAMERA) += via-camera.o + ++obj-$(CONFIG_VIDEO_OMAP3) += isp/ ++ + obj-$(CONFIG_USB_DABUSB) += dabusb.o + obj-$(CONFIG_USB_SE401) += se401.o + obj-$(CONFIG_USB_ZR364XX) += zr364xx.o +diff --git a/drivers/media/video/isp/Makefile b/drivers/media/video/isp/Makefile +new file mode 100644 +index 0000000..b1b3447 +--- /dev/null ++++ b/drivers/media/video/isp/Makefile +@@ -0,0 +1,13 @@ ++# Makefile for OMAP3 ISP driver ++ ++ifdef CONFIG_VIDEO_OMAP3_DEBUG ++EXTRA_CFLAGS += -DDEBUG ++endif ++ ++omap3-isp-objs += \ ++ isp.o ispqueue.o ispvideo.o \ ++ ispcsiphy.o ispccp2.o ispcsi2.o \ ++ ispccdc.o isppreview.o ispresizer.o \ ++ ispstat.o isph3a_aewb.o isph3a_af.o isphist.o ++ ++obj-$(CONFIG_VIDEO_OMAP3) += omap3-isp.o +diff --git a/drivers/media/video/isp/cfa_coef_table.h b/drivers/media/video/isp/cfa_coef_table.h +new file mode 100644 +index 0000000..4ec3fff +--- /dev/null ++++ b/drivers/media/video/isp/cfa_coef_table.h +@@ -0,0 +1,601 @@ ++/* ++ * cfa_coef_table.h ++ * ++ * TI OMAP3 ISP - CFA coefficients table ++ * ++ * Copyright (C) 2009-2010 Nokia Corporation ++ * ++ * Contacts: Laurent Pinchart ++ * Sakari Ailus ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++244, ++0, ++247, ++0, ++12, ++27, ++36, ++247, ++250, ++0, ++27, ++0, ++4, ++250, ++12, ++244, ++248, ++0, ++0, ++0, ++0, ++40, ++0, ++0, ++244, ++12, ++250, ++4, ++0, ++27, ++0, ++250, ++247, ++36, ++27, ++12, ++0, ++247, ++0, ++244, ++0, ++0, ++40, ++0, ++0, ++0, ++0, ++248, ++244, ++0, ++247, ++0, ++12, ++27, ++36, ++247, ++250, ++0, ++27, ++0, ++4, ++250, ++12, ++244, ++248, ++0, ++0, ++0, ++0, ++40, ++0, ++0, ++244, ++12, ++250, ++4, ++0, ++27, ++0, ++250, ++247, ++36, ++27, ++12, ++0, ++247, ++0, ++244, ++0, ++0, ++40, ++0, ++0, ++0, ++0, ++248, ++244, ++0, ++247, ++0, ++12, ++27, ++36, ++247, ++250, ++0, ++27, ++0, ++4, ++250, ++12, ++244, ++248, ++0, ++0, ++0, ++0, ++40, ++0, ++0, ++244, ++12, ++250, ++4, ++0, ++27, ++0, ++250, ++247, ++36, ++27, ++12, ++0, ++247, ++0, ++244, ++0, ++0, ++40, ++0, ++0, ++0, ++0, ++248, ++0, ++247, ++0, ++244, ++247, ++36, ++27, ++12, ++0, ++27, ++0, ++250, ++244, ++12, ++250, ++4, ++0, ++0, ++0, ++248, ++0, ++0, ++40, ++0, ++4, ++250, ++12, ++244, ++250, ++0, ++27, ++0, ++12, ++27, ++36, ++247, ++244, ++0, ++247, ++0, ++0, ++40, ++0, ++0, ++248, ++0, ++0, ++0, ++0, ++247, ++0, ++244, ++247, ++36, ++27, ++12, ++0, ++27, ++0, ++250, ++244, ++12, ++250, ++4, ++0, ++0, ++0, ++248, ++0, ++0, ++40, ++0, ++4, ++250, ++12, ++244, ++250, ++0, ++27, ++0, ++12, ++27, ++36, ++247, ++244, ++0, ++247, ++0, ++0, ++40, ++0, ++0, ++248, ++0, ++0, ++0, ++0, ++247, ++0, ++244, ++247, ++36, ++27, ++12, ++0, ++27, ++0, ++250, ++244, ++12, ++250, ++4, ++0, ++0, ++0, ++248, ++0, ++0, ++40, ++0, ++4, ++250, ++12, ++244, ++250, ++0, ++27, ++0, ++12, ++27, ++36, ++247, ++244, ++0, ++247, ++0, ++0, ++40, ++0, ++0, ++248, ++0, ++0, ++0, ++4, ++250, ++12, ++244, ++250, ++0, ++27, ++0, ++12, ++27, ++36, ++247, ++244, ++0, ++247, ++0, ++0, ++0, ++0, ++248, ++0, ++0, ++40, ++0, ++0, ++247, ++0, ++244, ++247, ++36, ++27, ++12, ++0, ++27, ++0, ++250, ++244, ++12, ++250, ++4, ++0, ++40, ++0, ++0, ++248, ++0, ++0, ++0, ++4, ++250, ++12, ++244, ++250, ++0, ++27, ++0, ++12, ++27, ++36, ++247, ++244, ++0, ++247, ++0, ++0, ++0, ++0, ++248, ++0, ++0, ++40, ++0, ++0, ++247, ++0, ++244, ++247, ++36, ++27, ++12, ++0, ++27, ++0, ++250, ++244, ++12, ++250, ++4, ++0, ++40, ++0, ++0, ++248, ++0, ++0, ++0, ++4, ++250, ++12, ++244, ++250, ++0, ++27, ++0, ++12, ++27, ++36, ++247, ++244, ++0, ++247, ++0, ++0, ++0, ++0, ++248, ++0, ++0, ++40, ++0, ++0, ++247, ++0, ++244, ++247, ++36, ++27, ++12, ++0, ++27, ++0, ++250, ++244, ++12, ++250, ++4, ++0, ++40, ++0, ++0, ++248, ++0, ++0, ++0, ++244, ++12, ++250, ++4, ++0, ++27, ++0, ++250, ++247, ++36, ++27, ++12, ++0, ++247, ++0, ++244, ++248, ++0, ++0, ++0, ++0, ++40, ++0, ++0, ++244, ++0, ++247, ++0, ++12, ++27, ++36, ++247, ++250, ++0, ++27, ++0, ++4, ++250, ++12, ++244, ++0, ++0, ++40, ++0, ++0, ++0, ++0, ++248, ++244, ++12, ++250, ++4, ++0, ++27, ++0, ++250, ++247, ++36, ++27, ++12, ++0, ++247, ++0, ++244, ++248, ++0, ++0, ++0, ++0, ++40, ++0, ++0, ++244, ++0, ++247, ++0, ++12, ++27, ++36, ++247, ++250, ++0, ++27, ++0, ++4, ++250, ++12, ++244, ++0, ++0, ++40, ++0, ++0, ++0, ++0, ++248, ++244, ++12, ++250, ++4, ++0, ++27, ++0, ++250, ++247, ++36, ++27, ++12, ++0, ++247, ++0, ++244, ++248, ++0, ++0, ++0, ++0, ++40, ++0, ++0, ++244, ++0, ++247, ++0, ++12, ++27, ++36, ++247, ++250, ++0, ++27, ++0, ++4, ++250, ++12, ++244, ++0, ++0, ++40, ++0, ++0, ++0, ++0, ++248 +diff --git a/drivers/media/video/isp/gamma_table.h b/drivers/media/video/isp/gamma_table.h +new file mode 100644 +index 0000000..c2f7ec1 +--- /dev/null ++++ b/drivers/media/video/isp/gamma_table.h +@@ -0,0 +1,90 @@ ++/* ++ * gamma_table.h ++ * ++ * TI OMAP3 ISP - Default gamma table for all components ++ * ++ * Copyright (C) 2010 Nokia Corporation ++ * Copyright (C) 2009 Texas Instruments, Inc. ++ * ++ * Contacts: Laurent Pinchart ++ * Sakari Ailus ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++ 0, 0, 1, 2, 3, 3, 4, 5, 6, 8, 10, 12, 14, 16, 18, 20, ++ 22, 23, 25, 26, 28, 29, 31, 32, 34, 35, 36, 37, 39, 40, 41, 42, ++ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 52, 53, 54, 55, 56, 57, ++ 58, 59, 60, 61, 62, 63, 63, 64, 65, 66, 66, 67, 68, 69, 69, 70, ++ 71, 72, 72, 73, 74, 75, 75, 76, 77, 78, 78, 79, 80, 81, 81, 82, ++ 83, 84, 84, 85, 86, 87, 88, 88, 89, 90, 91, 91, 92, 93, 94, 94, ++ 95, 96, 97, 97, 98, 98, 99, 99, 100, 100, 101, 101, 102, 103, 104, 104, ++105, 106, 107, 108, 108, 109, 110, 111, 111, 112, 113, 114, 114, 115, 116, 117, ++117, 118, 119, 119, 120, 120, 121, 121, 122, 122, 123, 123, 124, 124, 125, 125, ++126, 126, 127, 127, 128, 128, 129, 129, 130, 130, 131, 131, 132, 132, 133, 133, ++134, 134, 135, 135, 136, 136, 137, 137, 138, 138, 139, 139, 140, 140, 141, 141, ++142, 142, 143, 143, 144, 144, 145, 145, 146, 146, 147, 147, 148, 148, 149, 149, ++150, 150, 151, 151, 152, 152, 153, 153, 153, 153, 154, 154, 154, 154, 155, 155, ++156, 156, 157, 157, 158, 158, 158, 159, 159, 159, 160, 160, 160, 161, 161, 162, ++162, 163, 163, 164, 164, 164, 164, 165, 165, 165, 165, 166, 166, 167, 167, 168, ++168, 169, 169, 170, 170, 170, 170, 171, 171, 171, 171, 172, 172, 173, 173, 174, ++174, 175, 175, 176, 176, 176, 176, 177, 177, 177, 177, 178, 178, 178, 178, 179, ++179, 179, 179, 180, 180, 180, 180, 181, 181, 181, 181, 182, 182, 182, 182, 183, ++183, 183, 183, 184, 184, 184, 184, 185, 185, 185, 185, 186, 186, 186, 186, 187, ++187, 187, 187, 188, 188, 188, 188, 189, 189, 189, 189, 190, 190, 190, 190, 191, ++191, 191, 191, 192, 192, 192, 192, 193, 193, 193, 193, 194, 194, 194, 194, 195, ++195, 195, 195, 196, 196, 196, 196, 197, 197, 197, 197, 198, 198, 198, 198, 199, ++199, 199, 199, 200, 200, 200, 200, 201, 201, 201, 201, 202, 202, 202, 203, 203, ++203, 203, 204, 204, 204, 204, 205, 205, 205, 205, 206, 206, 206, 206, 207, 207, ++207, 207, 208, 208, 208, 208, 209, 209, 209, 209, 210, 210, 210, 210, 210, 210, ++210, 210, 210, 210, 210, 210, 211, 211, 211, 211, 211, 211, 211, 211, 211, 211, ++211, 212, 212, 212, 212, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, ++213, 214, 214, 214, 214, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, 215, ++216, 216, 216, 216, 217, 217, 217, 217, 218, 218, 218, 218, 219, 219, 219, 219, ++219, 219, 219, 219, 219, 219, 219, 219, 220, 220, 220, 220, 221, 221, 221, 221, ++221, 221, 221, 221, 221, 221, 221, 222, 222, 222, 222, 223, 223, 223, 223, 223, ++223, 223, 223, 223, 223, 223, 223, 224, 224, 224, 224, 225, 225, 225, 225, 225, ++225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 226, 226, ++226, 226, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 228, 228, ++228, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, 230, 230, 230, ++230, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 231, 232, 232, 232, ++232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, ++233, 233, 233, 233, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 234, 235, ++235, 235, 235, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, ++236, 236, 236, 236, 236, 236, 237, 237, 237, 237, 238, 238, 238, 238, 238, 238, ++238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, 238, ++238, 238, 238, 238, 238, 239, 239, 239, 239, 240, 240, 240, 240, 240, 240, 240, ++240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, ++240, 240, 240, 240, 241, 241, 241, 241, 242, 242, 242, 242, 242, 242, 242, 242, ++242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, ++242, 242, 243, 243, 243, 243, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, ++244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, ++244, 245, 245, 245, 245, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, ++246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, ++246, 246, 246, 246, 246, 246, 246, 247, 247, 247, 247, 248, 248, 248, 248, 248, ++248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, ++248, 248, 248, 248, 248, 248, 249, 249, 249, 249, 250, 250, 250, 250, 250, 250, ++250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, ++250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, ++250, 250, 250, 250, 251, 251, 251, 251, 252, 252, 252, 252, 252, 252, 252, 252, ++252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, ++252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, ++252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, ++252, 252, 252, 252, 252, 252, 252, 252, 253, 253, 253, 253, 253, 253, 253, 253, ++253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, ++253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, ++253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, ++253, 254, 254, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, ++255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, +diff --git a/drivers/media/video/isp/isp.c b/drivers/media/video/isp/isp.c +new file mode 100644 +index 0000000..6f8527c +--- /dev/null ++++ b/drivers/media/video/isp/isp.c +@@ -0,0 +1,2221 @@ ++/* ++ * isp.c ++ * ++ * TI OMAP3 ISP - Core ++ * ++ * Copyright (C) 2006-2010 Nokia Corporation ++ * Copyright (C) 2007-2009 Texas Instruments, Inc. ++ * ++ * Contacts: Laurent Pinchart ++ * Sakari Ailus ++ * ++ * Contributors: ++ * Laurent Pinchart ++ * Sakari Ailus ++ * David Cohen ++ * Stanimir Varbanov ++ * Vimarsh Zutshi ++ * Tuukka Toivonen ++ * Sergio Aguirre ++ * Antti Koskipaa ++ * Ivan T. Ivanov ++ * RaniSuneela ++ * Atanas Filipov ++ * Gjorgji Rosikopulos ++ * Hiroshi DOYU ++ * Nayden Kanchev ++ * Phil Carmody ++ * Artem Bityutskiy ++ * Dominic Curran ++ * Ilkka Myllyperkio ++ * Pallavi Kulkarni ++ * Vaibhav Hiremath ++ * Mohit Jalori ++ * Sameer Venkatraman ++ * Senthilvadivu Guruswamy ++ * Thara Gopinath ++ * Toni Leinonen ++ * Troy Laramy ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "isp.h" ++#include "ispreg.h" ++#include "ispccdc.h" ++#include "isppreview.h" ++#include "ispresizer.h" ++#include "ispcsi2.h" ++#include "ispccp2.h" ++#include "isph3a.h" ++#include "isphist.h" ++ ++static unsigned int autoidle; ++module_param(autoidle, int, 0444); ++MODULE_PARM_DESC(autoidle, "Enable OMAP3ISP AUTOIDLE support"); ++ ++static void isp_save_ctx(struct isp_device *isp); ++ ++static void isp_restore_ctx(struct isp_device *isp); ++ ++static const struct isp_res_mapping isp_res_maps[] = { ++ { ++ .isp_rev = ISP_REVISION_2_0, ++ .map = 1 << OMAP3_ISP_IOMEM_MAIN | ++ 1 << OMAP3_ISP_IOMEM_CCP2 | ++ 1 << OMAP3_ISP_IOMEM_CCDC | ++ 1 << OMAP3_ISP_IOMEM_HIST | ++ 1 << OMAP3_ISP_IOMEM_H3A | ++ 1 << OMAP3_ISP_IOMEM_PREV | ++ 1 << OMAP3_ISP_IOMEM_RESZ | ++ 1 << OMAP3_ISP_IOMEM_SBL | ++ 1 << OMAP3_ISP_IOMEM_CSI2A_REGS1 | ++ 1 << OMAP3_ISP_IOMEM_CSIPHY2, ++ }, ++ { ++ .isp_rev = ISP_REVISION_15_0, ++ .map = 1 << OMAP3_ISP_IOMEM_MAIN | ++ 1 << OMAP3_ISP_IOMEM_CCP2 | ++ 1 << OMAP3_ISP_IOMEM_CCDC | ++ 1 << OMAP3_ISP_IOMEM_HIST | ++ 1 << OMAP3_ISP_IOMEM_H3A | ++ 1 << OMAP3_ISP_IOMEM_PREV | ++ 1 << OMAP3_ISP_IOMEM_RESZ | ++ 1 << OMAP3_ISP_IOMEM_SBL | ++ 1 << OMAP3_ISP_IOMEM_CSI2A_REGS1 | ++ 1 << OMAP3_ISP_IOMEM_CSIPHY2 | ++ 1 << OMAP3_ISP_IOMEM_CSI2A_REGS2 | ++ 1 << OMAP3_ISP_IOMEM_CSI2C_REGS1 | ++ 1 << OMAP3_ISP_IOMEM_CSIPHY1 | ++ 1 << OMAP3_ISP_IOMEM_CSI2C_REGS2, ++ }, ++}; ++ ++/* Structure for saving/restoring ISP module registers */ ++static struct isp_reg isp_reg_list[] = { ++ {OMAP3_ISP_IOMEM_MAIN, ISP_SYSCONFIG, 0}, ++ {OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, 0}, ++ {OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL, 0}, ++ {0, ISP_TOK_TERM, 0} ++}; ++ ++/* ++ * omap3isp_flush - Post pending L3 bus writes by doing a register readback ++ * @isp: OMAP3 ISP device ++ * ++ * In order to force posting of pending writes, we need to write and ++ * readback the same register, in this case the revision register. ++ * ++ * See this link for reference: ++ * http://www.mail-archive.com/linux-omap@vger.kernel.org/msg08149.html ++ */ ++void omap3isp_flush(struct isp_device *isp) ++{ ++ isp_reg_writel(isp, 0, OMAP3_ISP_IOMEM_MAIN, ISP_REVISION); ++ isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_REVISION); ++} ++ ++/* ++ * isp_enable_interrupts - Enable ISP interrupts. ++ * @isp: OMAP3 ISP device ++ */ ++static void isp_enable_interrupts(struct isp_device *isp) ++{ ++ static const u32 irq = IRQ0ENABLE_CSIA_IRQ ++ | IRQ0ENABLE_CSIB_IRQ ++ | IRQ0ENABLE_CCDC_LSC_PREF_ERR_IRQ ++ | IRQ0ENABLE_CCDC_LSC_DONE_IRQ ++ | IRQ0ENABLE_CCDC_VD0_IRQ ++ | IRQ0ENABLE_CCDC_VD1_IRQ ++ | IRQ0ENABLE_HS_VS_IRQ ++ | IRQ0ENABLE_HIST_DONE_IRQ ++ | IRQ0ENABLE_H3A_AWB_DONE_IRQ ++ | IRQ0ENABLE_H3A_AF_DONE_IRQ ++ | IRQ0ENABLE_PRV_DONE_IRQ ++ | IRQ0ENABLE_RSZ_DONE_IRQ; ++ ++ isp_reg_writel(isp, irq, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS); ++ isp_reg_writel(isp, irq, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE); ++} ++ ++/* ++ * isp_disable_interrupts - Disable ISP interrupts. ++ * @isp: OMAP3 ISP device ++ */ ++static void isp_disable_interrupts(struct isp_device *isp) ++{ ++ isp_reg_writel(isp, 0, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE); ++} ++ ++/** ++ * isp_set_xclk - Configures the specified cam_xclk to the desired frequency. ++ * @isp: OMAP3 ISP device ++ * @xclk: Desired frequency of the clock in Hz. 0 = stable low, 1 is stable high ++ * @xclksel: XCLK to configure (0 = A, 1 = B). ++ * ++ * Configures the specified MCLK divisor in the ISP timing control register ++ * (TCTRL_CTRL) to generate the desired xclk clock value. ++ * ++ * Divisor = cam_mclk_hz / xclk ++ * ++ * Returns the final frequency that is actually being generated ++ **/ ++static u32 isp_set_xclk(struct isp_device *isp, u32 xclk, u8 xclksel) ++{ ++ u32 divisor; ++ u32 currentxclk; ++ unsigned long mclk_hz; ++ ++ if (!omap3isp_get(isp)) ++ return 0; ++ ++ mclk_hz = clk_get_rate(isp->clock[ISP_CLK_CAM_MCLK]); ++ ++ if (xclk >= mclk_hz) { ++ divisor = ISPTCTRL_CTRL_DIV_BYPASS; ++ currentxclk = mclk_hz; ++ } else if (xclk >= 2) { ++ divisor = mclk_hz / xclk; ++ if (divisor >= ISPTCTRL_CTRL_DIV_BYPASS) ++ divisor = ISPTCTRL_CTRL_DIV_BYPASS - 1; ++ currentxclk = mclk_hz / divisor; ++ } else { ++ divisor = xclk; ++ currentxclk = 0; ++ } ++ ++ switch (xclksel) { ++ case 0: ++ isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL, ++ ISPTCTRL_CTRL_DIVA_MASK, ++ divisor << ISPTCTRL_CTRL_DIVA_SHIFT); ++ dev_dbg(isp->dev, "isp_set_xclk(): cam_xclka set to %d Hz\n", ++ currentxclk); ++ break; ++ case 1: ++ isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL, ++ ISPTCTRL_CTRL_DIVB_MASK, ++ divisor << ISPTCTRL_CTRL_DIVB_SHIFT); ++ dev_dbg(isp->dev, "isp_set_xclk(): cam_xclkb set to %d Hz\n", ++ currentxclk); ++ break; ++ default: ++ omap3isp_put(isp); ++ dev_dbg(isp->dev, "ISP_ERR: isp_set_xclk(): Invalid requested " ++ "xclk. Must be 0 (A) or 1 (B).\n"); ++ return -EINVAL; ++ } ++ ++ /* Do we go from stable whatever to clock? */ ++ if (divisor >= 2 && isp->xclk_divisor[xclksel] < 2) ++ omap3isp_get(isp); ++ /* Stopping the clock. */ ++ else if (divisor < 2 && isp->xclk_divisor[xclksel] >= 2) ++ omap3isp_put(isp); ++ ++ isp->xclk_divisor[xclksel] = divisor; ++ ++ omap3isp_put(isp); ++ ++ return currentxclk; ++} ++ ++/* ++ * isp_power_settings - Sysconfig settings, for Power Management. ++ * @isp: OMAP3 ISP device ++ * @idle: Consider idle state. ++ * ++ * Sets the power settings for the ISP, and SBL bus. ++ */ ++static void isp_power_settings(struct isp_device *isp, int idle) ++{ ++ isp_reg_writel(isp, ++ ((idle ? ISP_SYSCONFIG_MIDLEMODE_SMARTSTANDBY : ++ ISP_SYSCONFIG_MIDLEMODE_FORCESTANDBY) << ++ ISP_SYSCONFIG_MIDLEMODE_SHIFT) | ++ ((isp->revision == ISP_REVISION_15_0) ? ++ ISP_SYSCONFIG_AUTOIDLE : 0), ++ OMAP3_ISP_IOMEM_MAIN, ISP_SYSCONFIG); ++ ++ if (isp->autoidle) ++ isp_reg_writel(isp, ISPCTRL_SBL_AUTOIDLE, OMAP3_ISP_IOMEM_MAIN, ++ ISP_CTRL); ++} ++ ++/* ++ * Configure the bridge and lane shifter. Valid inputs are ++ * ++ * CCDC_INPUT_PARALLEL: Parallel interface ++ * CCDC_INPUT_CSI2A: CSI2a receiver ++ * CCDC_INPUT_CCP2B: CCP2b receiver ++ * CCDC_INPUT_CSI2C: CSI2c receiver ++ * ++ * The bridge and lane shifter are configured according to the selected input ++ * and the ISP platform data. ++ */ ++void omap3isp_configure_bridge(struct isp_device *isp, ++ enum ccdc_input_entity input, ++ const struct isp_parallel_platform_data *pdata) ++{ ++ u32 ispctrl_val; ++ ++ ispctrl_val = isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL); ++ ispctrl_val &= ~ISPCTRL_SHIFT_MASK; ++ ispctrl_val &= ~ISPCTRL_PAR_CLK_POL_INV; ++ ispctrl_val &= ~ISPCTRL_PAR_SER_CLK_SEL_MASK; ++ ispctrl_val &= ~ISPCTRL_PAR_BRIDGE_MASK; ++ ++ switch (input) { ++ case CCDC_INPUT_PARALLEL: ++ ispctrl_val |= ISPCTRL_PAR_SER_CLK_SEL_PARALLEL; ++ ispctrl_val |= pdata->data_lane_shift << ISPCTRL_SHIFT_SHIFT; ++ ispctrl_val |= pdata->clk_pol << ISPCTRL_PAR_CLK_POL_SHIFT; ++ ispctrl_val |= pdata->bridge << ISPCTRL_PAR_BRIDGE_SHIFT; ++ break; ++ ++ case CCDC_INPUT_CSI2A: ++ ispctrl_val |= ISPCTRL_PAR_SER_CLK_SEL_CSIA; ++ break; ++ ++ case CCDC_INPUT_CCP2B: ++ ispctrl_val |= ISPCTRL_PAR_SER_CLK_SEL_CSIB; ++ break; ++ ++ case CCDC_INPUT_CSI2C: ++ ispctrl_val |= ISPCTRL_PAR_SER_CLK_SEL_CSIC; ++ break; ++ ++ default: ++ return; ++ } ++ ++ ispctrl_val &= ~ISPCTRL_SYNC_DETECT_MASK; ++ ispctrl_val |= ISPCTRL_SYNC_DETECT_VSRISE; ++ ++ isp_reg_writel(isp, ispctrl_val, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL); ++} ++ ++/** ++ * isp_set_pixel_clock - Configures the ISP pixel clock ++ * @isp: OMAP3 ISP device ++ * @pixelclk: Average pixel clock in Hz ++ * ++ * Set the average pixel clock required by the sensor. The ISP will use the ++ * lowest possible memory bandwidth settings compatible with the clock. ++ **/ ++static void isp_set_pixel_clock(struct isp_device *isp, unsigned int pixelclk) ++{ ++ isp->isp_ccdc.vpcfg.pixelclk = pixelclk; ++} ++ ++void omap3isp_hist_dma_done(struct isp_device *isp) ++{ ++ if (omap3isp_ccdc_busy(&isp->isp_ccdc) || ++ omap3isp_stat_pcr_busy(&isp->isp_hist)) { ++ /* Histogram cannot be enabled in this frame anymore */ ++ atomic_set(&isp->isp_hist.buf_err, 1); ++ dev_dbg(isp->dev, "hist: Out of synchronization with " ++ "CCDC. Ignoring next buffer.\n"); ++ } ++} ++ ++static inline void isp_isr_dbg(struct isp_device *isp, u32 irqstatus) ++{ ++ static const char *name[] = { ++ "CSIA_IRQ", ++ "res1", ++ "res2", ++ "CSIB_LCM_IRQ", ++ "CSIB_IRQ", ++ "res5", ++ "res6", ++ "res7", ++ "CCDC_VD0_IRQ", ++ "CCDC_VD1_IRQ", ++ "CCDC_VD2_IRQ", ++ "CCDC_ERR_IRQ", ++ "H3A_AF_DONE_IRQ", ++ "H3A_AWB_DONE_IRQ", ++ "res14", ++ "res15", ++ "HIST_DONE_IRQ", ++ "CCDC_LSC_DONE", ++ "CCDC_LSC_PREFETCH_COMPLETED", ++ "CCDC_LSC_PREFETCH_ERROR", ++ "PRV_DONE_IRQ", ++ "CBUFF_IRQ", ++ "res22", ++ "res23", ++ "RSZ_DONE_IRQ", ++ "OVF_IRQ", ++ "res26", ++ "res27", ++ "MMU_ERR_IRQ", ++ "OCP_ERR_IRQ", ++ "SEC_ERR_IRQ", ++ "HS_VS_IRQ", ++ }; ++ int i; ++ ++ dev_dbg(isp->dev, ""); ++ ++ for (i = 0; i < ARRAY_SIZE(name); i++) { ++ if ((1 << i) & irqstatus) ++ printk(KERN_CONT "%s ", name[i]); ++ } ++ printk(KERN_CONT "\n"); ++} ++ ++static void isp_isr_sbl(struct isp_device *isp) ++{ ++ struct device *dev = isp->dev; ++ u32 sbl_pcr; ++ ++ /* ++ * Handle shared buffer logic overflows for video buffers. ++ * ISPSBL_PCR_CCDCPRV_2_RSZ_OVF can be safely ignored. ++ */ ++ sbl_pcr = isp_reg_readl(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_PCR); ++ isp_reg_writel(isp, sbl_pcr, OMAP3_ISP_IOMEM_SBL, ISPSBL_PCR); ++ sbl_pcr &= ~ISPSBL_PCR_CCDCPRV_2_RSZ_OVF; ++ ++ if (sbl_pcr) ++ dev_dbg(dev, "SBL overflow (PCR = 0x%08x)\n", sbl_pcr); ++ ++ if (sbl_pcr & (ISPSBL_PCR_CCDC_WBL_OVF | ISPSBL_PCR_CSIA_WBL_OVF ++ | ISPSBL_PCR_CSIB_WBL_OVF)) { ++ isp->isp_ccdc.error = 1; ++ if (isp->isp_ccdc.output & CCDC_OUTPUT_PREVIEW) ++ isp->isp_prev.error = 1; ++ if (isp->isp_ccdc.output & CCDC_OUTPUT_RESIZER) ++ isp->isp_res.error = 1; ++ } ++ ++ if (sbl_pcr & ISPSBL_PCR_PRV_WBL_OVF) { ++ isp->isp_prev.error = 1; ++ if (isp->isp_res.input == RESIZER_INPUT_VP && ++ !(isp->isp_ccdc.output & CCDC_OUTPUT_RESIZER)) ++ isp->isp_res.error = 1; ++ } ++ ++ if (sbl_pcr & (ISPSBL_PCR_RSZ1_WBL_OVF ++ | ISPSBL_PCR_RSZ2_WBL_OVF ++ | ISPSBL_PCR_RSZ3_WBL_OVF ++ | ISPSBL_PCR_RSZ4_WBL_OVF)) ++ isp->isp_res.error = 1; ++ ++ if (sbl_pcr & ISPSBL_PCR_H3A_AF_WBL_OVF) ++ omap3isp_stat_sbl_overflow(&isp->isp_af); ++ ++ if (sbl_pcr & ISPSBL_PCR_H3A_AEAWB_WBL_OVF) ++ omap3isp_stat_sbl_overflow(&isp->isp_aewb); ++} ++ ++/* ++ * isp_isr - Interrupt Service Routine for Camera ISP module. ++ * @irq: Not used currently. ++ * @_isp: Pointer to the OMAP3 ISP device ++ * ++ * Handles the corresponding callback if plugged in. ++ * ++ * Returns IRQ_HANDLED when IRQ was correctly handled, or IRQ_NONE when the ++ * IRQ wasn't handled. ++ */ ++static irqreturn_t isp_isr(int irq, void *_isp) ++{ ++ static const u32 ccdc_events = IRQ0STATUS_CCDC_LSC_PREF_ERR_IRQ | ++ IRQ0STATUS_CCDC_LSC_DONE_IRQ | ++ IRQ0STATUS_CCDC_VD0_IRQ | ++ IRQ0STATUS_CCDC_VD1_IRQ | ++ IRQ0STATUS_HS_VS_IRQ; ++ struct isp_device *isp = _isp; ++ u32 irqstatus; ++ int ret; ++ ++ irqstatus = isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS); ++ isp_reg_writel(isp, irqstatus, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS); ++ ++ isp_isr_sbl(isp); ++ ++ if (irqstatus & IRQ0STATUS_CSIA_IRQ) { ++ ret = omap3isp_csi2_isr(&isp->isp_csi2a); ++ if (ret) ++ isp->isp_ccdc.error = 1; ++ } ++ ++ if (irqstatus & IRQ0STATUS_CSIB_IRQ) { ++ ret = omap3isp_ccp2_isr(&isp->isp_ccp2); ++ if (ret) ++ isp->isp_ccdc.error = 1; ++ } ++ ++ if (irqstatus & IRQ0STATUS_CCDC_VD0_IRQ) { ++ if (isp->isp_ccdc.output & CCDC_OUTPUT_PREVIEW) ++ omap3isp_preview_isr_frame_sync(&isp->isp_prev); ++ if (isp->isp_ccdc.output & CCDC_OUTPUT_RESIZER) ++ omap3isp_resizer_isr_frame_sync(&isp->isp_res); ++ omap3isp_stat_isr_frame_sync(&isp->isp_aewb); ++ omap3isp_stat_isr_frame_sync(&isp->isp_af); ++ omap3isp_stat_isr_frame_sync(&isp->isp_hist); ++ } ++ ++ if (irqstatus & ccdc_events) ++ omap3isp_ccdc_isr(&isp->isp_ccdc, irqstatus & ccdc_events); ++ ++ if (irqstatus & IRQ0STATUS_PRV_DONE_IRQ) { ++ if (isp->isp_prev.output & PREVIEW_OUTPUT_RESIZER) ++ omap3isp_resizer_isr_frame_sync(&isp->isp_res); ++ omap3isp_preview_isr(&isp->isp_prev); ++ } ++ ++ if (irqstatus & IRQ0STATUS_RSZ_DONE_IRQ) ++ omap3isp_resizer_isr(&isp->isp_res); ++ ++ if (irqstatus & IRQ0STATUS_H3A_AWB_DONE_IRQ) ++ omap3isp_stat_isr(&isp->isp_aewb); ++ ++ if (irqstatus & IRQ0STATUS_H3A_AF_DONE_IRQ) ++ omap3isp_stat_isr(&isp->isp_af); ++ ++ if (irqstatus & IRQ0STATUS_HIST_DONE_IRQ) ++ omap3isp_stat_isr(&isp->isp_hist); ++ ++ omap3isp_flush(isp); ++ ++#if defined(DEBUG) && defined(ISP_ISR_DEBUG) ++ isp_isr_dbg(isp, irqstatus); ++#endif ++ ++ return IRQ_HANDLED; ++} ++ ++/* ----------------------------------------------------------------------------- ++ * Pipeline power management ++ * ++ * Entities must be powered up when part of a pipeline that contains at least ++ * one open video device node. ++ * ++ * To achieve this use the entity use_count field to track the number of users. ++ * For entities corresponding to video device nodes the use_count field stores ++ * the users count of the node. For entities corresponding to subdevs the ++ * use_count field stores the total number of users of all video device nodes ++ * in the pipeline. ++ * ++ * The omap3isp_pipeline_pm_use() function must be called in the open() and ++ * close() handlers of video device nodes. It increments or decrements the use ++ * count of all subdev entities in the pipeline. ++ * ++ * To react to link management on powered pipelines, the link setup notification ++ * callback updates the use count of all entities in the source and sink sides ++ * of the link. ++ */ ++ ++/* ++ * isp_pipeline_pm_use_count - Count the number of users of a pipeline ++ * @entity: The entity ++ * ++ * Return the total number of users of all video device nodes in the pipeline. ++ */ ++static int isp_pipeline_pm_use_count(struct media_entity *entity) ++{ ++ struct media_entity_graph graph; ++ int use = 0; ++ ++ media_entity_graph_walk_start(&graph, entity); ++ ++ while ((entity = media_entity_graph_walk_next(&graph))) { ++ if (media_entity_type(entity) == MEDIA_ENT_T_DEVNODE) ++ use += entity->use_count; ++ } ++ ++ return use; ++} ++ ++/* ++ * isp_pipeline_pm_power_one - Apply power change to an entity ++ * @entity: The entity ++ * @change: Use count change ++ * ++ * Change the entity use count by @change. If the entity is a subdev update its ++ * power state by calling the core::s_power operation when the use count goes ++ * from 0 to != 0 or from != 0 to 0. ++ * ++ * Return 0 on success or a negative error code on failure. ++ */ ++static int isp_pipeline_pm_power_one(struct media_entity *entity, int change) ++{ ++ struct v4l2_subdev *subdev; ++ int ret; ++ ++ subdev = media_entity_type(entity) == MEDIA_ENT_T_V4L2_SUBDEV ++ ? media_entity_to_v4l2_subdev(entity) : NULL; ++ ++ if (entity->use_count == 0 && change > 0 && subdev != NULL) { ++ ret = v4l2_subdev_call(subdev, core, s_power, 1); ++ if (ret < 0 && ret != -ENOIOCTLCMD) ++ return ret; ++ } ++ ++ entity->use_count += change; ++ WARN_ON(entity->use_count < 0); ++ ++ if (entity->use_count == 0 && change < 0 && subdev != NULL) ++ v4l2_subdev_call(subdev, core, s_power, 0); ++ ++ return 0; ++} ++ ++/* ++ * isp_pipeline_pm_power - Apply power change to all entities in a pipeline ++ * @entity: The entity ++ * @change: Use count change ++ * ++ * Walk the pipeline to update the use count and the power state of all non-node ++ * entities. ++ * ++ * Return 0 on success or a negative error code on failure. ++ */ ++static int isp_pipeline_pm_power(struct media_entity *entity, int change) ++{ ++ struct media_entity_graph graph; ++ struct media_entity *first = entity; ++ int ret = 0; ++ ++ if (!change) ++ return 0; ++ ++ media_entity_graph_walk_start(&graph, entity); ++ ++ while (!ret && (entity = media_entity_graph_walk_next(&graph))) ++ if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE) ++ ret = isp_pipeline_pm_power_one(entity, change); ++ ++ if (!ret) ++ return 0; ++ ++ media_entity_graph_walk_start(&graph, first); ++ ++ while ((first = media_entity_graph_walk_next(&graph)) ++ && first != entity) ++ if (media_entity_type(first) != MEDIA_ENT_T_DEVNODE) ++ isp_pipeline_pm_power_one(first, -change); ++ ++ return ret; ++} ++ ++/* ++ * omap3isp_pipeline_pm_use - Update the use count of an entity ++ * @entity: The entity ++ * @use: Use (1) or stop using (0) the entity ++ * ++ * Update the use count of all entities in the pipeline and power entities on or ++ * off accordingly. ++ * ++ * Return 0 on success or a negative error code on failure. Powering entities ++ * off is assumed to never fail. No failure can occur when the use parameter is ++ * set to 0. ++ */ ++int omap3isp_pipeline_pm_use(struct media_entity *entity, int use) ++{ ++ int change = use ? 1 : -1; ++ int ret; ++ ++ mutex_lock(&entity->parent->graph_mutex); ++ ++ /* Apply use count to node. */ ++ entity->use_count += change; ++ WARN_ON(entity->use_count < 0); ++ ++ /* Apply power change to connected non-nodes. */ ++ ret = isp_pipeline_pm_power(entity, change); ++ ++ mutex_unlock(&entity->parent->graph_mutex); ++ ++ return ret; ++} ++ ++/* ++ * isp_pipeline_link_notify - Link management notification callback ++ * @source: Pad at the start of the link ++ * @sink: Pad at the end of the link ++ * @flags: New link flags that will be applied ++ * ++ * React to link management on powered pipelines by updating the use count of ++ * all entities in the source and sink sides of the link. Entities are powered ++ * on or off accordingly. ++ * ++ * Return 0 on success or a negative error code on failure. Powering entities ++ * off is assumed to never fail. This function will not fail for disconnection ++ * events. ++ */ ++static int isp_pipeline_link_notify(struct media_pad *source, ++ struct media_pad *sink, u32 flags) ++{ ++ int source_use = isp_pipeline_pm_use_count(source->entity); ++ int sink_use = isp_pipeline_pm_use_count(sink->entity); ++ int ret; ++ ++ if (!(flags & MEDIA_LNK_FL_ENABLED)) { ++ /* Powering off entities is assumed to never fail. */ ++ isp_pipeline_pm_power(source->entity, -sink_use); ++ isp_pipeline_pm_power(sink->entity, -source_use); ++ return 0; ++ } ++ ++ ret = isp_pipeline_pm_power(source->entity, sink_use); ++ if (ret < 0) ++ return ret; ++ ++ ret = isp_pipeline_pm_power(sink->entity, source_use); ++ if (ret < 0) ++ isp_pipeline_pm_power(source->entity, -sink_use); ++ ++ return ret; ++} ++ ++/* ----------------------------------------------------------------------------- ++ * Pipeline stream management ++ */ ++ ++/* ++ * isp_pipeline_enable - Enable streaming on a pipeline ++ * @pipe: ISP pipeline ++ * @mode: Stream mode (single shot or continuous) ++ * ++ * Walk the entities chain starting at the pipeline output video node and start ++ * all modules in the chain in the given mode. ++ * ++ * Return 0 if successfull, or the return value of the failed video::s_stream ++ * operation otherwise. ++ */ ++static int isp_pipeline_enable(struct isp_pipeline *pipe, ++ enum isp_pipeline_stream_state mode) ++{ ++ struct isp_device *isp = pipe->output->isp; ++ struct media_entity *entity; ++ struct media_pad *pad; ++ struct v4l2_subdev *subdev; ++ unsigned long flags; ++ int ret = 0; ++ ++ spin_lock_irqsave(&pipe->lock, flags); ++ pipe->state &= ~(ISP_PIPELINE_IDLE_INPUT | ISP_PIPELINE_IDLE_OUTPUT); ++ spin_unlock_irqrestore(&pipe->lock, flags); ++ ++ pipe->do_propagation = false; ++ ++ entity = &pipe->output->video.entity; ++ while (1) { ++ pad = &entity->pads[0]; ++ if (!(pad->flags & MEDIA_PAD_FL_INPUT)) ++ break; ++ ++ pad = media_entity_remote_source(pad); ++ if (pad == NULL || ++ media_entity_type(pad->entity) != ++ MEDIA_ENT_T_V4L2_SUBDEV) ++ break; ++ ++ entity = pad->entity; ++ subdev = media_entity_to_v4l2_subdev(entity); ++ ++ ret = v4l2_subdev_call(subdev, video, s_stream, mode); ++ if (ret < 0 && ret != -ENOIOCTLCMD) ++ break; ++ ++ if (subdev == &isp->isp_ccdc.subdev) { ++ v4l2_subdev_call(&isp->isp_aewb.subdev, video, ++ s_stream, mode); ++ v4l2_subdev_call(&isp->isp_af.subdev, video, ++ s_stream, mode); ++ v4l2_subdev_call(&isp->isp_hist.subdev, video, ++ s_stream, mode); ++ pipe->do_propagation = true; ++ } ++ } ++ ++ /* Frame number propagation. In continuous streaming mode the number ++ * is incremented in the frame start ISR. In mem-to-mem mode ++ * singleshot is used and frame start IRQs are not available. ++ * Thus we have to increment the number here. ++ */ ++ if (pipe->do_propagation && mode == ISP_PIPELINE_STREAM_SINGLESHOT) ++ atomic_inc(&pipe->frame_number); ++ ++ return ret; ++} ++ ++static int isp_pipeline_wait_resizer(struct isp_device *isp) ++{ ++ return omap3isp_resizer_busy(&isp->isp_res); ++} ++ ++static int isp_pipeline_wait_preview(struct isp_device *isp) ++{ ++ return omap3isp_preview_busy(&isp->isp_prev); ++} ++ ++static int isp_pipeline_wait_ccdc(struct isp_device *isp) ++{ ++ return omap3isp_stat_busy(&isp->isp_af) ++ || omap3isp_stat_busy(&isp->isp_aewb) ++ || omap3isp_stat_busy(&isp->isp_hist) ++ || omap3isp_ccdc_busy(&isp->isp_ccdc); ++} ++ ++#define ISP_STOP_TIMEOUT msecs_to_jiffies(1000) ++ ++static int isp_pipeline_wait(struct isp_device *isp, ++ int(*busy)(struct isp_device *isp)) ++{ ++ unsigned long timeout = jiffies + ISP_STOP_TIMEOUT; ++ ++ while (!time_after(jiffies, timeout)) { ++ if (!busy(isp)) ++ return 0; ++ } ++ ++ return 1; ++} ++ ++/* ++ * isp_pipeline_disable - Disable streaming on a pipeline ++ * @pipe: ISP pipeline ++ * ++ * Walk the entities chain starting at the pipeline output video node and stop ++ * all modules in the chain. Wait synchronously for the modules to be stopped if ++ * necessary. ++ * ++ * Return 0 if all modules have been properly stopped, or -ETIMEDOUT if a module ++ * can't be stopped (in which case a software reset of the ISP is probably ++ * necessary). ++ */ ++static int isp_pipeline_disable(struct isp_pipeline *pipe) ++{ ++ struct isp_device *isp = pipe->output->isp; ++ struct media_entity *entity; ++ struct media_pad *pad; ++ struct v4l2_subdev *subdev; ++ int failure = 0; ++ int ret; ++ ++ /* ++ * We need to stop all the modules after CCDC first or they'll ++ * never stop since they may not get a full frame from CCDC. ++ */ ++ entity = &pipe->output->video.entity; ++ while (1) { ++ pad = &entity->pads[0]; ++ if (!(pad->flags & MEDIA_PAD_FL_INPUT)) ++ break; ++ ++ pad = media_entity_remote_source(pad); ++ if (pad == NULL || ++ media_entity_type(pad->entity) != ++ MEDIA_ENT_T_V4L2_SUBDEV) ++ break; ++ ++ entity = pad->entity; ++ subdev = media_entity_to_v4l2_subdev(entity); ++ ++ if (subdev == &isp->isp_ccdc.subdev) { ++ v4l2_subdev_call(&isp->isp_aewb.subdev, ++ video, s_stream, 0); ++ v4l2_subdev_call(&isp->isp_af.subdev, ++ video, s_stream, 0); ++ v4l2_subdev_call(&isp->isp_hist.subdev, ++ video, s_stream, 0); ++ } ++ ++ v4l2_subdev_call(subdev, video, s_stream, 0); ++ ++ if (subdev == &isp->isp_res.subdev) { ++ ret = isp_pipeline_wait(isp, isp_pipeline_wait_resizer); ++ } else if (subdev == &isp->isp_prev.subdev) { ++ ret = isp_pipeline_wait(isp, isp_pipeline_wait_preview); ++ } else if (subdev == &isp->isp_ccdc.subdev) { ++ ret = isp_pipeline_wait(isp, isp_pipeline_wait_ccdc); ++ } else { ++ ret = 0; ++ } ++ ++ if (ret) { ++ dev_info(isp->dev, "Unable to stop %s\n", subdev->name); ++ failure = -ETIMEDOUT; ++ } ++ } ++ ++ return failure; ++} ++ ++/* ++ * omap3isp_pipeline_set_stream - Enable/disable streaming on a pipeline ++ * @pipe: ISP pipeline ++ * @state: Stream state (stopped, single shot or continuous) ++ * ++ * Set the pipeline to the given stream state. Pipelines can be started in ++ * single-shot or continuous mode. ++ * ++ * Return 0 if successfull, or the return value of the failed video::s_stream ++ * operation otherwise. ++ */ ++int omap3isp_pipeline_set_stream(struct isp_pipeline *pipe, ++ enum isp_pipeline_stream_state state) ++{ ++ int ret; ++ ++ if (state == ISP_PIPELINE_STREAM_STOPPED) ++ ret = isp_pipeline_disable(pipe); ++ else ++ ret = isp_pipeline_enable(pipe, state); ++ pipe->stream_state = state; ++ ++ return ret; ++} ++ ++/* ++ * isp_pipeline_resume - Resume streaming on a pipeline ++ * @pipe: ISP pipeline ++ * ++ * Resume video output and input and re-enable pipeline. ++ */ ++static void isp_pipeline_resume(struct isp_pipeline *pipe) ++{ ++ int singleshot = pipe->stream_state == ISP_PIPELINE_STREAM_SINGLESHOT; ++ ++ omap3isp_video_resume(pipe->output, !singleshot); ++ if (singleshot) ++ omap3isp_video_resume(pipe->input, 0); ++ isp_pipeline_enable(pipe, pipe->stream_state); ++} ++ ++/* ++ * isp_pipeline_suspend - Suspend streaming on a pipeline ++ * @pipe: ISP pipeline ++ * ++ * Suspend pipeline. ++ */ ++static void isp_pipeline_suspend(struct isp_pipeline *pipe) ++{ ++ isp_pipeline_disable(pipe); ++} ++ ++/* ++ * isp_pipeline_is_last - Verify if entity has an enbled link to the output ++ * video node ++ * @me: ISP module's media entity ++ * ++ * Returns 1 if the entity has an enabled link to the output video node or 0 ++ * otherwise. It's true only while pipeline can have no more than one output ++ * node. ++ */ ++static int isp_pipeline_is_last(struct media_entity *me) ++{ ++ struct isp_pipeline *pipe; ++ struct media_pad *pad; ++ ++ if (!me->pipe) ++ return 0; ++ pipe = to_isp_pipeline(me); ++ if (pipe->stream_state == ISP_PIPELINE_STREAM_STOPPED) ++ return 0; ++ pad = media_entity_remote_source(&pipe->output->pad); ++ return pad->entity == me; ++} ++ ++/* ++ * isp_suspend_module_pipeline - Suspend pipeline to which belongs the module ++ * @me: ISP module's media entity ++ * ++ * Suspend the whole pipeline if module's entity has an enabled link to the ++ * output video node. It works only while pipeline can have no more than one ++ * output node. ++ */ ++static void isp_suspend_module_pipeline(struct media_entity *me) ++{ ++ if (isp_pipeline_is_last(me)) ++ isp_pipeline_suspend(to_isp_pipeline(me)); ++} ++ ++/* ++ * isp_resume_module_pipeline - Resume pipeline to which belongs the module ++ * @me: ISP module's media entity ++ * ++ * Resume the whole pipeline if module's entity has an enabled link to the ++ * output video node. It works only while pipeline can have no more than one ++ * output node. ++ */ ++static void isp_resume_module_pipeline(struct media_entity *me) ++{ ++ if (isp_pipeline_is_last(me)) ++ isp_pipeline_resume(to_isp_pipeline(me)); ++} ++ ++/* ++ * isp_suspend_modules - Suspend ISP submodules. ++ * @isp: OMAP3 ISP device ++ * ++ * Returns 0 if suspend left in idle state all the submodules properly, ++ * or returns 1 if a general Reset is required to suspend the submodules. ++ */ ++static int isp_suspend_modules(struct isp_device *isp) ++{ ++ unsigned long timeout; ++ ++ omap3isp_stat_suspend(&isp->isp_aewb); ++ omap3isp_stat_suspend(&isp->isp_af); ++ omap3isp_stat_suspend(&isp->isp_hist); ++ isp_suspend_module_pipeline(&isp->isp_res.subdev.entity); ++ isp_suspend_module_pipeline(&isp->isp_prev.subdev.entity); ++ isp_suspend_module_pipeline(&isp->isp_ccdc.subdev.entity); ++ isp_suspend_module_pipeline(&isp->isp_csi2a.subdev.entity); ++ isp_suspend_module_pipeline(&isp->isp_ccp2.subdev.entity); ++ ++ timeout = jiffies + ISP_STOP_TIMEOUT; ++ while (omap3isp_stat_busy(&isp->isp_af) ++ || omap3isp_stat_busy(&isp->isp_aewb) ++ || omap3isp_stat_busy(&isp->isp_hist) ++ || omap3isp_preview_busy(&isp->isp_prev) ++ || omap3isp_resizer_busy(&isp->isp_res) ++ || omap3isp_ccdc_busy(&isp->isp_ccdc)) { ++ if (time_after(jiffies, timeout)) { ++ dev_info(isp->dev, "can't stop modules.\n"); ++ return 1; ++ } ++ msleep(1); ++ } ++ ++ return 0; ++} ++ ++/* ++ * isp_resume_modules - Resume ISP submodules. ++ * @isp: OMAP3 ISP device ++ */ ++static void isp_resume_modules(struct isp_device *isp) ++{ ++ omap3isp_stat_resume(&isp->isp_aewb); ++ omap3isp_stat_resume(&isp->isp_af); ++ omap3isp_stat_resume(&isp->isp_hist); ++ isp_resume_module_pipeline(&isp->isp_res.subdev.entity); ++ isp_resume_module_pipeline(&isp->isp_prev.subdev.entity); ++ isp_resume_module_pipeline(&isp->isp_ccdc.subdev.entity); ++ isp_resume_module_pipeline(&isp->isp_csi2a.subdev.entity); ++ isp_resume_module_pipeline(&isp->isp_ccp2.subdev.entity); ++} ++ ++/* ++ * isp_reset - Reset ISP with a timeout wait for idle. ++ * @isp: OMAP3 ISP device ++ */ ++static int isp_reset(struct isp_device *isp) ++{ ++ unsigned long timeout = 0; ++ ++ isp_reg_writel(isp, ++ isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_SYSCONFIG) ++ | ISP_SYSCONFIG_SOFTRESET, ++ OMAP3_ISP_IOMEM_MAIN, ISP_SYSCONFIG); ++ while (!(isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ++ ISP_SYSSTATUS) & 0x1)) { ++ if (timeout++ > 10000) { ++ dev_alert(isp->dev, "cannot reset ISP\n"); ++ return -ETIMEDOUT; ++ } ++ udelay(1); ++ } ++ ++ return 0; ++} ++ ++/* ++ * isp_save_context - Saves the values of the ISP module registers. ++ * @isp: OMAP3 ISP device ++ * @reg_list: Structure containing pairs of register address and value to ++ * modify on OMAP. ++ */ ++static void ++isp_save_context(struct isp_device *isp, struct isp_reg *reg_list) ++{ ++ struct isp_reg *next = reg_list; ++ ++ for (; next->reg != ISP_TOK_TERM; next++) ++ next->val = isp_reg_readl(isp, next->mmio_range, next->reg); ++} ++ ++/* ++ * isp_restore_context - Restores the values of the ISP module registers. ++ * @isp: OMAP3 ISP device ++ * @reg_list: Structure containing pairs of register address and value to ++ * modify on OMAP. ++ */ ++static void ++isp_restore_context(struct isp_device *isp, struct isp_reg *reg_list) ++{ ++ struct isp_reg *next = reg_list; ++ ++ for (; next->reg != ISP_TOK_TERM; next++) ++ isp_reg_writel(isp, next->val, next->mmio_range, next->reg); ++} ++ ++/* ++ * isp_save_ctx - Saves ISP, CCDC, HIST, H3A, PREV, RESZ & MMU context. ++ * @isp: OMAP3 ISP device ++ * ++ * Routine for saving the context of each module in the ISP. ++ * CCDC, HIST, H3A, PREV, RESZ and MMU. ++ */ ++static void isp_save_ctx(struct isp_device *isp) ++{ ++ isp_save_context(isp, isp_reg_list); ++ if (isp->iommu) ++ iommu_save_ctx(isp->iommu); ++} ++ ++/* ++ * isp_restore_ctx - Restores ISP, CCDC, HIST, H3A, PREV, RESZ & MMU context. ++ * @isp: OMAP3 ISP device ++ * ++ * Routine for restoring the context of each module in the ISP. ++ * CCDC, HIST, H3A, PREV, RESZ and MMU. ++ */ ++static void isp_restore_ctx(struct isp_device *isp) ++{ ++ isp_restore_context(isp, isp_reg_list); ++ if (isp->iommu) ++ iommu_restore_ctx(isp->iommu); ++ omap3isp_ccdc_restore_context(isp); ++ omap3isp_preview_restore_context(isp); ++} ++ ++/* ----------------------------------------------------------------------------- ++ * SBL resources management ++ */ ++#define OMAP3_ISP_SBL_READ (OMAP3_ISP_SBL_CSI1_READ | \ ++ OMAP3_ISP_SBL_CCDC_LSC_READ | \ ++ OMAP3_ISP_SBL_PREVIEW_READ | \ ++ OMAP3_ISP_SBL_RESIZER_READ) ++#define OMAP3_ISP_SBL_WRITE (OMAP3_ISP_SBL_CSI1_WRITE | \ ++ OMAP3_ISP_SBL_CSI2A_WRITE | \ ++ OMAP3_ISP_SBL_CSI2C_WRITE | \ ++ OMAP3_ISP_SBL_CCDC_WRITE | \ ++ OMAP3_ISP_SBL_PREVIEW_WRITE) ++ ++void omap3isp_sbl_enable(struct isp_device *isp, enum isp_sbl_resource res) ++{ ++ u32 sbl = 0; ++ ++ isp->sbl_resources |= res; ++ ++ if (isp->sbl_resources & OMAP3_ISP_SBL_CSI1_READ) ++ sbl |= ISPCTRL_SBL_SHARED_RPORTA; ++ ++ if (isp->sbl_resources & OMAP3_ISP_SBL_CCDC_LSC_READ) ++ sbl |= ISPCTRL_SBL_SHARED_RPORTB; ++ ++ if (isp->sbl_resources & OMAP3_ISP_SBL_CSI2C_WRITE) ++ sbl |= ISPCTRL_SBL_SHARED_WPORTC; ++ ++ if (isp->sbl_resources & OMAP3_ISP_SBL_RESIZER_WRITE) ++ sbl |= ISPCTRL_SBL_WR0_RAM_EN; ++ ++ if (isp->sbl_resources & OMAP3_ISP_SBL_WRITE) ++ sbl |= ISPCTRL_SBL_WR1_RAM_EN; ++ ++ if (isp->sbl_resources & OMAP3_ISP_SBL_READ) ++ sbl |= ISPCTRL_SBL_RD_RAM_EN; ++ ++ isp_reg_set(isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, sbl); ++} ++ ++void omap3isp_sbl_disable(struct isp_device *isp, enum isp_sbl_resource res) ++{ ++ u32 sbl = 0; ++ ++ isp->sbl_resources &= ~res; ++ ++ if (!(isp->sbl_resources & OMAP3_ISP_SBL_CSI1_READ)) ++ sbl |= ISPCTRL_SBL_SHARED_RPORTA; ++ ++ if (!(isp->sbl_resources & OMAP3_ISP_SBL_CCDC_LSC_READ)) ++ sbl |= ISPCTRL_SBL_SHARED_RPORTB; ++ ++ if (!(isp->sbl_resources & OMAP3_ISP_SBL_CSI2C_WRITE)) ++ sbl |= ISPCTRL_SBL_SHARED_WPORTC; ++ ++ if (!(isp->sbl_resources & OMAP3_ISP_SBL_RESIZER_WRITE)) ++ sbl |= ISPCTRL_SBL_WR0_RAM_EN; ++ ++ if (!(isp->sbl_resources & OMAP3_ISP_SBL_WRITE)) ++ sbl |= ISPCTRL_SBL_WR1_RAM_EN; ++ ++ if (!(isp->sbl_resources & OMAP3_ISP_SBL_READ)) ++ sbl |= ISPCTRL_SBL_RD_RAM_EN; ++ ++ isp_reg_clr(isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, sbl); ++} ++ ++/* ++ * isp_module_sync_idle - Helper to sync module with its idle state ++ * @me: ISP submodule's media entity ++ * @wait: ISP submodule's wait queue for streamoff/interrupt synchronization ++ * @stopping: flag which tells module wants to stop ++ * ++ * This function checks if ISP submodule needs to wait for next interrupt. If ++ * yes, makes the caller to sleep while waiting for such event. ++ */ ++int omap3isp_module_sync_idle(struct media_entity *me, wait_queue_head_t *wait, ++ atomic_t *stopping) ++{ ++ struct isp_pipeline *pipe = to_isp_pipeline(me); ++ ++ if (pipe->stream_state == ISP_PIPELINE_STREAM_STOPPED || ++ (pipe->stream_state == ISP_PIPELINE_STREAM_SINGLESHOT && ++ !isp_pipeline_ready(pipe))) ++ return 0; ++ ++ /* ++ * atomic_set() doesn't include memory barrier on ARM platform for SMP ++ * scenario. We'll call it here to avoid race conditions. ++ */ ++ atomic_set(stopping, 1); ++ smp_mb(); ++ ++ /* ++ * If module is the last one, it's writing to memory. In this case, ++ * it's necessary to check if the module is already paused due to ++ * DMA queue underrun or if it has to wait for next interrupt to be ++ * idle. ++ * If it isn't the last one, the function won't sleep but *stopping ++ * will still be set to warn next submodule caller's interrupt the ++ * module wants to be idle. ++ */ ++ if (isp_pipeline_is_last(me)) { ++ struct isp_video *video = pipe->output; ++ unsigned long flags; ++ spin_lock_irqsave(&video->queue->irqlock, flags); ++ if (video->dmaqueue_flags & ISP_VIDEO_DMAQUEUE_UNDERRUN) { ++ spin_unlock_irqrestore(&video->queue->irqlock, flags); ++ atomic_set(stopping, 0); ++ smp_mb(); ++ return 0; ++ } ++ spin_unlock_irqrestore(&video->queue->irqlock, flags); ++ if (!wait_event_timeout(*wait, !atomic_read(stopping), ++ msecs_to_jiffies(1000))) { ++ atomic_set(stopping, 0); ++ smp_mb(); ++ return -ETIMEDOUT; ++ } ++ } ++ ++ return 0; ++} ++ ++/* ++ * omap3isp_module_sync_is_stopped - Helper to verify if module was stopping ++ * @wait: ISP submodule's wait queue for streamoff/interrupt synchronization ++ * @stopping: flag which tells module wants to stop ++ * ++ * This function checks if ISP submodule was stopping. In case of yes, it ++ * notices the caller by setting stopping to 0 and waking up the wait queue. ++ * Returns 1 if it was stopping or 0 otherwise. ++ */ ++int omap3isp_module_sync_is_stopping(wait_queue_head_t *wait, ++ atomic_t *stopping) ++{ ++ if (atomic_cmpxchg(stopping, 1, 0)) { ++ wake_up(wait); ++ return 1; ++ } ++ ++ return 0; ++} ++ ++/* -------------------------------------------------------------------------- ++ * Clock management ++ */ ++ ++#define ISPCTRL_CLKS_MASK (ISPCTRL_H3A_CLK_EN | \ ++ ISPCTRL_HIST_CLK_EN | \ ++ ISPCTRL_RSZ_CLK_EN | \ ++ (ISPCTRL_CCDC_CLK_EN | ISPCTRL_CCDC_RAM_EN) | \ ++ (ISPCTRL_PREV_CLK_EN | ISPCTRL_PREV_RAM_EN)) ++ ++static void __isp_subclk_update(struct isp_device *isp) ++{ ++ u32 clk = 0; ++ ++ if (isp->subclk_resources & OMAP3_ISP_SUBCLK_H3A) ++ clk |= ISPCTRL_H3A_CLK_EN; ++ ++ if (isp->subclk_resources & OMAP3_ISP_SUBCLK_HIST) ++ clk |= ISPCTRL_HIST_CLK_EN; ++ ++ if (isp->subclk_resources & OMAP3_ISP_SUBCLK_RESIZER) ++ clk |= ISPCTRL_RSZ_CLK_EN; ++ ++ /* NOTE: For CCDC & Preview submodules, we need to affect internal ++ * RAM aswell. ++ */ ++ if (isp->subclk_resources & OMAP3_ISP_SUBCLK_CCDC) ++ clk |= ISPCTRL_CCDC_CLK_EN | ISPCTRL_CCDC_RAM_EN; ++ ++ if (isp->subclk_resources & OMAP3_ISP_SUBCLK_PREVIEW) ++ clk |= ISPCTRL_PREV_CLK_EN | ISPCTRL_PREV_RAM_EN; ++ ++ isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, ++ ISPCTRL_CLKS_MASK, clk); ++} ++ ++void omap3isp_subclk_enable(struct isp_device *isp, ++ enum isp_subclk_resource res) ++{ ++ isp->subclk_resources |= res; ++ ++ __isp_subclk_update(isp); ++} ++ ++void omap3isp_subclk_disable(struct isp_device *isp, ++ enum isp_subclk_resource res) ++{ ++ isp->subclk_resources &= ~res; ++ ++ __isp_subclk_update(isp); ++} ++ ++/* ++ * isp_enable_clocks - Enable ISP clocks ++ * @isp: OMAP3 ISP device ++ * ++ * Return 0 if successful, or clk_enable return value if any of tthem fails. ++ */ ++static int isp_enable_clocks(struct isp_device *isp) ++{ ++ int r; ++ unsigned long rate; ++ int divisor; ++ ++ /* ++ * cam_mclk clock chain: ++ * dpll4 -> dpll4_m5 -> dpll4_m5x2 -> cam_mclk ++ * ++ * In OMAP3630 dpll4_m5x2 != 2 x dpll4_m5 but both are ++ * set to the same value. Hence the rate set for dpll4_m5 ++ * has to be twice of what is set on OMAP3430 to get ++ * the required value for cam_mclk ++ */ ++ if (cpu_is_omap3630()) ++ divisor = 1; ++ else ++ divisor = 2; ++ ++ r = clk_enable(isp->clock[ISP_CLK_CAM_ICK]); ++ if (r) { ++ dev_err(isp->dev, "clk_enable cam_ick failed\n"); ++ goto out_clk_enable_ick; ++ } ++ r = clk_set_rate(isp->clock[ISP_CLK_DPLL4_M5_CK], ++ CM_CAM_MCLK_HZ/divisor); ++ if (r) { ++ dev_err(isp->dev, "clk_set_rate for dpll4_m5_ck failed\n"); ++ goto out_clk_enable_mclk; ++ } ++ r = clk_enable(isp->clock[ISP_CLK_CAM_MCLK]); ++ if (r) { ++ dev_err(isp->dev, "clk_enable cam_mclk failed\n"); ++ goto out_clk_enable_mclk; ++ } ++ rate = clk_get_rate(isp->clock[ISP_CLK_CAM_MCLK]); ++ if (rate != CM_CAM_MCLK_HZ) ++ dev_warn(isp->dev, "unexpected cam_mclk rate:\n" ++ " expected : %d\n" ++ " actual : %ld\n", CM_CAM_MCLK_HZ, rate); ++ r = clk_enable(isp->clock[ISP_CLK_CSI2_FCK]); ++ if (r) { ++ dev_err(isp->dev, "clk_enable csi2_fck failed\n"); ++ goto out_clk_enable_csi2_fclk; ++ } ++ return 0; ++ ++out_clk_enable_csi2_fclk: ++ clk_disable(isp->clock[ISP_CLK_CAM_MCLK]); ++out_clk_enable_mclk: ++ clk_disable(isp->clock[ISP_CLK_CAM_ICK]); ++out_clk_enable_ick: ++ return r; ++} ++ ++/* ++ * isp_disable_clocks - Disable ISP clocks ++ * @isp: OMAP3 ISP device ++ */ ++static void isp_disable_clocks(struct isp_device *isp) ++{ ++ clk_disable(isp->clock[ISP_CLK_CAM_ICK]); ++ clk_disable(isp->clock[ISP_CLK_CAM_MCLK]); ++ clk_disable(isp->clock[ISP_CLK_CSI2_FCK]); ++} ++ ++static const char *isp_clocks[] = { ++ "cam_ick", ++ "cam_mclk", ++ "dpll4_m5_ck", ++ "csi2_96m_fck", ++ "l3_ick", ++}; ++ ++static void isp_put_clocks(struct isp_device *isp) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(isp_clocks); ++i) { ++ if (isp->clock[i]) { ++ clk_put(isp->clock[i]); ++ isp->clock[i] = NULL; ++ } ++ } ++} ++ ++static int isp_get_clocks(struct isp_device *isp) ++{ ++ struct clk *clk; ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(isp_clocks); ++i) { ++ clk = clk_get(isp->dev, isp_clocks[i]); ++ if (IS_ERR(clk)) { ++ dev_err(isp->dev, "clk_get %s failed\n", isp_clocks[i]); ++ isp_put_clocks(isp); ++ return PTR_ERR(clk); ++ } ++ ++ isp->clock[i] = clk; ++ } ++ ++ return 0; ++} ++ ++/* ++ * omap3isp_get - Acquire the ISP resource. ++ * ++ * Initializes the clocks for the first acquire. ++ * ++ * Increment the reference count on the ISP. If the first reference is taken, ++ * enable clocks and power-up all submodules. ++ * ++ * Return a pointer to the ISP device structure, or NULL if an error occured. ++ */ ++struct isp_device *omap3isp_get(struct isp_device *isp) ++{ ++ struct isp_device *__isp = isp; ++ ++ if (isp == NULL) ++ return NULL; ++ ++ mutex_lock(&isp->isp_mutex); ++ if (isp->ref_count > 0) ++ goto out; ++ ++ if (isp_enable_clocks(isp) < 0) { ++ __isp = NULL; ++ goto out; ++ } ++ ++ /* We don't want to restore context before saving it! */ ++ if (isp->has_context) ++ isp_restore_ctx(isp); ++ else ++ isp->has_context = 1; ++ ++ isp_enable_interrupts(isp); ++ ++out: ++ if (__isp != NULL) ++ isp->ref_count++; ++ mutex_unlock(&isp->isp_mutex); ++ ++ return __isp; ++} ++ ++/* ++ * omap3isp_put - Release the ISP ++ * ++ * Decrement the reference count on the ISP. If the last reference is released, ++ * power-down all submodules, disable clocks and free temporary buffers. ++ */ ++void omap3isp_put(struct isp_device *isp) ++{ ++ if (isp == NULL) ++ return; ++ ++ mutex_lock(&isp->isp_mutex); ++ BUG_ON(isp->ref_count == 0); ++ if (--isp->ref_count == 0) { ++ isp_disable_interrupts(isp); ++ isp_save_ctx(isp); ++ isp_disable_clocks(isp); ++ } ++ mutex_unlock(&isp->isp_mutex); ++} ++ ++/* -------------------------------------------------------------------------- ++ * Platform device driver ++ */ ++ ++/* ++ * omap3isp_print_status - Prints the values of the ISP Control Module registers ++ * @isp: OMAP3 ISP device ++ */ ++#define ISP_PRINT_REGISTER(isp, name)\ ++ dev_dbg(isp->dev, "###ISP " #name "=0x%08x\n", \ ++ isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_##name)) ++#define SBL_PRINT_REGISTER(isp, name)\ ++ dev_dbg(isp->dev, "###SBL " #name "=0x%08x\n", \ ++ isp_reg_readl(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_##name)) ++ ++void omap3isp_print_status(struct isp_device *isp) ++{ ++ dev_dbg(isp->dev, "-------------ISP Register dump--------------\n"); ++ ++ ISP_PRINT_REGISTER(isp, SYSCONFIG); ++ ISP_PRINT_REGISTER(isp, SYSSTATUS); ++ ISP_PRINT_REGISTER(isp, IRQ0ENABLE); ++ ISP_PRINT_REGISTER(isp, IRQ0STATUS); ++ ISP_PRINT_REGISTER(isp, TCTRL_GRESET_LENGTH); ++ ISP_PRINT_REGISTER(isp, TCTRL_PSTRB_REPLAY); ++ ISP_PRINT_REGISTER(isp, CTRL); ++ ISP_PRINT_REGISTER(isp, TCTRL_CTRL); ++ ISP_PRINT_REGISTER(isp, TCTRL_FRAME); ++ ISP_PRINT_REGISTER(isp, TCTRL_PSTRB_DELAY); ++ ISP_PRINT_REGISTER(isp, TCTRL_STRB_DELAY); ++ ISP_PRINT_REGISTER(isp, TCTRL_SHUT_DELAY); ++ ISP_PRINT_REGISTER(isp, TCTRL_PSTRB_LENGTH); ++ ISP_PRINT_REGISTER(isp, TCTRL_STRB_LENGTH); ++ ISP_PRINT_REGISTER(isp, TCTRL_SHUT_LENGTH); ++ ++ SBL_PRINT_REGISTER(isp, PCR); ++ SBL_PRINT_REGISTER(isp, SDR_REQ_EXP); ++ ++ dev_dbg(isp->dev, "--------------------------------------------\n"); ++} ++ ++#ifdef CONFIG_PM ++ ++/* ++ * Power management support. ++ * ++ * As the ISP can't properly handle an input video stream interruption on a non ++ * frame boundary, the ISP pipelines need to be stopped before sensors get ++ * suspended. However, as suspending the sensors can require a running clock, ++ * which can be provided by the ISP, the ISP can't be completely suspended ++ * before the sensor. ++ * ++ * To solve this problem power management support is split into prepare/complete ++ * and suspend/resume operations. The pipelines are stopped in prepare() and the ++ * ISP clocks get disabled in suspend(). Similarly, the clocks are reenabled in ++ * resume(), and the the pipelines are restarted in complete(). ++ * ++ * TODO: PM dependencies between the ISP and sensors are not modeled explicitly ++ * yet. ++ */ ++static int isp_pm_prepare(struct device *dev) ++{ ++ struct isp_device *isp = dev_get_drvdata(dev); ++ int reset; ++ ++ WARN_ON(mutex_is_locked(&isp->isp_mutex)); ++ ++ if (isp->ref_count == 0) ++ return 0; ++ ++ reset = isp_suspend_modules(isp); ++ isp_disable_interrupts(isp); ++ isp_save_ctx(isp); ++ if (reset) ++ isp_reset(isp); ++ ++ return 0; ++} ++ ++static int isp_pm_suspend(struct device *dev) ++{ ++ struct isp_device *isp = dev_get_drvdata(dev); ++ ++ WARN_ON(mutex_is_locked(&isp->isp_mutex)); ++ ++ if (isp->ref_count) ++ isp_disable_clocks(isp); ++ ++ return 0; ++} ++ ++static int isp_pm_resume(struct device *dev) ++{ ++ struct isp_device *isp = dev_get_drvdata(dev); ++ ++ if (isp->ref_count == 0) ++ return 0; ++ ++ return isp_enable_clocks(isp); ++} ++ ++static void isp_pm_complete(struct device *dev) ++{ ++ struct isp_device *isp = dev_get_drvdata(dev); ++ ++ if (isp->ref_count == 0) ++ return; ++ ++ isp_restore_ctx(isp); ++ isp_enable_interrupts(isp); ++ isp_resume_modules(isp); ++} ++ ++#else ++ ++#define isp_pm_prepare NULL ++#define isp_pm_suspend NULL ++#define isp_pm_resume NULL ++#define isp_pm_complete NULL ++ ++#endif /* CONFIG_PM */ ++ ++static void isp_unregister_entities(struct isp_device *isp) ++{ ++ omap3isp_csi2_unregister_entities(&isp->isp_csi2a); ++ omap3isp_ccp2_unregister_entities(&isp->isp_ccp2); ++ omap3isp_ccdc_unregister_entities(&isp->isp_ccdc); ++ omap3isp_preview_unregister_entities(&isp->isp_prev); ++ omap3isp_resizer_unregister_entities(&isp->isp_res); ++ omap3isp_stat_unregister_entities(&isp->isp_aewb); ++ omap3isp_stat_unregister_entities(&isp->isp_af); ++ omap3isp_stat_unregister_entities(&isp->isp_hist); ++ ++ v4l2_device_unregister(&isp->v4l2_dev); ++ media_device_unregister(&isp->media_dev); ++} ++ ++/* ++ * isp_register_subdev_group - Register a group of subdevices ++ * @isp: OMAP3 ISP device ++ * @board_info: I2C subdevs board information array ++ * ++ * Register all I2C subdevices in the board_info array. The array must be ++ * terminated by a NULL entry, and the first entry must be the sensor. ++ * ++ * Return a pointer to the sensor media entity if it has been successfully ++ * registered, or NULL otherwise. ++ */ ++static struct v4l2_subdev * ++isp_register_subdev_group(struct isp_device *isp, ++ struct isp_subdev_i2c_board_info *board_info) ++{ ++ struct v4l2_subdev *sensor = NULL; ++ unsigned int first; ++ ++ if (board_info->board_info == NULL) ++ return NULL; ++ ++ for (first = 1; board_info->board_info; ++board_info, first = 0) { ++ struct v4l2_subdev *subdev; ++ struct i2c_adapter *adapter; ++ ++ adapter = i2c_get_adapter(board_info->i2c_adapter_id); ++ if (adapter == NULL) { ++ printk(KERN_ERR "%s: Unable to get I2C adapter %d for " ++ "device %s\n", __func__, ++ board_info->i2c_adapter_id, ++ board_info->board_info->type); ++ continue; ++ } ++ ++ subdev = v4l2_i2c_new_subdev_board(&isp->v4l2_dev, adapter, ++ board_info->board_info, NULL, 1); ++ if (subdev == NULL) { ++ printk(KERN_ERR "%s: Unable to register subdev %s\n", ++ __func__, board_info->board_info->type); ++ continue; ++ } ++ ++ if (first) ++ sensor = subdev; ++ } ++ ++ return sensor; ++} ++ ++static int isp_register_entities(struct isp_device *isp) ++{ ++ struct isp_platform_data *pdata = isp->pdata; ++ struct isp_v4l2_subdevs_group *subdevs; ++ int ret; ++ ++ isp->media_dev.dev = isp->dev; ++ strlcpy(isp->media_dev.model, "TI OMAP3 ISP", ++ sizeof(isp->media_dev.model)); ++ isp->media_dev.link_notify = isp_pipeline_link_notify; ++ ret = media_device_register(&isp->media_dev); ++ if (ret < 0) { ++ printk(KERN_ERR "%s: Media device registration failed (%d)\n", ++ __func__, ret); ++ return ret; ++ } ++ ++ isp->v4l2_dev.mdev = &isp->media_dev; ++ ret = v4l2_device_register(isp->dev, &isp->v4l2_dev); ++ if (ret < 0) { ++ printk(KERN_ERR "%s: V4L2 device registration failed (%d)\n", ++ __func__, ret); ++ goto done; ++ } ++ ++ /* Register internal entities */ ++ ret = omap3isp_ccp2_register_entities(&isp->isp_ccp2, &isp->v4l2_dev); ++ if (ret < 0) ++ goto done; ++ ++ ret = omap3isp_csi2_register_entities(&isp->isp_csi2a, &isp->v4l2_dev); ++ if (ret < 0) ++ goto done; ++ ++ ret = omap3isp_ccdc_register_entities(&isp->isp_ccdc, &isp->v4l2_dev); ++ if (ret < 0) ++ goto done; ++ ++ ret = omap3isp_preview_register_entities(&isp->isp_prev, ++ &isp->v4l2_dev); ++ if (ret < 0) ++ goto done; ++ ++ ret = omap3isp_resizer_register_entities(&isp->isp_res, &isp->v4l2_dev); ++ if (ret < 0) ++ goto done; ++ ++ ret = omap3isp_stat_register_entities(&isp->isp_aewb, &isp->v4l2_dev); ++ if (ret < 0) ++ goto done; ++ ++ ret = omap3isp_stat_register_entities(&isp->isp_af, &isp->v4l2_dev); ++ if (ret < 0) ++ goto done; ++ ++ ret = omap3isp_stat_register_entities(&isp->isp_hist, &isp->v4l2_dev); ++ if (ret < 0) ++ goto done; ++ ++ /* Register external entities */ ++ for (subdevs = pdata->subdevs; subdevs->subdevs; ++subdevs) { ++ struct v4l2_subdev *sensor; ++ struct media_entity *input; ++ unsigned int flags; ++ unsigned int pad; ++ ++ sensor = isp_register_subdev_group(isp, subdevs->subdevs); ++ if (sensor == NULL) ++ continue; ++ ++ sensor->host_priv = subdevs; ++ ++ /* Connect the sensor to the correct interface module. Parallel ++ * sensors are connected directly to the CCDC, while serial ++ * sensors are connected to the CSI2a, CCP2b or CSI2c receiver ++ * through CSIPHY1 or CSIPHY2. ++ */ ++ switch (subdevs->interface) { ++ case ISP_INTERFACE_PARALLEL: ++ input = &isp->isp_ccdc.subdev.entity; ++ pad = CCDC_PAD_SINK; ++ flags = 0; ++ break; ++ ++ case ISP_INTERFACE_CSI2A_PHY2: ++ input = &isp->isp_csi2a.subdev.entity; ++ pad = CSI2_PAD_SINK; ++ flags = MEDIA_LNK_FL_IMMUTABLE ++ | MEDIA_LNK_FL_ENABLED; ++ break; ++ ++ case ISP_INTERFACE_CCP2B_PHY1: ++ case ISP_INTERFACE_CCP2B_PHY2: ++ input = &isp->isp_ccp2.subdev.entity; ++ pad = CCP2_PAD_SINK; ++ flags = 0; ++ break; ++ ++ case ISP_INTERFACE_CSI2C_PHY1: ++ input = &isp->isp_csi2c.subdev.entity; ++ pad = CSI2_PAD_SINK; ++ flags = MEDIA_LNK_FL_IMMUTABLE ++ | MEDIA_LNK_FL_ENABLED; ++ break; ++ ++ default: ++ printk(KERN_ERR "%s: invalid interface type %u\n", ++ __func__, subdevs->interface); ++ ret = -EINVAL; ++ goto done; ++ } ++ ++ ret = media_entity_create_link(&sensor->entity, 0, input, pad, ++ flags); ++ if (ret < 0) ++ goto done; ++ } ++ ++done: ++ if (ret < 0) ++ isp_unregister_entities(isp); ++ ++ return ret; ++} ++ ++static void isp_cleanup_modules(struct isp_device *isp) ++{ ++ omap3isp_h3a_aewb_cleanup(isp); ++ omap3isp_h3a_af_cleanup(isp); ++ omap3isp_hist_cleanup(isp); ++ omap3isp_resizer_cleanup(isp); ++ omap3isp_preview_cleanup(isp); ++ omap3isp_ccdc_cleanup(isp); ++ omap3isp_ccp2_cleanup(isp); ++ omap3isp_csi2_cleanup(isp); ++} ++ ++static int isp_initialize_modules(struct isp_device *isp) ++{ ++ int ret; ++ ++ ret = omap3isp_csiphy_init(isp); ++ if (ret < 0) { ++ dev_err(isp->dev, "CSI PHY initialization failed\n"); ++ goto error_csiphy; ++ } ++ ++ ret = omap3isp_csi2_init(isp); ++ if (ret < 0) { ++ dev_err(isp->dev, "CSI2 initialization failed\n"); ++ goto error_csi2; ++ } ++ ++ ret = omap3isp_ccp2_init(isp); ++ if (ret < 0) { ++ dev_err(isp->dev, "CCP2 initialization failed\n"); ++ goto error_ccp2; ++ } ++ ++ ret = omap3isp_ccdc_init(isp); ++ if (ret < 0) { ++ dev_err(isp->dev, "CCDC initialization failed\n"); ++ goto error_ccdc; ++ } ++ ++ ret = omap3isp_preview_init(isp); ++ if (ret < 0) { ++ dev_err(isp->dev, "Preview initialization failed\n"); ++ goto error_preview; ++ } ++ ++ ret = omap3isp_resizer_init(isp); ++ if (ret < 0) { ++ dev_err(isp->dev, "Resizer initialization failed\n"); ++ goto error_resizer; ++ } ++ ++ ret = omap3isp_hist_init(isp); ++ if (ret < 0) { ++ dev_err(isp->dev, "Histogram initialization failed\n"); ++ goto error_hist; ++ } ++ ++ ret = omap3isp_h3a_aewb_init(isp); ++ if (ret < 0) { ++ dev_err(isp->dev, "H3A AEWB initialization failed\n"); ++ goto error_h3a_aewb; ++ } ++ ++ ret = omap3isp_h3a_af_init(isp); ++ if (ret < 0) { ++ dev_err(isp->dev, "H3A AF initialization failed\n"); ++ goto error_h3a_af; ++ } ++ ++ /* Connect the submodules. */ ++ ret = media_entity_create_link( ++ &isp->isp_csi2a.subdev.entity, CSI2_PAD_SOURCE, ++ &isp->isp_ccdc.subdev.entity, CCDC_PAD_SINK, 0); ++ if (ret < 0) ++ goto error_link; ++ ++ ret = media_entity_create_link( ++ &isp->isp_ccp2.subdev.entity, CCP2_PAD_SOURCE, ++ &isp->isp_ccdc.subdev.entity, CCDC_PAD_SINK, 0); ++ if (ret < 0) ++ goto error_link; ++ ++ ret = media_entity_create_link( ++ &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP, ++ &isp->isp_prev.subdev.entity, PREV_PAD_SINK, 0); ++ if (ret < 0) ++ goto error_link; ++ ++ ret = media_entity_create_link( ++ &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_OF, ++ &isp->isp_res.subdev.entity, RESZ_PAD_SINK, 0); ++ if (ret < 0) ++ goto error_link; ++ ++ ret = media_entity_create_link( ++ &isp->isp_prev.subdev.entity, PREV_PAD_SOURCE, ++ &isp->isp_res.subdev.entity, RESZ_PAD_SINK, 0); ++ if (ret < 0) ++ goto error_link; ++ ++ ret = media_entity_create_link( ++ &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP, ++ &isp->isp_aewb.subdev.entity, 0, ++ MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE); ++ if (ret < 0) ++ goto error_link; ++ ++ ret = media_entity_create_link( ++ &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP, ++ &isp->isp_af.subdev.entity, 0, ++ MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE); ++ if (ret < 0) ++ goto error_link; ++ ++ ret = media_entity_create_link( ++ &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP, ++ &isp->isp_hist.subdev.entity, 0, ++ MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE); ++ if (ret < 0) ++ goto error_link; ++ ++ return 0; ++ ++error_link: ++ omap3isp_h3a_af_cleanup(isp); ++error_h3a_af: ++ omap3isp_h3a_aewb_cleanup(isp); ++error_h3a_aewb: ++ omap3isp_hist_cleanup(isp); ++error_hist: ++ omap3isp_resizer_cleanup(isp); ++error_resizer: ++ omap3isp_preview_cleanup(isp); ++error_preview: ++ omap3isp_ccdc_cleanup(isp); ++error_ccdc: ++ omap3isp_ccp2_cleanup(isp); ++error_ccp2: ++ omap3isp_csi2_cleanup(isp); ++error_csi2: ++error_csiphy: ++ return ret; ++} ++ ++/* ++ * isp_remove - Remove ISP platform device ++ * @pdev: Pointer to ISP platform device ++ * ++ * Always returns 0. ++ */ ++static int isp_remove(struct platform_device *pdev) ++{ ++ struct isp_device *isp = platform_get_drvdata(pdev); ++ int i; ++ ++ isp_unregister_entities(isp); ++ isp_cleanup_modules(isp); ++ ++ omap3isp_get(isp); ++ iommu_put(isp->iommu); ++ omap3isp_put(isp); ++ ++ free_irq(isp->irq_num, isp); ++ isp_put_clocks(isp); ++ ++ for (i = 0; i < OMAP3_ISP_IOMEM_LAST; i++) { ++ if (isp->mmio_base[i]) { ++ iounmap(isp->mmio_base[i]); ++ isp->mmio_base[i] = NULL; ++ } ++ ++ if (isp->mmio_base_phys[i]) { ++ release_mem_region(isp->mmio_base_phys[i], ++ isp->mmio_size[i]); ++ isp->mmio_base_phys[i] = 0; ++ } ++ } ++ ++ regulator_put(isp->isp_csiphy1.vdd); ++ regulator_put(isp->isp_csiphy2.vdd); ++ kfree(isp); ++ ++ return 0; ++} ++ ++static int isp_map_mem_resource(struct platform_device *pdev, ++ struct isp_device *isp, ++ enum isp_mem_resources res) ++{ ++ struct resource *mem; ++ ++ /* request the mem region for the camera registers */ ++ ++ mem = platform_get_resource(pdev, IORESOURCE_MEM, res); ++ if (!mem) { ++ dev_err(isp->dev, "no mem resource?\n"); ++ return -ENODEV; ++ } ++ ++ if (!request_mem_region(mem->start, resource_size(mem), pdev->name)) { ++ dev_err(isp->dev, ++ "cannot reserve camera register I/O region\n"); ++ return -ENODEV; ++ } ++ isp->mmio_base_phys[res] = mem->start; ++ isp->mmio_size[res] = resource_size(mem); ++ ++ /* map the region */ ++ isp->mmio_base[res] = ioremap_nocache(isp->mmio_base_phys[res], ++ isp->mmio_size[res]); ++ if (!isp->mmio_base[res]) { ++ dev_err(isp->dev, "cannot map camera register I/O region\n"); ++ return -ENODEV; ++ } ++ ++ return 0; ++} ++ ++/* ++ * isp_probe - Probe ISP platform device ++ * @pdev: Pointer to ISP platform device ++ * ++ * Returns 0 if successful, ++ * -ENOMEM if no memory available, ++ * -ENODEV if no platform device resources found ++ * or no space for remapping registers, ++ * -EINVAL if couldn't install ISR, ++ * or clk_get return error value. ++ */ ++static int isp_probe(struct platform_device *pdev) ++{ ++ struct isp_platform_data *pdata = pdev->dev.platform_data; ++ struct isp_device *isp; ++ int ret; ++ int i, m; ++ ++ if (pdata == NULL) ++ return -EINVAL; ++ ++ isp = kzalloc(sizeof(*isp), GFP_KERNEL); ++ if (!isp) { ++ dev_err(&pdev->dev, "could not allocate memory\n"); ++ return -ENOMEM; ++ } ++ ++ isp->autoidle = autoidle; ++ isp->platform_cb.set_xclk = isp_set_xclk; ++ isp->platform_cb.set_pixel_clock = isp_set_pixel_clock; ++ ++ mutex_init(&isp->isp_mutex); ++ spin_lock_init(&isp->stat_lock); ++ ++ isp->dev = &pdev->dev; ++ isp->pdata = pdata; ++ isp->ref_count = 0; ++ ++ isp->raw_dmamask = DMA_BIT_MASK(32); ++ isp->dev->dma_mask = &isp->raw_dmamask; ++ isp->dev->coherent_dma_mask = DMA_BIT_MASK(32); ++ ++ platform_set_drvdata(pdev, isp); ++ ++ /* Regulators */ ++ isp->isp_csiphy1.vdd = regulator_get(&pdev->dev, "VDD_CSIPHY1"); ++ isp->isp_csiphy2.vdd = regulator_get(&pdev->dev, "VDD_CSIPHY2"); ++ ++ /* Clocks */ ++ ret = isp_map_mem_resource(pdev, isp, OMAP3_ISP_IOMEM_MAIN); ++ if (ret < 0) ++ goto error; ++ ++ ret = isp_get_clocks(isp); ++ if (ret < 0) ++ goto error; ++ ++ if (omap3isp_get(isp) == NULL) ++ goto error; ++ ++ ret = isp_reset(isp); ++ if (ret < 0) ++ goto error_isp; ++ ++ /* Memory resources */ ++ isp->revision = isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_REVISION); ++ dev_info(isp->dev, "Revision %d.%d found\n", ++ (isp->revision & 0xf0) >> 4, isp->revision & 0x0f); ++ ++ for (m = 0; m < ARRAY_SIZE(isp_res_maps); m++) ++ if (isp->revision == isp_res_maps[m].isp_rev) ++ break; ++ ++ if (m == ARRAY_SIZE(isp_res_maps)) { ++ dev_err(isp->dev, "No resource map found for ISP rev %d.%d\n", ++ (isp->revision & 0xf0) >> 4, isp->revision & 0xf); ++ ret = -ENODEV; ++ goto error_isp; ++ } ++ ++ for (i = 1; i < OMAP3_ISP_IOMEM_LAST; i++) { ++ if (isp_res_maps[m].map & 1 << i) { ++ ret = isp_map_mem_resource(pdev, isp, i); ++ if (ret) ++ goto error_isp; ++ } ++ } ++ ++ /* IOMMU */ ++ isp->iommu = iommu_get("isp"); ++ if (IS_ERR_OR_NULL(isp->iommu)) { ++ isp->iommu = NULL; ++ ret = -ENODEV; ++ goto error_isp; ++ } ++ ++ /* Interrupt */ ++ isp->irq_num = platform_get_irq(pdev, 0); ++ if (isp->irq_num <= 0) { ++ dev_err(isp->dev, "No IRQ resource\n"); ++ ret = -ENODEV; ++ goto error_isp; ++ } ++ ++ if (request_irq(isp->irq_num, isp_isr, IRQF_SHARED, "OMAP3 ISP", isp)) { ++ dev_err(isp->dev, "Unable to request IRQ\n"); ++ ret = -EINVAL; ++ goto error_isp; ++ } ++ ++ /* Entities */ ++ ret = isp_initialize_modules(isp); ++ if (ret < 0) ++ goto error_irq; ++ ++ ret = isp_register_entities(isp); ++ if (ret < 0) ++ goto error_modules; ++ ++ isp_power_settings(isp, 1); ++ omap3isp_put(isp); ++ ++ return 0; ++ ++error_modules: ++ isp_cleanup_modules(isp); ++error_irq: ++ free_irq(isp->irq_num, isp); ++error_isp: ++ iommu_put(isp->iommu); ++ omap3isp_put(isp); ++error: ++ isp_put_clocks(isp); ++ ++ for (i = 0; i < OMAP3_ISP_IOMEM_LAST; i++) { ++ if (isp->mmio_base[i]) { ++ iounmap(isp->mmio_base[i]); ++ isp->mmio_base[i] = NULL; ++ } ++ ++ if (isp->mmio_base_phys[i]) { ++ release_mem_region(isp->mmio_base_phys[i], ++ isp->mmio_size[i]); ++ isp->mmio_base_phys[i] = 0; ++ } ++ } ++ regulator_put(isp->isp_csiphy2.vdd); ++ regulator_put(isp->isp_csiphy1.vdd); ++ platform_set_drvdata(pdev, NULL); ++ kfree(isp); ++ ++ return ret; ++} ++ ++static const struct dev_pm_ops omap3isp_pm_ops = { ++ .prepare = isp_pm_prepare, ++ .suspend = isp_pm_suspend, ++ .resume = isp_pm_resume, ++ .complete = isp_pm_complete, ++}; ++ ++static struct platform_device_id omap3isp_id_table[] = { ++ { "omap3isp", 0 }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(platform, omap3isp_id_table); ++ ++static struct platform_driver omap3isp_driver = { ++ .probe = isp_probe, ++ .remove = isp_remove, ++ .id_table = omap3isp_id_table, ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "omap3isp", ++ .pm = &omap3isp_pm_ops, ++ }, ++}; ++ ++/* ++ * isp_init - ISP module initialization. ++ */ ++static int __init isp_init(void) ++{ ++ return platform_driver_register(&omap3isp_driver); ++} ++ ++/* ++ * isp_cleanup - ISP module cleanup. ++ */ ++static void __exit isp_cleanup(void) ++{ ++ platform_driver_unregister(&omap3isp_driver); ++} ++ ++module_init(isp_init); ++module_exit(isp_cleanup); ++ ++MODULE_AUTHOR("Nokia Corporation"); ++MODULE_DESCRIPTION("TI OMAP3 ISP driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/video/isp/isp.h b/drivers/media/video/isp/isp.h +new file mode 100644 +index 0000000..44590a5 +--- /dev/null ++++ b/drivers/media/video/isp/isp.h +@@ -0,0 +1,427 @@ ++/* ++ * isp.h ++ * ++ * TI OMAP3 ISP - Core ++ * ++ * Copyright (C) 2009-2010 Nokia Corporation ++ * Copyright (C) 2009 Texas Instruments, Inc. ++ * ++ * Contacts: Laurent Pinchart ++ * Sakari Ailus ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#ifndef OMAP3_ISP_CORE_H ++#define OMAP3_ISP_CORE_H ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "ispstat.h" ++#include "ispccdc.h" ++#include "ispreg.h" ++#include "ispresizer.h" ++#include "isppreview.h" ++#include "ispcsiphy.h" ++#include "ispcsi2.h" ++#include "ispccp2.h" ++ ++#define IOMMU_FLAG (IOVMF_ENDIAN_LITTLE | IOVMF_ELSZ_8) ++ ++#define ISP_TOK_TERM 0xFFFFFFFF /* ++ * terminating token for ISP ++ * modules reg list ++ */ ++#define to_isp_device(ptr_module) \ ++ container_of(ptr_module, struct isp_device, isp_##ptr_module) ++#define to_device(ptr_module) \ ++ (to_isp_device(ptr_module)->dev) ++ ++enum isp_mem_resources { ++ OMAP3_ISP_IOMEM_MAIN, ++ OMAP3_ISP_IOMEM_CCP2, ++ OMAP3_ISP_IOMEM_CCDC, ++ OMAP3_ISP_IOMEM_HIST, ++ OMAP3_ISP_IOMEM_H3A, ++ OMAP3_ISP_IOMEM_PREV, ++ OMAP3_ISP_IOMEM_RESZ, ++ OMAP3_ISP_IOMEM_SBL, ++ OMAP3_ISP_IOMEM_CSI2A_REGS1, ++ OMAP3_ISP_IOMEM_CSIPHY2, ++ OMAP3_ISP_IOMEM_CSI2A_REGS2, ++ OMAP3_ISP_IOMEM_CSI2C_REGS1, ++ OMAP3_ISP_IOMEM_CSIPHY1, ++ OMAP3_ISP_IOMEM_CSI2C_REGS2, ++ OMAP3_ISP_IOMEM_LAST ++}; ++ ++enum isp_sbl_resource { ++ OMAP3_ISP_SBL_CSI1_READ = 0x1, ++ OMAP3_ISP_SBL_CSI1_WRITE = 0x2, ++ OMAP3_ISP_SBL_CSI2A_WRITE = 0x4, ++ OMAP3_ISP_SBL_CSI2C_WRITE = 0x8, ++ OMAP3_ISP_SBL_CCDC_LSC_READ = 0x10, ++ OMAP3_ISP_SBL_CCDC_WRITE = 0x20, ++ OMAP3_ISP_SBL_PREVIEW_READ = 0x40, ++ OMAP3_ISP_SBL_PREVIEW_WRITE = 0x80, ++ OMAP3_ISP_SBL_RESIZER_READ = 0x100, ++ OMAP3_ISP_SBL_RESIZER_WRITE = 0x200, ++}; ++ ++enum isp_subclk_resource { ++ OMAP3_ISP_SUBCLK_CCDC = (1 << 0), ++ OMAP3_ISP_SUBCLK_H3A = (1 << 1), ++ OMAP3_ISP_SUBCLK_HIST = (1 << 2), ++ OMAP3_ISP_SUBCLK_PREVIEW = (1 << 3), ++ OMAP3_ISP_SUBCLK_RESIZER = (1 << 4), ++}; ++ ++enum isp_interface_type { ++ ISP_INTERFACE_PARALLEL, ++ ISP_INTERFACE_CSI2A_PHY2, ++ ISP_INTERFACE_CCP2B_PHY1, ++ ISP_INTERFACE_CCP2B_PHY2, ++ ISP_INTERFACE_CSI2C_PHY1, ++}; ++ ++#define ISP_REVISION_1_0 0x10 ++#define ISP_REVISION_2_0 0x20 ++#define ISP_REVISION_15_0 0xF0 ++ ++/* ++ * struct isp_res_mapping - Map ISP io resources to ISP revision. ++ * @isp_rev: ISP_REVISION_x_x ++ * @map: bitmap for enum isp_mem_resources ++ */ ++struct isp_res_mapping { ++ u32 isp_rev; ++ u32 map; ++}; ++ ++/* ++ * struct isp_reg - Structure for ISP register values. ++ * @reg: 32-bit Register address. ++ * @val: 32-bit Register value. ++ */ ++struct isp_reg { ++ enum isp_mem_resources mmio_range; ++ u32 reg; ++ u32 val; ++}; ++ ++/** ++ * struct isp_parallel_platform_data - Parallel interface platform data ++ * @width: Parallel bus width in bits (8, 10, 11 or 12) ++ * @data_lane_shift: Data lane shifter ++ * 0 - CAMEXT[13:0] -> CAM[13:0] ++ * 1 - CAMEXT[13:2] -> CAM[11:0] ++ * 2 - CAMEXT[13:4] -> CAM[9:0] ++ * 3 - CAMEXT[13:6] -> CAM[7:0] ++ * @clk_pol: Pixel clock polarity ++ * 0 - Non Inverted, 1 - Inverted ++ * @bridge: CCDC Bridge input control ++ * ISPCTRL_PAR_BRIDGE_DISABLE - Disable ++ * ISPCTRL_PAR_BRIDGE_LENDIAN - Little endian ++ * ISPCTRL_PAR_BRIDGE_BENDIAN - Big endian ++ */ ++struct isp_parallel_platform_data { ++ unsigned int width; ++ unsigned int data_lane_shift:2; ++ unsigned int clk_pol:1; ++ unsigned int bridge:4; ++}; ++ ++/** ++ * struct isp_ccp2_platform_data - CCP2 interface platform data ++ * @strobe_clk_pol: Strobe/clock polarity ++ * 0 - Non Inverted, 1 - Inverted ++ * @crc: Enable the cyclic redundancy check ++ * @ccp2_mode: Enable CCP2 compatibility mode ++ * 0 - MIPI-CSI1 mode, 1 - CCP2 mode ++ * @phy_layer: Physical layer selection ++ * ISPCCP2_CTRL_PHY_SEL_CLOCK - Data/clock physical layer ++ * ISPCCP2_CTRL_PHY_SEL_STROBE - Data/strobe physical layer ++ * @vpclk_div: Video port output clock control ++ */ ++struct isp_ccp2_platform_data { ++ unsigned int strobe_clk_pol:1; ++ unsigned int crc:1; ++ unsigned int ccp2_mode:1; ++ unsigned int phy_layer:1; ++ unsigned int vpclk_div:2; ++}; ++ ++/** ++ * struct isp_csi2_platform_data - CSI2 interface platform data ++ * @crc: Enable the cyclic redundancy check ++ * @vpclk_div: Video port output clock control ++ */ ++struct isp_csi2_platform_data { ++ unsigned crc:1; ++ unsigned vpclk_div:2; ++}; ++ ++struct isp_subdev_i2c_board_info { ++ struct i2c_board_info *board_info; ++ int i2c_adapter_id; ++}; ++ ++struct isp_v4l2_subdevs_group { ++ struct isp_subdev_i2c_board_info *subdevs; ++ enum isp_interface_type interface; ++ union { ++ struct isp_parallel_platform_data parallel; ++ struct isp_ccp2_platform_data ccp2; ++ struct isp_csi2_platform_data csi2; ++ } bus; /* gcc < 4.6.0 chokes on anonymous union initializers */ ++}; ++ ++struct isp_platform_data { ++ struct isp_v4l2_subdevs_group *subdevs; ++}; ++ ++struct isp_platform_callback { ++ u32 (*set_xclk)(struct isp_device *isp, u32 xclk, u8 xclksel); ++ int (*csiphy_config)(struct isp_csiphy *phy, ++ struct isp_csiphy_dphy_cfg *dphy, ++ struct isp_csiphy_lanes_cfg *lanes); ++ void (*set_pixel_clock)(struct isp_device *isp, unsigned int pixelclk); ++}; ++ ++/* ++ * struct isp_device - ISP device structure. ++ * @dev: Device pointer specific to the OMAP3 ISP. ++ * @revision: Stores current ISP module revision. ++ * @irq_num: Currently used IRQ number. ++ * @mmio_base: Array with kernel base addresses for ioremapped ISP register ++ * regions. ++ * @mmio_base_phys: Array with physical L4 bus addresses for ISP register ++ * regions. ++ * @mmio_size: Array with ISP register regions size in bytes. ++ * @raw_dmamask: Raw DMA mask ++ * @stat_lock: Spinlock for handling statistics ++ * @isp_mutex: Mutex for serializing requests to ISP. ++ * @has_context: Context has been saved at least once and can be restored. ++ * @ref_count: Reference count for handling multiple ISP requests. ++ * @cam_ick: Pointer to camera interface clock structure. ++ * @cam_mclk: Pointer to camera functional clock structure. ++ * @dpll4_m5_ck: Pointer to DPLL4 M5 clock structure. ++ * @csi2_fck: Pointer to camera CSI2 complexIO clock structure. ++ * @l3_ick: Pointer to OMAP3 L3 bus interface clock. ++ * @irq: Currently attached ISP ISR callbacks information structure. ++ * @isp_af: Pointer to current settings for ISP AutoFocus SCM. ++ * @isp_hist: Pointer to current settings for ISP Histogram SCM. ++ * @isp_h3a: Pointer to current settings for ISP Auto Exposure and ++ * White Balance SCM. ++ * @isp_res: Pointer to current settings for ISP Resizer. ++ * @isp_prev: Pointer to current settings for ISP Preview. ++ * @isp_ccdc: Pointer to current settings for ISP CCDC. ++ * @iommu: Pointer to requested IOMMU instance for ISP. ++ * @platform_cb: ISP driver callback function pointers for platform code ++ * ++ * This structure is used to store the OMAP ISP Information. ++ */ ++struct isp_device { ++ struct v4l2_device v4l2_dev; ++ struct media_device media_dev; ++ struct device *dev; ++ u32 revision; ++ ++ /* platform HW resources */ ++ struct isp_platform_data *pdata; ++ unsigned int irq_num; ++ ++ void __iomem *mmio_base[OMAP3_ISP_IOMEM_LAST]; ++ unsigned long mmio_base_phys[OMAP3_ISP_IOMEM_LAST]; ++ resource_size_t mmio_size[OMAP3_ISP_IOMEM_LAST]; ++ ++ u64 raw_dmamask; ++ ++ /* ISP Obj */ ++ spinlock_t stat_lock; /* common lock for statistic drivers */ ++ struct mutex isp_mutex; /* For handling ref_count field */ ++ int has_context; ++ int ref_count; ++ unsigned int autoidle; ++ u32 xclk_divisor[2]; /* Two clocks, a and b. */ ++#define ISP_CLK_CAM_ICK 0 ++#define ISP_CLK_CAM_MCLK 1 ++#define ISP_CLK_DPLL4_M5_CK 2 ++#define ISP_CLK_CSI2_FCK 3 ++#define ISP_CLK_L3_ICK 4 ++ struct clk *clock[5]; ++ ++ /* ISP modules */ ++ struct ispstat isp_af; ++ struct ispstat isp_aewb; ++ struct ispstat isp_hist; ++ struct isp_res_device isp_res; ++ struct isp_prev_device isp_prev; ++ struct isp_ccdc_device isp_ccdc; ++ struct isp_csi2_device isp_csi2a; ++ struct isp_csi2_device isp_csi2c; ++ struct isp_ccp2_device isp_ccp2; ++ struct isp_csiphy isp_csiphy1; ++ struct isp_csiphy isp_csiphy2; ++ ++ unsigned int sbl_resources; ++ unsigned int subclk_resources; ++ ++ struct iommu *iommu; ++ ++ struct isp_platform_callback platform_cb; ++}; ++ ++#define v4l2_dev_to_isp_device(dev) \ ++ container_of(dev, struct isp_device, v4l2_dev) ++ ++void omap3isp_hist_dma_done(struct isp_device *isp); ++ ++void omap3isp_flush(struct isp_device *isp); ++ ++int omap3isp_module_sync_idle(struct media_entity *me, wait_queue_head_t *wait, ++ atomic_t *stopping); ++ ++int omap3isp_module_sync_is_stopping(wait_queue_head_t *wait, ++ atomic_t *stopping); ++ ++int omap3isp_pipeline_set_stream(struct isp_pipeline *pipe, ++ enum isp_pipeline_stream_state state); ++void omap3isp_configure_bridge(struct isp_device *isp, ++ enum ccdc_input_entity input, ++ const struct isp_parallel_platform_data *pdata); ++ ++#define ISP_XCLK_NONE -1 ++#define ISP_XCLK_A 0 ++#define ISP_XCLK_B 1 ++ ++struct isp_device *omap3isp_get(struct isp_device *isp); ++void omap3isp_put(struct isp_device *isp); ++ ++void omap3isp_print_status(struct isp_device *isp); ++ ++void omap3isp_sbl_enable(struct isp_device *isp, enum isp_sbl_resource res); ++void omap3isp_sbl_disable(struct isp_device *isp, enum isp_sbl_resource res); ++ ++void omap3isp_subclk_enable(struct isp_device *isp, ++ enum isp_subclk_resource res); ++void omap3isp_subclk_disable(struct isp_device *isp, ++ enum isp_subclk_resource res); ++ ++int omap3isp_pipeline_pm_use(struct media_entity *entity, int use); ++ ++int omap3isp_register_entities(struct platform_device *pdev, ++ struct v4l2_device *v4l2_dev); ++void omap3isp_unregister_entities(struct platform_device *pdev); ++ ++/* ++ * isp_reg_readl - Read value of an OMAP3 ISP register ++ * @dev: Device pointer specific to the OMAP3 ISP. ++ * @isp_mmio_range: Range to which the register offset refers to. ++ * @reg_offset: Register offset to read from. ++ * ++ * Returns an unsigned 32 bit value with the required register contents. ++ */ ++static inline ++u32 isp_reg_readl(struct isp_device *isp, enum isp_mem_resources isp_mmio_range, ++ u32 reg_offset) ++{ ++ return __raw_readl(isp->mmio_base[isp_mmio_range] + reg_offset); ++} ++ ++/* ++ * isp_reg_writel - Write value to an OMAP3 ISP register ++ * @dev: Device pointer specific to the OMAP3 ISP. ++ * @reg_value: 32 bit value to write to the register. ++ * @isp_mmio_range: Range to which the register offset refers to. ++ * @reg_offset: Register offset to write into. ++ */ ++static inline ++void isp_reg_writel(struct isp_device *isp, u32 reg_value, ++ enum isp_mem_resources isp_mmio_range, u32 reg_offset) ++{ ++ __raw_writel(reg_value, isp->mmio_base[isp_mmio_range] + reg_offset); ++} ++ ++/* ++ * isp_reg_and - Clear individual bits in an OMAP3 ISP register ++ * @dev: Device pointer specific to the OMAP3 ISP. ++ * @mmio_range: Range to which the register offset refers to. ++ * @reg: Register offset to work on. ++ * @clr_bits: 32 bit value which would be cleared in the register. ++ */ ++static inline ++void isp_reg_clr(struct isp_device *isp, enum isp_mem_resources mmio_range, ++ u32 reg, u32 clr_bits) ++{ ++ u32 v = isp_reg_readl(isp, mmio_range, reg); ++ ++ isp_reg_writel(isp, v & ~clr_bits, mmio_range, reg); ++} ++ ++/* ++ * isp_reg_set - Set individual bits in an OMAP3 ISP register ++ * @dev: Device pointer specific to the OMAP3 ISP. ++ * @mmio_range: Range to which the register offset refers to. ++ * @reg: Register offset to work on. ++ * @set_bits: 32 bit value which would be set in the register. ++ */ ++static inline ++void isp_reg_set(struct isp_device *isp, enum isp_mem_resources mmio_range, ++ u32 reg, u32 set_bits) ++{ ++ u32 v = isp_reg_readl(isp, mmio_range, reg); ++ ++ isp_reg_writel(isp, v | set_bits, mmio_range, reg); ++} ++ ++/* ++ * isp_reg_clr_set - Clear and set invidial bits in an OMAP3 ISP register ++ * @dev: Device pointer specific to the OMAP3 ISP. ++ * @mmio_range: Range to which the register offset refers to. ++ * @reg: Register offset to work on. ++ * @clr_bits: 32 bit value which would be cleared in the register. ++ * @set_bits: 32 bit value which would be set in the register. ++ * ++ * The clear operation is done first, and then the set operation. ++ */ ++static inline ++void isp_reg_clr_set(struct isp_device *isp, enum isp_mem_resources mmio_range, ++ u32 reg, u32 clr_bits, u32 set_bits) ++{ ++ u32 v = isp_reg_readl(isp, mmio_range, reg); ++ ++ isp_reg_writel(isp, (v & ~clr_bits) | set_bits, mmio_range, reg); ++} ++ ++static inline enum v4l2_buf_type ++isp_pad_buffer_type(const struct v4l2_subdev *subdev, int pad) ++{ ++ if (pad >= subdev->entity.num_pads) ++ return 0; ++ ++ if (subdev->entity.pads[pad].flags & MEDIA_PAD_FL_INPUT) ++ return V4L2_BUF_TYPE_VIDEO_OUTPUT; ++ else ++ return V4L2_BUF_TYPE_VIDEO_CAPTURE; ++} ++ ++#endif /* OMAP3_ISP_CORE_H */ +diff --git a/drivers/media/video/isp/ispccdc.c b/drivers/media/video/isp/ispccdc.c +new file mode 100644 +index 0000000..cb71e4f +--- /dev/null ++++ b/drivers/media/video/isp/ispccdc.c +@@ -0,0 +1,2280 @@ ++/* ++ * ispccdc.c ++ * ++ * TI OMAP3 ISP - CCDC module ++ * ++ * Copyright (C) 2009-2010 Nokia Corporation ++ * Copyright (C) 2009 Texas Instruments, Inc. ++ * ++ * Contacts: Laurent Pinchart ++ * Sakari Ailus ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "isp.h" ++#include "ispreg.h" ++#include "ispccdc.h" ++ ++static struct v4l2_mbus_framefmt * ++__ccdc_get_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, ++ unsigned int pad, enum v4l2_subdev_format_whence which); ++ ++static const unsigned int ccdc_fmts[] = { ++ V4L2_MBUS_FMT_Y8_1X8, ++ V4L2_MBUS_FMT_SGRBG10_1X10, ++ V4L2_MBUS_FMT_SRGGB10_1X10, ++ V4L2_MBUS_FMT_SBGGR10_1X10, ++ V4L2_MBUS_FMT_SGBRG10_1X10, ++ V4L2_MBUS_FMT_SGRBG12_1X12, ++ V4L2_MBUS_FMT_SRGGB12_1X12, ++ V4L2_MBUS_FMT_SBGGR12_1X12, ++ V4L2_MBUS_FMT_SGBRG12_1X12, ++}; ++ ++/* ++ * ccdc_print_status - Print current CCDC Module register values. ++ * @ccdc: Pointer to ISP CCDC device. ++ * ++ * Also prints other debug information stored in the CCDC module. ++ */ ++#define CCDC_PRINT_REGISTER(isp, name)\ ++ dev_dbg(isp->dev, "###CCDC " #name "=0x%08x\n", \ ++ isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_##name)) ++ ++static void ccdc_print_status(struct isp_ccdc_device *ccdc) ++{ ++ struct isp_device *isp = to_isp_device(ccdc); ++ ++ dev_dbg(isp->dev, "-------------CCDC Register dump-------------\n"); ++ ++ CCDC_PRINT_REGISTER(isp, PCR); ++ CCDC_PRINT_REGISTER(isp, SYN_MODE); ++ CCDC_PRINT_REGISTER(isp, HD_VD_WID); ++ CCDC_PRINT_REGISTER(isp, PIX_LINES); ++ CCDC_PRINT_REGISTER(isp, HORZ_INFO); ++ CCDC_PRINT_REGISTER(isp, VERT_START); ++ CCDC_PRINT_REGISTER(isp, VERT_LINES); ++ CCDC_PRINT_REGISTER(isp, CULLING); ++ CCDC_PRINT_REGISTER(isp, HSIZE_OFF); ++ CCDC_PRINT_REGISTER(isp, SDOFST); ++ CCDC_PRINT_REGISTER(isp, SDR_ADDR); ++ CCDC_PRINT_REGISTER(isp, CLAMP); ++ CCDC_PRINT_REGISTER(isp, DCSUB); ++ CCDC_PRINT_REGISTER(isp, COLPTN); ++ CCDC_PRINT_REGISTER(isp, BLKCMP); ++ CCDC_PRINT_REGISTER(isp, FPC); ++ CCDC_PRINT_REGISTER(isp, FPC_ADDR); ++ CCDC_PRINT_REGISTER(isp, VDINT); ++ CCDC_PRINT_REGISTER(isp, ALAW); ++ CCDC_PRINT_REGISTER(isp, REC656IF); ++ CCDC_PRINT_REGISTER(isp, CFG); ++ CCDC_PRINT_REGISTER(isp, FMTCFG); ++ CCDC_PRINT_REGISTER(isp, FMT_HORZ); ++ CCDC_PRINT_REGISTER(isp, FMT_VERT); ++ CCDC_PRINT_REGISTER(isp, PRGEVEN0); ++ CCDC_PRINT_REGISTER(isp, PRGEVEN1); ++ CCDC_PRINT_REGISTER(isp, PRGODD0); ++ CCDC_PRINT_REGISTER(isp, PRGODD1); ++ CCDC_PRINT_REGISTER(isp, VP_OUT); ++ CCDC_PRINT_REGISTER(isp, LSC_CONFIG); ++ CCDC_PRINT_REGISTER(isp, LSC_INITIAL); ++ CCDC_PRINT_REGISTER(isp, LSC_TABLE_BASE); ++ CCDC_PRINT_REGISTER(isp, LSC_TABLE_OFFSET); ++ ++ dev_dbg(isp->dev, "--------------------------------------------\n"); ++} ++ ++/* ++ * omap3isp_ccdc_busy - Get busy state of the CCDC. ++ * @ccdc: Pointer to ISP CCDC device. ++ */ ++int omap3isp_ccdc_busy(struct isp_ccdc_device *ccdc) ++{ ++ struct isp_device *isp = to_isp_device(ccdc); ++ ++ return isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_PCR) & ++ ISPCCDC_PCR_BUSY; ++} ++ ++/* ----------------------------------------------------------------------------- ++ * Lens Shading Compensation ++ */ ++ ++/* ++ * ccdc_lsc_validate_config - Check that LSC configuration is valid. ++ * @ccdc: Pointer to ISP CCDC device. ++ * @lsc_cfg: the LSC configuration to check. ++ * ++ * Returns 0 if the LSC configuration is valid, or -EINVAL if invalid. ++ */ ++static int ccdc_lsc_validate_config(struct isp_ccdc_device *ccdc, ++ struct omap3isp_ccdc_lsc_config *lsc_cfg) ++{ ++ struct isp_device *isp = to_isp_device(ccdc); ++ struct v4l2_mbus_framefmt *format; ++ unsigned int paxel_width, paxel_height; ++ unsigned int paxel_shift_x, paxel_shift_y; ++ unsigned int min_width, min_height, min_size; ++ unsigned int input_width, input_height; ++ ++ paxel_shift_x = lsc_cfg->gain_mode_m; ++ paxel_shift_y = lsc_cfg->gain_mode_n; ++ ++ if ((paxel_shift_x < 2) || (paxel_shift_x > 6) || ++ (paxel_shift_y < 2) || (paxel_shift_y > 6)) { ++ dev_dbg(isp->dev, "CCDC: LSC: Invalid paxel size\n"); ++ return -EINVAL; ++ } ++ ++ if (lsc_cfg->offset & 3) { ++ dev_dbg(isp->dev, "CCDC: LSC: Offset must be a multiple of " ++ "4\n"); ++ return -EINVAL; ++ } ++ ++ if ((lsc_cfg->initial_x & 1) || (lsc_cfg->initial_y & 1)) { ++ dev_dbg(isp->dev, "CCDC: LSC: initial_x and y must be even\n"); ++ return -EINVAL; ++ } ++ ++ format = __ccdc_get_format(ccdc, NULL, CCDC_PAD_SINK, ++ V4L2_SUBDEV_FORMAT_ACTIVE); ++ input_width = format->width; ++ input_height = format->height; ++ ++ /* Calculate minimum bytesize for validation */ ++ paxel_width = 1 << paxel_shift_x; ++ min_width = ((input_width + lsc_cfg->initial_x + paxel_width - 1) ++ >> paxel_shift_x) + 1; ++ ++ paxel_height = 1 << paxel_shift_y; ++ min_height = ((input_height + lsc_cfg->initial_y + paxel_height - 1) ++ >> paxel_shift_y) + 1; ++ ++ min_size = 4 * min_width * min_height; ++ if (min_size > lsc_cfg->size) { ++ dev_dbg(isp->dev, "CCDC: LSC: too small table\n"); ++ return -EINVAL; ++ } ++ if (lsc_cfg->offset < (min_width * 4)) { ++ dev_dbg(isp->dev, "CCDC: LSC: Offset is too small\n"); ++ return -EINVAL; ++ } ++ if ((lsc_cfg->size / lsc_cfg->offset) < min_height) { ++ dev_dbg(isp->dev, "CCDC: LSC: Wrong size/offset combination\n"); ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++/* ++ * ccdc_lsc_program_table - Program Lens Shading Compensation table address. ++ * @ccdc: Pointer to ISP CCDC device. ++ */ ++static void ccdc_lsc_program_table(struct isp_ccdc_device *ccdc, u32 addr) ++{ ++ isp_reg_writel(to_isp_device(ccdc), addr, ++ OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_TABLE_BASE); ++} ++ ++/* ++ * ccdc_lsc_setup_regs - Configures the lens shading compensation module ++ * @ccdc: Pointer to ISP CCDC device. ++ */ ++static void ccdc_lsc_setup_regs(struct isp_ccdc_device *ccdc, ++ struct omap3isp_ccdc_lsc_config *cfg) ++{ ++ struct isp_device *isp = to_isp_device(ccdc); ++ int reg; ++ ++ isp_reg_writel(isp, cfg->offset, OMAP3_ISP_IOMEM_CCDC, ++ ISPCCDC_LSC_TABLE_OFFSET); ++ ++ reg = 0; ++ reg |= cfg->gain_mode_n << ISPCCDC_LSC_GAIN_MODE_N_SHIFT; ++ reg |= cfg->gain_mode_m << ISPCCDC_LSC_GAIN_MODE_M_SHIFT; ++ reg |= cfg->gain_format << ISPCCDC_LSC_GAIN_FORMAT_SHIFT; ++ isp_reg_writel(isp, reg, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_CONFIG); ++ ++ reg = 0; ++ reg &= ~ISPCCDC_LSC_INITIAL_X_MASK; ++ reg |= cfg->initial_x << ISPCCDC_LSC_INITIAL_X_SHIFT; ++ reg &= ~ISPCCDC_LSC_INITIAL_Y_MASK; ++ reg |= cfg->initial_y << ISPCCDC_LSC_INITIAL_Y_SHIFT; ++ isp_reg_writel(isp, reg, OMAP3_ISP_IOMEM_CCDC, ++ ISPCCDC_LSC_INITIAL); ++} ++ ++static int ccdc_lsc_wait_prefetch(struct isp_ccdc_device *ccdc) ++{ ++ struct isp_device *isp = to_isp_device(ccdc); ++ unsigned int wait; ++ ++ isp_reg_writel(isp, IRQ0STATUS_CCDC_LSC_PREF_COMP_IRQ, ++ OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS); ++ ++ /* timeout 1 ms */ ++ for (wait = 0; wait < 1000; wait++) { ++ if (isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS) & ++ IRQ0STATUS_CCDC_LSC_PREF_COMP_IRQ) { ++ isp_reg_writel(isp, IRQ0STATUS_CCDC_LSC_PREF_COMP_IRQ, ++ OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS); ++ return 0; ++ } ++ ++ rmb(); ++ udelay(1); ++ } ++ ++ return -ETIMEDOUT; ++} ++ ++/* ++ * __ccdc_lsc_enable - Enables/Disables the Lens Shading Compensation module. ++ * @ccdc: Pointer to ISP CCDC device. ++ * @enable: 0 Disables LSC, 1 Enables LSC. ++ */ ++static int __ccdc_lsc_enable(struct isp_ccdc_device *ccdc, int enable) ++{ ++ struct isp_device *isp = to_isp_device(ccdc); ++ const struct v4l2_mbus_framefmt *format = ++ __ccdc_get_format(ccdc, NULL, CCDC_PAD_SINK, ++ V4L2_SUBDEV_FORMAT_ACTIVE); ++ ++ if ((format->code != V4L2_MBUS_FMT_SGRBG10_1X10) && ++ (format->code != V4L2_MBUS_FMT_SRGGB10_1X10) && ++ (format->code != V4L2_MBUS_FMT_SBGGR10_1X10) && ++ (format->code != V4L2_MBUS_FMT_SGBRG10_1X10)) ++ return -EINVAL; ++ ++ if (enable) ++ omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_CCDC_LSC_READ); ++ ++ isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_CONFIG, ++ ISPCCDC_LSC_ENABLE, enable ? ISPCCDC_LSC_ENABLE : 0); ++ ++ if (enable) { ++ if (ccdc_lsc_wait_prefetch(ccdc) < 0) { ++ isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ++ ISPCCDC_LSC_CONFIG, ISPCCDC_LSC_ENABLE); ++ ccdc->lsc.state = LSC_STATE_STOPPED; ++ dev_warn(to_device(ccdc), "LSC prefecth timeout\n"); ++ return -ETIMEDOUT; ++ } ++ ccdc->lsc.state = LSC_STATE_RUNNING; ++ } else { ++ ccdc->lsc.state = LSC_STATE_STOPPING; ++ } ++ ++ return 0; ++} ++ ++static int ccdc_lsc_busy(struct isp_ccdc_device *ccdc) ++{ ++ struct isp_device *isp = to_isp_device(ccdc); ++ ++ return isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_CONFIG) & ++ ISPCCDC_LSC_BUSY; ++} ++ ++/* __ccdc_lsc_configure - Apply a new configuration to the LSC engine ++ * @ccdc: Pointer to ISP CCDC device ++ * @req: New configuration request ++ * ++ * context: in_interrupt() ++ */ ++static int __ccdc_lsc_configure(struct isp_ccdc_device *ccdc, ++ struct ispccdc_lsc_config_req *req) ++{ ++ if (!req->enable) ++ return -EINVAL; ++ ++ if (ccdc_lsc_validate_config(ccdc, &req->config) < 0) { ++ dev_dbg(to_device(ccdc), "Discard LSC configuration\n"); ++ return -EINVAL; ++ } ++ ++ if (ccdc_lsc_busy(ccdc)) ++ return -EBUSY; ++ ++ ccdc_lsc_setup_regs(ccdc, &req->config); ++ ccdc_lsc_program_table(ccdc, req->table); ++ return 0; ++} ++ ++/* ++ * ccdc_lsc_error_handler - Handle LSC prefetch error scenario. ++ * @ccdc: Pointer to ISP CCDC device. ++ * ++ * Disables LSC, and defers enablement to shadow registers update time. ++ */ ++static void ccdc_lsc_error_handler(struct isp_ccdc_device *ccdc) ++{ ++ struct isp_device *isp = to_isp_device(ccdc); ++ /* ++ * From OMAP3 TRM: When this event is pending, the module ++ * goes into transparent mode (output =input). Normal ++ * operation can be resumed at the start of the next frame ++ * after: ++ * 1) Clearing this event ++ * 2) Disabling the LSC module ++ * 3) Enabling it ++ */ ++ isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_CONFIG, ++ ISPCCDC_LSC_ENABLE); ++ ccdc->lsc.state = LSC_STATE_STOPPED; ++} ++ ++static void ccdc_lsc_free_request(struct isp_ccdc_device *ccdc, ++ struct ispccdc_lsc_config_req *req) ++{ ++ struct isp_device *isp = to_isp_device(ccdc); ++ ++ if (req == NULL) ++ return; ++ ++ if (req->iovm) ++ dma_unmap_sg(isp->dev, req->iovm->sgt->sgl, ++ req->iovm->sgt->nents, DMA_TO_DEVICE); ++ if (req->table) ++ iommu_vfree(isp->iommu, req->table); ++ kfree(req); ++} ++ ++static void ccdc_lsc_free_queue(struct isp_ccdc_device *ccdc, ++ struct list_head *queue) ++{ ++ struct ispccdc_lsc_config_req *req, *n; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ccdc->lsc.req_lock, flags); ++ list_for_each_entry_safe(req, n, queue, list) { ++ list_del(&req->list); ++ spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags); ++ ccdc_lsc_free_request(ccdc, req); ++ spin_lock_irqsave(&ccdc->lsc.req_lock, flags); ++ } ++ spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags); ++} ++ ++static void ccdc_lsc_free_table_work(struct work_struct *work) ++{ ++ struct isp_ccdc_device *ccdc; ++ struct ispccdc_lsc *lsc; ++ ++ lsc = container_of(work, struct ispccdc_lsc, table_work); ++ ccdc = container_of(lsc, struct isp_ccdc_device, lsc); ++ ++ ccdc_lsc_free_queue(ccdc, &lsc->free_queue); ++} ++ ++/* ++ * ccdc_lsc_config - Configure the LSC module from a userspace request ++ * ++ * Store the request LSC configuration in the LSC engine request pointer. The ++ * configuration will be applied to the hardware when the CCDC will be enabled, ++ * or at the next LSC interrupt if the CCDC is already running. ++ */ ++static int ccdc_lsc_config(struct isp_ccdc_device *ccdc, ++ struct omap3isp_ccdc_update_config *config) ++{ ++ struct isp_device *isp = to_isp_device(ccdc); ++ struct ispccdc_lsc_config_req *req; ++ unsigned long flags; ++ void *table; ++ u16 update; ++ int ret; ++ ++ update = config->update & ++ (OMAP3ISP_CCDC_CONFIG_LSC | OMAP3ISP_CCDC_TBL_LSC); ++ if (!update) ++ return 0; ++ ++ if (update != (OMAP3ISP_CCDC_CONFIG_LSC | OMAP3ISP_CCDC_TBL_LSC)) { ++ dev_dbg(to_device(ccdc), "%s: Both LSC configuration and table " ++ "need to be supplied\n", __func__); ++ return -EINVAL; ++ } ++ ++ req = kzalloc(sizeof(*req), GFP_KERNEL); ++ if (req == NULL) ++ return -ENOMEM; ++ ++ if (config->flag & OMAP3ISP_CCDC_CONFIG_LSC) { ++ if (copy_from_user(&req->config, config->lsc_cfg, ++ sizeof(req->config))) { ++ ret = -EFAULT; ++ goto done; ++ } ++ ++ req->enable = 1; ++ ++ req->table = iommu_vmalloc(isp->iommu, 0, req->config.size, ++ IOMMU_FLAG); ++ if (IS_ERR_VALUE(req->table)) { ++ req->table = 0; ++ ret = -ENOMEM; ++ goto done; ++ } ++ ++ req->iovm = find_iovm_area(isp->iommu, req->table); ++ if (req->iovm == NULL) { ++ ret = -ENOMEM; ++ goto done; ++ } ++ ++ if (!dma_map_sg(isp->dev, req->iovm->sgt->sgl, ++ req->iovm->sgt->nents, DMA_TO_DEVICE)) { ++ ret = -ENOMEM; ++ req->iovm = NULL; ++ goto done; ++ } ++ ++ dma_sync_sg_for_cpu(isp->dev, req->iovm->sgt->sgl, ++ req->iovm->sgt->nents, DMA_TO_DEVICE); ++ ++ table = da_to_va(isp->iommu, req->table); ++ if (copy_from_user(table, config->lsc, req->config.size)) { ++ ret = -EFAULT; ++ goto done; ++ } ++ ++ dma_sync_sg_for_device(isp->dev, req->iovm->sgt->sgl, ++ req->iovm->sgt->nents, DMA_TO_DEVICE); ++ } ++ ++ spin_lock_irqsave(&ccdc->lsc.req_lock, flags); ++ if (ccdc->lsc.request) { ++ list_add_tail(&ccdc->lsc.request->list, &ccdc->lsc.free_queue); ++ schedule_work(&ccdc->lsc.table_work); ++ } ++ ccdc->lsc.request = req; ++ spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags); ++ ++ ret = 0; ++ ++done: ++ if (ret < 0) ++ ccdc_lsc_free_request(ccdc, req); ++ ++ return ret; ++} ++ ++static inline int ccdc_lsc_is_configured(struct isp_ccdc_device *ccdc) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ccdc->lsc.req_lock, flags); ++ if (ccdc->lsc.active) { ++ spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags); ++ return 1; ++ } ++ spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags); ++ return 0; ++} ++ ++static int ccdc_lsc_enable(struct isp_ccdc_device *ccdc) ++{ ++ struct ispccdc_lsc *lsc = &ccdc->lsc; ++ ++ if (lsc->state != LSC_STATE_STOPPED) ++ return -EINVAL; ++ ++ if (lsc->active) { ++ list_add_tail(&lsc->active->list, &lsc->free_queue); ++ lsc->active = NULL; ++ } ++ ++ if (__ccdc_lsc_configure(ccdc, lsc->request) < 0) { ++ omap3isp_sbl_disable(to_isp_device(ccdc), ++ OMAP3_ISP_SBL_CCDC_LSC_READ); ++ list_add_tail(&lsc->request->list, &lsc->free_queue); ++ lsc->request = NULL; ++ goto done; ++ } ++ ++ lsc->active = lsc->request; ++ lsc->request = NULL; ++ __ccdc_lsc_enable(ccdc, 1); ++ ++done: ++ if (!list_empty(&lsc->free_queue)) ++ schedule_work(&lsc->table_work); ++ ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------------- ++ * Parameters configuration ++ */ ++ ++/* ++ * ccdc_configure_clamp - Configure optical-black or digital clamping ++ * @ccdc: Pointer to ISP CCDC device. ++ * ++ * The CCDC performs either optical-black or digital clamp. Configure and enable ++ * the selected clamp method. ++ */ ++static void ccdc_configure_clamp(struct isp_ccdc_device *ccdc) ++{ ++ struct isp_device *isp = to_isp_device(ccdc); ++ u32 clamp; ++ ++ if (ccdc->obclamp) { ++ clamp = ccdc->clamp.obgain << ISPCCDC_CLAMP_OBGAIN_SHIFT; ++ clamp |= ccdc->clamp.oblen << ISPCCDC_CLAMP_OBSLEN_SHIFT; ++ clamp |= ccdc->clamp.oblines << ISPCCDC_CLAMP_OBSLN_SHIFT; ++ clamp |= ccdc->clamp.obstpixel << ISPCCDC_CLAMP_OBST_SHIFT; ++ isp_reg_writel(isp, clamp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CLAMP); ++ } else { ++ isp_reg_writel(isp, ccdc->clamp.dcsubval, ++ OMAP3_ISP_IOMEM_CCDC, ISPCCDC_DCSUB); ++ } ++ ++ isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CLAMP, ++ ISPCCDC_CLAMP_CLAMPEN, ++ ccdc->obclamp ? ISPCCDC_CLAMP_CLAMPEN : 0); ++} ++ ++/* ++ * ccdc_configure_fpc - Configure Faulty Pixel Correction ++ * @ccdc: Pointer to ISP CCDC device. ++ */ ++static void ccdc_configure_fpc(struct isp_ccdc_device *ccdc) ++{ ++ struct isp_device *isp = to_isp_device(ccdc); ++ ++ isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FPC, ISPCCDC_FPC_FPCEN); ++ ++ if (!ccdc->fpc_en) ++ return; ++ ++ isp_reg_writel(isp, ccdc->fpc.fpcaddr, OMAP3_ISP_IOMEM_CCDC, ++ ISPCCDC_FPC_ADDR); ++ /* The FPNUM field must be set before enabling FPC. */ ++ isp_reg_writel(isp, (ccdc->fpc.fpnum << ISPCCDC_FPC_FPNUM_SHIFT), ++ OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FPC); ++ isp_reg_writel(isp, (ccdc->fpc.fpnum << ISPCCDC_FPC_FPNUM_SHIFT) | ++ ISPCCDC_FPC_FPCEN, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FPC); ++} ++ ++/* ++ * ccdc_configure_black_comp - Configure Black Level Compensation. ++ * @ccdc: Pointer to ISP CCDC device. ++ */ ++static void ccdc_configure_black_comp(struct isp_ccdc_device *ccdc) ++{ ++ struct isp_device *isp = to_isp_device(ccdc); ++ u32 blcomp; ++ ++ blcomp = ccdc->blcomp.b_mg << ISPCCDC_BLKCMP_B_MG_SHIFT; ++ blcomp |= ccdc->blcomp.gb_g << ISPCCDC_BLKCMP_GB_G_SHIFT; ++ blcomp |= ccdc->blcomp.gr_cy << ISPCCDC_BLKCMP_GR_CY_SHIFT; ++ blcomp |= ccdc->blcomp.r_ye << ISPCCDC_BLKCMP_R_YE_SHIFT; ++ ++ isp_reg_writel(isp, blcomp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_BLKCMP); ++} ++ ++/* ++ * ccdc_configure_lpf - Configure Low-Pass Filter (LPF). ++ * @ccdc: Pointer to ISP CCDC device. ++ */ ++static void ccdc_configure_lpf(struct isp_ccdc_device *ccdc) ++{ ++ struct isp_device *isp = to_isp_device(ccdc); ++ ++ isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE, ++ ISPCCDC_SYN_MODE_LPF, ++ ccdc->lpf ? ISPCCDC_SYN_MODE_LPF : 0); ++} ++ ++/* ++ * ccdc_configure_alaw - Configure A-law compression. ++ * @ccdc: Pointer to ISP CCDC device. ++ */ ++static void ccdc_configure_alaw(struct isp_ccdc_device *ccdc) ++{ ++ struct isp_device *isp = to_isp_device(ccdc); ++ u32 alaw = 0; ++ ++ switch (ccdc->syncif.datsz) { ++ case 8: ++ return; ++ ++ case 10: ++ alaw = ISPCCDC_ALAW_GWDI_9_0; ++ break; ++ case 11: ++ alaw = ISPCCDC_ALAW_GWDI_10_1; ++ break; ++ case 12: ++ alaw = ISPCCDC_ALAW_GWDI_11_2; ++ break; ++ case 13: ++ alaw = ISPCCDC_ALAW_GWDI_12_3; ++ break; ++ } ++ ++ if (ccdc->alaw) ++ alaw |= ISPCCDC_ALAW_CCDTBL; ++ ++ isp_reg_writel(isp, alaw, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_ALAW); ++} ++ ++/* ++ * ccdc_config_imgattr - Configure sensor image specific attributes. ++ * @ccdc: Pointer to ISP CCDC device. ++ * @colptn: Color pattern of the sensor. ++ */ ++static void ccdc_config_imgattr(struct isp_ccdc_device *ccdc, u32 colptn) ++{ ++ struct isp_device *isp = to_isp_device(ccdc); ++ ++ isp_reg_writel(isp, colptn, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_COLPTN); ++} ++ ++/* ++ * ccdc_config - Set CCDC configuration from userspace ++ * @ccdc: Pointer to ISP CCDC device. ++ * @userspace_add: Structure containing CCDC configuration sent from userspace. ++ * ++ * Returns 0 if successful, -EINVAL if the pointer to the configuration ++ * structure is null, or the copy_from_user function fails to copy user space ++ * memory to kernel space memory. ++ */ ++static int ccdc_config(struct isp_ccdc_device *ccdc, ++ struct omap3isp_ccdc_update_config *ccdc_struct) ++{ ++ struct isp_device *isp = to_isp_device(ccdc); ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ccdc->lock, flags); ++ ccdc->shadow_update = 1; ++ spin_unlock_irqrestore(&ccdc->lock, flags); ++ ++ if (OMAP3ISP_CCDC_ALAW & ccdc_struct->update) { ++ ccdc->alaw = !!(OMAP3ISP_CCDC_ALAW & ccdc_struct->flag); ++ ccdc->update |= OMAP3ISP_CCDC_ALAW; ++ } ++ ++ if (OMAP3ISP_CCDC_LPF & ccdc_struct->update) { ++ ccdc->lpf = !!(OMAP3ISP_CCDC_LPF & ccdc_struct->flag); ++ ccdc->update |= OMAP3ISP_CCDC_LPF; ++ } ++ ++ if (OMAP3ISP_CCDC_BLCLAMP & ccdc_struct->update) { ++ if (copy_from_user(&ccdc->clamp, ccdc_struct->bclamp, ++ sizeof(ccdc->clamp))) { ++ ccdc->shadow_update = 0; ++ return -EFAULT; ++ } ++ ++ ccdc->obclamp = !!(OMAP3ISP_CCDC_BLCLAMP & ccdc_struct->flag); ++ ccdc->update |= OMAP3ISP_CCDC_BLCLAMP; ++ } ++ ++ if (OMAP3ISP_CCDC_BCOMP & ccdc_struct->update) { ++ if (copy_from_user(&ccdc->blcomp, ccdc_struct->blcomp, ++ sizeof(ccdc->blcomp))) { ++ ccdc->shadow_update = 0; ++ return -EFAULT; ++ } ++ ++ ccdc->update |= OMAP3ISP_CCDC_BCOMP; ++ } ++ ++ ccdc->shadow_update = 0; ++ ++ if (OMAP3ISP_CCDC_FPC & ccdc_struct->update) { ++ u32 table_old = 0; ++ u32 table_new; ++ u32 size; ++ ++ if (ccdc->state != ISP_PIPELINE_STREAM_STOPPED) ++ return -EBUSY; ++ ++ ccdc->fpc_en = !!(OMAP3ISP_CCDC_FPC & ccdc_struct->flag); ++ ++ if (ccdc->fpc_en) { ++ if (copy_from_user(&ccdc->fpc, ccdc_struct->fpc, ++ sizeof(ccdc->fpc))) ++ return -EFAULT; ++ ++ /* ++ * table_new must be 64-bytes aligned, but it's ++ * already done by iommu_vmalloc(). ++ */ ++ size = ccdc->fpc.fpnum * 4; ++ table_new = iommu_vmalloc(isp->iommu, 0, size, ++ IOMMU_FLAG); ++ if (IS_ERR_VALUE(table_new)) ++ return -ENOMEM; ++ ++ if (copy_from_user(da_to_va(isp->iommu, table_new), ++ (__force void __user *) ++ ccdc->fpc.fpcaddr, size)) { ++ iommu_vfree(isp->iommu, table_new); ++ return -EFAULT; ++ } ++ ++ table_old = ccdc->fpc.fpcaddr; ++ ccdc->fpc.fpcaddr = table_new; ++ } ++ ++ ccdc_configure_fpc(ccdc); ++ if (table_old != 0) ++ iommu_vfree(isp->iommu, table_old); ++ } ++ ++ return ccdc_lsc_config(ccdc, ccdc_struct); ++} ++ ++static void ccdc_apply_controls(struct isp_ccdc_device *ccdc) ++{ ++ if (ccdc->update & OMAP3ISP_CCDC_ALAW) { ++ ccdc_configure_alaw(ccdc); ++ ccdc->update &= ~OMAP3ISP_CCDC_ALAW; ++ } ++ ++ if (ccdc->update & OMAP3ISP_CCDC_LPF) { ++ ccdc_configure_lpf(ccdc); ++ ccdc->update &= ~OMAP3ISP_CCDC_LPF; ++ } ++ ++ if (ccdc->update & OMAP3ISP_CCDC_BLCLAMP) { ++ ccdc_configure_clamp(ccdc); ++ ccdc->update &= ~OMAP3ISP_CCDC_BLCLAMP; ++ } ++ ++ if (ccdc->update & OMAP3ISP_CCDC_BCOMP) { ++ ccdc_configure_black_comp(ccdc); ++ ccdc->update &= ~OMAP3ISP_CCDC_BCOMP; ++ } ++} ++ ++/* ++ * omap3isp_ccdc_restore_context - Restore values of the CCDC module registers ++ * @dev: Pointer to ISP device ++ */ ++void omap3isp_ccdc_restore_context(struct isp_device *isp) ++{ ++ struct isp_ccdc_device *ccdc = &isp->isp_ccdc; ++ ++ isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CFG, ISPCCDC_CFG_VDLC); ++ ++ ccdc->update = OMAP3ISP_CCDC_ALAW | OMAP3ISP_CCDC_LPF ++ | OMAP3ISP_CCDC_BLCLAMP | OMAP3ISP_CCDC_BCOMP; ++ ccdc_apply_controls(ccdc); ++ ccdc_configure_fpc(ccdc); ++} ++ ++/* ----------------------------------------------------------------------------- ++ * Format- and pipeline-related configuration helpers ++ */ ++ ++/* ++ * ccdc_config_vp - Configure the Video Port. ++ * @ccdc: Pointer to ISP CCDC device. ++ */ ++static void ccdc_config_vp(struct isp_ccdc_device *ccdc) ++{ ++ struct isp_pipeline *pipe = to_isp_pipeline(&ccdc->subdev.entity); ++ struct isp_device *isp = to_isp_device(ccdc); ++ unsigned long l3_ick = pipe->l3_ick; ++ unsigned int max_div = isp->revision == ISP_REVISION_15_0 ? 64 : 8; ++ unsigned int div = 0; ++ u32 fmtcfg_vp; ++ ++ fmtcfg_vp = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMTCFG) ++ & ~(ISPCCDC_FMTCFG_VPIN_MASK | ISPCCDC_FMTCFG_VPIF_FRQ_MASK); ++ ++ switch (ccdc->syncif.datsz) { ++ case 8: ++ case 10: ++ fmtcfg_vp |= ISPCCDC_FMTCFG_VPIN_9_0; ++ break; ++ case 11: ++ fmtcfg_vp |= ISPCCDC_FMTCFG_VPIN_10_1; ++ break; ++ case 12: ++ fmtcfg_vp |= ISPCCDC_FMTCFG_VPIN_11_2; ++ break; ++ case 13: ++ fmtcfg_vp |= ISPCCDC_FMTCFG_VPIN_12_3; ++ break; ++ }; ++ ++ if (pipe->input) ++ div = DIV_ROUND_UP(l3_ick, pipe->max_rate); ++ else if (ccdc->vpcfg.pixelclk) ++ div = l3_ick / ccdc->vpcfg.pixelclk; ++ ++ div = clamp(div, 2U, max_div); ++ fmtcfg_vp |= (div - 2) << ISPCCDC_FMTCFG_VPIF_FRQ_SHIFT; ++ ++ isp_reg_writel(isp, fmtcfg_vp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMTCFG); ++} ++ ++/* ++ * ccdc_enable_vp - Enable Video Port. ++ * @ccdc: Pointer to ISP CCDC device. ++ * @enable: 0 Disables VP, 1 Enables VP ++ * ++ * This is needed for outputting image to Preview, H3A and HIST ISP submodules. ++ */ ++static void ccdc_enable_vp(struct isp_ccdc_device *ccdc, u8 enable) ++{ ++ struct isp_device *isp = to_isp_device(ccdc); ++ ++ isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMTCFG, ++ ISPCCDC_FMTCFG_VPEN, enable ? ISPCCDC_FMTCFG_VPEN : 0); ++} ++ ++/* ++ * ccdc_config_outlineoffset - Configure memory saving output line offset ++ * @ccdc: Pointer to ISP CCDC device. ++ * @offset: Address offset to start a new line. Must be twice the ++ * Output width and aligned on 32 byte boundary ++ * @oddeven: Specifies the odd/even line pattern to be chosen to store the ++ * output. ++ * @numlines: Set the value 0-3 for +1-4lines, 4-7 for -1-4lines. ++ * ++ * - Configures the output line offset when stored in memory ++ * - Sets the odd/even line pattern to store the output ++ * (EVENEVEN (1), ODDEVEN (2), EVENODD (3), ODDODD (4)) ++ * - Configures the number of even and odd line fields in case of rearranging ++ * the lines. ++ */ ++static void ccdc_config_outlineoffset(struct isp_ccdc_device *ccdc, ++ u32 offset, u8 oddeven, u8 numlines) ++{ ++ struct isp_device *isp = to_isp_device(ccdc); ++ ++ isp_reg_writel(isp, offset & 0xffff, ++ OMAP3_ISP_IOMEM_CCDC, ISPCCDC_HSIZE_OFF); ++ ++ isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST, ++ ISPCCDC_SDOFST_FINV); ++ ++ isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST, ++ ISPCCDC_SDOFST_FOFST_4L); ++ ++ switch (oddeven) { ++ case EVENEVEN: ++ isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST, ++ (numlines & 0x7) << ISPCCDC_SDOFST_LOFST0_SHIFT); ++ break; ++ case ODDEVEN: ++ isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST, ++ (numlines & 0x7) << ISPCCDC_SDOFST_LOFST1_SHIFT); ++ break; ++ case EVENODD: ++ isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST, ++ (numlines & 0x7) << ISPCCDC_SDOFST_LOFST2_SHIFT); ++ break; ++ case ODDODD: ++ isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST, ++ (numlines & 0x7) << ISPCCDC_SDOFST_LOFST3_SHIFT); ++ break; ++ default: ++ break; ++ } ++} ++ ++/* ++ * ccdc_set_outaddr - Set memory address to save output image ++ * @ccdc: Pointer to ISP CCDC device. ++ * @addr: ISP MMU Mapped 32-bit memory address aligned on 32 byte boundary. ++ * ++ * Sets the memory address where the output will be saved. ++ */ ++static void ccdc_set_outaddr(struct isp_ccdc_device *ccdc, u32 addr) ++{ ++ struct isp_device *isp = to_isp_device(ccdc); ++ ++ isp_reg_writel(isp, addr, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDR_ADDR); ++} ++ ++/* ++ * omap3isp_ccdc_max_rate - Calculate maximum input data rate based on the input ++ * @ccdc: Pointer to ISP CCDC device. ++ * @max_rate: Maximum calculated data rate. ++ * ++ * Returns in *max_rate less value between calculated and passed ++ */ ++void omap3isp_ccdc_max_rate(struct isp_ccdc_device *ccdc, ++ unsigned int *max_rate) ++{ ++ struct isp_pipeline *pipe = to_isp_pipeline(&ccdc->subdev.entity); ++ unsigned int rate; ++ ++ if (pipe == NULL) ++ return; ++ ++ /* ++ * TRM says that for parallel sensors the maximum data rate ++ * should be 90% form L3/2 clock, otherwise just L3/2. ++ */ ++ if (ccdc->input == CCDC_INPUT_PARALLEL) ++ rate = pipe->l3_ick / 2 * 9 / 10; ++ else ++ rate = pipe->l3_ick / 2; ++ ++ *max_rate = min(*max_rate, rate); ++} ++ ++/* ++ * ccdc_config_sync_if - Set CCDC sync interface configuration ++ * @ccdc: Pointer to ISP CCDC device. ++ * @syncif: Structure containing the sync parameters like field state, CCDC in ++ * master/slave mode, raw/yuv data, polarity of data, field, hs, vs ++ * signals. ++ */ ++static void ccdc_config_sync_if(struct isp_ccdc_device *ccdc, ++ struct ispccdc_syncif *syncif) ++{ ++ struct isp_device *isp = to_isp_device(ccdc); ++ u32 syn_mode = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ++ ISPCCDC_SYN_MODE); ++ ++ syn_mode |= ISPCCDC_SYN_MODE_VDHDEN; ++ ++ if (syncif->fldstat) ++ syn_mode |= ISPCCDC_SYN_MODE_FLDSTAT; ++ else ++ syn_mode &= ~ISPCCDC_SYN_MODE_FLDSTAT; ++ ++ syn_mode &= ~ISPCCDC_SYN_MODE_DATSIZ_MASK; ++ switch (syncif->datsz) { ++ case 8: ++ syn_mode |= ISPCCDC_SYN_MODE_DATSIZ_8; ++ break; ++ case 10: ++ syn_mode |= ISPCCDC_SYN_MODE_DATSIZ_10; ++ break; ++ case 11: ++ syn_mode |= ISPCCDC_SYN_MODE_DATSIZ_11; ++ break; ++ case 12: ++ syn_mode |= ISPCCDC_SYN_MODE_DATSIZ_12; ++ break; ++ }; ++ ++ if (syncif->fldmode) ++ syn_mode |= ISPCCDC_SYN_MODE_FLDMODE; ++ else ++ syn_mode &= ~ISPCCDC_SYN_MODE_FLDMODE; ++ ++ if (syncif->datapol) ++ syn_mode |= ISPCCDC_SYN_MODE_DATAPOL; ++ else ++ syn_mode &= ~ISPCCDC_SYN_MODE_DATAPOL; ++ ++ if (syncif->fldpol) ++ syn_mode |= ISPCCDC_SYN_MODE_FLDPOL; ++ else ++ syn_mode &= ~ISPCCDC_SYN_MODE_FLDPOL; ++ ++ if (syncif->hdpol) ++ syn_mode |= ISPCCDC_SYN_MODE_HDPOL; ++ else ++ syn_mode &= ~ISPCCDC_SYN_MODE_HDPOL; ++ ++ if (syncif->vdpol) ++ syn_mode |= ISPCCDC_SYN_MODE_VDPOL; ++ else ++ syn_mode &= ~ISPCCDC_SYN_MODE_VDPOL; ++ ++ if (syncif->ccdc_mastermode) { ++ syn_mode |= ISPCCDC_SYN_MODE_FLDOUT | ISPCCDC_SYN_MODE_VDHDOUT; ++ isp_reg_writel(isp, ++ syncif->hs_width << ISPCCDC_HD_VD_WID_HDW_SHIFT ++ | syncif->vs_width << ISPCCDC_HD_VD_WID_VDW_SHIFT, ++ OMAP3_ISP_IOMEM_CCDC, ++ ISPCCDC_HD_VD_WID); ++ ++ isp_reg_writel(isp, ++ syncif->ppln << ISPCCDC_PIX_LINES_PPLN_SHIFT ++ | syncif->hlprf << ISPCCDC_PIX_LINES_HLPRF_SHIFT, ++ OMAP3_ISP_IOMEM_CCDC, ++ ISPCCDC_PIX_LINES); ++ } else ++ syn_mode &= ~(ISPCCDC_SYN_MODE_FLDOUT | ++ ISPCCDC_SYN_MODE_VDHDOUT); ++ ++ isp_reg_writel(isp, syn_mode, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE); ++ ++ if (!syncif->bt_r656_en) ++ isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_REC656IF, ++ ISPCCDC_REC656IF_R656ON); ++} ++ ++/* CCDC formats descriptions */ ++static const u32 ccdc_sgrbg_pattern = ++ ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP0PLC0_SHIFT | ++ ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP0PLC1_SHIFT | ++ ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP0PLC2_SHIFT | ++ ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP0PLC3_SHIFT | ++ ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP1PLC0_SHIFT | ++ ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP1PLC1_SHIFT | ++ ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP1PLC2_SHIFT | ++ ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP1PLC3_SHIFT | ++ ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP2PLC0_SHIFT | ++ ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP2PLC1_SHIFT | ++ ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP2PLC2_SHIFT | ++ ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP2PLC3_SHIFT | ++ ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP3PLC0_SHIFT | ++ ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP3PLC1_SHIFT | ++ ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP3PLC2_SHIFT | ++ ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP3PLC3_SHIFT; ++ ++static const u32 ccdc_srggb_pattern = ++ ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP0PLC0_SHIFT | ++ ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP0PLC1_SHIFT | ++ ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP0PLC2_SHIFT | ++ ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP0PLC3_SHIFT | ++ ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP1PLC0_SHIFT | ++ ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP1PLC1_SHIFT | ++ ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP1PLC2_SHIFT | ++ ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP1PLC3_SHIFT | ++ ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP2PLC0_SHIFT | ++ ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP2PLC1_SHIFT | ++ ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP2PLC2_SHIFT | ++ ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP2PLC3_SHIFT | ++ ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP3PLC0_SHIFT | ++ ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP3PLC1_SHIFT | ++ ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP3PLC2_SHIFT | ++ ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP3PLC3_SHIFT; ++ ++static const u32 ccdc_sbggr_pattern = ++ ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP0PLC0_SHIFT | ++ ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP0PLC1_SHIFT | ++ ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP0PLC2_SHIFT | ++ ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP0PLC3_SHIFT | ++ ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP1PLC0_SHIFT | ++ ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP1PLC1_SHIFT | ++ ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP1PLC2_SHIFT | ++ ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP1PLC3_SHIFT | ++ ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP2PLC0_SHIFT | ++ ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP2PLC1_SHIFT | ++ ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP2PLC2_SHIFT | ++ ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP2PLC3_SHIFT | ++ ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP3PLC0_SHIFT | ++ ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP3PLC1_SHIFT | ++ ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP3PLC2_SHIFT | ++ ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP3PLC3_SHIFT; ++ ++static const u32 ccdc_sgbrg_pattern = ++ ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP0PLC0_SHIFT | ++ ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP0PLC1_SHIFT | ++ ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP0PLC2_SHIFT | ++ ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP0PLC3_SHIFT | ++ ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP1PLC0_SHIFT | ++ ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP1PLC1_SHIFT | ++ ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP1PLC2_SHIFT | ++ ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP1PLC3_SHIFT | ++ ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP2PLC0_SHIFT | ++ ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP2PLC1_SHIFT | ++ ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP2PLC2_SHIFT | ++ ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP2PLC3_SHIFT | ++ ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP3PLC0_SHIFT | ++ ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP3PLC1_SHIFT | ++ ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP3PLC2_SHIFT | ++ ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP3PLC3_SHIFT; ++ ++static void ccdc_configure(struct isp_ccdc_device *ccdc) ++{ ++ struct isp_device *isp = to_isp_device(ccdc); ++ struct isp_parallel_platform_data *pdata = NULL; ++ struct v4l2_subdev *sensor; ++ struct v4l2_mbus_framefmt *format; ++ struct media_pad *pad; ++ unsigned long flags; ++ u32 syn_mode; ++ u32 ccdc_pattern; ++ ++ if (ccdc->input == CCDC_INPUT_PARALLEL) { ++ pad = media_entity_remote_source(&ccdc->pads[CCDC_PAD_SINK]); ++ sensor = media_entity_to_v4l2_subdev(pad->entity); ++ pdata = &((struct isp_v4l2_subdevs_group *)sensor->host_priv) ++ ->bus.parallel; ++ } ++ ++ omap3isp_configure_bridge(isp, ccdc->input, pdata); ++ ++ ccdc->syncif.datsz = pdata ? pdata->width : 10; ++ ccdc_config_sync_if(ccdc, &ccdc->syncif); ++ ++ /* CCDC_PAD_SINK */ ++ format = &ccdc->formats[CCDC_PAD_SINK]; ++ ++ syn_mode = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE); ++ ++ /* Use the raw, unprocessed data when writing to memory. The H3A and ++ * histogram modules are still fed with lens shading corrected data. ++ */ ++ syn_mode &= ~ISPCCDC_SYN_MODE_VP2SDR; ++ ++ if (ccdc->output & CCDC_OUTPUT_MEMORY) ++ syn_mode |= ISPCCDC_SYN_MODE_WEN; ++ else ++ syn_mode &= ~ISPCCDC_SYN_MODE_WEN; ++ ++ if (ccdc->output & CCDC_OUTPUT_RESIZER) ++ syn_mode |= ISPCCDC_SYN_MODE_SDR2RSZ; ++ else ++ syn_mode &= ~ISPCCDC_SYN_MODE_SDR2RSZ; ++ ++ /* Use PACK8 mode for 1byte per pixel formats. */ ++ if (omap3isp_video_format_info(format->code)->bpp <= 8) ++ syn_mode |= ISPCCDC_SYN_MODE_PACK8; ++ else ++ syn_mode &= ~ISPCCDC_SYN_MODE_PACK8; ++ ++ isp_reg_writel(isp, syn_mode, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE); ++ ++ /* Mosaic filter */ ++ switch (format->code) { ++ case V4L2_MBUS_FMT_SRGGB10_1X10: ++ case V4L2_MBUS_FMT_SRGGB12_1X12: ++ ccdc_pattern = ccdc_srggb_pattern; ++ break; ++ case V4L2_MBUS_FMT_SBGGR10_1X10: ++ case V4L2_MBUS_FMT_SBGGR12_1X12: ++ ccdc_pattern = ccdc_sbggr_pattern; ++ break; ++ case V4L2_MBUS_FMT_SGBRG10_1X10: ++ case V4L2_MBUS_FMT_SGBRG12_1X12: ++ ccdc_pattern = ccdc_sgbrg_pattern; ++ break; ++ default: ++ /* Use GRBG */ ++ ccdc_pattern = ccdc_sgrbg_pattern; ++ break; ++ } ++ ccdc_config_imgattr(ccdc, ccdc_pattern); ++ ++ /* Generate VD0 on the last line of the image and VD1 on the ++ * 2/3 height line. ++ */ ++ isp_reg_writel(isp, ((format->height - 2) << ISPCCDC_VDINT_0_SHIFT) | ++ ((format->height * 2 / 3) << ISPCCDC_VDINT_1_SHIFT), ++ OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VDINT); ++ ++ /* CCDC_PAD_SOURCE_OF */ ++ format = &ccdc->formats[CCDC_PAD_SOURCE_OF]; ++ ++ isp_reg_writel(isp, (0 << ISPCCDC_HORZ_INFO_SPH_SHIFT) | ++ ((format->width - 1) << ISPCCDC_HORZ_INFO_NPH_SHIFT), ++ OMAP3_ISP_IOMEM_CCDC, ISPCCDC_HORZ_INFO); ++ isp_reg_writel(isp, 0 << ISPCCDC_VERT_START_SLV0_SHIFT, ++ OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VERT_START); ++ isp_reg_writel(isp, (format->height - 1) ++ << ISPCCDC_VERT_LINES_NLV_SHIFT, ++ OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VERT_LINES); ++ ++ ccdc_config_outlineoffset(ccdc, ccdc->video_out.bpl_value, 0, 0); ++ ++ /* CCDC_PAD_SOURCE_VP */ ++ format = &ccdc->formats[CCDC_PAD_SOURCE_VP]; ++ ++ isp_reg_writel(isp, (0 << ISPCCDC_FMT_HORZ_FMTSPH_SHIFT) | ++ (format->width << ISPCCDC_FMT_HORZ_FMTLNH_SHIFT), ++ OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_HORZ); ++ isp_reg_writel(isp, (0 << ISPCCDC_FMT_VERT_FMTSLV_SHIFT) | ++ ((format->height + 1) << ISPCCDC_FMT_VERT_FMTLNV_SHIFT), ++ OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_VERT); ++ ++ isp_reg_writel(isp, (format->width << ISPCCDC_VP_OUT_HORZ_NUM_SHIFT) | ++ (format->height << ISPCCDC_VP_OUT_VERT_NUM_SHIFT), ++ OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VP_OUT); ++ ++ spin_lock_irqsave(&ccdc->lsc.req_lock, flags); ++ if (ccdc->lsc.request == NULL) ++ goto unlock; ++ ++ WARN_ON(ccdc->lsc.active); ++ ++ /* Get last good LSC configuration. If it is not supported for ++ * the current active resolution discard it. ++ */ ++ if (ccdc->lsc.active == NULL && ++ __ccdc_lsc_configure(ccdc, ccdc->lsc.request) == 0) { ++ ccdc->lsc.active = ccdc->lsc.request; ++ } else { ++ list_add_tail(&ccdc->lsc.request->list, &ccdc->lsc.free_queue); ++ schedule_work(&ccdc->lsc.table_work); ++ } ++ ++ ccdc->lsc.request = NULL; ++ ++unlock: ++ spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags); ++ ++ ccdc_apply_controls(ccdc); ++} ++ ++static void __ccdc_enable(struct isp_ccdc_device *ccdc, int enable) ++{ ++ struct isp_device *isp = to_isp_device(ccdc); ++ ++ isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_PCR, ++ ISPCCDC_PCR_EN, enable ? ISPCCDC_PCR_EN : 0); ++} ++ ++static int ccdc_disable(struct isp_ccdc_device *ccdc) ++{ ++ unsigned long flags; ++ int ret = 0; ++ ++ spin_lock_irqsave(&ccdc->lock, flags); ++ if (ccdc->state == ISP_PIPELINE_STREAM_CONTINUOUS) ++ ccdc->stopping = CCDC_STOP_REQUEST; ++ spin_unlock_irqrestore(&ccdc->lock, flags); ++ ++ ret = wait_event_timeout(ccdc->wait, ++ ccdc->stopping == CCDC_STOP_FINISHED, ++ msecs_to_jiffies(2000)); ++ if (ret == 0) { ++ ret = -ETIMEDOUT; ++ dev_warn(to_device(ccdc), "CCDC stop timeout!\n"); ++ } ++ ++ omap3isp_sbl_disable(to_isp_device(ccdc), OMAP3_ISP_SBL_CCDC_LSC_READ); ++ ++ mutex_lock(&ccdc->ioctl_lock); ++ ccdc_lsc_free_request(ccdc, ccdc->lsc.request); ++ ccdc->lsc.request = ccdc->lsc.active; ++ ccdc->lsc.active = NULL; ++ cancel_work_sync(&ccdc->lsc.table_work); ++ ccdc_lsc_free_queue(ccdc, &ccdc->lsc.free_queue); ++ mutex_unlock(&ccdc->ioctl_lock); ++ ++ ccdc->stopping = CCDC_STOP_NOT_REQUESTED; ++ ++ return ret > 0 ? 0 : ret; ++} ++ ++static void ccdc_enable(struct isp_ccdc_device *ccdc) ++{ ++ if (ccdc_lsc_is_configured(ccdc)) ++ __ccdc_lsc_enable(ccdc, 1); ++ __ccdc_enable(ccdc, 1); ++} ++ ++/* ----------------------------------------------------------------------------- ++ * Interrupt handling ++ */ ++ ++/* ++ * ccdc_sbl_busy - Poll idle state of CCDC and related SBL memory write bits ++ * @ccdc: Pointer to ISP CCDC device. ++ * ++ * Returns zero if the CCDC is idle and the image has been written to ++ * memory, too. ++ */ ++static int ccdc_sbl_busy(struct isp_ccdc_device *ccdc) ++{ ++ struct isp_device *isp = to_isp_device(ccdc); ++ ++ return omap3isp_ccdc_busy(ccdc) ++ | (isp_reg_readl(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_CCDC_WR_0) & ++ ISPSBL_CCDC_WR_0_DATA_READY) ++ | (isp_reg_readl(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_CCDC_WR_1) & ++ ISPSBL_CCDC_WR_0_DATA_READY) ++ | (isp_reg_readl(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_CCDC_WR_2) & ++ ISPSBL_CCDC_WR_0_DATA_READY) ++ | (isp_reg_readl(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_CCDC_WR_3) & ++ ISPSBL_CCDC_WR_0_DATA_READY); ++} ++ ++/* ++ * ccdc_sbl_wait_idle - Wait until the CCDC and related SBL are idle ++ * @ccdc: Pointer to ISP CCDC device. ++ * @max_wait: Max retry count in us for wait for idle/busy transition. ++ */ ++static int ccdc_sbl_wait_idle(struct isp_ccdc_device *ccdc, ++ unsigned int max_wait) ++{ ++ unsigned int wait = 0; ++ ++ if (max_wait == 0) ++ max_wait = 10000; /* 10 ms */ ++ ++ for (wait = 0; wait <= max_wait; wait++) { ++ if (!ccdc_sbl_busy(ccdc)) ++ return 0; ++ ++ rmb(); ++ udelay(1); ++ } ++ ++ return -EBUSY; ++} ++ ++/* __ccdc_handle_stopping - Handle CCDC and/or LSC stopping sequence ++ * @ccdc: Pointer to ISP CCDC device. ++ * @event: Pointing which event trigger handler ++ * ++ * Return 1 when the event and stopping request combination is satisfyied, ++ * zero otherwise. ++ */ ++static int __ccdc_handle_stopping(struct isp_ccdc_device *ccdc, u32 event) ++{ ++ int rval = 0; ++ ++ switch ((ccdc->stopping & 3) | event) { ++ case CCDC_STOP_REQUEST | CCDC_EVENT_VD1: ++ if (ccdc->lsc.state != LSC_STATE_STOPPED) ++ __ccdc_lsc_enable(ccdc, 0); ++ __ccdc_enable(ccdc, 0); ++ ccdc->stopping = CCDC_STOP_EXECUTED; ++ return 1; ++ ++ case CCDC_STOP_EXECUTED | CCDC_EVENT_VD0: ++ ccdc->stopping |= CCDC_STOP_CCDC_FINISHED; ++ if (ccdc->lsc.state == LSC_STATE_STOPPED) ++ ccdc->stopping |= CCDC_STOP_LSC_FINISHED; ++ rval = 1; ++ break; ++ ++ case CCDC_STOP_EXECUTED | CCDC_EVENT_LSC_DONE: ++ ccdc->stopping |= CCDC_STOP_LSC_FINISHED; ++ rval = 1; ++ break; ++ ++ case CCDC_STOP_EXECUTED | CCDC_EVENT_VD1: ++ return 1; ++ } ++ ++ if (ccdc->stopping == CCDC_STOP_FINISHED) { ++ wake_up(&ccdc->wait); ++ rval = 1; ++ } ++ ++ return rval; ++} ++ ++static void ccdc_hs_vs_isr(struct isp_ccdc_device *ccdc) ++{ ++ struct video_device *vdev = &ccdc->subdev.devnode; ++ struct v4l2_event event; ++ ++ memset(&event, 0, sizeof(event)); ++ event.type = V4L2_EVENT_OMAP3ISP_HS_VS; ++ ++ v4l2_event_queue(vdev, &event); ++} ++ ++/* ++ * ccdc_lsc_isr - Handle LSC events ++ * @ccdc: Pointer to ISP CCDC device. ++ * @events: LSC events ++ */ ++static void ccdc_lsc_isr(struct isp_ccdc_device *ccdc, u32 events) ++{ ++ unsigned long flags; ++ ++ if (events & IRQ0STATUS_CCDC_LSC_PREF_ERR_IRQ) { ++ ccdc_lsc_error_handler(ccdc); ++ ccdc->error = 1; ++ dev_dbg(to_device(ccdc), "lsc prefetch error\n"); ++ } ++ ++ if (!(events & IRQ0STATUS_CCDC_LSC_DONE_IRQ)) ++ return; ++ ++ /* LSC_DONE interrupt occur, there are two cases ++ * 1. stopping for reconfiguration ++ * 2. stopping because of STREAM OFF command ++ */ ++ spin_lock_irqsave(&ccdc->lsc.req_lock, flags); ++ ++ if (ccdc->lsc.state == LSC_STATE_STOPPING) ++ ccdc->lsc.state = LSC_STATE_STOPPED; ++ ++ if (__ccdc_handle_stopping(ccdc, CCDC_EVENT_LSC_DONE)) ++ goto done; ++ ++ if (ccdc->lsc.state != LSC_STATE_RECONFIG) ++ goto done; ++ ++ /* LSC is in STOPPING state, change to the new state */ ++ ccdc->lsc.state = LSC_STATE_STOPPED; ++ ++ /* This is an exception. Start of frame and LSC_DONE interrupt ++ * have been received on the same time. Skip this event and wait ++ * for better times. ++ */ ++ if (events & IRQ0STATUS_HS_VS_IRQ) ++ goto done; ++ ++ /* The LSC engine is stopped at this point. Enable it if there's a ++ * pending request. ++ */ ++ if (ccdc->lsc.request == NULL) ++ goto done; ++ ++ ccdc_lsc_enable(ccdc); ++ ++done: ++ spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags); ++} ++ ++static int ccdc_isr_buffer(struct isp_ccdc_device *ccdc) ++{ ++ struct isp_pipeline *pipe = to_isp_pipeline(&ccdc->subdev.entity); ++ struct isp_device *isp = to_isp_device(ccdc); ++ struct isp_buffer *buffer; ++ int restart = 0; ++ ++ /* The CCDC generates VD0 interrupts even when disabled (the datasheet ++ * doesn't explicitly state if that's supposed to happen or not, so it ++ * can be considered as a hardware bug or as a feature, but we have to ++ * deal with it anyway). Disabling the CCDC when no buffer is available ++ * would thus not be enough, we need to handle the situation explicitly. ++ */ ++ if (list_empty(&ccdc->video_out.dmaqueue)) ++ goto done; ++ ++ /* We're in continuous mode, and memory writes were disabled due to a ++ * buffer underrun. Reenable them now that we have a buffer. The buffer ++ * address has been set in ccdc_video_queue. ++ */ ++ if (ccdc->state == ISP_PIPELINE_STREAM_CONTINUOUS && ccdc->underrun) { ++ restart = 1; ++ ccdc->underrun = 0; ++ goto done; ++ } ++ ++ if (ccdc_sbl_wait_idle(ccdc, 1000)) { ++ dev_info(isp->dev, "CCDC won't become idle!\n"); ++ goto done; ++ } ++ ++ buffer = omap3isp_video_buffer_next(&ccdc->video_out, ccdc->error); ++ if (buffer != NULL) { ++ ccdc_set_outaddr(ccdc, buffer->isp_addr); ++ restart = 1; ++ } ++ ++ pipe->state |= ISP_PIPELINE_IDLE_OUTPUT; ++ ++ if (ccdc->state == ISP_PIPELINE_STREAM_SINGLESHOT && ++ isp_pipeline_ready(pipe)) ++ omap3isp_pipeline_set_stream(pipe, ++ ISP_PIPELINE_STREAM_SINGLESHOT); ++ ++done: ++ ccdc->error = 0; ++ return restart; ++} ++ ++/* ++ * ccdc_vd0_isr - Handle VD0 event ++ * @ccdc: Pointer to ISP CCDC device. ++ * ++ * Executes LSC deferred enablement before next frame starts. ++ */ ++static void ccdc_vd0_isr(struct isp_ccdc_device *ccdc) ++{ ++ unsigned long flags; ++ int restart = 0; ++ ++ if (ccdc->output & CCDC_OUTPUT_MEMORY) ++ restart = ccdc_isr_buffer(ccdc); ++ ++ spin_lock_irqsave(&ccdc->lock, flags); ++ if (__ccdc_handle_stopping(ccdc, CCDC_EVENT_VD0)) { ++ spin_unlock_irqrestore(&ccdc->lock, flags); ++ return; ++ } ++ ++ if (!ccdc->shadow_update) ++ ccdc_apply_controls(ccdc); ++ spin_unlock_irqrestore(&ccdc->lock, flags); ++ ++ if (restart) ++ ccdc_enable(ccdc); ++} ++ ++/* ++ * ccdc_vd1_isr - Handle VD1 event ++ * @ccdc: Pointer to ISP CCDC device. ++ */ ++static void ccdc_vd1_isr(struct isp_ccdc_device *ccdc) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ccdc->lsc.req_lock, flags); ++ ++ /* ++ * Depending on the CCDC pipeline state, CCDC stopping should be ++ * handled differently. In SINGLESHOT we emulate an internal CCDC ++ * stopping because the CCDC hw works only in continuous mode. ++ * When CONTINUOUS pipeline state is used and the CCDC writes it's ++ * data to memory the CCDC and LSC are stopped immediately but ++ * without change the CCDC stopping state machine. The CCDC ++ * stopping state machine should be used only when user request ++ * for stopping is received (SINGLESHOT is an exeption). ++ */ ++ switch (ccdc->state) { ++ case ISP_PIPELINE_STREAM_SINGLESHOT: ++ ccdc->stopping = CCDC_STOP_REQUEST; ++ break; ++ ++ case ISP_PIPELINE_STREAM_CONTINUOUS: ++ if (ccdc->output & CCDC_OUTPUT_MEMORY) { ++ if (ccdc->lsc.state != LSC_STATE_STOPPED) ++ __ccdc_lsc_enable(ccdc, 0); ++ __ccdc_enable(ccdc, 0); ++ } ++ break; ++ ++ case ISP_PIPELINE_STREAM_STOPPED: ++ break; ++ } ++ ++ if (__ccdc_handle_stopping(ccdc, CCDC_EVENT_VD1)) ++ goto done; ++ ++ if (ccdc->lsc.request == NULL) ++ goto done; ++ ++ /* ++ * LSC need to be reconfigured. Stop it here and on next LSC_DONE IRQ ++ * do the appropriate changes in registers ++ */ ++ if (ccdc->lsc.state == LSC_STATE_RUNNING) { ++ __ccdc_lsc_enable(ccdc, 0); ++ ccdc->lsc.state = LSC_STATE_RECONFIG; ++ goto done; ++ } ++ ++ /* LSC has been in STOPPED state, enable it */ ++ if (ccdc->lsc.state == LSC_STATE_STOPPED) ++ ccdc_lsc_enable(ccdc); ++ ++done: ++ spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags); ++} ++ ++/* ++ * omap3isp_ccdc_isr - Configure CCDC during interframe time. ++ * @ccdc: Pointer to ISP CCDC device. ++ * @events: CCDC events ++ */ ++int omap3isp_ccdc_isr(struct isp_ccdc_device *ccdc, u32 events) ++{ ++ if (ccdc->state == ISP_PIPELINE_STREAM_STOPPED) ++ return 0; ++ ++ if (events & IRQ0STATUS_CCDC_VD1_IRQ) ++ ccdc_vd1_isr(ccdc); ++ ++ ccdc_lsc_isr(ccdc, events); ++ ++ if (events & IRQ0STATUS_CCDC_VD0_IRQ) ++ ccdc_vd0_isr(ccdc); ++ ++ if (events & IRQ0STATUS_HS_VS_IRQ) ++ ccdc_hs_vs_isr(ccdc); ++ ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------------- ++ * ISP video operations ++ */ ++ ++static int ccdc_video_queue(struct isp_video *video, struct isp_buffer *buffer) ++{ ++ struct isp_ccdc_device *ccdc = &video->isp->isp_ccdc; ++ ++ if (!(ccdc->output & CCDC_OUTPUT_MEMORY)) ++ return -ENODEV; ++ ++ ccdc_set_outaddr(ccdc, buffer->isp_addr); ++ ++ /* We now have a buffer queued on the output, restart the pipeline in ++ * on the next CCDC interrupt if running in continuous mode (or when ++ * starting the stream). ++ */ ++ ccdc->underrun = 1; ++ ++ return 0; ++} ++ ++static const struct isp_video_operations ccdc_video_ops = { ++ .queue = ccdc_video_queue, ++}; ++ ++/* ----------------------------------------------------------------------------- ++ * V4L2 subdev operations ++ */ ++ ++/* ++ * ccdc_ioctl - CCDC module private ioctl's ++ * @sd: ISP CCDC V4L2 subdevice ++ * @cmd: ioctl command ++ * @arg: ioctl argument ++ * ++ * Return 0 on success or a negative error code otherwise. ++ */ ++static long ccdc_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) ++{ ++ struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); ++ int ret; ++ ++ switch (cmd) { ++ case VIDIOC_OMAP3ISP_CCDC_CFG: ++ mutex_lock(&ccdc->ioctl_lock); ++ ret = ccdc_config(ccdc, arg); ++ mutex_unlock(&ccdc->ioctl_lock); ++ break; ++ ++ default: ++ return -ENOIOCTLCMD; ++ } ++ ++ return ret; ++} ++ ++static int ccdc_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, ++ struct v4l2_event_subscription *sub) ++{ ++ if (sub->type != V4L2_EVENT_OMAP3ISP_HS_VS) ++ return -EINVAL; ++ ++ return v4l2_event_subscribe(fh, sub); ++} ++ ++static int ccdc_unsubscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, ++ struct v4l2_event_subscription *sub) ++{ ++ return v4l2_event_unsubscribe(fh, sub); ++} ++ ++/* ++ * ccdc_set_stream - Enable/Disable streaming on the CCDC module ++ * @sd: ISP CCDC V4L2 subdevice ++ * @enable: Enable/disable stream ++ * ++ * When writing to memory, the CCDC hardware can't be enabled without a memory ++ * buffer to write to. As the s_stream operation is called in response to a ++ * STREAMON call without any buffer queued yet, just update the enabled field ++ * and return immediately. The CCDC will be enabled in ccdc_isr_buffer(). ++ * ++ * When not writing to memory enable the CCDC immediately. ++ */ ++static int ccdc_set_stream(struct v4l2_subdev *sd, int enable) ++{ ++ struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); ++ struct isp_device *isp = to_isp_device(ccdc); ++ int ret = 0; ++ ++ if (ccdc->state == ISP_PIPELINE_STREAM_STOPPED) { ++ if (enable == ISP_PIPELINE_STREAM_STOPPED) ++ return 0; ++ ++ omap3isp_subclk_enable(isp, OMAP3_ISP_SUBCLK_CCDC); ++ isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CFG, ++ ISPCCDC_CFG_VDLC); ++ ++ ccdc_configure(ccdc); ++ ++ /* TODO: Don't configure the video port if all of its output ++ * links are inactive. ++ */ ++ ccdc_config_vp(ccdc); ++ ccdc_enable_vp(ccdc, 1); ++ ccdc->error = 0; ++ ccdc_print_status(ccdc); ++ } ++ ++ switch (enable) { ++ case ISP_PIPELINE_STREAM_CONTINUOUS: ++ if (ccdc->output & CCDC_OUTPUT_MEMORY) ++ omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_CCDC_WRITE); ++ ++ if (ccdc->underrun || !(ccdc->output & CCDC_OUTPUT_MEMORY)) ++ ccdc_enable(ccdc); ++ ++ ccdc->underrun = 0; ++ break; ++ ++ case ISP_PIPELINE_STREAM_SINGLESHOT: ++ if (ccdc->output & CCDC_OUTPUT_MEMORY && ++ ccdc->state != ISP_PIPELINE_STREAM_SINGLESHOT) ++ omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_CCDC_WRITE); ++ ++ ccdc_enable(ccdc); ++ break; ++ ++ case ISP_PIPELINE_STREAM_STOPPED: ++ ret = ccdc_disable(ccdc); ++ if (ccdc->output & CCDC_OUTPUT_MEMORY) ++ omap3isp_sbl_disable(isp, OMAP3_ISP_SBL_CCDC_WRITE); ++ omap3isp_subclk_disable(isp, OMAP3_ISP_SUBCLK_CCDC); ++ ccdc->underrun = 0; ++ break; ++ } ++ ++ ccdc->state = enable; ++ return ret; ++} ++ ++static struct v4l2_mbus_framefmt * ++__ccdc_get_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, ++ unsigned int pad, enum v4l2_subdev_format_whence which) ++{ ++ if (which == V4L2_SUBDEV_FORMAT_TRY) ++ return v4l2_subdev_get_try_format(fh, pad); ++ else ++ return &ccdc->formats[pad]; ++} ++ ++/* ++ * ccdc_try_format - Try video format on a pad ++ * @ccdc: ISP CCDC device ++ * @fh : V4L2 subdev file handle ++ * @pad: Pad number ++ * @fmt: Format ++ */ ++static void ++ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, ++ unsigned int pad, struct v4l2_mbus_framefmt *fmt, ++ enum v4l2_subdev_format_whence which) ++{ ++ struct v4l2_mbus_framefmt *format; ++ const struct isp_format_info *info; ++ unsigned int width = fmt->width; ++ unsigned int height = fmt->height; ++ unsigned int i; ++ ++ switch (pad) { ++ case CCDC_PAD_SINK: ++ /* TODO: If the CCDC output formatter pad is connected directly ++ * to the resizer, only YUV formats can be used. ++ */ ++ for (i = 0; i < ARRAY_SIZE(ccdc_fmts); i++) { ++ if (fmt->code == ccdc_fmts[i]) ++ break; ++ } ++ ++ /* If not found, use SGRBG10 as default */ ++ if (i >= ARRAY_SIZE(ccdc_fmts)) ++ fmt->code = V4L2_MBUS_FMT_SGRBG10_1X10; ++ ++ /* Clamp the input size. */ ++ fmt->width = clamp_t(u32, width, 32, 4096); ++ fmt->height = clamp_t(u32, height, 32, 4096); ++ break; ++ ++ case CCDC_PAD_SOURCE_OF: ++ format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, which); ++ memcpy(fmt, format, sizeof(*fmt)); ++ ++ /* The data formatter truncates the number of horizontal output ++ * pixels to a multiple of 16. To avoid clipping data, allow ++ * callers to request an output size bigger than the input size ++ * up to the nearest multiple of 16. ++ */ ++ fmt->width = clamp_t(u32, width, 32, (fmt->width + 15) & ~15); ++ fmt->width &= ~15; ++ fmt->height = clamp_t(u32, height, 32, fmt->height); ++ break; ++ ++ case CCDC_PAD_SOURCE_VP: ++ format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, which); ++ memcpy(fmt, format, sizeof(*fmt)); ++ ++ /* The video port interface truncates the data to 10 bits. */ ++ info = omap3isp_video_format_info(fmt->code); ++ fmt->code = info->truncated; ++ ++ /* The number of lines that can be clocked out from the video ++ * port output must be at least one line less than the number ++ * of input lines. ++ */ ++ fmt->width = clamp_t(u32, width, 32, fmt->width); ++ fmt->height = clamp_t(u32, height, 32, fmt->height - 1); ++ break; ++ } ++ ++ /* Data is written to memory unpacked, each 10-bit or 12-bit pixel is ++ * stored on 2 bytes. ++ */ ++ fmt->colorspace = V4L2_COLORSPACE_SRGB; ++ fmt->field = V4L2_FIELD_NONE; ++} ++ ++/* ++ * ccdc_enum_mbus_code - Handle pixel format enumeration ++ * @sd : pointer to v4l2 subdev structure ++ * @fh : V4L2 subdev file handle ++ * @code : pointer to v4l2_subdev_mbus_code_enum structure ++ * return -EINVAL or zero on success ++ */ ++static int ccdc_enum_mbus_code(struct v4l2_subdev *sd, ++ struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_mbus_code_enum *code) ++{ ++ struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); ++ struct v4l2_mbus_framefmt *format; ++ ++ switch (code->pad) { ++ case CCDC_PAD_SINK: ++ if (code->index >= ARRAY_SIZE(ccdc_fmts)) ++ return -EINVAL; ++ ++ code->code = ccdc_fmts[code->index]; ++ break; ++ ++ case CCDC_PAD_SOURCE_OF: ++ case CCDC_PAD_SOURCE_VP: ++ /* No format conversion inside CCDC */ ++ if (code->index != 0) ++ return -EINVAL; ++ ++ format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, ++ V4L2_SUBDEV_FORMAT_TRY); ++ ++ code->code = format->code; ++ break; ++ ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int ccdc_enum_frame_size(struct v4l2_subdev *sd, ++ struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_frame_size_enum *fse) ++{ ++ struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); ++ struct v4l2_mbus_framefmt format; ++ ++ if (fse->index != 0) ++ return -EINVAL; ++ ++ format.code = fse->code; ++ format.width = 1; ++ format.height = 1; ++ ccdc_try_format(ccdc, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); ++ fse->min_width = format.width; ++ fse->min_height = format.height; ++ ++ if (format.code != fse->code) ++ return -EINVAL; ++ ++ format.code = fse->code; ++ format.width = -1; ++ format.height = -1; ++ ccdc_try_format(ccdc, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); ++ fse->max_width = format.width; ++ fse->max_height = format.height; ++ ++ return 0; ++} ++ ++/* ++ * ccdc_get_format - Retrieve the video format on a pad ++ * @sd : ISP CCDC V4L2 subdevice ++ * @fh : V4L2 subdev file handle ++ * @fmt: Format ++ * ++ * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond ++ * to the format type. ++ */ ++static int ccdc_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_format *fmt) ++{ ++ struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); ++ struct v4l2_mbus_framefmt *format; ++ ++ format = __ccdc_get_format(ccdc, fh, fmt->pad, fmt->which); ++ if (format == NULL) ++ return -EINVAL; ++ ++ fmt->format = *format; ++ return 0; ++} ++ ++/* ++ * ccdc_set_format - Set the video format on a pad ++ * @sd : ISP CCDC V4L2 subdevice ++ * @fh : V4L2 subdev file handle ++ * @fmt: Format ++ * ++ * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond ++ * to the format type. ++ */ ++static int ccdc_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_format *fmt) ++{ ++ struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); ++ struct v4l2_mbus_framefmt *format; ++ ++ format = __ccdc_get_format(ccdc, fh, fmt->pad, fmt->which); ++ if (format == NULL) ++ return -EINVAL; ++ ++ ccdc_try_format(ccdc, fh, fmt->pad, &fmt->format, fmt->which); ++ *format = fmt->format; ++ ++ /* Propagate the format from sink to source */ ++ if (fmt->pad == CCDC_PAD_SINK) { ++ format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SOURCE_OF, ++ fmt->which); ++ *format = fmt->format; ++ ccdc_try_format(ccdc, fh, CCDC_PAD_SOURCE_OF, format, ++ fmt->which); ++ ++ format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SOURCE_VP, ++ fmt->which); ++ *format = fmt->format; ++ ccdc_try_format(ccdc, fh, CCDC_PAD_SOURCE_VP, format, ++ fmt->which); ++ } ++ ++ return 0; ++} ++ ++/* ++ * ccdc_init_formats - Initialize formats on all pads ++ * @sd: ISP CCDC V4L2 subdevice ++ * @fh: V4L2 subdev file handle ++ * ++ * Initialize all pad formats with default values. If fh is not NULL, try ++ * formats are initialized on the file handle. Otherwise active formats are ++ * initialized on the device. ++ */ ++static int ccdc_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) ++{ ++ struct v4l2_subdev_format format; ++ ++ memset(&format, 0, sizeof(format)); ++ format.pad = CCDC_PAD_SINK; ++ format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; ++ format.format.code = V4L2_MBUS_FMT_SGRBG10_1X10; ++ format.format.width = 4096; ++ format.format.height = 4096; ++ ccdc_set_format(sd, fh, &format); ++ ++ return 0; ++} ++ ++/* V4L2 subdev core operations */ ++static const struct v4l2_subdev_core_ops ccdc_v4l2_core_ops = { ++ .queryctrl = v4l2_subdev_queryctrl, ++ .querymenu = v4l2_subdev_querymenu, ++ .g_ctrl = v4l2_subdev_g_ctrl, ++ .s_ctrl = v4l2_subdev_s_ctrl, ++ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, ++ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, ++ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, ++ .ioctl = ccdc_ioctl, ++ .subscribe_event = ccdc_subscribe_event, ++ .unsubscribe_event = ccdc_unsubscribe_event, ++}; ++ ++/* V4L2 subdev file operations */ ++static const struct v4l2_subdev_file_ops ccdc_v4l2_file_ops = { ++ .open = ccdc_init_formats, ++}; ++ ++/* V4L2 subdev video operations */ ++static const struct v4l2_subdev_video_ops ccdc_v4l2_video_ops = { ++ .s_stream = ccdc_set_stream, ++}; ++ ++/* V4L2 subdev pad operations */ ++static const struct v4l2_subdev_pad_ops ccdc_v4l2_pad_ops = { ++ .enum_mbus_code = ccdc_enum_mbus_code, ++ .enum_frame_size = ccdc_enum_frame_size, ++ .get_fmt = ccdc_get_format, ++ .set_fmt = ccdc_set_format, ++}; ++ ++/* V4L2 subdev operations */ ++static const struct v4l2_subdev_ops ccdc_v4l2_ops = { ++ .core = &ccdc_v4l2_core_ops, ++ .file = &ccdc_v4l2_file_ops, ++ .video = &ccdc_v4l2_video_ops, ++ .pad = &ccdc_v4l2_pad_ops, ++}; ++ ++/* ----------------------------------------------------------------------------- ++ * Media entity operations ++ */ ++ ++/* ++ * ccdc_link_setup - Setup CCDC connections ++ * @entity: CCDC media entity ++ * @local: Pad at the local end of the link ++ * @remote: Pad at the remote end of the link ++ * @flags: Link flags ++ * ++ * return -EINVAL or zero on success ++ */ ++static int ccdc_link_setup(struct media_entity *entity, ++ const struct media_pad *local, ++ const struct media_pad *remote, u32 flags) ++{ ++ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); ++ struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); ++ struct isp_device *isp = to_isp_device(ccdc); ++ ++ switch (local->index | media_entity_type(remote->entity)) { ++ case CCDC_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: ++ /* Read from the sensor (parallel interface), CCP2, CSI2a or ++ * CSI2c. ++ */ ++ if (!(flags & MEDIA_LNK_FL_ENABLED)) { ++ ccdc->input = CCDC_INPUT_NONE; ++ break; ++ } ++ ++ if (ccdc->input != CCDC_INPUT_NONE) ++ return -EBUSY; ++ ++ if (remote->entity == &isp->isp_ccp2.subdev.entity) ++ ccdc->input = CCDC_INPUT_CCP2B; ++ else if (remote->entity == &isp->isp_csi2a.subdev.entity) ++ ccdc->input = CCDC_INPUT_CSI2A; ++ else if (remote->entity == &isp->isp_csi2c.subdev.entity) ++ ccdc->input = CCDC_INPUT_CSI2C; ++ else ++ ccdc->input = CCDC_INPUT_PARALLEL; ++ ++ break; ++ ++ /* ++ * The ISP core doesn't support pipelines with multiple video outputs. ++ * Revisit this when it will be implemented, and return -EBUSY for now. ++ */ ++ ++ case CCDC_PAD_SOURCE_VP | MEDIA_ENT_T_V4L2_SUBDEV: ++ /* Write to preview engine, histogram and H3A. When none of ++ * those links are active, the video port can be disabled. ++ */ ++ if (flags & MEDIA_LNK_FL_ENABLED) { ++ if (ccdc->output & ~CCDC_OUTPUT_PREVIEW) ++ return -EBUSY; ++ ccdc->output |= CCDC_OUTPUT_PREVIEW; ++ } else { ++ ccdc->output &= ~CCDC_OUTPUT_PREVIEW; ++ } ++ break; ++ ++ case CCDC_PAD_SOURCE_OF | MEDIA_ENT_T_DEVNODE: ++ /* Write to memory */ ++ if (flags & MEDIA_LNK_FL_ENABLED) { ++ if (ccdc->output & ~CCDC_OUTPUT_MEMORY) ++ return -EBUSY; ++ ccdc->output |= CCDC_OUTPUT_MEMORY; ++ } else { ++ ccdc->output &= ~CCDC_OUTPUT_MEMORY; ++ } ++ break; ++ ++ case CCDC_PAD_SOURCE_OF | MEDIA_ENT_T_V4L2_SUBDEV: ++ /* Write to resizer */ ++ if (flags & MEDIA_LNK_FL_ENABLED) { ++ if (ccdc->output & ~CCDC_OUTPUT_RESIZER) ++ return -EBUSY; ++ ccdc->output |= CCDC_OUTPUT_RESIZER; ++ } else { ++ ccdc->output &= ~CCDC_OUTPUT_RESIZER; ++ } ++ break; ++ ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++/* media operations */ ++static const struct media_entity_operations ccdc_media_ops = { ++ .link_setup = ccdc_link_setup, ++}; ++ ++/* ++ * ccdc_init_entities - Initialize V4L2 subdev and media entity ++ * @ccdc: ISP CCDC module ++ * ++ * Return 0 on success and a negative error code on failure. ++ */ ++static int ccdc_init_entities(struct isp_ccdc_device *ccdc) ++{ ++ struct v4l2_subdev *sd = &ccdc->subdev; ++ struct media_pad *pads = ccdc->pads; ++ struct media_entity *me = &sd->entity; ++ int ret; ++ ++ ccdc->input = CCDC_INPUT_NONE; ++ ++ v4l2_subdev_init(sd, &ccdc_v4l2_ops); ++ strlcpy(sd->name, "OMAP3 ISP CCDC", sizeof(sd->name)); ++ sd->grp_id = 1 << 16; /* group ID for isp subdevs */ ++ v4l2_set_subdevdata(sd, ccdc); ++ sd->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE; ++ sd->nevents = OMAP3ISP_CCDC_NEVENTS; ++ ++ v4l2_ctrl_handler_init(&ccdc->ctrls, 1); ++ sd->ctrl_handler = &ccdc->ctrls; ++ ++ pads[CCDC_PAD_SINK].flags = MEDIA_PAD_FL_INPUT; ++ pads[CCDC_PAD_SOURCE_VP].flags = MEDIA_PAD_FL_OUTPUT; ++ pads[CCDC_PAD_SOURCE_OF].flags = MEDIA_PAD_FL_OUTPUT; ++ ++ me->ops = &ccdc_media_ops; ++ ret = media_entity_init(me, CCDC_PADS_NUM, pads, 0); ++ if (ret < 0) ++ return ret; ++ ++ ccdc_init_formats(sd, NULL); ++ ++ ccdc->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ ccdc->video_out.ops = &ccdc_video_ops; ++ ccdc->video_out.isp = to_isp_device(ccdc); ++ ccdc->video_out.capture_mem = PAGE_ALIGN(4096 * 4096) * 3; ++ ccdc->video_out.bpl_alignment = 32; ++ ++ ret = omap3isp_video_init(&ccdc->video_out, "CCDC"); ++ if (ret < 0) ++ return ret; ++ ++ /* Connect the CCDC subdev to the video node. */ ++ ret = media_entity_create_link(&ccdc->subdev.entity, CCDC_PAD_SOURCE_OF, ++ &ccdc->video_out.video.entity, 0, 0); ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++void omap3isp_ccdc_unregister_entities(struct isp_ccdc_device *ccdc) ++{ ++ media_entity_cleanup(&ccdc->subdev.entity); ++ ++ v4l2_device_unregister_subdev(&ccdc->subdev); ++ v4l2_ctrl_handler_free(&ccdc->ctrls); ++ omap3isp_video_unregister(&ccdc->video_out); ++} ++ ++int omap3isp_ccdc_register_entities(struct isp_ccdc_device *ccdc, ++ struct v4l2_device *vdev) ++{ ++ int ret; ++ ++ /* Register the subdev and video node. */ ++ ret = v4l2_device_register_subdev(vdev, &ccdc->subdev); ++ if (ret < 0) ++ goto error; ++ ++ ret = omap3isp_video_register(&ccdc->video_out, vdev); ++ if (ret < 0) ++ goto error; ++ ++ return 0; ++ ++error: ++ omap3isp_ccdc_unregister_entities(ccdc); ++ return ret; ++} ++ ++/* ----------------------------------------------------------------------------- ++ * ISP CCDC initialisation and cleanup ++ */ ++ ++/* ++ * omap3isp_ccdc_init - CCDC module initialization. ++ * @dev: Device pointer specific to the OMAP3 ISP. ++ * ++ * TODO: Get the initialisation values from platform data. ++ * ++ * Return 0 on success or a negative error code otherwise. ++ */ ++int omap3isp_ccdc_init(struct isp_device *isp) ++{ ++ struct isp_ccdc_device *ccdc = &isp->isp_ccdc; ++ ++ spin_lock_init(&ccdc->lock); ++ init_waitqueue_head(&ccdc->wait); ++ mutex_init(&ccdc->ioctl_lock); ++ ++ ccdc->stopping = CCDC_STOP_NOT_REQUESTED; ++ ++ INIT_WORK(&ccdc->lsc.table_work, ccdc_lsc_free_table_work); ++ ccdc->lsc.state = LSC_STATE_STOPPED; ++ INIT_LIST_HEAD(&ccdc->lsc.free_queue); ++ spin_lock_init(&ccdc->lsc.req_lock); ++ ++ ccdc->syncif.ccdc_mastermode = 0; ++ ccdc->syncif.datapol = 0; ++ ccdc->syncif.datsz = 0; ++ ccdc->syncif.fldmode = 0; ++ ccdc->syncif.fldout = 0; ++ ccdc->syncif.fldpol = 0; ++ ccdc->syncif.fldstat = 0; ++ ccdc->syncif.hdpol = 0; ++ ccdc->syncif.vdpol = 0; ++ ++ ccdc->clamp.oblen = 0; ++ ccdc->clamp.dcsubval = 0; ++ ++ ccdc->vpcfg.pixelclk = 0; ++ ++ ccdc->update = OMAP3ISP_CCDC_BLCLAMP; ++ ccdc_apply_controls(ccdc); ++ ++ return ccdc_init_entities(ccdc); ++} ++ ++/* ++ * omap3isp_ccdc_cleanup - CCDC module cleanup. ++ * @dev: Device pointer specific to the OMAP3 ISP. ++ */ ++void omap3isp_ccdc_cleanup(struct isp_device *isp) ++{ ++ struct isp_ccdc_device *ccdc = &isp->isp_ccdc; ++ ++ /* Free LSC requests. As the CCDC is stopped there's no active request, ++ * so only the pending request and the free queue need to be handled. ++ */ ++ ccdc_lsc_free_request(ccdc, ccdc->lsc.request); ++ cancel_work_sync(&ccdc->lsc.table_work); ++ ccdc_lsc_free_queue(ccdc, &ccdc->lsc.free_queue); ++ ++ if (ccdc->fpc.fpcaddr != 0) ++ iommu_vfree(isp->iommu, ccdc->fpc.fpcaddr); ++} +diff --git a/drivers/media/video/isp/ispccdc.h b/drivers/media/video/isp/ispccdc.h +new file mode 100644 +index 0000000..5c00e2c +--- /dev/null ++++ b/drivers/media/video/isp/ispccdc.h +@@ -0,0 +1,223 @@ ++/* ++ * ispccdc.h ++ * ++ * TI OMAP3 ISP - CCDC module ++ * ++ * Copyright (C) 2009-2010 Nokia Corporation ++ * Copyright (C) 2009 Texas Instruments, Inc. ++ * ++ * Contacts: Laurent Pinchart ++ * Sakari Ailus ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#ifndef OMAP3_ISP_CCDC_H ++#define OMAP3_ISP_CCDC_H ++ ++#include ++#include ++#include ++ ++#include "ispvideo.h" ++ ++enum ccdc_input_entity { ++ CCDC_INPUT_NONE, ++ CCDC_INPUT_PARALLEL, ++ CCDC_INPUT_CSI2A, ++ CCDC_INPUT_CCP2B, ++ CCDC_INPUT_CSI2C ++}; ++ ++#define CCDC_OUTPUT_MEMORY (1 << 0) ++#define CCDC_OUTPUT_PREVIEW (1 << 1) ++#define CCDC_OUTPUT_RESIZER (1 << 2) ++ ++#define OMAP3ISP_CCDC_NEVENTS 16 ++ ++/* ++ * struct ispccdc_syncif - Structure for Sync Interface between sensor and CCDC ++ * @ccdc_mastermode: Master mode. 1 - Master, 0 - Slave. ++ * @fldstat: Field state. 0 - Odd Field, 1 - Even Field. ++ * @datsz: Data size. ++ * @fldmode: 0 - Progressive, 1 - Interlaced. ++ * @datapol: 0 - Positive, 1 - Negative. ++ * @fldpol: 0 - Positive, 1 - Negative. ++ * @hdpol: 0 - Positive, 1 - Negative. ++ * @vdpol: 0 - Positive, 1 - Negative. ++ * @fldout: 0 - Input, 1 - Output. ++ * @hs_width: Width of the Horizontal Sync pulse, used for HS/VS Output. ++ * @vs_width: Width of the Vertical Sync pulse, used for HS/VS Output. ++ * @ppln: Number of pixels per line, used for HS/VS Output. ++ * @hlprf: Number of half lines per frame, used for HS/VS Output. ++ * @bt_r656_en: 1 - Enable ITU-R BT656 mode, 0 - Sync mode. ++ */ ++struct ispccdc_syncif { ++ u8 ccdc_mastermode; ++ u8 fldstat; ++ u8 datsz; ++ u8 fldmode; ++ u8 datapol; ++ u8 fldpol; ++ u8 hdpol; ++ u8 vdpol; ++ u8 fldout; ++ u8 hs_width; ++ u8 vs_width; ++ u8 ppln; ++ u8 hlprf; ++ u8 bt_r656_en; ++}; ++ ++/* ++ * struct ispccdc_vp - Structure for Video Port parameters ++ * @pixelclk: Input pixel clock in Hz ++ */ ++struct ispccdc_vp { ++ unsigned int pixelclk; ++}; ++ ++enum ispccdc_lsc_state { ++ LSC_STATE_STOPPED = 0, ++ LSC_STATE_STOPPING = 1, ++ LSC_STATE_RUNNING = 2, ++ LSC_STATE_RECONFIG = 3, ++}; ++ ++struct ispccdc_lsc_config_req { ++ struct list_head list; ++ struct omap3isp_ccdc_lsc_config config; ++ unsigned char enable; ++ u32 table; ++ struct iovm_struct *iovm; ++}; ++ ++/* ++ * ispccdc_lsc - CCDC LSC parameters ++ * @update_config: Set when user changes config ++ * @request_enable: Whether LSC is requested to be enabled ++ * @config: LSC config set by user ++ * @update_table: Set when user provides a new LSC table to table_new ++ * @table_new: LSC table set by user, ISP address ++ * @table_inuse: LSC table currently in use, ISP address ++ */ ++struct ispccdc_lsc { ++ enum ispccdc_lsc_state state; ++ struct work_struct table_work; ++ ++ /* LSC queue of configurations */ ++ spinlock_t req_lock; ++ struct ispccdc_lsc_config_req *request; /* requested configuration */ ++ struct ispccdc_lsc_config_req *active; /* active configuration */ ++ struct list_head free_queue; /* configurations for freeing */ ++}; ++ ++#define CCDC_STOP_NOT_REQUESTED 0x00 ++#define CCDC_STOP_REQUEST 0x01 ++#define CCDC_STOP_EXECUTED (0x02 | CCDC_STOP_REQUEST) ++#define CCDC_STOP_CCDC_FINISHED 0x04 ++#define CCDC_STOP_LSC_FINISHED 0x08 ++#define CCDC_STOP_FINISHED \ ++ (CCDC_STOP_EXECUTED | CCDC_STOP_CCDC_FINISHED | CCDC_STOP_LSC_FINISHED) ++ ++#define CCDC_EVENT_VD1 0x10 ++#define CCDC_EVENT_VD0 0x20 ++#define CCDC_EVENT_LSC_DONE 0x40 ++ ++/* Sink and source CCDC pads */ ++#define CCDC_PAD_SINK 0 ++#define CCDC_PAD_SOURCE_OF 1 ++#define CCDC_PAD_SOURCE_VP 2 ++#define CCDC_PADS_NUM 3 ++ ++/* ++ * struct isp_ccdc_device - Structure for the CCDC module to store its own ++ * information ++ * @subdev: V4L2 subdevice ++ * @pads: Sink and source media entity pads ++ * @formats: Active video formats ++ * @ctrls: V4L2 controls handler ++ * @input: Active input ++ * @output: Active outputs ++ * @video_out: Output video node ++ * @error: A hardware error occured during capture ++ * @alaw: A-law compression enabled (1) or disabled (0) ++ * @lpf: Low pass filter enabled (1) or disabled (0) ++ * @obclamp: Optical-black clamp enabled (1) or disabled (0) ++ * @fpc_en: Faulty pixels correction enabled (1) or disabled (0) ++ * @blcomp: Black level compensation configuration ++ * @clamp: Optical-black or digital clamp configuration ++ * @fpc: Faulty pixels correction configuration ++ * @lsc: Lens shading compensation configuration ++ * @update: Bitmask of controls to update during the next interrupt ++ * @shadow_update: Controls update in progress by userspace ++ * @syncif: Interface synchronization configuration ++ * @vpcfg: Video port configuration ++ * @underrun: A buffer underrun occured and a new buffer has been queued ++ * @state: Streaming state ++ * @lock: Serializes shadow_update with interrupt handler ++ * @wait: Wait queue used to stop the module ++ * @stopping: Stopping state ++ * @ioctl_lock: Serializes ioctl calls and LSC requests freeing ++ */ ++struct isp_ccdc_device { ++ struct v4l2_subdev subdev; ++ struct media_pad pads[CCDC_PADS_NUM]; ++ struct v4l2_mbus_framefmt formats[CCDC_PADS_NUM]; ++ ++ struct v4l2_ctrl_handler ctrls; ++ ++ enum ccdc_input_entity input; ++ unsigned int output; ++ struct isp_video video_out; ++ unsigned int error; ++ ++ unsigned int alaw:1, ++ lpf:1, ++ obclamp:1, ++ fpc_en:1; ++ struct omap3isp_ccdc_blcomp blcomp; ++ struct omap3isp_ccdc_bclamp clamp; ++ struct omap3isp_ccdc_fpc fpc; ++ struct ispccdc_lsc lsc; ++ unsigned int update; ++ unsigned int shadow_update; ++ ++ struct ispccdc_syncif syncif; ++ struct ispccdc_vp vpcfg; ++ ++ unsigned int underrun:1; ++ enum isp_pipeline_stream_state state; ++ spinlock_t lock; ++ wait_queue_head_t wait; ++ unsigned int stopping; ++ struct mutex ioctl_lock; ++}; ++ ++struct isp_device; ++ ++int omap3isp_ccdc_init(struct isp_device *isp); ++void omap3isp_ccdc_cleanup(struct isp_device *isp); ++int omap3isp_ccdc_register_entities(struct isp_ccdc_device *ccdc, ++ struct v4l2_device *vdev); ++void omap3isp_ccdc_unregister_entities(struct isp_ccdc_device *ccdc); ++ ++int omap3isp_ccdc_busy(struct isp_ccdc_device *isp_ccdc); ++int omap3isp_ccdc_isr(struct isp_ccdc_device *isp_ccdc, u32 events); ++void omap3isp_ccdc_restore_context(struct isp_device *isp); ++void omap3isp_ccdc_max_rate(struct isp_ccdc_device *ccdc, ++ unsigned int *max_rate); ++ ++#endif /* OMAP3_ISP_CCDC_H */ +diff --git a/drivers/media/video/isp/ispccp2.c b/drivers/media/video/isp/ispccp2.c +new file mode 100644 +index 0000000..efcf827 +--- /dev/null ++++ b/drivers/media/video/isp/ispccp2.c +@@ -0,0 +1,1189 @@ ++/* ++ * ispccp2.c ++ * ++ * TI OMAP3 ISP - CCP2 module ++ * ++ * Copyright (C) 2010 Nokia Corporation ++ * Copyright (C) 2010 Texas Instruments, Inc. ++ * ++ * Contacts: Laurent Pinchart ++ * Sakari Ailus ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "isp.h" ++#include "ispreg.h" ++#include "ispccp2.h" ++ ++/* Number of LCX channels */ ++#define CCP2_LCx_CHANS_NUM 3 ++/* Max/Min size for CCP2 video port */ ++#define ISPCCP2_DAT_START_MIN 0 ++#define ISPCCP2_DAT_START_MAX 4095 ++#define ISPCCP2_DAT_SIZE_MIN 0 ++#define ISPCCP2_DAT_SIZE_MAX 4095 ++#define ISPCCP2_VPCLK_FRACDIV 65536 ++#define ISPCCP2_LCx_CTRL_FORMAT_RAW8_DPCM10_VP 0x12 ++#define ISPCCP2_LCx_CTRL_FORMAT_RAW10_VP 0x16 ++/* Max/Min size for CCP2 memory channel */ ++#define ISPCCP2_LCM_HSIZE_COUNT_MIN 16 ++#define ISPCCP2_LCM_HSIZE_COUNT_MAX 8191 ++#define ISPCCP2_LCM_HSIZE_SKIP_MIN 0 ++#define ISPCCP2_LCM_HSIZE_SKIP_MAX 8191 ++#define ISPCCP2_LCM_VSIZE_MIN 1 ++#define ISPCCP2_LCM_VSIZE_MAX 8191 ++#define ISPCCP2_LCM_HWORDS_MIN 1 ++#define ISPCCP2_LCM_HWORDS_MAX 4095 ++#define ISPCCP2_LCM_CTRL_BURST_SIZE_32X 5 ++#define ISPCCP2_LCM_CTRL_READ_THROTTLE_FULL 0 ++#define ISPCCP2_LCM_CTRL_SRC_DECOMPR_DPCM10 2 ++#define ISPCCP2_LCM_CTRL_SRC_FORMAT_RAW8 2 ++#define ISPCCP2_LCM_CTRL_SRC_FORMAT_RAW10 3 ++#define ISPCCP2_LCM_CTRL_DST_FORMAT_RAW10 3 ++#define ISPCCP2_LCM_CTRL_DST_PORT_VP 0 ++#define ISPCCP2_LCM_CTRL_DST_PORT_MEM 1 ++ ++/* Set only the required bits */ ++#define BIT_SET(var, shift, mask, val) \ ++ do { \ ++ var = ((var) & ~((mask) << (shift))) \ ++ | ((val) << (shift)); \ ++ } while (0) ++ ++/* ++ * ccp2_print_status - Print current CCP2 module register values. ++ */ ++#define CCP2_PRINT_REGISTER(isp, name)\ ++ dev_dbg(isp->dev, "###CCP2 " #name "=0x%08x\n", \ ++ isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_##name)) ++ ++static void ccp2_print_status(struct isp_ccp2_device *ccp2) ++{ ++ struct isp_device *isp = to_isp_device(ccp2); ++ ++ dev_dbg(isp->dev, "-------------CCP2 Register dump-------------\n"); ++ ++ CCP2_PRINT_REGISTER(isp, SYSCONFIG); ++ CCP2_PRINT_REGISTER(isp, SYSSTATUS); ++ CCP2_PRINT_REGISTER(isp, LC01_IRQENABLE); ++ CCP2_PRINT_REGISTER(isp, LC01_IRQSTATUS); ++ CCP2_PRINT_REGISTER(isp, LC23_IRQENABLE); ++ CCP2_PRINT_REGISTER(isp, LC23_IRQSTATUS); ++ CCP2_PRINT_REGISTER(isp, LCM_IRQENABLE); ++ CCP2_PRINT_REGISTER(isp, LCM_IRQSTATUS); ++ CCP2_PRINT_REGISTER(isp, CTRL); ++ CCP2_PRINT_REGISTER(isp, LCx_CTRL(0)); ++ CCP2_PRINT_REGISTER(isp, LCx_CODE(0)); ++ CCP2_PRINT_REGISTER(isp, LCx_STAT_START(0)); ++ CCP2_PRINT_REGISTER(isp, LCx_STAT_SIZE(0)); ++ CCP2_PRINT_REGISTER(isp, LCx_SOF_ADDR(0)); ++ CCP2_PRINT_REGISTER(isp, LCx_EOF_ADDR(0)); ++ CCP2_PRINT_REGISTER(isp, LCx_DAT_START(0)); ++ CCP2_PRINT_REGISTER(isp, LCx_DAT_SIZE(0)); ++ CCP2_PRINT_REGISTER(isp, LCx_DAT_PING_ADDR(0)); ++ CCP2_PRINT_REGISTER(isp, LCx_DAT_PONG_ADDR(0)); ++ CCP2_PRINT_REGISTER(isp, LCx_DAT_OFST(0)); ++ CCP2_PRINT_REGISTER(isp, LCM_CTRL); ++ CCP2_PRINT_REGISTER(isp, LCM_VSIZE); ++ CCP2_PRINT_REGISTER(isp, LCM_HSIZE); ++ CCP2_PRINT_REGISTER(isp, LCM_PREFETCH); ++ CCP2_PRINT_REGISTER(isp, LCM_SRC_ADDR); ++ CCP2_PRINT_REGISTER(isp, LCM_SRC_OFST); ++ CCP2_PRINT_REGISTER(isp, LCM_DST_ADDR); ++ CCP2_PRINT_REGISTER(isp, LCM_DST_OFST); ++ ++ dev_dbg(isp->dev, "--------------------------------------------\n"); ++} ++ ++/* ++ * ccp2_reset - Reset the CCP2 ++ * @ccp2: pointer to ISP CCP2 device ++ */ ++static void ccp2_reset(struct isp_ccp2_device *ccp2) ++{ ++ struct isp_device *isp = to_isp_device(ccp2); ++ int i = 0; ++ ++ /* Reset the CSI1/CCP2B and wait for reset to complete */ ++ isp_reg_set(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_SYSCONFIG, ++ ISPCCP2_SYSCONFIG_SOFT_RESET); ++ while (!(isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_SYSSTATUS) & ++ ISPCCP2_SYSSTATUS_RESET_DONE)) { ++ udelay(10); ++ if (i++ > 10) { /* try read 10 times */ ++ dev_warn(isp->dev, ++ "omap3_isp: timeout waiting for ccp2 reset\n"); ++ break; ++ } ++ } ++} ++ ++/* ++ * ccp2_pwr_cfg - Configure the power mode settings ++ * @ccp2: pointer to ISP CCP2 device ++ */ ++static void ccp2_pwr_cfg(struct isp_ccp2_device *ccp2) ++{ ++ struct isp_device *isp = to_isp_device(ccp2); ++ ++ isp_reg_writel(isp, ISPCCP2_SYSCONFIG_MSTANDBY_MODE_SMART | ++ ((isp->revision == ISP_REVISION_15_0 && isp->autoidle) ? ++ ISPCCP2_SYSCONFIG_AUTO_IDLE : 0), ++ OMAP3_ISP_IOMEM_CCP2, ISPCCP2_SYSCONFIG); ++} ++ ++/* ++ * ccp2_if_enable - Enable CCP2 interface. ++ * @ccp2: pointer to ISP CCP2 device ++ * @enable: enable/disable flag ++ */ ++static void ccp2_if_enable(struct isp_ccp2_device *ccp2, u8 enable) ++{ ++ struct isp_device *isp = to_isp_device(ccp2); ++ struct isp_pipeline *pipe = to_isp_pipeline(&ccp2->subdev.entity); ++ int i; ++ ++ /* Enable/Disable all the LCx channels */ ++ for (i = 0; i < CCP2_LCx_CHANS_NUM; i++) ++ isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCx_CTRL(i), ++ ISPCCP2_LCx_CTRL_CHAN_EN, ++ enable ? ISPCCP2_LCx_CTRL_CHAN_EN : 0); ++ ++ /* Enable/Disable ccp2 interface in ccp2 mode */ ++ isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_CTRL, ++ ISPCCP2_CTRL_MODE | ISPCCP2_CTRL_IF_EN, ++ enable ? (ISPCCP2_CTRL_MODE | ISPCCP2_CTRL_IF_EN) : 0); ++ ++ /* For frame count propagation */ ++ if (pipe->do_propagation) { ++ /* We may want the Frame Start IRQ from LC0 */ ++ if (enable) ++ isp_reg_set(isp, OMAP3_ISP_IOMEM_CCP2, ++ ISPCCP2_LC01_IRQENABLE, ++ ISPCCP2_LC01_IRQSTATUS_LC0_FS_IRQ); ++ else ++ isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCP2, ++ ISPCCP2_LC01_IRQENABLE, ++ ISPCCP2_LC01_IRQSTATUS_LC0_FS_IRQ); ++ } ++} ++ ++/* ++ * ccp2_mem_enable - Enable CCP2 memory interface. ++ * @ccp2: pointer to ISP CCP2 device ++ * @enable: enable/disable flag ++ */ ++static void ccp2_mem_enable(struct isp_ccp2_device *ccp2, u8 enable) ++{ ++ struct isp_device *isp = to_isp_device(ccp2); ++ ++ if (enable) ++ ccp2_if_enable(ccp2, 0); ++ ++ /* Enable/Disable ccp2 interface in ccp2 mode */ ++ isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_CTRL, ++ ISPCCP2_CTRL_MODE, enable ? ISPCCP2_CTRL_MODE : 0); ++ ++ isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCM_CTRL, ++ ISPCCP2_LCM_CTRL_CHAN_EN, ++ enable ? ISPCCP2_LCM_CTRL_CHAN_EN : 0); ++} ++ ++/* ++ * ccp2_phyif_config - Initialize CCP2 phy interface config ++ * @ccp2: Pointer to ISP CCP2 device ++ * @config: CCP2 platform data ++ * ++ * Configure the CCP2 physical interface module from platform data. ++ * ++ * Returns -EIO if strobe is chosen in CSI1 mode, or 0 on success. ++ */ ++static int ccp2_phyif_config(struct isp_ccp2_device *ccp2, ++ const struct isp_ccp2_platform_data *pdata) ++{ ++ struct isp_device *isp = to_isp_device(ccp2); ++ u32 val; ++ ++ /* CCP2B mode */ ++ val = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_CTRL) | ++ ISPCCP2_CTRL_IO_OUT_SEL | ISPCCP2_CTRL_MODE; ++ /* Data/strobe physical layer */ ++ BIT_SET(val, ISPCCP2_CTRL_PHY_SEL_SHIFT, ISPCCP2_CTRL_PHY_SEL_MASK, ++ pdata->phy_layer); ++ BIT_SET(val, ISPCCP2_CTRL_INV_SHIFT, ISPCCP2_CTRL_INV_MASK, ++ pdata->strobe_clk_pol); ++ isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_CTRL); ++ ++ val = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_CTRL); ++ if (!(val & ISPCCP2_CTRL_MODE)) { ++ if (pdata->ccp2_mode) ++ dev_warn(isp->dev, "OMAP3 CCP2 bus not available\n"); ++ if (pdata->phy_layer == ISPCCP2_CTRL_PHY_SEL_STROBE) ++ /* Strobe mode requires CCP2 */ ++ return -EIO; ++ } ++ ++ return 0; ++} ++ ++/* ++ * ccp2_vp_config - Initialize CCP2 video port interface. ++ * @ccp2: Pointer to ISP CCP2 device ++ * @vpclk_div: Video port divisor ++ * ++ * Configure the CCP2 video port with the given clock divisor. The valid divisor ++ * values depend on the ISP revision: ++ * ++ * - revision 1.0 and 2.0 1 to 4 ++ * - revision 15.0 1 to 65536 ++ * ++ * The exact divisor value used might differ from the requested value, as ISP ++ * revision 15.0 represent the divisor by 65536 divided by an integer. ++ */ ++static void ccp2_vp_config(struct isp_ccp2_device *ccp2, ++ unsigned int vpclk_div) ++{ ++ struct isp_device *isp = to_isp_device(ccp2); ++ u32 val; ++ ++ /* ISPCCP2_CTRL Video port */ ++ val = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_CTRL); ++ val |= ISPCCP2_CTRL_VP_ONLY_EN; /* Disable the memory write port */ ++ ++ if (isp->revision == ISP_REVISION_15_0) { ++ vpclk_div = clamp_t(unsigned int, vpclk_div, 1, 65536); ++ vpclk_div = min(ISPCCP2_VPCLK_FRACDIV / vpclk_div, 65535U); ++ BIT_SET(val, ISPCCP2_CTRL_VPCLK_DIV_SHIFT, ++ ISPCCP2_CTRL_VPCLK_DIV_MASK, vpclk_div); ++ } else { ++ vpclk_div = clamp_t(unsigned int, vpclk_div, 1, 4); ++ BIT_SET(val, ISPCCP2_CTRL_VP_OUT_CTRL_SHIFT, ++ ISPCCP2_CTRL_VP_OUT_CTRL_MASK, vpclk_div - 1); ++ } ++ ++ isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_CTRL); ++} ++ ++/* ++ * ccp2_lcx_config - Initialize CCP2 logical channel interface. ++ * @ccp2: Pointer to ISP CCP2 device ++ * @config: Pointer to ISP LCx config structure. ++ * ++ * This will analyze the parameters passed by the interface config ++ * and configure CSI1/CCP2 logical channel ++ * ++ */ ++static void ccp2_lcx_config(struct isp_ccp2_device *ccp2, ++ struct isp_interface_lcx_config *config) ++{ ++ struct isp_device *isp = to_isp_device(ccp2); ++ u32 val, format; ++ ++ switch (config->format) { ++ case V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8: ++ format = ISPCCP2_LCx_CTRL_FORMAT_RAW8_DPCM10_VP; ++ break; ++ case V4L2_MBUS_FMT_SGRBG10_1X10: ++ default: ++ format = ISPCCP2_LCx_CTRL_FORMAT_RAW10_VP; /* RAW10+VP */ ++ break; ++ } ++ /* ISPCCP2_LCx_CTRL logical channel #0 */ ++ val = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCx_CTRL(0)) ++ | (ISPCCP2_LCx_CTRL_REGION_EN); /* Region */ ++ ++ if (isp->revision == ISP_REVISION_15_0) { ++ /* CRC */ ++ BIT_SET(val, ISPCCP2_LCx_CTRL_CRC_SHIFT_15_0, ++ ISPCCP2_LCx_CTRL_CRC_MASK, ++ config->crc); ++ /* Format = RAW10+VP or RAW8+DPCM10+VP*/ ++ BIT_SET(val, ISPCCP2_LCx_CTRL_FORMAT_SHIFT_15_0, ++ ISPCCP2_LCx_CTRL_FORMAT_MASK_15_0, format); ++ } else { ++ BIT_SET(val, ISPCCP2_LCx_CTRL_CRC_SHIFT, ++ ISPCCP2_LCx_CTRL_CRC_MASK, ++ config->crc); ++ ++ BIT_SET(val, ISPCCP2_LCx_CTRL_FORMAT_SHIFT, ++ ISPCCP2_LCx_CTRL_FORMAT_MASK, format); ++ } ++ isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCx_CTRL(0)); ++ ++ /* ISPCCP2_DAT_START for logical channel #0 */ ++ isp_reg_writel(isp, config->data_start << ISPCCP2_LCx_DAT_SHIFT, ++ OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCx_DAT_START(0)); ++ ++ /* ISPCCP2_DAT_SIZE for logical channel #0 */ ++ isp_reg_writel(isp, config->data_size << ISPCCP2_LCx_DAT_SHIFT, ++ OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCx_DAT_SIZE(0)); ++ ++ /* Enable error IRQs for logical channel #0 */ ++ val = ISPCCP2_LC01_IRQSTATUS_LC0_FIFO_OVF_IRQ | ++ ISPCCP2_LC01_IRQSTATUS_LC0_CRC_IRQ | ++ ISPCCP2_LC01_IRQSTATUS_LC0_FSP_IRQ | ++ ISPCCP2_LC01_IRQSTATUS_LC0_FW_IRQ | ++ ISPCCP2_LC01_IRQSTATUS_LC0_FS_IRQ | ++ ISPCCP2_LC01_IRQSTATUS_LC0_FSC_IRQ | ++ ISPCCP2_LC01_IRQSTATUS_LC0_SSC_IRQ; ++ ++ isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LC01_IRQSTATUS); ++ isp_reg_set(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LC01_IRQENABLE, val); ++} ++ ++/* ++ * ccp2_if_configure - Configure ccp2 with data from sensor ++ * @ccp2: Pointer to ISP CCP2 device ++ * ++ * Return 0 on success or a negative error code ++ */ ++static int ccp2_if_configure(struct isp_ccp2_device *ccp2) ++{ ++ const struct isp_v4l2_subdevs_group *pdata; ++ struct v4l2_mbus_framefmt *format; ++ struct media_pad *pad; ++ struct v4l2_subdev *sensor; ++ u32 lines = 0; ++ int ret; ++ ++ ccp2_pwr_cfg(ccp2); ++ ++ pad = media_entity_remote_source(&ccp2->pads[CCP2_PAD_SINK]); ++ sensor = media_entity_to_v4l2_subdev(pad->entity); ++ pdata = sensor->host_priv; ++ ++ ret = ccp2_phyif_config(ccp2, &pdata->bus.ccp2); ++ if (ret < 0) ++ return ret; ++ ++ ccp2_vp_config(ccp2, pdata->bus.ccp2.vpclk_div + 1); ++ ++ v4l2_subdev_call(sensor, sensor, g_skip_top_lines, &lines); ++ ++ format = &ccp2->formats[CCP2_PAD_SINK]; ++ ++ ccp2->if_cfg.data_start = lines; ++ ccp2->if_cfg.crc = pdata->bus.ccp2.crc; ++ ccp2->if_cfg.format = format->code; ++ ccp2->if_cfg.data_size = format->height; ++ ++ ccp2_lcx_config(ccp2, &ccp2->if_cfg); ++ ++ return 0; ++} ++ ++static int ccp2_adjust_bandwidth(struct isp_ccp2_device *ccp2) ++{ ++ struct isp_pipeline *pipe = to_isp_pipeline(&ccp2->subdev.entity); ++ struct isp_device *isp = to_isp_device(ccp2); ++ const struct v4l2_mbus_framefmt *ofmt = &ccp2->formats[CCP2_PAD_SOURCE]; ++ unsigned long l3_ick = pipe->l3_ick; ++ struct v4l2_fract *timeperframe; ++ unsigned int vpclk_div = 2; ++ unsigned int value; ++ u64 bound; ++ u64 area; ++ ++ /* Compute the minimum clock divisor, based on the pipeline maximum ++ * data rate. This is an absolute lower bound if we don't want SBL ++ * overflows, so round the value up. ++ */ ++ vpclk_div = max_t(unsigned int, DIV_ROUND_UP(l3_ick, pipe->max_rate), ++ vpclk_div); ++ ++ /* Compute the maximum clock divisor, based on the requested frame rate. ++ * This is a soft lower bound to achieve a frame rate equal or higher ++ * than the requested value, so round the value down. ++ */ ++ timeperframe = &pipe->max_timeperframe; ++ ++ if (timeperframe->numerator) { ++ area = ofmt->width * ofmt->height; ++ bound = div_u64(area * timeperframe->denominator, ++ timeperframe->numerator); ++ value = min_t(u64, bound, l3_ick); ++ vpclk_div = max_t(unsigned int, l3_ick / value, vpclk_div); ++ } ++ ++ dev_dbg(isp->dev, "%s: minimum clock divisor = %u\n", __func__, ++ vpclk_div); ++ ++ return vpclk_div; ++} ++ ++/* ++ * ccp2_mem_configure - Initialize CCP2 memory input/output interface ++ * @ccp2: Pointer to ISP CCP2 device ++ * @config: Pointer to ISP mem interface config structure ++ * ++ * This will analyze the parameters passed by the interface config ++ * structure, and configure the respective registers for proper ++ * CSI1/CCP2 memory input. ++ */ ++static void ccp2_mem_configure(struct isp_ccp2_device *ccp2, ++ struct isp_interface_mem_config *config) ++{ ++ struct isp_device *isp = to_isp_device(ccp2); ++ u32 sink_pixcode = ccp2->formats[CCP2_PAD_SINK].code; ++ u32 source_pixcode = ccp2->formats[CCP2_PAD_SOURCE].code; ++ unsigned int dpcm_decompress = 0; ++ u32 val, hwords; ++ ++ if (sink_pixcode != source_pixcode && ++ sink_pixcode == V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8) ++ dpcm_decompress = 1; ++ ++ ccp2_pwr_cfg(ccp2); ++ ++ /* Hsize, Skip */ ++ isp_reg_writel(isp, ISPCCP2_LCM_HSIZE_SKIP_MIN | ++ (config->hsize_count << ISPCCP2_LCM_HSIZE_SHIFT), ++ OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCM_HSIZE); ++ ++ /* Vsize, no. of lines */ ++ isp_reg_writel(isp, config->vsize_count << ISPCCP2_LCM_VSIZE_SHIFT, ++ OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCM_VSIZE); ++ ++ if (ccp2->video_in.bpl_padding == 0) ++ config->src_ofst = 0; ++ else ++ config->src_ofst = ccp2->video_in.bpl_value; ++ ++ isp_reg_writel(isp, config->src_ofst, OMAP3_ISP_IOMEM_CCP2, ++ ISPCCP2_LCM_SRC_OFST); ++ ++ /* Source and Destination formats */ ++ val = ISPCCP2_LCM_CTRL_DST_FORMAT_RAW10 << ++ ISPCCP2_LCM_CTRL_DST_FORMAT_SHIFT; ++ ++ if (dpcm_decompress) { ++ /* source format is RAW8 */ ++ val |= ISPCCP2_LCM_CTRL_SRC_FORMAT_RAW8 << ++ ISPCCP2_LCM_CTRL_SRC_FORMAT_SHIFT; ++ ++ /* RAW8 + DPCM10 - simple predictor */ ++ val |= ISPCCP2_LCM_CTRL_SRC_DPCM_PRED; ++ ++ /* enable source DPCM decompression */ ++ val |= ISPCCP2_LCM_CTRL_SRC_DECOMPR_DPCM10 << ++ ISPCCP2_LCM_CTRL_SRC_DECOMPR_SHIFT; ++ } else { ++ /* source format is RAW10 */ ++ val |= ISPCCP2_LCM_CTRL_SRC_FORMAT_RAW10 << ++ ISPCCP2_LCM_CTRL_SRC_FORMAT_SHIFT; ++ } ++ ++ /* Burst size to 32x64 */ ++ val |= ISPCCP2_LCM_CTRL_BURST_SIZE_32X << ++ ISPCCP2_LCM_CTRL_BURST_SIZE_SHIFT; ++ ++ isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCM_CTRL); ++ ++ /* Prefetch setup */ ++ if (dpcm_decompress) ++ hwords = (ISPCCP2_LCM_HSIZE_SKIP_MIN + ++ config->hsize_count) >> 3; ++ else ++ hwords = (ISPCCP2_LCM_HSIZE_SKIP_MIN + ++ config->hsize_count) >> 2; ++ ++ isp_reg_writel(isp, hwords << ISPCCP2_LCM_PREFETCH_SHIFT, ++ OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCM_PREFETCH); ++ ++ /* Video port */ ++ isp_reg_set(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_CTRL, ++ ISPCCP2_CTRL_IO_OUT_SEL | ISPCCP2_CTRL_MODE); ++ ccp2_vp_config(ccp2, ccp2_adjust_bandwidth(ccp2)); ++ ++ /* Clear LCM interrupts */ ++ isp_reg_writel(isp, ISPCCP2_LCM_IRQSTATUS_OCPERROR_IRQ | ++ ISPCCP2_LCM_IRQSTATUS_EOF_IRQ, ++ OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCM_IRQSTATUS); ++ ++ /* Enable LCM interupts */ ++ isp_reg_set(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCM_IRQENABLE, ++ ISPCCP2_LCM_IRQSTATUS_EOF_IRQ | ++ ISPCCP2_LCM_IRQSTATUS_OCPERROR_IRQ); ++} ++ ++/* ++ * ccp2_set_inaddr - Sets memory address of input frame. ++ * @ccp2: Pointer to ISP CCP2 device ++ * @addr: 32bit memory address aligned on 32byte boundary. ++ * ++ * Configures the memory address from which the input frame is to be read. ++ */ ++static void ccp2_set_inaddr(struct isp_ccp2_device *ccp2, u32 addr) ++{ ++ struct isp_device *isp = to_isp_device(ccp2); ++ ++ isp_reg_writel(isp, addr, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCM_SRC_ADDR); ++} ++ ++/* ----------------------------------------------------------------------------- ++ * Interrupt handling ++ */ ++ ++static void ccp2_isr_buffer(struct isp_ccp2_device *ccp2) ++{ ++ struct isp_pipeline *pipe = to_isp_pipeline(&ccp2->subdev.entity); ++ struct isp_buffer *buffer; ++ ++ buffer = omap3isp_video_buffer_next(&ccp2->video_in, ccp2->error); ++ if (buffer != NULL) ++ ccp2_set_inaddr(ccp2, buffer->isp_addr); ++ ++ pipe->state |= ISP_PIPELINE_IDLE_INPUT; ++ ++ if (ccp2->state == ISP_PIPELINE_STREAM_SINGLESHOT) { ++ if (isp_pipeline_ready(pipe)) ++ omap3isp_pipeline_set_stream(pipe, ++ ISP_PIPELINE_STREAM_SINGLESHOT); ++ } ++ ++ ccp2->error = 0; ++} ++ ++/* ++ * omap3isp_ccp2_isr - Handle ISP CCP2 interrupts ++ * @ccp2: Pointer to ISP CCP2 device ++ * ++ * This will handle the CCP2 interrupts ++ * ++ * Returns -EIO in case of error, or 0 on success. ++ */ ++int omap3isp_ccp2_isr(struct isp_ccp2_device *ccp2) ++{ ++ struct isp_device *isp = to_isp_device(ccp2); ++ int ret = 0; ++ static const u32 ISPCCP2_LC01_ERROR = ++ ISPCCP2_LC01_IRQSTATUS_LC0_FIFO_OVF_IRQ | ++ ISPCCP2_LC01_IRQSTATUS_LC0_CRC_IRQ | ++ ISPCCP2_LC01_IRQSTATUS_LC0_FSP_IRQ | ++ ISPCCP2_LC01_IRQSTATUS_LC0_FW_IRQ | ++ ISPCCP2_LC01_IRQSTATUS_LC0_FSC_IRQ | ++ ISPCCP2_LC01_IRQSTATUS_LC0_SSC_IRQ; ++ u32 lcx_irqstatus, lcm_irqstatus; ++ ++ /* First clear the interrupts */ ++ lcx_irqstatus = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCP2, ++ ISPCCP2_LC01_IRQSTATUS); ++ isp_reg_writel(isp, lcx_irqstatus, OMAP3_ISP_IOMEM_CCP2, ++ ISPCCP2_LC01_IRQSTATUS); ++ ++ lcm_irqstatus = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCP2, ++ ISPCCP2_LCM_IRQSTATUS); ++ isp_reg_writel(isp, lcm_irqstatus, OMAP3_ISP_IOMEM_CCP2, ++ ISPCCP2_LCM_IRQSTATUS); ++ /* Errors */ ++ if (lcx_irqstatus & ISPCCP2_LC01_ERROR) { ++ ccp2->error = 1; ++ dev_dbg(isp->dev, "CCP2 err:%x\n", lcx_irqstatus); ++ return -EIO; ++ } ++ ++ if (lcm_irqstatus & ISPCCP2_LCM_IRQSTATUS_OCPERROR_IRQ) { ++ ccp2->error = 1; ++ dev_dbg(isp->dev, "CCP2 OCP err:%x\n", lcm_irqstatus); ++ ret = -EIO; ++ } ++ ++ if (omap3isp_module_sync_is_stopping(&ccp2->wait, &ccp2->stopping)) ++ return 0; ++ ++ /* Frame number propagation */ ++ if (lcx_irqstatus & ISPCCP2_LC01_IRQSTATUS_LC0_FS_IRQ) { ++ struct isp_pipeline *pipe = ++ to_isp_pipeline(&ccp2->subdev.entity); ++ if (pipe->do_propagation) ++ atomic_inc(&pipe->frame_number); ++ } ++ ++ /* Handle queued buffers on frame end interrupts */ ++ if (lcm_irqstatus & ISPCCP2_LCM_IRQSTATUS_EOF_IRQ) ++ ccp2_isr_buffer(ccp2); ++ ++ return ret; ++} ++ ++/* ----------------------------------------------------------------------------- ++ * V4L2 subdev operations ++ */ ++ ++static const unsigned int ccp2_fmts[] = { ++ V4L2_MBUS_FMT_SGRBG10_1X10, ++ V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, ++}; ++ ++/* ++ * __ccp2_get_format - helper function for getting ccp2 format ++ * @ccp2 : Pointer to ISP CCP2 device ++ * @fh : V4L2 subdev file handle ++ * @pad : pad number ++ * @which : wanted subdev format ++ * return format structure or NULL on error ++ */ ++static struct v4l2_mbus_framefmt * ++__ccp2_get_format(struct isp_ccp2_device *ccp2, struct v4l2_subdev_fh *fh, ++ unsigned int pad, enum v4l2_subdev_format_whence which) ++{ ++ if (which == V4L2_SUBDEV_FORMAT_TRY) ++ return v4l2_subdev_get_try_format(fh, pad); ++ else ++ return &ccp2->formats[pad]; ++} ++ ++/* ++ * ccp2_try_format - Handle try format by pad subdev method ++ * @ccp2 : Pointer to ISP CCP2 device ++ * @fh : V4L2 subdev file handle ++ * @pad : pad num ++ * @fmt : pointer to v4l2 mbus format structure ++ * @which : wanted subdev format ++ */ ++static void ccp2_try_format(struct isp_ccp2_device *ccp2, ++ struct v4l2_subdev_fh *fh, unsigned int pad, ++ struct v4l2_mbus_framefmt *fmt, ++ enum v4l2_subdev_format_whence which) ++{ ++ struct v4l2_mbus_framefmt *format; ++ ++ switch (pad) { ++ case CCP2_PAD_SINK: ++ if (fmt->code != V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8) ++ fmt->code = V4L2_MBUS_FMT_SGRBG10_1X10; ++ ++ if (ccp2->input == CCP2_INPUT_SENSOR) { ++ fmt->width = clamp_t(u32, fmt->width, ++ ISPCCP2_DAT_START_MIN, ++ ISPCCP2_DAT_START_MAX); ++ fmt->height = clamp_t(u32, fmt->height, ++ ISPCCP2_DAT_SIZE_MIN, ++ ISPCCP2_DAT_SIZE_MAX); ++ } else if (ccp2->input == CCP2_INPUT_MEMORY) { ++ fmt->width = clamp_t(u32, fmt->width, ++ ISPCCP2_LCM_HSIZE_COUNT_MIN, ++ ISPCCP2_LCM_HSIZE_COUNT_MAX); ++ fmt->height = clamp_t(u32, fmt->height, ++ ISPCCP2_LCM_VSIZE_MIN, ++ ISPCCP2_LCM_VSIZE_MAX); ++ } ++ break; ++ ++ case CCP2_PAD_SOURCE: ++ /* Source format - copy sink format and change pixel code ++ * to SGRBG10_1X10 as we don't support CCP2 write to memory. ++ * When CCP2 write to memory feature will be added this ++ * should be changed properly. ++ */ ++ format = __ccp2_get_format(ccp2, fh, CCP2_PAD_SINK, which); ++ memcpy(fmt, format, sizeof(*fmt)); ++ fmt->code = V4L2_MBUS_FMT_SGRBG10_1X10; ++ break; ++ } ++ ++ fmt->field = V4L2_FIELD_NONE; ++ fmt->colorspace = V4L2_COLORSPACE_SRGB; ++} ++ ++/* ++ * ccp2_enum_mbus_code - Handle pixel format enumeration ++ * @sd : pointer to v4l2 subdev structure ++ * @fh : V4L2 subdev file handle ++ * @code : pointer to v4l2_subdev_mbus_code_enum structure ++ * return -EINVAL or zero on success ++ */ ++static int ccp2_enum_mbus_code(struct v4l2_subdev *sd, ++ struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_mbus_code_enum *code) ++{ ++ struct isp_ccp2_device *ccp2 = v4l2_get_subdevdata(sd); ++ struct v4l2_mbus_framefmt *format; ++ ++ if (code->pad == CCP2_PAD_SINK) { ++ if (code->index >= ARRAY_SIZE(ccp2_fmts)) ++ return -EINVAL; ++ ++ code->code = ccp2_fmts[code->index]; ++ } else { ++ if (code->index != 0) ++ return -EINVAL; ++ ++ format = __ccp2_get_format(ccp2, fh, CCP2_PAD_SINK, ++ V4L2_SUBDEV_FORMAT_TRY); ++ code->code = format->code; ++ } ++ ++ return 0; ++} ++ ++static int ccp2_enum_frame_size(struct v4l2_subdev *sd, ++ struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_frame_size_enum *fse) ++{ ++ struct isp_ccp2_device *ccp2 = v4l2_get_subdevdata(sd); ++ struct v4l2_mbus_framefmt format; ++ ++ if (fse->index != 0) ++ return -EINVAL; ++ ++ format.code = fse->code; ++ format.width = 1; ++ format.height = 1; ++ ccp2_try_format(ccp2, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); ++ fse->min_width = format.width; ++ fse->min_height = format.height; ++ ++ if (format.code != fse->code) ++ return -EINVAL; ++ ++ format.code = fse->code; ++ format.width = -1; ++ format.height = -1; ++ ccp2_try_format(ccp2, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); ++ fse->max_width = format.width; ++ fse->max_height = format.height; ++ ++ return 0; ++} ++ ++/* ++ * ccp2_get_format - Handle get format by pads subdev method ++ * @sd : pointer to v4l2 subdev structure ++ * @fh : V4L2 subdev file handle ++ * @fmt : pointer to v4l2 subdev format structure ++ * return -EINVAL or zero on sucess ++ */ ++static int ccp2_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_format *fmt) ++{ ++ struct isp_ccp2_device *ccp2 = v4l2_get_subdevdata(sd); ++ struct v4l2_mbus_framefmt *format; ++ ++ format = __ccp2_get_format(ccp2, fh, fmt->pad, fmt->which); ++ if (format == NULL) ++ return -EINVAL; ++ ++ fmt->format = *format; ++ return 0; ++} ++ ++/* ++ * ccp2_set_format - Handle set format by pads subdev method ++ * @sd : pointer to v4l2 subdev structure ++ * @fh : V4L2 subdev file handle ++ * @fmt : pointer to v4l2 subdev format structure ++ * returns zero ++ */ ++static int ccp2_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_format *fmt) ++{ ++ struct isp_ccp2_device *ccp2 = v4l2_get_subdevdata(sd); ++ struct v4l2_mbus_framefmt *format; ++ ++ format = __ccp2_get_format(ccp2, fh, fmt->pad, fmt->which); ++ if (format == NULL) ++ return -EINVAL; ++ ++ ccp2_try_format(ccp2, fh, fmt->pad, &fmt->format, fmt->which); ++ *format = fmt->format; ++ ++ /* Propagate the format from sink to source */ ++ if (fmt->pad == CCP2_PAD_SINK) { ++ format = __ccp2_get_format(ccp2, fh, CCP2_PAD_SOURCE, ++ fmt->which); ++ *format = fmt->format; ++ ccp2_try_format(ccp2, fh, CCP2_PAD_SOURCE, format, fmt->which); ++ } ++ ++ return 0; ++} ++ ++/* ++ * ccp2_init_formats - Initialize formats on all pads ++ * @sd: ISP CCP2 V4L2 subdevice ++ * @fh: V4L2 subdev file handle ++ * ++ * Initialize all pad formats with default values. If fh is not NULL, try ++ * formats are initialized on the file handle. Otherwise active formats are ++ * initialized on the device. ++ */ ++static int ccp2_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) ++{ ++ struct v4l2_subdev_format format; ++ ++ memset(&format, 0, sizeof(format)); ++ format.pad = CCP2_PAD_SINK; ++ format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; ++ format.format.code = V4L2_MBUS_FMT_SGRBG10_1X10; ++ format.format.width = 4096; ++ format.format.height = 4096; ++ ccp2_set_format(sd, fh, &format); ++ ++ return 0; ++} ++ ++/* ++ * ccp2_s_stream - Enable/Disable streaming on ccp2 subdev ++ * @sd : pointer to v4l2 subdev structure ++ * @enable: 1 == Enable, 0 == Disable ++ * return zero ++ */ ++static int ccp2_s_stream(struct v4l2_subdev *sd, int enable) ++{ ++ struct isp_ccp2_device *ccp2 = v4l2_get_subdevdata(sd); ++ struct isp_device *isp = to_isp_device(ccp2); ++ struct device *dev = to_device(ccp2); ++ int ret; ++ ++ if (ccp2->state == ISP_PIPELINE_STREAM_STOPPED) { ++ if (enable == ISP_PIPELINE_STREAM_STOPPED) ++ return 0; ++ atomic_set(&ccp2->stopping, 0); ++ ccp2->error = 0; ++ } ++ ++ switch (enable) { ++ case ISP_PIPELINE_STREAM_CONTINUOUS: ++ if (ccp2->phy) { ++ ret = omap3isp_csiphy_acquire(ccp2->phy); ++ if (ret < 0) ++ return ret; ++ } ++ ++ ccp2_if_configure(ccp2); ++ ccp2_print_status(ccp2); ++ ++ /* Enable CSI1/CCP2 interface */ ++ ccp2_if_enable(ccp2, 1); ++ break; ++ ++ case ISP_PIPELINE_STREAM_SINGLESHOT: ++ if (ccp2->state != ISP_PIPELINE_STREAM_SINGLESHOT) { ++ struct v4l2_mbus_framefmt *format; ++ ++ format = &ccp2->formats[CCP2_PAD_SINK]; ++ ++ ccp2->mem_cfg.hsize_count = format->width; ++ ccp2->mem_cfg.vsize_count = format->height; ++ ccp2->mem_cfg.src_ofst = 0; ++ ++ ccp2_mem_configure(ccp2, &ccp2->mem_cfg); ++ omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_CSI1_READ); ++ ccp2_print_status(ccp2); ++ } ++ ccp2_mem_enable(ccp2, 1); ++ break; ++ ++ case ISP_PIPELINE_STREAM_STOPPED: ++ if (omap3isp_module_sync_idle(&sd->entity, &ccp2->wait, ++ &ccp2->stopping)) ++ dev_dbg(dev, "%s: module stop timeout.\n", sd->name); ++ if (ccp2->input == CCP2_INPUT_MEMORY) { ++ ccp2_mem_enable(ccp2, 0); ++ omap3isp_sbl_disable(isp, OMAP3_ISP_SBL_CSI1_READ); ++ } else if (ccp2->input == CCP2_INPUT_SENSOR) { ++ /* Disable CSI1/CCP2 interface */ ++ ccp2_if_enable(ccp2, 0); ++ if (ccp2->phy) ++ omap3isp_csiphy_release(ccp2->phy); ++ } ++ break; ++ } ++ ++ ccp2->state = enable; ++ return 0; ++} ++ ++/* subdev core operations */ ++static const struct v4l2_subdev_core_ops ccp2_sd_core_ops = { ++ .queryctrl = v4l2_subdev_queryctrl, ++ .querymenu = v4l2_subdev_querymenu, ++ .g_ctrl = v4l2_subdev_g_ctrl, ++ .s_ctrl = v4l2_subdev_s_ctrl, ++ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, ++ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, ++ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, ++}; ++ ++/* subdev file operations */ ++static const struct v4l2_subdev_file_ops ccp2_sd_file_ops = { ++ .open = ccp2_init_formats, ++}; ++ ++/* subdev video operations */ ++static const struct v4l2_subdev_video_ops ccp2_sd_video_ops = { ++ .s_stream = ccp2_s_stream, ++}; ++ ++/* subdev pad operations */ ++static const struct v4l2_subdev_pad_ops ccp2_sd_pad_ops = { ++ .enum_mbus_code = ccp2_enum_mbus_code, ++ .enum_frame_size = ccp2_enum_frame_size, ++ .get_fmt = ccp2_get_format, ++ .set_fmt = ccp2_set_format, ++}; ++ ++/* subdev operations */ ++static const struct v4l2_subdev_ops ccp2_sd_ops = { ++ .core = &ccp2_sd_core_ops, ++ .file = &ccp2_sd_file_ops, ++ .video = &ccp2_sd_video_ops, ++ .pad = &ccp2_sd_pad_ops, ++}; ++ ++/* -------------------------------------------------------------------------- ++ * ISP ccp2 video device node ++ */ ++ ++/* ++ * ccp2_video_queue - Queue video buffer. ++ * @video : Pointer to isp video structure ++ * @buffer: Pointer to isp_buffer structure ++ * return -EIO or zero on success ++ */ ++static int ccp2_video_queue(struct isp_video *video, struct isp_buffer *buffer) ++{ ++ struct isp_ccp2_device *ccp2 = &video->isp->isp_ccp2; ++ ++ ccp2_set_inaddr(ccp2, buffer->isp_addr); ++ return 0; ++} ++ ++static const struct isp_video_operations ccp2_video_ops = { ++ .queue = ccp2_video_queue, ++}; ++ ++/* ----------------------------------------------------------------------------- ++ * Media entity operations ++ */ ++ ++/* ++ * ccp2_link_setup - Setup ccp2 connections. ++ * @entity : Pointer to media entity structure ++ * @local : Pointer to local pad array ++ * @remote : Pointer to remote pad array ++ * @flags : Link flags ++ * return -EINVAL on error or zero on success ++ */ ++static int ccp2_link_setup(struct media_entity *entity, ++ const struct media_pad *local, ++ const struct media_pad *remote, u32 flags) ++{ ++ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); ++ struct isp_ccp2_device *ccp2 = v4l2_get_subdevdata(sd); ++ ++ switch (local->index | media_entity_type(remote->entity)) { ++ case CCP2_PAD_SINK | MEDIA_ENT_T_DEVNODE: ++ /* read from memory */ ++ if (flags & MEDIA_LNK_FL_ENABLED) { ++ if (ccp2->input == CCP2_INPUT_SENSOR) ++ return -EBUSY; ++ ccp2->input = CCP2_INPUT_MEMORY; ++ } else { ++ if (ccp2->input == CCP2_INPUT_MEMORY) ++ ccp2->input = CCP2_INPUT_NONE; ++ } ++ break; ++ ++ case CCP2_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: ++ /* read from sensor/phy */ ++ if (flags & MEDIA_LNK_FL_ENABLED) { ++ if (ccp2->input == CCP2_INPUT_MEMORY) ++ return -EBUSY; ++ ccp2->input = CCP2_INPUT_SENSOR; ++ } else { ++ if (ccp2->input == CCP2_INPUT_SENSOR) ++ ccp2->input = CCP2_INPUT_NONE; ++ } break; ++ ++ case CCP2_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV: ++ /* write to video port/ccdc */ ++ if (flags & MEDIA_LNK_FL_ENABLED) ++ ccp2->output = CCP2_OUTPUT_CCDC; ++ else ++ ccp2->output = CCP2_OUTPUT_NONE; ++ break; ++ ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++/* media operations */ ++static const struct media_entity_operations ccp2_media_ops = { ++ .link_setup = ccp2_link_setup, ++}; ++ ++/* ++ * ccp2_init_entities - Initialize ccp2 subdev and media entity. ++ * @ccp2: Pointer to ISP CCP2 device ++ * return negative error code or zero on success ++ */ ++static int ccp2_init_entities(struct isp_ccp2_device *ccp2) ++{ ++ struct v4l2_subdev *sd = &ccp2->subdev; ++ struct media_pad *pads = ccp2->pads; ++ struct media_entity *me = &sd->entity; ++ int ret; ++ ++ ccp2->input = CCP2_INPUT_NONE; ++ ccp2->output = CCP2_OUTPUT_NONE; ++ ++ v4l2_subdev_init(sd, &ccp2_sd_ops); ++ strlcpy(sd->name, "OMAP3 ISP CCP2", sizeof(sd->name)); ++ sd->grp_id = 1 << 16; /* group ID for isp subdevs */ ++ v4l2_set_subdevdata(sd, ccp2); ++ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; ++ ++ v4l2_ctrl_handler_init(&ccp2->ctrls, 1); ++ sd->ctrl_handler = &ccp2->ctrls; ++ ++ pads[CCP2_PAD_SINK].flags = MEDIA_PAD_FL_INPUT; ++ pads[CCP2_PAD_SOURCE].flags = MEDIA_PAD_FL_OUTPUT; ++ ++ me->ops = &ccp2_media_ops; ++ ret = media_entity_init(me, CCP2_PADS_NUM, pads, 0); ++ if (ret < 0) ++ return ret; ++ ++ ccp2_init_formats(sd, NULL); ++ ++ /* ++ * The CCP2 has weird line alignment requirements, possibly caused by ++ * DPCM8 decompression. Line length for data read from memory must be a ++ * multiple of 128 bits (16 bytes) in continuous mode (when no padding ++ * is present at end of lines). Additionally, if padding is used, the ++ * padded line length must be a multiple of 32 bytes. To simplify the ++ * implementation we use a fixed 32 bytes alignment regardless of the ++ * input format and width. If strict 128 bits alignment support is ++ * required ispvideo will need to be made aware of this special dual ++ * alignement requirements. ++ */ ++ ccp2->video_in.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; ++ ccp2->video_in.bpl_alignment = 32; ++ ccp2->video_in.bpl_max = 0xffffffe0; ++ ccp2->video_in.isp = to_isp_device(ccp2); ++ ccp2->video_in.ops = &ccp2_video_ops; ++ ccp2->video_in.capture_mem = PAGE_ALIGN(4096 * 4096) * 3; ++ ++ ret = omap3isp_video_init(&ccp2->video_in, "CCP2"); ++ if (ret < 0) ++ return ret; ++ ++ /* Connect the video node to the ccp2 subdev. */ ++ ret = media_entity_create_link(&ccp2->video_in.video.entity, 0, ++ &ccp2->subdev.entity, CCP2_PAD_SINK, 0); ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++/* ++ * omap3isp_ccp2_unregister_entities - Unregister media entities: subdev ++ * @ccp2: Pointer to ISP CCP2 device ++ */ ++void omap3isp_ccp2_unregister_entities(struct isp_ccp2_device *ccp2) ++{ ++ media_entity_cleanup(&ccp2->subdev.entity); ++ ++ v4l2_device_unregister_subdev(&ccp2->subdev); ++ v4l2_ctrl_handler_free(&ccp2->ctrls); ++ omap3isp_video_unregister(&ccp2->video_in); ++} ++ ++/* ++ * omap3isp_ccp2_register_entities - Register the subdev media entity ++ * @ccp2: Pointer to ISP CCP2 device ++ * @vdev: Pointer to v4l device ++ * return negative error code or zero on success ++ */ ++ ++int omap3isp_ccp2_register_entities(struct isp_ccp2_device *ccp2, ++ struct v4l2_device *vdev) ++{ ++ int ret; ++ ++ /* Register the subdev and video nodes. */ ++ ret = v4l2_device_register_subdev(vdev, &ccp2->subdev); ++ if (ret < 0) ++ goto error; ++ ++ ret = omap3isp_video_register(&ccp2->video_in, vdev); ++ if (ret < 0) ++ goto error; ++ ++ return 0; ++ ++error: ++ omap3isp_ccp2_unregister_entities(ccp2); ++ return ret; ++} ++ ++/* ----------------------------------------------------------------------------- ++ * ISP ccp2 initialisation and cleanup ++ */ ++ ++/* ++ * omap3isp_ccp2_cleanup - CCP2 un-initialization ++ * @isp : Pointer to ISP device ++ */ ++void omap3isp_ccp2_cleanup(struct isp_device *isp) ++{ ++} ++ ++/* ++ * omap3isp_ccp2_init - CCP2 initialization. ++ * @isp : Pointer to ISP device ++ * return negative error code or zero on success ++ */ ++int omap3isp_ccp2_init(struct isp_device *isp) ++{ ++ struct isp_ccp2_device *ccp2 = &isp->isp_ccp2; ++ int ret; ++ ++ init_waitqueue_head(&ccp2->wait); ++ ++ /* On the OMAP36xx, the CCP2 uses the CSI PHY1 or PHY2, shared with ++ * the CSI2c or CSI2a receivers. The PHY then needs to be explicitly ++ * configured. ++ * ++ * TODO: Don't hardcode the usage of PHY1 (shared with CSI2c). ++ */ ++ if (isp->revision == ISP_REVISION_15_0) ++ ccp2->phy = &isp->isp_csiphy1; ++ ++ ret = ccp2_init_entities(ccp2); ++ if (ret < 0) ++ goto out; ++ ++ ccp2_reset(ccp2); ++out: ++ if (ret) ++ omap3isp_ccp2_cleanup(isp); ++ ++ return ret; ++} +diff --git a/drivers/media/video/isp/ispccp2.h b/drivers/media/video/isp/ispccp2.h +new file mode 100644 +index 0000000..1c1504e +--- /dev/null ++++ b/drivers/media/video/isp/ispccp2.h +@@ -0,0 +1,101 @@ ++/* ++ * ispccp2.h ++ * ++ * TI OMAP3 ISP - CCP2 module ++ * ++ * Copyright (C) 2010 Nokia Corporation ++ * Copyright (C) 2010 Texas Instruments, Inc. ++ * ++ * Contacts: Laurent Pinchart ++ * Sakari Ailus ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#ifndef OMAP3_ISP_CCP2_H ++#define OMAP3_ISP_CCP2_H ++ ++#include ++#include ++ ++struct isp_device; ++struct isp_csiphy; ++ ++/* Sink and source ccp2 pads */ ++#define CCP2_PAD_SINK 0 ++#define CCP2_PAD_SOURCE 1 ++#define CCP2_PADS_NUM 2 ++ ++/* CCP2 input media entity */ ++enum ccp2_input_entity { ++ CCP2_INPUT_NONE, ++ CCP2_INPUT_SENSOR, ++ CCP2_INPUT_MEMORY, ++}; ++ ++/* CCP2 output media entity */ ++enum ccp2_output_entity { ++ CCP2_OUTPUT_NONE, ++ CCP2_OUTPUT_CCDC, ++ CCP2_OUTPUT_MEMORY, ++}; ++ ++ ++/* Logical channel configuration */ ++struct isp_interface_lcx_config { ++ int crc; ++ u32 data_start; ++ u32 data_size; ++ u32 format; ++}; ++ ++/* Memory channel configuration */ ++struct isp_interface_mem_config { ++ u32 dst_port; ++ u32 vsize_count; ++ u32 hsize_count; ++ u32 src_ofst; ++ u32 dst_ofst; ++}; ++ ++/* CCP2 device */ ++struct isp_ccp2_device { ++ struct v4l2_subdev subdev; ++ struct v4l2_mbus_framefmt formats[CCP2_PADS_NUM]; ++ struct media_pad pads[CCP2_PADS_NUM]; ++ ++ struct v4l2_ctrl_handler ctrls; ++ ++ enum ccp2_input_entity input; ++ enum ccp2_output_entity output; ++ struct isp_interface_lcx_config if_cfg; ++ struct isp_interface_mem_config mem_cfg; ++ struct isp_video video_in; ++ struct isp_csiphy *phy; ++ unsigned int error; ++ enum isp_pipeline_stream_state state; ++ wait_queue_head_t wait; ++ atomic_t stopping; ++}; ++ ++/* Function declarations */ ++int omap3isp_ccp2_init(struct isp_device *isp); ++void omap3isp_ccp2_cleanup(struct isp_device *isp); ++int omap3isp_ccp2_register_entities(struct isp_ccp2_device *ccp2, ++ struct v4l2_device *vdev); ++void omap3isp_ccp2_unregister_entities(struct isp_ccp2_device *ccp2); ++int omap3isp_ccp2_isr(struct isp_ccp2_device *ccp2); ++ ++#endif /* OMAP3_ISP_CCP2_H */ +diff --git a/drivers/media/video/isp/ispcsi2.c b/drivers/media/video/isp/ispcsi2.c +new file mode 100644 +index 0000000..30ced95 +--- /dev/null ++++ b/drivers/media/video/isp/ispcsi2.c +@@ -0,0 +1,1332 @@ ++/* ++ * ispcsi2.c ++ * ++ * TI OMAP3 ISP - CSI2 module ++ * ++ * Copyright (C) 2010 Nokia Corporation ++ * Copyright (C) 2009 Texas Instruments, Inc. ++ * ++ * Contacts: Laurent Pinchart ++ * Sakari Ailus ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++#include ++#include ++#include ++#include ++ ++#include "isp.h" ++#include "ispreg.h" ++#include "ispcsi2.h" ++ ++/* ++ * csi2_if_enable - Enable CSI2 Receiver interface. ++ * @enable: enable flag ++ * ++ */ ++static void csi2_if_enable(struct isp_device *isp, ++ struct isp_csi2_device *csi2, u8 enable) ++{ ++ struct isp_csi2_ctrl_cfg *currctrl = &csi2->ctrl; ++ ++ isp_reg_clr_set(isp, csi2->regs1, ISPCSI2_CTRL, ISPCSI2_CTRL_IF_EN, ++ enable ? ISPCSI2_CTRL_IF_EN : 0); ++ ++ currctrl->if_enable = enable; ++} ++ ++/* ++ * csi2_recv_config - CSI2 receiver module configuration. ++ * @currctrl: isp_csi2_ctrl_cfg structure ++ * ++ */ ++static void csi2_recv_config(struct isp_device *isp, ++ struct isp_csi2_device *csi2, ++ struct isp_csi2_ctrl_cfg *currctrl) ++{ ++ u32 reg; ++ ++ reg = isp_reg_readl(isp, csi2->regs1, ISPCSI2_CTRL); ++ ++ if (currctrl->frame_mode) ++ reg |= ISPCSI2_CTRL_FRAME; ++ else ++ reg &= ~ISPCSI2_CTRL_FRAME; ++ ++ if (currctrl->vp_clk_enable) ++ reg |= ISPCSI2_CTRL_VP_CLK_EN; ++ else ++ reg &= ~ISPCSI2_CTRL_VP_CLK_EN; ++ ++ if (currctrl->vp_only_enable) ++ reg |= ISPCSI2_CTRL_VP_ONLY_EN; ++ else ++ reg &= ~ISPCSI2_CTRL_VP_ONLY_EN; ++ ++ reg &= ~ISPCSI2_CTRL_VP_OUT_CTRL_MASK; ++ reg |= currctrl->vp_out_ctrl << ISPCSI2_CTRL_VP_OUT_CTRL_SHIFT; ++ ++ if (currctrl->ecc_enable) ++ reg |= ISPCSI2_CTRL_ECC_EN; ++ else ++ reg &= ~ISPCSI2_CTRL_ECC_EN; ++ ++ isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_CTRL); ++} ++ ++static const unsigned int csi2_input_fmts[] = { ++ V4L2_MBUS_FMT_SGRBG10_1X10, ++ V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, ++ V4L2_MBUS_FMT_SRGGB10_1X10, ++ V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8, ++ V4L2_MBUS_FMT_SBGGR10_1X10, ++ V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8, ++ V4L2_MBUS_FMT_SGBRG10_1X10, ++ V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8, ++}; ++ ++/* To set the format on the CSI2 requires a mapping function that takes ++ * the following inputs: ++ * - 2 different formats (at this time) ++ * - 2 destinations (mem, vp+mem) (vp only handled separately) ++ * - 2 decompression options (on, off) ++ * - 2 isp revisions (certain format must be handled differently on OMAP3630) ++ * Output should be CSI2 frame format code ++ * Array indices as follows: [format][dest][decompr][is_3630] ++ * Not all combinations are valid. 0 means invalid. ++ */ ++static const u16 __csi2_fmt_map[2][2][2][2] = { ++ /* RAW10 formats */ ++ { ++ /* Output to memory */ ++ { ++ /* No DPCM decompression */ ++ { CSI2_PIX_FMT_RAW10_EXP16, CSI2_PIX_FMT_RAW10_EXP16 }, ++ /* DPCM decompression */ ++ { 0, 0 }, ++ }, ++ /* Output to both */ ++ { ++ /* No DPCM decompression */ ++ { CSI2_PIX_FMT_RAW10_EXP16_VP, ++ CSI2_PIX_FMT_RAW10_EXP16_VP }, ++ /* DPCM decompression */ ++ { 0, 0 }, ++ }, ++ }, ++ /* RAW10 DPCM8 formats */ ++ { ++ /* Output to memory */ ++ { ++ /* No DPCM decompression */ ++ { CSI2_PIX_FMT_RAW8, CSI2_USERDEF_8BIT_DATA1 }, ++ /* DPCM decompression */ ++ { CSI2_PIX_FMT_RAW8_DPCM10_EXP16, ++ CSI2_USERDEF_8BIT_DATA1_DPCM10 }, ++ }, ++ /* Output to both */ ++ { ++ /* No DPCM decompression */ ++ { CSI2_PIX_FMT_RAW8_VP, ++ CSI2_PIX_FMT_RAW8_VP }, ++ /* DPCM decompression */ ++ { CSI2_PIX_FMT_RAW8_DPCM10_VP, ++ CSI2_USERDEF_8BIT_DATA1_DPCM10_VP }, ++ }, ++ }, ++}; ++ ++/* ++ * csi2_ctx_map_format - Map CSI2 sink media bus format to CSI2 format ID ++ * @csi2: ISP CSI2 device ++ * ++ * Returns CSI2 physical format id ++ */ ++static u16 csi2_ctx_map_format(struct isp_csi2_device *csi2) ++{ ++ const struct v4l2_mbus_framefmt *fmt = &csi2->formats[CSI2_PAD_SINK]; ++ int fmtidx, destidx, is_3630; ++ ++ switch (fmt->code) { ++ case V4L2_MBUS_FMT_SGRBG10_1X10: ++ case V4L2_MBUS_FMT_SRGGB10_1X10: ++ case V4L2_MBUS_FMT_SBGGR10_1X10: ++ case V4L2_MBUS_FMT_SGBRG10_1X10: ++ fmtidx = 0; ++ break; ++ case V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8: ++ case V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8: ++ case V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8: ++ case V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8: ++ fmtidx = 1; ++ break; ++ default: ++ WARN(1, KERN_ERR "CSI2: pixel format %08x unsupported!\n", ++ fmt->code); ++ return 0; ++ } ++ ++ if (!(csi2->output & CSI2_OUTPUT_CCDC) && ++ !(csi2->output & CSI2_OUTPUT_MEMORY)) { ++ /* Neither output enabled is a valid combination */ ++ return CSI2_PIX_FMT_OTHERS; ++ } ++ ++ /* If we need to skip frames at the beginning of the stream disable the ++ * video port to avoid sending the skipped frames to the CCDC. ++ */ ++ destidx = csi2->frame_skip ? 0 : !!(csi2->output & CSI2_OUTPUT_CCDC); ++ is_3630 = csi2->isp->revision == ISP_REVISION_15_0; ++ ++ return __csi2_fmt_map[fmtidx][destidx][csi2->dpcm_decompress][is_3630]; ++} ++ ++/* ++ * csi2_set_outaddr - Set memory address to save output image ++ * @csi2: Pointer to ISP CSI2a device. ++ * @addr: ISP MMU Mapped 32-bit memory address aligned on 32 byte boundary. ++ * ++ * Sets the memory address where the output will be saved. ++ * ++ * Returns 0 if successful, or -EINVAL if the address is not in the 32 byte ++ * boundary. ++ */ ++static void csi2_set_outaddr(struct isp_csi2_device *csi2, u32 addr) ++{ ++ struct isp_device *isp = csi2->isp; ++ struct isp_csi2_ctx_cfg *ctx = &csi2->contexts[0]; ++ ++ ctx->ping_addr = ctx->pong_addr = addr; ++ isp_reg_writel(isp, ctx->ping_addr, ++ csi2->regs1, ISPCSI2_CTX_DAT_PING_ADDR(ctx->ctxnum)); ++ isp_reg_writel(isp, ctx->pong_addr, ++ csi2->regs1, ISPCSI2_CTX_DAT_PONG_ADDR(ctx->ctxnum)); ++} ++ ++/* ++ * is_usr_def_mapping - Checks whether USER_DEF_MAPPING should ++ * be enabled by CSI2. ++ * @format_id: mapped format id ++ * ++ */ ++static inline int is_usr_def_mapping(u32 format_id) ++{ ++ return (format_id & 0x40) ? 1 : 0; ++} ++ ++/* ++ * csi2_ctx_enable - Enable specified CSI2 context ++ * @ctxnum: Context number, valid between 0 and 7 values. ++ * @enable: enable ++ * ++ */ ++static void csi2_ctx_enable(struct isp_device *isp, ++ struct isp_csi2_device *csi2, u8 ctxnum, u8 enable) ++{ ++ struct isp_csi2_ctx_cfg *ctx = &csi2->contexts[ctxnum]; ++ unsigned int skip = 0; ++ u32 reg; ++ ++ reg = isp_reg_readl(isp, csi2->regs1, ISPCSI2_CTX_CTRL1(ctxnum)); ++ ++ if (enable) { ++ if (csi2->frame_skip) ++ skip = csi2->frame_skip; ++ else if (csi2->output & CSI2_OUTPUT_MEMORY) ++ skip = 1; ++ ++ reg &= ~ISPCSI2_CTX_CTRL1_COUNT_MASK; ++ reg |= ISPCSI2_CTX_CTRL1_COUNT_UNLOCK ++ | (skip << ISPCSI2_CTX_CTRL1_COUNT_SHIFT) ++ | ISPCSI2_CTX_CTRL1_CTX_EN; ++ } else { ++ reg &= ~ISPCSI2_CTX_CTRL1_CTX_EN; ++ } ++ ++ isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_CTX_CTRL1(ctxnum)); ++ ctx->enabled = enable; ++} ++ ++/* ++ * csi2_ctx_config - CSI2 context configuration. ++ * @ctx: context configuration ++ * ++ */ ++static void csi2_ctx_config(struct isp_device *isp, ++ struct isp_csi2_device *csi2, ++ struct isp_csi2_ctx_cfg *ctx) ++{ ++ u32 reg; ++ ++ /* Set up CSI2_CTx_CTRL1 */ ++ reg = isp_reg_readl(isp, csi2->regs1, ISPCSI2_CTX_CTRL1(ctx->ctxnum)); ++ ++ if (ctx->eof_enabled) ++ reg |= ISPCSI2_CTX_CTRL1_EOF_EN; ++ else ++ reg &= ~ISPCSI2_CTX_CTRL1_EOF_EN; ++ ++ if (ctx->eol_enabled) ++ reg |= ISPCSI2_CTX_CTRL1_EOL_EN; ++ else ++ reg &= ~ISPCSI2_CTX_CTRL1_EOL_EN; ++ ++ if (ctx->checksum_enabled) ++ reg |= ISPCSI2_CTX_CTRL1_CS_EN; ++ else ++ reg &= ~ISPCSI2_CTX_CTRL1_CS_EN; ++ ++ isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_CTX_CTRL1(ctx->ctxnum)); ++ ++ /* Set up CSI2_CTx_CTRL2 */ ++ reg = isp_reg_readl(isp, csi2->regs1, ISPCSI2_CTX_CTRL2(ctx->ctxnum)); ++ ++ reg &= ~(ISPCSI2_CTX_CTRL2_VIRTUAL_ID_MASK); ++ reg |= ctx->virtual_id << ISPCSI2_CTX_CTRL2_VIRTUAL_ID_SHIFT; ++ ++ reg &= ~(ISPCSI2_CTX_CTRL2_FORMAT_MASK); ++ reg |= ctx->format_id << ISPCSI2_CTX_CTRL2_FORMAT_SHIFT; ++ ++ if (ctx->dpcm_decompress) { ++ if (ctx->dpcm_predictor) ++ reg |= ISPCSI2_CTX_CTRL2_DPCM_PRED; ++ else ++ reg &= ~ISPCSI2_CTX_CTRL2_DPCM_PRED; ++ } ++ ++ if (is_usr_def_mapping(ctx->format_id)) { ++ reg &= ~ISPCSI2_CTX_CTRL2_USER_DEF_MAP_MASK; ++ reg |= 2 << ISPCSI2_CTX_CTRL2_USER_DEF_MAP_SHIFT; ++ } ++ ++ isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_CTX_CTRL2(ctx->ctxnum)); ++ ++ /* Set up CSI2_CTx_CTRL3 */ ++ reg = isp_reg_readl(isp, csi2->regs1, ISPCSI2_CTX_CTRL3(ctx->ctxnum)); ++ reg &= ~(ISPCSI2_CTX_CTRL3_ALPHA_MASK); ++ reg |= (ctx->alpha << ISPCSI2_CTX_CTRL3_ALPHA_SHIFT); ++ ++ isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_CTX_CTRL3(ctx->ctxnum)); ++ ++ /* Set up CSI2_CTx_DAT_OFST */ ++ reg = isp_reg_readl(isp, csi2->regs1, ++ ISPCSI2_CTX_DAT_OFST(ctx->ctxnum)); ++ reg &= ~ISPCSI2_CTX_DAT_OFST_OFST_MASK; ++ reg |= ctx->data_offset << ISPCSI2_CTX_DAT_OFST_OFST_SHIFT; ++ isp_reg_writel(isp, reg, csi2->regs1, ++ ISPCSI2_CTX_DAT_OFST(ctx->ctxnum)); ++ ++ isp_reg_writel(isp, ctx->ping_addr, ++ csi2->regs1, ISPCSI2_CTX_DAT_PING_ADDR(ctx->ctxnum)); ++ ++ isp_reg_writel(isp, ctx->pong_addr, ++ csi2->regs1, ISPCSI2_CTX_DAT_PONG_ADDR(ctx->ctxnum)); ++} ++ ++/* ++ * csi2_timing_config - CSI2 timing configuration. ++ * @timing: csi2_timing_cfg structure ++ */ ++static void csi2_timing_config(struct isp_device *isp, ++ struct isp_csi2_device *csi2, ++ struct isp_csi2_timing_cfg *timing) ++{ ++ u32 reg; ++ ++ reg = isp_reg_readl(isp, csi2->regs1, ISPCSI2_TIMING); ++ ++ if (timing->force_rx_mode) ++ reg |= ISPCSI2_TIMING_FORCE_RX_MODE_IO(timing->ionum); ++ else ++ reg &= ~ISPCSI2_TIMING_FORCE_RX_MODE_IO(timing->ionum); ++ ++ if (timing->stop_state_16x) ++ reg |= ISPCSI2_TIMING_STOP_STATE_X16_IO(timing->ionum); ++ else ++ reg &= ~ISPCSI2_TIMING_STOP_STATE_X16_IO(timing->ionum); ++ ++ if (timing->stop_state_4x) ++ reg |= ISPCSI2_TIMING_STOP_STATE_X4_IO(timing->ionum); ++ else ++ reg &= ~ISPCSI2_TIMING_STOP_STATE_X4_IO(timing->ionum); ++ ++ reg &= ~ISPCSI2_TIMING_STOP_STATE_COUNTER_IO_MASK(timing->ionum); ++ reg |= timing->stop_state_counter << ++ ISPCSI2_TIMING_STOP_STATE_COUNTER_IO_SHIFT(timing->ionum); ++ ++ isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_TIMING); ++} ++ ++/* ++ * csi2_irq_ctx_set - Enables CSI2 Context IRQs. ++ * @enable: Enable/disable CSI2 Context interrupts ++ */ ++static void csi2_irq_ctx_set(struct isp_device *isp, ++ struct isp_csi2_device *csi2, int enable) ++{ ++ u32 reg = ISPCSI2_CTX_IRQSTATUS_FE_IRQ; ++ int i; ++ ++ if (csi2->use_fs_irq) ++ reg |= ISPCSI2_CTX_IRQSTATUS_FS_IRQ; ++ ++ for (i = 0; i < 8; i++) { ++ isp_reg_writel(isp, reg, csi2->regs1, ++ ISPCSI2_CTX_IRQSTATUS(i)); ++ if (enable) ++ isp_reg_set(isp, csi2->regs1, ISPCSI2_CTX_IRQENABLE(i), ++ reg); ++ else ++ isp_reg_clr(isp, csi2->regs1, ISPCSI2_CTX_IRQENABLE(i), ++ reg); ++ } ++} ++ ++/* ++ * csi2_irq_complexio1_set - Enables CSI2 ComplexIO IRQs. ++ * @enable: Enable/disable CSI2 ComplexIO #1 interrupts ++ */ ++static void csi2_irq_complexio1_set(struct isp_device *isp, ++ struct isp_csi2_device *csi2, int enable) ++{ ++ u32 reg; ++ reg = ISPCSI2_PHY_IRQENABLE_STATEALLULPMEXIT | ++ ISPCSI2_PHY_IRQENABLE_STATEALLULPMENTER | ++ ISPCSI2_PHY_IRQENABLE_STATEULPM5 | ++ ISPCSI2_PHY_IRQENABLE_ERRCONTROL5 | ++ ISPCSI2_PHY_IRQENABLE_ERRESC5 | ++ ISPCSI2_PHY_IRQENABLE_ERRSOTSYNCHS5 | ++ ISPCSI2_PHY_IRQENABLE_ERRSOTHS5 | ++ ISPCSI2_PHY_IRQENABLE_STATEULPM4 | ++ ISPCSI2_PHY_IRQENABLE_ERRCONTROL4 | ++ ISPCSI2_PHY_IRQENABLE_ERRESC4 | ++ ISPCSI2_PHY_IRQENABLE_ERRSOTSYNCHS4 | ++ ISPCSI2_PHY_IRQENABLE_ERRSOTHS4 | ++ ISPCSI2_PHY_IRQENABLE_STATEULPM3 | ++ ISPCSI2_PHY_IRQENABLE_ERRCONTROL3 | ++ ISPCSI2_PHY_IRQENABLE_ERRESC3 | ++ ISPCSI2_PHY_IRQENABLE_ERRSOTSYNCHS3 | ++ ISPCSI2_PHY_IRQENABLE_ERRSOTHS3 | ++ ISPCSI2_PHY_IRQENABLE_STATEULPM2 | ++ ISPCSI2_PHY_IRQENABLE_ERRCONTROL2 | ++ ISPCSI2_PHY_IRQENABLE_ERRESC2 | ++ ISPCSI2_PHY_IRQENABLE_ERRSOTSYNCHS2 | ++ ISPCSI2_PHY_IRQENABLE_ERRSOTHS2 | ++ ISPCSI2_PHY_IRQENABLE_STATEULPM1 | ++ ISPCSI2_PHY_IRQENABLE_ERRCONTROL1 | ++ ISPCSI2_PHY_IRQENABLE_ERRESC1 | ++ ISPCSI2_PHY_IRQENABLE_ERRSOTSYNCHS1 | ++ ISPCSI2_PHY_IRQENABLE_ERRSOTHS1; ++ isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_PHY_IRQSTATUS); ++ if (enable) ++ reg |= isp_reg_readl(isp, csi2->regs1, ISPCSI2_PHY_IRQENABLE); ++ else ++ reg = 0; ++ isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_PHY_IRQENABLE); ++} ++ ++/* ++ * csi2_irq_status_set - Enables CSI2 Status IRQs. ++ * @enable: Enable/disable CSI2 Status interrupts ++ */ ++static void csi2_irq_status_set(struct isp_device *isp, ++ struct isp_csi2_device *csi2, int enable) ++{ ++ u32 reg; ++ reg = ISPCSI2_IRQSTATUS_OCP_ERR_IRQ | ++ ISPCSI2_IRQSTATUS_SHORT_PACKET_IRQ | ++ ISPCSI2_IRQSTATUS_ECC_CORRECTION_IRQ | ++ ISPCSI2_IRQSTATUS_ECC_NO_CORRECTION_IRQ | ++ ISPCSI2_IRQSTATUS_COMPLEXIO2_ERR_IRQ | ++ ISPCSI2_IRQSTATUS_COMPLEXIO1_ERR_IRQ | ++ ISPCSI2_IRQSTATUS_FIFO_OVF_IRQ | ++ ISPCSI2_IRQSTATUS_CONTEXT(0); ++ isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_IRQSTATUS); ++ if (enable) ++ reg |= isp_reg_readl(isp, csi2->regs1, ISPCSI2_IRQENABLE); ++ else ++ reg = 0; ++ ++ isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_IRQENABLE); ++} ++ ++/* ++ * omap3isp_csi2_reset - Resets the CSI2 module. ++ * ++ * Must be called with the phy lock held. ++ * ++ * Returns 0 if successful, or -EBUSY if power command didn't respond. ++ */ ++int omap3isp_csi2_reset(struct isp_csi2_device *csi2) ++{ ++ struct isp_device *isp = csi2->isp; ++ u8 soft_reset_retries = 0; ++ u32 reg; ++ int i; ++ ++ if (!csi2->available) ++ return -ENODEV; ++ ++ if (csi2->phy->phy_in_use) ++ return -EBUSY; ++ ++ isp_reg_set(isp, csi2->regs1, ISPCSI2_SYSCONFIG, ++ ISPCSI2_SYSCONFIG_SOFT_RESET); ++ ++ do { ++ reg = isp_reg_readl(isp, csi2->regs1, ISPCSI2_SYSSTATUS) & ++ ISPCSI2_SYSSTATUS_RESET_DONE; ++ if (reg == ISPCSI2_SYSSTATUS_RESET_DONE) ++ break; ++ soft_reset_retries++; ++ if (soft_reset_retries < 5) ++ udelay(100); ++ } while (soft_reset_retries < 5); ++ ++ if (soft_reset_retries == 5) { ++ printk(KERN_ERR "CSI2: Soft reset try count exceeded!\n"); ++ return -EBUSY; ++ } ++ ++ if (isp->revision == ISP_REVISION_15_0) ++ isp_reg_set(isp, csi2->regs1, ISPCSI2_PHY_CFG, ++ ISPCSI2_PHY_CFG_RESET_CTRL); ++ ++ i = 100; ++ do { ++ reg = isp_reg_readl(isp, csi2->phy->phy_regs, ISPCSIPHY_REG1) ++ & ISPCSIPHY_REG1_RESET_DONE_CTRLCLK; ++ if (reg == ISPCSIPHY_REG1_RESET_DONE_CTRLCLK) ++ break; ++ udelay(100); ++ } while (--i > 0); ++ ++ if (i == 0) { ++ printk(KERN_ERR ++ "CSI2: Reset for CSI2_96M_FCLK domain Failed!\n"); ++ return -EBUSY; ++ } ++ ++ if (isp->autoidle) ++ isp_reg_clr_set(isp, csi2->regs1, ISPCSI2_SYSCONFIG, ++ ISPCSI2_SYSCONFIG_MSTANDBY_MODE_MASK | ++ ISPCSI2_SYSCONFIG_AUTO_IDLE, ++ ISPCSI2_SYSCONFIG_MSTANDBY_MODE_SMART | ++ ((isp->revision == ISP_REVISION_15_0) ? ++ ISPCSI2_SYSCONFIG_AUTO_IDLE : 0)); ++ else ++ isp_reg_clr_set(isp, csi2->regs1, ISPCSI2_SYSCONFIG, ++ ISPCSI2_SYSCONFIG_MSTANDBY_MODE_MASK | ++ ISPCSI2_SYSCONFIG_AUTO_IDLE, ++ ISPCSI2_SYSCONFIG_MSTANDBY_MODE_NO); ++ ++ return 0; ++} ++ ++static int csi2_configure(struct isp_csi2_device *csi2) ++{ ++ const struct isp_v4l2_subdevs_group *pdata; ++ struct isp_device *isp = csi2->isp; ++ struct isp_csi2_timing_cfg *timing = &csi2->timing[0]; ++ struct v4l2_subdev *sensor; ++ struct media_pad *pad; ++ ++ /* ++ * CSI2 fields that can be updated while the context has ++ * been enabled or the interface has been enabled are not ++ * updated dynamically currently. So we do not allow to ++ * reconfigure if either has been enabled ++ */ ++ if (csi2->contexts[0].enabled || csi2->ctrl.if_enable) ++ return -EBUSY; ++ ++ pad = media_entity_remote_source(&csi2->pads[CSI2_PAD_SINK]); ++ sensor = media_entity_to_v4l2_subdev(pad->entity); ++ pdata = sensor->host_priv; ++ ++ csi2->frame_skip = 0; ++ v4l2_subdev_call(sensor, sensor, g_skip_frames, &csi2->frame_skip); ++ ++ csi2->ctrl.vp_out_ctrl = pdata->bus.csi2.vpclk_div; ++ csi2->ctrl.frame_mode = ISP_CSI2_FRAME_IMMEDIATE; ++ csi2->ctrl.ecc_enable = pdata->bus.csi2.crc; ++ ++ timing->ionum = 1; ++ timing->force_rx_mode = 1; ++ timing->stop_state_16x = 1; ++ timing->stop_state_4x = 1; ++ timing->stop_state_counter = 0x1FF; ++ ++ /* ++ * The CSI2 receiver can't do any format conversion except DPCM ++ * decompression, so every set_format call configures both pads ++ * and enables DPCM decompression as a special case: ++ */ ++ if (csi2->formats[CSI2_PAD_SINK].code != ++ csi2->formats[CSI2_PAD_SOURCE].code) ++ csi2->dpcm_decompress = true; ++ else ++ csi2->dpcm_decompress = false; ++ ++ csi2->contexts[0].format_id = csi2_ctx_map_format(csi2); ++ ++ if (csi2->video_out.bpl_padding == 0) ++ csi2->contexts[0].data_offset = 0; ++ else ++ csi2->contexts[0].data_offset = csi2->video_out.bpl_value; ++ ++ /* ++ * Enable end of frame and end of line signals generation for ++ * context 0. These signals are generated from CSI2 receiver to ++ * qualify the last pixel of a frame and the last pixel of a line. ++ * Without enabling the signals CSI2 receiver writes data to memory ++ * beyond buffer size and/or data line offset is not handled correctly. ++ */ ++ csi2->contexts[0].eof_enabled = 1; ++ csi2->contexts[0].eol_enabled = 1; ++ ++ csi2_irq_complexio1_set(isp, csi2, 1); ++ csi2_irq_ctx_set(isp, csi2, 1); ++ csi2_irq_status_set(isp, csi2, 1); ++ ++ /* Set configuration (timings, format and links) */ ++ csi2_timing_config(isp, csi2, timing); ++ csi2_recv_config(isp, csi2, &csi2->ctrl); ++ csi2_ctx_config(isp, csi2, &csi2->contexts[0]); ++ ++ return 0; ++} ++ ++/* ++ * csi2_print_status - Prints CSI2 debug information. ++ */ ++#define CSI2_PRINT_REGISTER(isp, regs, name)\ ++ dev_dbg(isp->dev, "###CSI2 " #name "=0x%08x\n", \ ++ isp_reg_readl(isp, regs, ISPCSI2_##name)) ++ ++static void csi2_print_status(struct isp_csi2_device *csi2) ++{ ++ struct isp_device *isp = csi2->isp; ++ ++ if (!csi2->available) ++ return; ++ ++ dev_dbg(isp->dev, "-------------CSI2 Register dump-------------\n"); ++ ++ CSI2_PRINT_REGISTER(isp, csi2->regs1, SYSCONFIG); ++ CSI2_PRINT_REGISTER(isp, csi2->regs1, SYSSTATUS); ++ CSI2_PRINT_REGISTER(isp, csi2->regs1, IRQENABLE); ++ CSI2_PRINT_REGISTER(isp, csi2->regs1, IRQSTATUS); ++ CSI2_PRINT_REGISTER(isp, csi2->regs1, CTRL); ++ CSI2_PRINT_REGISTER(isp, csi2->regs1, DBG_H); ++ CSI2_PRINT_REGISTER(isp, csi2->regs1, GNQ); ++ CSI2_PRINT_REGISTER(isp, csi2->regs1, PHY_CFG); ++ CSI2_PRINT_REGISTER(isp, csi2->regs1, PHY_IRQSTATUS); ++ CSI2_PRINT_REGISTER(isp, csi2->regs1, SHORT_PACKET); ++ CSI2_PRINT_REGISTER(isp, csi2->regs1, PHY_IRQENABLE); ++ CSI2_PRINT_REGISTER(isp, csi2->regs1, DBG_P); ++ CSI2_PRINT_REGISTER(isp, csi2->regs1, TIMING); ++ CSI2_PRINT_REGISTER(isp, csi2->regs1, CTX_CTRL1(0)); ++ CSI2_PRINT_REGISTER(isp, csi2->regs1, CTX_CTRL2(0)); ++ CSI2_PRINT_REGISTER(isp, csi2->regs1, CTX_DAT_OFST(0)); ++ CSI2_PRINT_REGISTER(isp, csi2->regs1, CTX_DAT_PING_ADDR(0)); ++ CSI2_PRINT_REGISTER(isp, csi2->regs1, CTX_DAT_PONG_ADDR(0)); ++ CSI2_PRINT_REGISTER(isp, csi2->regs1, CTX_IRQENABLE(0)); ++ CSI2_PRINT_REGISTER(isp, csi2->regs1, CTX_IRQSTATUS(0)); ++ CSI2_PRINT_REGISTER(isp, csi2->regs1, CTX_CTRL3(0)); ++ ++ dev_dbg(isp->dev, "--------------------------------------------\n"); ++} ++ ++/* ----------------------------------------------------------------------------- ++ * Interrupt handling ++ */ ++ ++/* ++ * csi2_isr_buffer - Does buffer handling at end-of-frame ++ * when writing to memory. ++ */ ++static void csi2_isr_buffer(struct isp_csi2_device *csi2) ++{ ++ struct isp_device *isp = csi2->isp; ++ struct isp_buffer *buffer; ++ ++ csi2_ctx_enable(isp, csi2, 0, 0); ++ ++ buffer = omap3isp_video_buffer_next(&csi2->video_out, 0); ++ ++ /* ++ * Let video queue operation restart engine if there is an underrun ++ * condition. ++ */ ++ if (buffer == NULL) ++ return; ++ ++ csi2_set_outaddr(csi2, buffer->isp_addr); ++ csi2_ctx_enable(isp, csi2, 0, 1); ++} ++ ++static void csi2_isr_ctx(struct isp_csi2_device *csi2, ++ struct isp_csi2_ctx_cfg *ctx) ++{ ++ struct isp_device *isp = csi2->isp; ++ unsigned int n = ctx->ctxnum; ++ u32 status; ++ ++ status = isp_reg_readl(isp, csi2->regs1, ISPCSI2_CTX_IRQSTATUS(n)); ++ isp_reg_writel(isp, status, csi2->regs1, ISPCSI2_CTX_IRQSTATUS(n)); ++ ++ /* Propagate frame number */ ++ if (status & ISPCSI2_CTX_IRQSTATUS_FS_IRQ) { ++ struct isp_pipeline *pipe = ++ to_isp_pipeline(&csi2->subdev.entity); ++ if (pipe->do_propagation) ++ atomic_inc(&pipe->frame_number); ++ } ++ ++ if (!(status & ISPCSI2_CTX_IRQSTATUS_FE_IRQ)) ++ return; ++ ++ /* Skip interrupts until we reach the frame skip count. The CSI2 will be ++ * automatically disabled, as the frame skip count has been programmed ++ * in the CSI2_CTx_CTRL1::COUNT field, so reenable it. ++ * ++ * It would have been nice to rely on the FRAME_NUMBER interrupt instead ++ * but it turned out that the interrupt is only generated when the CSI2 ++ * writes to memory (the CSI2_CTx_CTRL1::COUNT field is decreased ++ * correctly and reaches 0 when data is forwarded to the video port only ++ * but no interrupt arrives). Maybe a CSI2 hardware bug. ++ */ ++ if (csi2->frame_skip) { ++ csi2->frame_skip--; ++ if (csi2->frame_skip == 0) { ++ ctx->format_id = csi2_ctx_map_format(csi2); ++ csi2_ctx_config(isp, csi2, ctx); ++ csi2_ctx_enable(isp, csi2, n, 1); ++ } ++ return; ++ } ++ ++ if (csi2->output & CSI2_OUTPUT_MEMORY) ++ csi2_isr_buffer(csi2); ++} ++ ++/* ++ * omap3isp_csi2_isr - CSI2 interrupt handling. ++ * ++ * Return -EIO on Transmission error ++ */ ++int omap3isp_csi2_isr(struct isp_csi2_device *csi2) ++{ ++ u32 csi2_irqstatus, cpxio1_irqstatus; ++ struct isp_device *isp = csi2->isp; ++ int retval = 0; ++ ++ if (!csi2->available) ++ return -ENODEV; ++ ++ csi2_irqstatus = isp_reg_readl(isp, csi2->regs1, ISPCSI2_IRQSTATUS); ++ isp_reg_writel(isp, csi2_irqstatus, csi2->regs1, ISPCSI2_IRQSTATUS); ++ ++ /* Failure Cases */ ++ if (csi2_irqstatus & ISPCSI2_IRQSTATUS_COMPLEXIO1_ERR_IRQ) { ++ cpxio1_irqstatus = isp_reg_readl(isp, csi2->regs1, ++ ISPCSI2_PHY_IRQSTATUS); ++ isp_reg_writel(isp, cpxio1_irqstatus, ++ csi2->regs1, ISPCSI2_PHY_IRQSTATUS); ++ dev_dbg(isp->dev, "CSI2: ComplexIO Error IRQ " ++ "%x\n", cpxio1_irqstatus); ++ retval = -EIO; ++ } ++ ++ if (csi2_irqstatus & (ISPCSI2_IRQSTATUS_OCP_ERR_IRQ | ++ ISPCSI2_IRQSTATUS_SHORT_PACKET_IRQ | ++ ISPCSI2_IRQSTATUS_ECC_NO_CORRECTION_IRQ | ++ ISPCSI2_IRQSTATUS_COMPLEXIO2_ERR_IRQ | ++ ISPCSI2_IRQSTATUS_FIFO_OVF_IRQ)) { ++ dev_dbg(isp->dev, "CSI2 Err:" ++ " OCP:%d," ++ " Short_pack:%d," ++ " ECC:%d," ++ " CPXIO2:%d," ++ " FIFO_OVF:%d," ++ "\n", ++ (csi2_irqstatus & ++ ISPCSI2_IRQSTATUS_OCP_ERR_IRQ) ? 1 : 0, ++ (csi2_irqstatus & ++ ISPCSI2_IRQSTATUS_SHORT_PACKET_IRQ) ? 1 : 0, ++ (csi2_irqstatus & ++ ISPCSI2_IRQSTATUS_ECC_NO_CORRECTION_IRQ) ? 1 : 0, ++ (csi2_irqstatus & ++ ISPCSI2_IRQSTATUS_COMPLEXIO2_ERR_IRQ) ? 1 : 0, ++ (csi2_irqstatus & ++ ISPCSI2_IRQSTATUS_FIFO_OVF_IRQ) ? 1 : 0); ++ retval = -EIO; ++ } ++ ++ if (omap3isp_module_sync_is_stopping(&csi2->wait, &csi2->stopping)) ++ return 0; ++ ++ /* Successful cases */ ++ if (csi2_irqstatus & ISPCSI2_IRQSTATUS_CONTEXT(0)) ++ csi2_isr_ctx(csi2, &csi2->contexts[0]); ++ ++ if (csi2_irqstatus & ISPCSI2_IRQSTATUS_ECC_CORRECTION_IRQ) ++ dev_dbg(isp->dev, "CSI2: ECC correction done\n"); ++ ++ return retval; ++} ++ ++/* ----------------------------------------------------------------------------- ++ * ISP video operations ++ */ ++ ++/* ++ * csi2_queue - Queues the first buffer when using memory output ++ * @video: The video node ++ * @buffer: buffer to queue ++ */ ++static int csi2_queue(struct isp_video *video, struct isp_buffer *buffer) ++{ ++ struct isp_device *isp = video->isp; ++ struct isp_csi2_device *csi2 = &isp->isp_csi2a; ++ ++ csi2_set_outaddr(csi2, buffer->isp_addr); ++ ++ /* ++ * If streaming was enabled before there was a buffer queued ++ * or underrun happened in the ISR, the hardware was not enabled ++ * and DMA queue flag ISP_VIDEO_DMAQUEUE_UNDERRUN is still set. ++ * Enable it now. ++ */ ++ if (csi2->video_out.dmaqueue_flags & ISP_VIDEO_DMAQUEUE_UNDERRUN) { ++ /* Enable / disable context 0 and IRQs */ ++ csi2_if_enable(isp, csi2, 1); ++ csi2_ctx_enable(isp, csi2, 0, 1); ++ isp_video_dmaqueue_flags_clr(&csi2->video_out); ++ } ++ ++ return 0; ++} ++ ++static const struct isp_video_operations csi2_ispvideo_ops = { ++ .queue = csi2_queue, ++}; ++ ++/* ----------------------------------------------------------------------------- ++ * V4L2 subdev operations ++ */ ++ ++static struct v4l2_mbus_framefmt * ++__csi2_get_format(struct isp_csi2_device *csi2, struct v4l2_subdev_fh *fh, ++ unsigned int pad, enum v4l2_subdev_format_whence which) ++{ ++ if (which == V4L2_SUBDEV_FORMAT_TRY) ++ return v4l2_subdev_get_try_format(fh, pad); ++ else ++ return &csi2->formats[pad]; ++} ++ ++static void ++csi2_try_format(struct isp_csi2_device *csi2, struct v4l2_subdev_fh *fh, ++ unsigned int pad, struct v4l2_mbus_framefmt *fmt, ++ enum v4l2_subdev_format_whence which) ++{ ++ enum v4l2_mbus_pixelcode pixelcode; ++ struct v4l2_mbus_framefmt *format; ++ const struct isp_format_info *info; ++ unsigned int i; ++ ++ switch (pad) { ++ case CSI2_PAD_SINK: ++ /* Clamp the width and height to valid range (1-8191). */ ++ for (i = 0; i < ARRAY_SIZE(csi2_input_fmts); i++) { ++ if (fmt->code == csi2_input_fmts[i]) ++ break; ++ } ++ ++ /* If not found, use SGRBG10 as default */ ++ if (i >= ARRAY_SIZE(csi2_input_fmts)) ++ fmt->code = V4L2_MBUS_FMT_SGRBG10_1X10; ++ ++ fmt->width = clamp_t(u32, fmt->width, 1, 8191); ++ fmt->height = clamp_t(u32, fmt->height, 1, 8191); ++ break; ++ ++ case CSI2_PAD_SOURCE: ++ /* Source format same as sink format, except for DPCM ++ * compression. ++ */ ++ pixelcode = fmt->code; ++ format = __csi2_get_format(csi2, fh, CSI2_PAD_SINK, which); ++ memcpy(fmt, format, sizeof(*fmt)); ++ ++ /* ++ * Only Allow DPCM decompression, and check that the ++ * pattern is preserved ++ */ ++ info = omap3isp_video_format_info(fmt->code); ++ if (info->uncompressed == pixelcode) ++ fmt->code = pixelcode; ++ break; ++ } ++ ++ /* RGB, non-interlaced */ ++ fmt->colorspace = V4L2_COLORSPACE_SRGB; ++ fmt->field = V4L2_FIELD_NONE; ++} ++ ++/* ++ * csi2_enum_mbus_code - Handle pixel format enumeration ++ * @sd : pointer to v4l2 subdev structure ++ * @fh : V4L2 subdev file handle ++ * @code : pointer to v4l2_subdev_mbus_code_enum structure ++ * return -EINVAL or zero on success ++ */ ++static int csi2_enum_mbus_code(struct v4l2_subdev *sd, ++ struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_mbus_code_enum *code) ++{ ++ struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd); ++ struct v4l2_mbus_framefmt *format; ++ const struct isp_format_info *info; ++ ++ if (code->pad == CSI2_PAD_SINK) { ++ if (code->index >= ARRAY_SIZE(csi2_input_fmts)) ++ return -EINVAL; ++ ++ code->code = csi2_input_fmts[code->index]; ++ } else { ++ format = __csi2_get_format(csi2, fh, CSI2_PAD_SINK, ++ V4L2_SUBDEV_FORMAT_TRY); ++ switch (code->index) { ++ case 0: ++ /* Passthrough sink pad code */ ++ code->code = format->code; ++ break; ++ case 1: ++ /* Uncompressed code */ ++ info = omap3isp_video_format_info(format->code); ++ if (info->uncompressed == format->code) ++ return -EINVAL; ++ ++ code->code = info->uncompressed; ++ break; ++ default: ++ return -EINVAL; ++ } ++ } ++ ++ return 0; ++} ++ ++static int csi2_enum_frame_size(struct v4l2_subdev *sd, ++ struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_frame_size_enum *fse) ++{ ++ struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd); ++ struct v4l2_mbus_framefmt format; ++ ++ if (fse->index != 0) ++ return -EINVAL; ++ ++ format.code = fse->code; ++ format.width = 1; ++ format.height = 1; ++ csi2_try_format(csi2, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); ++ fse->min_width = format.width; ++ fse->min_height = format.height; ++ ++ if (format.code != fse->code) ++ return -EINVAL; ++ ++ format.code = fse->code; ++ format.width = -1; ++ format.height = -1; ++ csi2_try_format(csi2, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); ++ fse->max_width = format.width; ++ fse->max_height = format.height; ++ ++ return 0; ++} ++ ++/* ++ * csi2_get_format - Handle get format by pads subdev method ++ * @sd : pointer to v4l2 subdev structure ++ * @fh : V4L2 subdev file handle ++ * @fmt: pointer to v4l2 subdev format structure ++ * return -EINVAL or zero on sucess ++ */ ++static int csi2_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_format *fmt) ++{ ++ struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd); ++ struct v4l2_mbus_framefmt *format; ++ ++ format = __csi2_get_format(csi2, fh, fmt->pad, fmt->which); ++ if (format == NULL) ++ return -EINVAL; ++ ++ fmt->format = *format; ++ return 0; ++} ++ ++/* ++ * csi2_set_format - Handle set format by pads subdev method ++ * @sd : pointer to v4l2 subdev structure ++ * @fh : V4L2 subdev file handle ++ * @fmt: pointer to v4l2 subdev format structure ++ * return -EINVAL or zero on success ++ */ ++static int csi2_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_format *fmt) ++{ ++ struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd); ++ struct v4l2_mbus_framefmt *format; ++ ++ format = __csi2_get_format(csi2, fh, fmt->pad, fmt->which); ++ if (format == NULL) ++ return -EINVAL; ++ ++ csi2_try_format(csi2, fh, fmt->pad, &fmt->format, fmt->which); ++ *format = fmt->format; ++ ++ /* Propagate the format from sink to source */ ++ if (fmt->pad == CSI2_PAD_SINK) { ++ format = __csi2_get_format(csi2, fh, CSI2_PAD_SOURCE, ++ fmt->which); ++ *format = fmt->format; ++ csi2_try_format(csi2, fh, CSI2_PAD_SOURCE, format, fmt->which); ++ } ++ ++ return 0; ++} ++ ++/* ++ * csi2_init_formats - Initialize formats on all pads ++ * @sd: ISP CSI2 V4L2 subdevice ++ * @fh: V4L2 subdev file handle ++ * ++ * Initialize all pad formats with default values. If fh is not NULL, try ++ * formats are initialized on the file handle. Otherwise active formats are ++ * initialized on the device. ++ */ ++static int csi2_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) ++{ ++ struct v4l2_subdev_format format; ++ ++ memset(&format, 0, sizeof(format)); ++ format.pad = CSI2_PAD_SINK; ++ format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; ++ format.format.code = V4L2_MBUS_FMT_SGRBG10_1X10; ++ format.format.width = 4096; ++ format.format.height = 4096; ++ csi2_set_format(sd, fh, &format); ++ ++ return 0; ++} ++ ++/* ++ * csi2_set_stream - Enable/Disable streaming on the CSI2 module ++ * @sd: ISP CSI2 V4L2 subdevice ++ * @enable: ISP pipeline stream state ++ * ++ * Return 0 on success or a negative error code otherwise. ++ */ ++static int csi2_set_stream(struct v4l2_subdev *sd, int enable) ++{ ++ struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd); ++ struct isp_device *isp = csi2->isp; ++ struct isp_pipeline *pipe = to_isp_pipeline(&csi2->subdev.entity); ++ struct isp_video *video_out = &csi2->video_out; ++ ++ switch (enable) { ++ case ISP_PIPELINE_STREAM_CONTINUOUS: ++ if (omap3isp_csiphy_acquire(csi2->phy) < 0) ++ return -ENODEV; ++ csi2->use_fs_irq = pipe->do_propagation; ++ if (csi2->output & CSI2_OUTPUT_MEMORY) ++ omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_CSI2A_WRITE); ++ csi2_configure(csi2); ++ csi2_print_status(csi2); ++ ++ /* ++ * When outputting to memory with no buffer available, let the ++ * buffer queue handler start the hardware. A DMA queue flag ++ * ISP_VIDEO_DMAQUEUE_QUEUED will be set as soon as there is ++ * a buffer available. ++ */ ++ if (csi2->output & CSI2_OUTPUT_MEMORY && ++ !(video_out->dmaqueue_flags & ISP_VIDEO_DMAQUEUE_QUEUED)) ++ break; ++ /* Enable context 0 and IRQs */ ++ atomic_set(&csi2->stopping, 0); ++ csi2_ctx_enable(isp, csi2, 0, 1); ++ csi2_if_enable(isp, csi2, 1); ++ isp_video_dmaqueue_flags_clr(video_out); ++ break; ++ ++ case ISP_PIPELINE_STREAM_STOPPED: ++ if (csi2->state == ISP_PIPELINE_STREAM_STOPPED) ++ return 0; ++ if (omap3isp_module_sync_idle(&sd->entity, &csi2->wait, ++ &csi2->stopping)) ++ dev_dbg(isp->dev, "%s: module stop timeout.\n", ++ sd->name); ++ csi2_ctx_enable(isp, csi2, 0, 0); ++ csi2_if_enable(isp, csi2, 0); ++ csi2_irq_ctx_set(isp, csi2, 0); ++ omap3isp_csiphy_release(csi2->phy); ++ isp_video_dmaqueue_flags_clr(video_out); ++ omap3isp_sbl_disable(isp, OMAP3_ISP_SBL_CSI2A_WRITE); ++ break; ++ } ++ ++ csi2->state = enable; ++ return 0; ++} ++ ++/* subdev core operations */ ++static const struct v4l2_subdev_core_ops csi2_core_ops = { ++ .queryctrl = v4l2_subdev_queryctrl, ++ .querymenu = v4l2_subdev_querymenu, ++ .g_ctrl = v4l2_subdev_g_ctrl, ++ .s_ctrl = v4l2_subdev_s_ctrl, ++ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, ++ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, ++ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, ++}; ++ ++/* subdev file operations */ ++static const struct v4l2_subdev_file_ops csi2_file_ops = { ++ .open = csi2_init_formats, ++}; ++ ++/* subdev video operations */ ++static const struct v4l2_subdev_video_ops csi2_video_ops = { ++ .s_stream = csi2_set_stream, ++}; ++ ++/* subdev pad operations */ ++static const struct v4l2_subdev_pad_ops csi2_pad_ops = { ++ .enum_mbus_code = csi2_enum_mbus_code, ++ .enum_frame_size = csi2_enum_frame_size, ++ .get_fmt = csi2_get_format, ++ .set_fmt = csi2_set_format, ++}; ++ ++/* subdev operations */ ++static const struct v4l2_subdev_ops csi2_ops = { ++ .core = &csi2_core_ops, ++ .file = &csi2_file_ops, ++ .video = &csi2_video_ops, ++ .pad = &csi2_pad_ops, ++}; ++ ++/* ----------------------------------------------------------------------------- ++ * Media entity operations ++ */ ++ ++/* ++ * csi2_link_setup - Setup CSI2 connections. ++ * @entity : Pointer to media entity structure ++ * @local : Pointer to local pad array ++ * @remote : Pointer to remote pad array ++ * @flags : Link flags ++ * return -EINVAL or zero on success ++ */ ++static int csi2_link_setup(struct media_entity *entity, ++ const struct media_pad *local, ++ const struct media_pad *remote, u32 flags) ++{ ++ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); ++ struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd); ++ struct isp_csi2_ctrl_cfg *ctrl = &csi2->ctrl; ++ ++ /* ++ * The ISP core doesn't support pipelines with multiple video outputs. ++ * Revisit this when it will be implemented, and return -EBUSY for now. ++ */ ++ ++ switch (local->index | media_entity_type(remote->entity)) { ++ case CSI2_PAD_SOURCE | MEDIA_ENT_T_DEVNODE: ++ if (flags & MEDIA_LNK_FL_ENABLED) { ++ if (csi2->output & ~CSI2_OUTPUT_MEMORY) ++ return -EBUSY; ++ csi2->output |= CSI2_OUTPUT_MEMORY; ++ } else { ++ csi2->output &= ~CSI2_OUTPUT_MEMORY; ++ } ++ break; ++ ++ case CSI2_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV: ++ if (flags & MEDIA_LNK_FL_ENABLED) { ++ if (csi2->output & ~CSI2_OUTPUT_CCDC) ++ return -EBUSY; ++ csi2->output |= CSI2_OUTPUT_CCDC; ++ } else { ++ csi2->output &= ~CSI2_OUTPUT_CCDC; ++ } ++ break; ++ ++ default: ++ /* Link from camera to CSI2 is fixed... */ ++ return -EINVAL; ++ } ++ ++ ctrl->vp_only_enable = ++ (csi2->output & CSI2_OUTPUT_MEMORY) ? false : true; ++ ctrl->vp_clk_enable = !!(csi2->output & CSI2_OUTPUT_CCDC); ++ ++ return 0; ++} ++ ++/* media operations */ ++static const struct media_entity_operations csi2_media_ops = { ++ .link_setup = csi2_link_setup, ++}; ++ ++/* ++ * csi2_init_entities - Initialize subdev and media entity. ++ * @csi2: Pointer to csi2 structure. ++ * return -ENOMEM or zero on success ++ */ ++static int csi2_init_entities(struct isp_csi2_device *csi2) ++{ ++ struct v4l2_subdev *sd = &csi2->subdev; ++ struct media_pad *pads = csi2->pads; ++ struct media_entity *me = &sd->entity; ++ int ret; ++ ++ v4l2_subdev_init(sd, &csi2_ops); ++ strlcpy(sd->name, "OMAP3 ISP CSI2a", sizeof(sd->name)); ++ ++ sd->grp_id = 1 << 16; /* group ID for isp subdevs */ ++ v4l2_set_subdevdata(sd, csi2); ++ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; ++ ++ v4l2_ctrl_handler_init(&csi2->ctrls, 1); ++ sd->ctrl_handler = &csi2->ctrls; ++ ++ pads[CSI2_PAD_SOURCE].flags = MEDIA_PAD_FL_OUTPUT; ++ pads[CSI2_PAD_SINK].flags = MEDIA_PAD_FL_INPUT; ++ ++ me->ops = &csi2_media_ops; ++ ret = media_entity_init(me, CSI2_PADS_NUM, pads, 0); ++ if (ret < 0) ++ return ret; ++ ++ csi2_init_formats(sd, NULL); ++ ++ /* Video device node */ ++ csi2->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ csi2->video_out.ops = &csi2_ispvideo_ops; ++ csi2->video_out.bpl_alignment = 32; ++ csi2->video_out.bpl_zero_padding = 1; ++ csi2->video_out.bpl_max = 0x1ffe0; ++ csi2->video_out.isp = csi2->isp; ++ csi2->video_out.capture_mem = PAGE_ALIGN(4096 * 4096) * 3; ++ ++ ret = omap3isp_video_init(&csi2->video_out, "CSI2a"); ++ if (ret < 0) ++ return ret; ++ ++ /* Connect the CSI2 subdev to the video node. */ ++ ret = media_entity_create_link(&csi2->subdev.entity, CSI2_PAD_SOURCE, ++ &csi2->video_out.video.entity, 0, 0); ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++void omap3isp_csi2_unregister_entities(struct isp_csi2_device *csi2) ++{ ++ media_entity_cleanup(&csi2->subdev.entity); ++ ++ v4l2_device_unregister_subdev(&csi2->subdev); ++ v4l2_ctrl_handler_free(&csi2->ctrls); ++ omap3isp_video_unregister(&csi2->video_out); ++} ++ ++int omap3isp_csi2_register_entities(struct isp_csi2_device *csi2, ++ struct v4l2_device *vdev) ++{ ++ int ret; ++ ++ /* Register the subdev and video nodes. */ ++ ret = v4l2_device_register_subdev(vdev, &csi2->subdev); ++ if (ret < 0) ++ goto error; ++ ++ ret = omap3isp_video_register(&csi2->video_out, vdev); ++ if (ret < 0) ++ goto error; ++ ++ return 0; ++ ++error: ++ omap3isp_csi2_unregister_entities(csi2); ++ return ret; ++} ++ ++/* ----------------------------------------------------------------------------- ++ * ISP CSI2 initialisation and cleanup ++ */ ++ ++/* ++ * omap3isp_csi2_cleanup - Routine for module driver cleanup ++ */ ++void omap3isp_csi2_cleanup(struct isp_device *isp) ++{ ++} ++ ++/* ++ * omap3isp_csi2_init - Routine for module driver init ++ */ ++int omap3isp_csi2_init(struct isp_device *isp) ++{ ++ struct isp_csi2_device *csi2a = &isp->isp_csi2a; ++ struct isp_csi2_device *csi2c = &isp->isp_csi2c; ++ int ret; ++ ++ csi2a->isp = isp; ++ csi2a->available = 1; ++ csi2a->regs1 = OMAP3_ISP_IOMEM_CSI2A_REGS1; ++ csi2a->regs2 = OMAP3_ISP_IOMEM_CSI2A_REGS2; ++ csi2a->phy = &isp->isp_csiphy2; ++ csi2a->state = ISP_PIPELINE_STREAM_STOPPED; ++ init_waitqueue_head(&csi2a->wait); ++ ++ ret = csi2_init_entities(csi2a); ++ if (ret < 0) ++ goto fail; ++ ++ if (isp->revision == ISP_REVISION_15_0) { ++ csi2c->isp = isp; ++ csi2c->available = 1; ++ csi2c->regs1 = OMAP3_ISP_IOMEM_CSI2C_REGS1; ++ csi2c->regs2 = OMAP3_ISP_IOMEM_CSI2C_REGS2; ++ csi2c->phy = &isp->isp_csiphy1; ++ csi2c->state = ISP_PIPELINE_STREAM_STOPPED; ++ init_waitqueue_head(&csi2c->wait); ++ } ++ ++ return 0; ++fail: ++ omap3isp_csi2_cleanup(isp); ++ return ret; ++} +diff --git a/drivers/media/video/isp/ispcsi2.h b/drivers/media/video/isp/ispcsi2.h +new file mode 100644 +index 0000000..b367326 +--- /dev/null ++++ b/drivers/media/video/isp/ispcsi2.h +@@ -0,0 +1,169 @@ ++/* ++ * ispcsi2.h ++ * ++ * TI OMAP3 ISP - CSI2 module ++ * ++ * Copyright (C) 2010 Nokia Corporation ++ * Copyright (C) 2009 Texas Instruments, Inc. ++ * ++ * Contacts: Laurent Pinchart ++ * Sakari Ailus ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#ifndef OMAP3_ISP_CSI2_H ++#define OMAP3_ISP_CSI2_H ++ ++#include ++#include ++#include ++ ++struct isp_csiphy; ++ ++/* This is not an exhaustive list */ ++enum isp_csi2_pix_formats { ++ CSI2_PIX_FMT_OTHERS = 0, ++ CSI2_PIX_FMT_YUV422_8BIT = 0x1e, ++ CSI2_PIX_FMT_YUV422_8BIT_VP = 0x9e, ++ CSI2_PIX_FMT_RAW10_EXP16 = 0xab, ++ CSI2_PIX_FMT_RAW10_EXP16_VP = 0x12f, ++ CSI2_PIX_FMT_RAW8 = 0x2a, ++ CSI2_PIX_FMT_RAW8_DPCM10_EXP16 = 0x2aa, ++ CSI2_PIX_FMT_RAW8_DPCM10_VP = 0x32a, ++ CSI2_PIX_FMT_RAW8_VP = 0x12a, ++ CSI2_USERDEF_8BIT_DATA1_DPCM10_VP = 0x340, ++ CSI2_USERDEF_8BIT_DATA1_DPCM10 = 0x2c0, ++ CSI2_USERDEF_8BIT_DATA1 = 0x40, ++}; ++ ++enum isp_csi2_irqevents { ++ OCP_ERR_IRQ = 0x4000, ++ SHORT_PACKET_IRQ = 0x2000, ++ ECC_CORRECTION_IRQ = 0x1000, ++ ECC_NO_CORRECTION_IRQ = 0x800, ++ COMPLEXIO2_ERR_IRQ = 0x400, ++ COMPLEXIO1_ERR_IRQ = 0x200, ++ FIFO_OVF_IRQ = 0x100, ++ CONTEXT7 = 0x80, ++ CONTEXT6 = 0x40, ++ CONTEXT5 = 0x20, ++ CONTEXT4 = 0x10, ++ CONTEXT3 = 0x8, ++ CONTEXT2 = 0x4, ++ CONTEXT1 = 0x2, ++ CONTEXT0 = 0x1, ++}; ++ ++enum isp_csi2_ctx_irqevents { ++ CTX_ECC_CORRECTION = 0x100, ++ CTX_LINE_NUMBER = 0x80, ++ CTX_FRAME_NUMBER = 0x40, ++ CTX_CS = 0x20, ++ CTX_LE = 0x8, ++ CTX_LS = 0x4, ++ CTX_FE = 0x2, ++ CTX_FS = 0x1, ++}; ++ ++enum isp_csi2_frame_mode { ++ ISP_CSI2_FRAME_IMMEDIATE, ++ ISP_CSI2_FRAME_AFTERFEC, ++}; ++ ++#define ISP_CSI2_MAX_CTX_NUM 7 ++ ++struct isp_csi2_ctx_cfg { ++ u8 ctxnum; /* context number 0 - 7 */ ++ u8 dpcm_decompress; ++ ++ /* Fields in CSI2_CTx_CTRL2 - locked by CSI2_CTx_CTRL1.CTX_EN */ ++ u8 virtual_id; ++ u16 format_id; /* as in CSI2_CTx_CTRL2[9:0] */ ++ u8 dpcm_predictor; /* 1: simple, 0: advanced */ ++ ++ /* Fields in CSI2_CTx_CTRL1/3 - Shadowed */ ++ u16 alpha; ++ u16 data_offset; ++ u32 ping_addr; ++ u32 pong_addr; ++ u8 eof_enabled; ++ u8 eol_enabled; ++ u8 checksum_enabled; ++ u8 enabled; ++}; ++ ++struct isp_csi2_timing_cfg { ++ u8 ionum; /* IO1 or IO2 as in CSI2_TIMING */ ++ unsigned force_rx_mode:1; ++ unsigned stop_state_16x:1; ++ unsigned stop_state_4x:1; ++ u16 stop_state_counter; ++}; ++ ++struct isp_csi2_ctrl_cfg { ++ bool vp_clk_enable; ++ bool vp_only_enable; ++ u8 vp_out_ctrl; ++ enum isp_csi2_frame_mode frame_mode; ++ bool ecc_enable; ++ bool if_enable; ++}; ++ ++#define CSI2_PAD_SINK 0 ++#define CSI2_PAD_SOURCE 1 ++#define CSI2_PADS_NUM 2 ++ ++#define CSI2_OUTPUT_CCDC (1 << 0) ++#define CSI2_OUTPUT_MEMORY (1 << 1) ++ ++struct isp_csi2_device { ++ struct v4l2_subdev subdev; ++ struct media_pad pads[CSI2_PADS_NUM]; ++ struct v4l2_mbus_framefmt formats[CSI2_PADS_NUM]; ++ ++ struct v4l2_ctrl_handler ctrls; ++ ++ struct isp_video video_out; ++ struct isp_device *isp; ++ ++ u8 available; /* Is the IP present on the silicon? */ ++ ++ /* mem resources - enums as defined in enum isp_mem_resources */ ++ u8 regs1; ++ u8 regs2; ++ ++ u32 output; /* output to CCDC, memory or both? */ ++ bool dpcm_decompress; ++ unsigned int frame_skip; ++ bool use_fs_irq; ++ ++ struct isp_csiphy *phy; ++ struct isp_csi2_ctx_cfg contexts[ISP_CSI2_MAX_CTX_NUM + 1]; ++ struct isp_csi2_timing_cfg timing[2]; ++ struct isp_csi2_ctrl_cfg ctrl; ++ enum isp_pipeline_stream_state state; ++ wait_queue_head_t wait; ++ atomic_t stopping; ++}; ++ ++int omap3isp_csi2_isr(struct isp_csi2_device *csi2); ++int omap3isp_csi2_reset(struct isp_csi2_device *csi2); ++int omap3isp_csi2_init(struct isp_device *isp); ++void omap3isp_csi2_cleanup(struct isp_device *isp); ++void omap3isp_csi2_unregister_entities(struct isp_csi2_device *csi2); ++int omap3isp_csi2_register_entities(struct isp_csi2_device *csi2, ++ struct v4l2_device *vdev); ++#endif /* OMAP3_ISP_CSI2_H */ +diff --git a/drivers/media/video/isp/ispcsiphy.c b/drivers/media/video/isp/ispcsiphy.c +new file mode 100644 +index 0000000..59cd477 +--- /dev/null ++++ b/drivers/media/video/isp/ispcsiphy.c +@@ -0,0 +1,247 @@ ++/* ++ * ispcsiphy.c ++ * ++ * TI OMAP3 ISP - CSI PHY module ++ * ++ * Copyright (C) 2010 Nokia Corporation ++ * Copyright (C) 2009 Texas Instruments, Inc. ++ * ++ * Contacts: Laurent Pinchart ++ * Sakari Ailus ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#include ++#include ++#include ++ ++#include "isp.h" ++#include "ispreg.h" ++#include "ispcsiphy.h" ++ ++/* ++ * csiphy_lanes_config - Configuration of CSIPHY lanes. ++ * ++ * Updates HW configuration. ++ * Called with phy->mutex taken. ++ */ ++static void csiphy_lanes_config(struct isp_csiphy *phy) ++{ ++ unsigned int i; ++ u32 reg; ++ ++ reg = isp_reg_readl(phy->isp, phy->cfg_regs, ISPCSI2_PHY_CFG); ++ ++ for (i = 0; i < phy->num_data_lanes; i++) { ++ reg &= ~(ISPCSI2_PHY_CFG_DATA_POL_MASK(i + 1) | ++ ISPCSI2_PHY_CFG_DATA_POSITION_MASK(i + 1)); ++ reg |= (phy->lanes.data[i].pol << ++ ISPCSI2_PHY_CFG_DATA_POL_SHIFT(i + 1)); ++ reg |= (phy->lanes.data[i].pos << ++ ISPCSI2_PHY_CFG_DATA_POSITION_SHIFT(i + 1)); ++ } ++ ++ reg &= ~(ISPCSI2_PHY_CFG_CLOCK_POL_MASK | ++ ISPCSI2_PHY_CFG_CLOCK_POSITION_MASK); ++ reg |= phy->lanes.clk.pol << ISPCSI2_PHY_CFG_CLOCK_POL_SHIFT; ++ reg |= phy->lanes.clk.pos << ISPCSI2_PHY_CFG_CLOCK_POSITION_SHIFT; ++ ++ isp_reg_writel(phy->isp, reg, phy->cfg_regs, ISPCSI2_PHY_CFG); ++} ++ ++/* ++ * csiphy_power_autoswitch_enable ++ * @enable: Sets or clears the autoswitch function enable flag. ++ */ ++static void csiphy_power_autoswitch_enable(struct isp_csiphy *phy, bool enable) ++{ ++ isp_reg_clr_set(phy->isp, phy->cfg_regs, ISPCSI2_PHY_CFG, ++ ISPCSI2_PHY_CFG_PWR_AUTO, ++ enable ? ISPCSI2_PHY_CFG_PWR_AUTO : 0); ++} ++ ++/* ++ * csiphy_set_power ++ * @power: Power state to be set. ++ * ++ * Returns 0 if successful, or -EBUSY if the retry count is exceeded. ++ */ ++static int csiphy_set_power(struct isp_csiphy *phy, u32 power) ++{ ++ u32 reg; ++ u8 retry_count; ++ ++ isp_reg_clr_set(phy->isp, phy->cfg_regs, ISPCSI2_PHY_CFG, ++ ISPCSI2_PHY_CFG_PWR_CMD_MASK, power); ++ ++ retry_count = 0; ++ do { ++ udelay(50); ++ reg = isp_reg_readl(phy->isp, phy->cfg_regs, ISPCSI2_PHY_CFG) & ++ ISPCSI2_PHY_CFG_PWR_STATUS_MASK; ++ ++ if (reg != power >> 2) ++ retry_count++; ++ ++ } while ((reg != power >> 2) && (retry_count < 100)); ++ ++ if (retry_count == 100) { ++ printk(KERN_ERR "CSI2 CIO set power failed!\n"); ++ return -EBUSY; ++ } ++ ++ return 0; ++} ++ ++/* ++ * csiphy_dphy_config - Configure CSI2 D-PHY parameters. ++ * ++ * Called with phy->mutex taken. ++ */ ++static void csiphy_dphy_config(struct isp_csiphy *phy) ++{ ++ u32 reg; ++ ++ /* Set up ISPCSIPHY_REG0 */ ++ reg = isp_reg_readl(phy->isp, phy->phy_regs, ISPCSIPHY_REG0); ++ ++ reg &= ~(ISPCSIPHY_REG0_THS_TERM_MASK | ++ ISPCSIPHY_REG0_THS_SETTLE_MASK); ++ reg |= phy->dphy.ths_term << ISPCSIPHY_REG0_THS_TERM_SHIFT; ++ reg |= phy->dphy.ths_settle << ISPCSIPHY_REG0_THS_SETTLE_SHIFT; ++ ++ isp_reg_writel(phy->isp, reg, phy->phy_regs, ISPCSIPHY_REG0); ++ ++ /* Set up ISPCSIPHY_REG1 */ ++ reg = isp_reg_readl(phy->isp, phy->phy_regs, ISPCSIPHY_REG1); ++ ++ reg &= ~(ISPCSIPHY_REG1_TCLK_TERM_MASK | ++ ISPCSIPHY_REG1_TCLK_MISS_MASK | ++ ISPCSIPHY_REG1_TCLK_SETTLE_MASK); ++ reg |= phy->dphy.tclk_term << ISPCSIPHY_REG1_TCLK_TERM_SHIFT; ++ reg |= phy->dphy.tclk_miss << ISPCSIPHY_REG1_TCLK_MISS_SHIFT; ++ reg |= phy->dphy.tclk_settle << ISPCSIPHY_REG1_TCLK_SETTLE_SHIFT; ++ ++ isp_reg_writel(phy->isp, reg, phy->phy_regs, ISPCSIPHY_REG1); ++} ++ ++static int csiphy_config(struct isp_csiphy *phy, ++ struct isp_csiphy_dphy_cfg *dphy, ++ struct isp_csiphy_lanes_cfg *lanes) ++{ ++ unsigned int used_lanes = 0; ++ unsigned int i; ++ ++ /* Clock and data lanes verification */ ++ for (i = 0; i < phy->num_data_lanes; i++) { ++ if (lanes->data[i].pol > 1 || lanes->data[i].pos > 3) ++ return -EINVAL; ++ ++ if (used_lanes & (1 << lanes->data[i].pos)) ++ return -EINVAL; ++ ++ used_lanes |= 1 << lanes->data[i].pos; ++ } ++ ++ if (lanes->clk.pol > 1 || lanes->clk.pos > 3) ++ return -EINVAL; ++ ++ if (lanes->clk.pos == 0 || used_lanes & (1 << lanes->clk.pos)) ++ return -EINVAL; ++ ++ mutex_lock(&phy->mutex); ++ phy->dphy = *dphy; ++ phy->lanes = *lanes; ++ mutex_unlock(&phy->mutex); ++ ++ return 0; ++} ++ ++int omap3isp_csiphy_acquire(struct isp_csiphy *phy) ++{ ++ int rval; ++ ++ if (phy->vdd == NULL) { ++ dev_err(phy->isp->dev, "Power regulator for CSI PHY not " ++ "available\n"); ++ return -ENODEV; ++ } ++ ++ mutex_lock(&phy->mutex); ++ ++ rval = regulator_enable(phy->vdd); ++ if (rval < 0) ++ goto done; ++ ++ omap3isp_csi2_reset(phy->csi2); ++ ++ csiphy_dphy_config(phy); ++ csiphy_lanes_config(phy); ++ ++ rval = csiphy_set_power(phy, ISPCSI2_PHY_CFG_PWR_CMD_ON); ++ if (rval) { ++ regulator_disable(phy->vdd); ++ goto done; ++ } ++ ++ csiphy_power_autoswitch_enable(phy, true); ++ phy->phy_in_use = 1; ++ ++done: ++ mutex_unlock(&phy->mutex); ++ return rval; ++} ++ ++void omap3isp_csiphy_release(struct isp_csiphy *phy) ++{ ++ mutex_lock(&phy->mutex); ++ if (phy->phy_in_use) { ++ csiphy_power_autoswitch_enable(phy, false); ++ csiphy_set_power(phy, ISPCSI2_PHY_CFG_PWR_CMD_OFF); ++ regulator_disable(phy->vdd); ++ phy->phy_in_use = 0; ++ } ++ mutex_unlock(&phy->mutex); ++} ++ ++/* ++ * omap3isp_csiphy_init - Initialize the CSI PHY frontends ++ */ ++int omap3isp_csiphy_init(struct isp_device *isp) ++{ ++ struct isp_csiphy *phy1 = &isp->isp_csiphy1; ++ struct isp_csiphy *phy2 = &isp->isp_csiphy2; ++ ++ isp->platform_cb.csiphy_config = csiphy_config; ++ ++ phy2->isp = isp; ++ phy2->csi2 = &isp->isp_csi2a; ++ phy2->num_data_lanes = ISP_CSIPHY2_NUM_DATA_LANES; ++ phy2->cfg_regs = OMAP3_ISP_IOMEM_CSI2A_REGS1; ++ phy2->phy_regs = OMAP3_ISP_IOMEM_CSIPHY2; ++ mutex_init(&phy2->mutex); ++ ++ if (isp->revision == ISP_REVISION_15_0) { ++ phy1->isp = isp; ++ phy1->csi2 = &isp->isp_csi2c; ++ phy1->num_data_lanes = ISP_CSIPHY1_NUM_DATA_LANES; ++ phy1->cfg_regs = OMAP3_ISP_IOMEM_CSI2C_REGS1; ++ phy1->phy_regs = OMAP3_ISP_IOMEM_CSIPHY1; ++ mutex_init(&phy1->mutex); ++ } ++ ++ return 0; ++} +diff --git a/drivers/media/video/isp/ispcsiphy.h b/drivers/media/video/isp/ispcsiphy.h +new file mode 100644 +index 0000000..39a7e6b +--- /dev/null ++++ b/drivers/media/video/isp/ispcsiphy.h +@@ -0,0 +1,74 @@ ++/* ++ * ispcsiphy.h ++ * ++ * TI OMAP3 ISP - CSI PHY module ++ * ++ * Copyright (C) 2010 Nokia Corporation ++ * Copyright (C) 2009 Texas Instruments, Inc. ++ * ++ * Contacts: Laurent Pinchart ++ * Sakari Ailus ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#ifndef OMAP3_ISP_CSI_PHY_H ++#define OMAP3_ISP_CSI_PHY_H ++ ++struct isp_csi2_device; ++struct regulator; ++ ++struct csiphy_lane { ++ u8 pos; ++ u8 pol; ++}; ++ ++#define ISP_CSIPHY2_NUM_DATA_LANES 2 ++#define ISP_CSIPHY1_NUM_DATA_LANES 1 ++ ++struct isp_csiphy_lanes_cfg { ++ struct csiphy_lane data[ISP_CSIPHY2_NUM_DATA_LANES]; ++ struct csiphy_lane clk; ++}; ++ ++struct isp_csiphy_dphy_cfg { ++ u8 ths_term; ++ u8 ths_settle; ++ u8 tclk_term; ++ unsigned tclk_miss:1; ++ u8 tclk_settle; ++}; ++ ++struct isp_csiphy { ++ struct isp_device *isp; ++ struct mutex mutex; /* serialize csiphy configuration */ ++ u8 phy_in_use; ++ struct isp_csi2_device *csi2; ++ struct regulator *vdd; ++ ++ /* mem resources - enums as defined in enum isp_mem_resources */ ++ unsigned int cfg_regs; ++ unsigned int phy_regs; ++ ++ u8 num_data_lanes; /* number of CSI2 Data Lanes supported */ ++ struct isp_csiphy_lanes_cfg lanes; ++ struct isp_csiphy_dphy_cfg dphy; ++}; ++ ++int omap3isp_csiphy_acquire(struct isp_csiphy *phy); ++void omap3isp_csiphy_release(struct isp_csiphy *phy); ++int omap3isp_csiphy_init(struct isp_device *isp); ++ ++#endif /* OMAP3_ISP_CSI_PHY_H */ +diff --git a/drivers/media/video/isp/isph3a.h b/drivers/media/video/isp/isph3a.h +new file mode 100644 +index 0000000..6d43529 +--- /dev/null ++++ b/drivers/media/video/isp/isph3a.h +@@ -0,0 +1,117 @@ ++/* ++ * isph3a.h ++ * ++ * TI OMAP3 ISP - H3A AF module ++ * ++ * Copyright (C) 2010 Nokia Corporation ++ * Copyright (C) 2009 Texas Instruments, Inc. ++ * ++ * Contacts: David Cohen ++ * Laurent Pinchart ++ * Sakari Ailus ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#ifndef OMAP3_ISP_H3A_H ++#define OMAP3_ISP_H3A_H ++ ++#include ++ ++/* ++ * ---------- ++ * -H3A AEWB- ++ * ---------- ++ */ ++ ++#define AEWB_PACKET_SIZE 16 ++#define AEWB_SATURATION_LIMIT 0x3ff ++ ++/* Flags for changed registers */ ++#define PCR_CHNG (1 << 0) ++#define AEWWIN1_CHNG (1 << 1) ++#define AEWINSTART_CHNG (1 << 2) ++#define AEWINBLK_CHNG (1 << 3) ++#define AEWSUBWIN_CHNG (1 << 4) ++#define PRV_WBDGAIN_CHNG (1 << 5) ++#define PRV_WBGAIN_CHNG (1 << 6) ++ ++/* ISPH3A REGISTERS bits */ ++#define ISPH3A_PCR_AF_EN (1 << 0) ++#define ISPH3A_PCR_AF_ALAW_EN (1 << 1) ++#define ISPH3A_PCR_AF_MED_EN (1 << 2) ++#define ISPH3A_PCR_AF_BUSY (1 << 15) ++#define ISPH3A_PCR_AEW_EN (1 << 16) ++#define ISPH3A_PCR_AEW_ALAW_EN (1 << 17) ++#define ISPH3A_PCR_AEW_BUSY (1 << 18) ++#define ISPH3A_PCR_AEW_MASK (ISPH3A_PCR_AEW_ALAW_EN | \ ++ ISPH3A_PCR_AEW_AVE2LMT_MASK) ++ ++/* ++ * -------- ++ * -H3A AF- ++ * -------- ++ */ ++ ++/* Peripheral Revision */ ++#define AFPID 0x0 ++ ++#define AFCOEF_OFFSET 0x00000004 /* COEF base address */ ++ ++/* PCR fields */ ++#define AF_BUSYAF (1 << 15) ++#define AF_FVMODE (1 << 14) ++#define AF_RGBPOS (0x7 << 11) ++#define AF_MED_TH (0xFF << 3) ++#define AF_MED_EN (1 << 2) ++#define AF_ALAW_EN (1 << 1) ++#define AF_EN (1 << 0) ++#define AF_PCR_MASK (AF_FVMODE | AF_RGBPOS | AF_MED_TH | \ ++ AF_MED_EN | AF_ALAW_EN) ++ ++/* AFPAX1 fields */ ++#define AF_PAXW (0x7F << 16) ++#define AF_PAXH 0x7F ++ ++/* AFPAX2 fields */ ++#define AF_AFINCV (0xF << 13) ++#define AF_PAXVC (0x7F << 6) ++#define AF_PAXHC 0x3F ++ ++/* AFPAXSTART fields */ ++#define AF_PAXSH (0xFFF<<16) ++#define AF_PAXSV 0xFFF ++ ++/* COEFFICIENT MASK */ ++#define AF_COEF_MASK0 0xFFF ++#define AF_COEF_MASK1 (0xFFF<<16) ++ ++/* BIT SHIFTS */ ++#define AF_RGBPOS_SHIFT 11 ++#define AF_MED_TH_SHIFT 3 ++#define AF_PAXW_SHIFT 16 ++#define AF_LINE_INCR_SHIFT 13 ++#define AF_VT_COUNT_SHIFT 6 ++#define AF_HZ_START_SHIFT 16 ++#define AF_COEF_SHIFT 16 ++ ++/* Init and cleanup functions */ ++int omap3isp_h3a_aewb_init(struct isp_device *isp); ++int omap3isp_h3a_af_init(struct isp_device *isp); ++ ++void omap3isp_h3a_aewb_cleanup(struct isp_device *isp); ++void omap3isp_h3a_af_cleanup(struct isp_device *isp); ++ ++#endif /* OMAP3_ISP_H3A_H */ +diff --git a/drivers/media/video/isp/isph3a_aewb.c b/drivers/media/video/isp/isph3a_aewb.c +new file mode 100644 +index 0000000..b4e97f2 +--- /dev/null ++++ b/drivers/media/video/isp/isph3a_aewb.c +@@ -0,0 +1,374 @@ ++/* ++ * isph3a.c ++ * ++ * TI OMAP3 ISP - H3A module ++ * ++ * Copyright (C) 2010 Nokia Corporation ++ * Copyright (C) 2009 Texas Instruments, Inc. ++ * ++ * Contacts: David Cohen ++ * Laurent Pinchart ++ * Sakari Ailus ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#include ++#include ++ ++#include "isp.h" ++#include "isph3a.h" ++#include "ispstat.h" ++ ++/* ++ * h3a_aewb_update_regs - Helper function to update h3a registers. ++ */ ++static void h3a_aewb_setup_regs(struct ispstat *aewb, void *priv) ++{ ++ struct omap3isp_h3a_aewb_config *conf = priv; ++ u32 pcr; ++ u32 win1; ++ u32 start; ++ u32 blk; ++ u32 subwin; ++ ++ if (aewb->state == ISPSTAT_DISABLED) ++ return; ++ ++ isp_reg_writel(aewb->isp, aewb->active_buf->iommu_addr, ++ OMAP3_ISP_IOMEM_H3A, ISPH3A_AEWBUFST); ++ ++ if (!aewb->update) ++ return; ++ ++ /* Converting config metadata into reg values */ ++ pcr = conf->saturation_limit << ISPH3A_PCR_AEW_AVE2LMT_SHIFT; ++ pcr |= !!conf->alaw_enable << ISPH3A_PCR_AEW_ALAW_EN_SHIFT; ++ ++ win1 = ((conf->win_height >> 1) - 1) << ISPH3A_AEWWIN1_WINH_SHIFT; ++ win1 |= ((conf->win_width >> 1) - 1) << ISPH3A_AEWWIN1_WINW_SHIFT; ++ win1 |= (conf->ver_win_count - 1) << ISPH3A_AEWWIN1_WINVC_SHIFT; ++ win1 |= (conf->hor_win_count - 1) << ISPH3A_AEWWIN1_WINHC_SHIFT; ++ ++ start = conf->hor_win_start << ISPH3A_AEWINSTART_WINSH_SHIFT; ++ start |= conf->ver_win_start << ISPH3A_AEWINSTART_WINSV_SHIFT; ++ ++ blk = conf->blk_ver_win_start << ISPH3A_AEWINBLK_WINSV_SHIFT; ++ blk |= ((conf->blk_win_height >> 1) - 1) << ISPH3A_AEWINBLK_WINH_SHIFT; ++ ++ subwin = ((conf->subsample_ver_inc >> 1) - 1) << ++ ISPH3A_AEWSUBWIN_AEWINCV_SHIFT; ++ subwin |= ((conf->subsample_hor_inc >> 1) - 1) << ++ ISPH3A_AEWSUBWIN_AEWINCH_SHIFT; ++ ++ isp_reg_writel(aewb->isp, win1, OMAP3_ISP_IOMEM_H3A, ISPH3A_AEWWIN1); ++ isp_reg_writel(aewb->isp, start, OMAP3_ISP_IOMEM_H3A, ++ ISPH3A_AEWINSTART); ++ isp_reg_writel(aewb->isp, blk, OMAP3_ISP_IOMEM_H3A, ISPH3A_AEWINBLK); ++ isp_reg_writel(aewb->isp, subwin, OMAP3_ISP_IOMEM_H3A, ++ ISPH3A_AEWSUBWIN); ++ isp_reg_clr_set(aewb->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR, ++ ISPH3A_PCR_AEW_MASK, pcr); ++ ++ aewb->update = 0; ++ aewb->config_counter += aewb->inc_config; ++ aewb->inc_config = 0; ++ aewb->buf_size = conf->buf_size; ++} ++ ++static void h3a_aewb_enable(struct ispstat *aewb, int enable) ++{ ++ if (enable) { ++ isp_reg_set(aewb->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR, ++ ISPH3A_PCR_AEW_EN); ++ /* This bit is already set if AF is enabled */ ++ if (aewb->isp->isp_af.state != ISPSTAT_ENABLED) ++ isp_reg_set(aewb->isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, ++ ISPCTRL_H3A_CLK_EN); ++ } else { ++ isp_reg_clr(aewb->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR, ++ ISPH3A_PCR_AEW_EN); ++ /* This bit can't be cleared if AF is enabled */ ++ if (aewb->isp->isp_af.state != ISPSTAT_ENABLED) ++ isp_reg_clr(aewb->isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, ++ ISPCTRL_H3A_CLK_EN); ++ } ++} ++ ++static int h3a_aewb_busy(struct ispstat *aewb) ++{ ++ return isp_reg_readl(aewb->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR) ++ & ISPH3A_PCR_BUSYAEAWB; ++} ++ ++static u32 h3a_aewb_get_buf_size(struct omap3isp_h3a_aewb_config *conf) ++{ ++ /* Number of configured windows + extra row for black data */ ++ u32 win_count = (conf->ver_win_count + 1) * conf->hor_win_count; ++ ++ /* ++ * Unsaturated block counts for each 8 windows. ++ * 1 extra for the last (win_count % 8) windows if win_count is not ++ * divisible by 8. ++ */ ++ win_count += (win_count + 7) / 8; ++ ++ return win_count * AEWB_PACKET_SIZE; ++} ++ ++static int h3a_aewb_validate_params(struct ispstat *aewb, void *new_conf) ++{ ++ struct omap3isp_h3a_aewb_config *user_cfg = new_conf; ++ u32 buf_size; ++ ++ if (unlikely(user_cfg->saturation_limit > ++ OMAP3ISP_AEWB_MAX_SATURATION_LIM)) ++ return -EINVAL; ++ ++ if (unlikely(user_cfg->win_height < OMAP3ISP_AEWB_MIN_WIN_H || ++ user_cfg->win_height > OMAP3ISP_AEWB_MAX_WIN_H || ++ user_cfg->win_height & 0x01)) ++ return -EINVAL; ++ ++ if (unlikely(user_cfg->win_width < OMAP3ISP_AEWB_MIN_WIN_W || ++ user_cfg->win_width > OMAP3ISP_AEWB_MAX_WIN_W || ++ user_cfg->win_width & 0x01)) ++ return -EINVAL; ++ ++ if (unlikely(user_cfg->ver_win_count < OMAP3ISP_AEWB_MIN_WINVC || ++ user_cfg->ver_win_count > OMAP3ISP_AEWB_MAX_WINVC)) ++ return -EINVAL; ++ ++ if (unlikely(user_cfg->hor_win_count < OMAP3ISP_AEWB_MIN_WINHC || ++ user_cfg->hor_win_count > OMAP3ISP_AEWB_MAX_WINHC)) ++ return -EINVAL; ++ ++ if (unlikely(user_cfg->ver_win_start > OMAP3ISP_AEWB_MAX_WINSTART)) ++ return -EINVAL; ++ ++ if (unlikely(user_cfg->hor_win_start > OMAP3ISP_AEWB_MAX_WINSTART)) ++ return -EINVAL; ++ ++ if (unlikely(user_cfg->blk_ver_win_start > OMAP3ISP_AEWB_MAX_WINSTART)) ++ return -EINVAL; ++ ++ if (unlikely(user_cfg->blk_win_height < OMAP3ISP_AEWB_MIN_WIN_H || ++ user_cfg->blk_win_height > OMAP3ISP_AEWB_MAX_WIN_H || ++ user_cfg->blk_win_height & 0x01)) ++ return -EINVAL; ++ ++ if (unlikely(user_cfg->subsample_ver_inc < OMAP3ISP_AEWB_MIN_SUB_INC || ++ user_cfg->subsample_ver_inc > OMAP3ISP_AEWB_MAX_SUB_INC || ++ user_cfg->subsample_ver_inc & 0x01)) ++ return -EINVAL; ++ ++ if (unlikely(user_cfg->subsample_hor_inc < OMAP3ISP_AEWB_MIN_SUB_INC || ++ user_cfg->subsample_hor_inc > OMAP3ISP_AEWB_MAX_SUB_INC || ++ user_cfg->subsample_hor_inc & 0x01)) ++ return -EINVAL; ++ ++ buf_size = h3a_aewb_get_buf_size(user_cfg); ++ if (buf_size > user_cfg->buf_size) ++ user_cfg->buf_size = buf_size; ++ else if (user_cfg->buf_size > OMAP3ISP_AEWB_MAX_BUF_SIZE) ++ user_cfg->buf_size = OMAP3ISP_AEWB_MAX_BUF_SIZE; ++ ++ return 0; ++} ++ ++/* ++ * h3a_aewb_set_params - Helper function to check & store user given params. ++ * @new_conf: Pointer to AE and AWB parameters struct. ++ * ++ * As most of them are busy-lock registers, need to wait until AEW_BUSY = 0 to ++ * program them during ISR. ++ */ ++static void h3a_aewb_set_params(struct ispstat *aewb, void *new_conf) ++{ ++ struct omap3isp_h3a_aewb_config *user_cfg = new_conf; ++ struct omap3isp_h3a_aewb_config *cur_cfg = aewb->priv; ++ int update = 0; ++ ++ if (cur_cfg->saturation_limit != user_cfg->saturation_limit) { ++ cur_cfg->saturation_limit = user_cfg->saturation_limit; ++ update = 1; ++ } ++ if (cur_cfg->alaw_enable != user_cfg->alaw_enable) { ++ cur_cfg->alaw_enable = user_cfg->alaw_enable; ++ update = 1; ++ } ++ if (cur_cfg->win_height != user_cfg->win_height) { ++ cur_cfg->win_height = user_cfg->win_height; ++ update = 1; ++ } ++ if (cur_cfg->win_width != user_cfg->win_width) { ++ cur_cfg->win_width = user_cfg->win_width; ++ update = 1; ++ } ++ if (cur_cfg->ver_win_count != user_cfg->ver_win_count) { ++ cur_cfg->ver_win_count = user_cfg->ver_win_count; ++ update = 1; ++ } ++ if (cur_cfg->hor_win_count != user_cfg->hor_win_count) { ++ cur_cfg->hor_win_count = user_cfg->hor_win_count; ++ update = 1; ++ } ++ if (cur_cfg->ver_win_start != user_cfg->ver_win_start) { ++ cur_cfg->ver_win_start = user_cfg->ver_win_start; ++ update = 1; ++ } ++ if (cur_cfg->hor_win_start != user_cfg->hor_win_start) { ++ cur_cfg->hor_win_start = user_cfg->hor_win_start; ++ update = 1; ++ } ++ if (cur_cfg->blk_ver_win_start != user_cfg->blk_ver_win_start) { ++ cur_cfg->blk_ver_win_start = user_cfg->blk_ver_win_start; ++ update = 1; ++ } ++ if (cur_cfg->blk_win_height != user_cfg->blk_win_height) { ++ cur_cfg->blk_win_height = user_cfg->blk_win_height; ++ update = 1; ++ } ++ if (cur_cfg->subsample_ver_inc != user_cfg->subsample_ver_inc) { ++ cur_cfg->subsample_ver_inc = user_cfg->subsample_ver_inc; ++ update = 1; ++ } ++ if (cur_cfg->subsample_hor_inc != user_cfg->subsample_hor_inc) { ++ cur_cfg->subsample_hor_inc = user_cfg->subsample_hor_inc; ++ update = 1; ++ } ++ ++ if (update || !aewb->configured) { ++ aewb->inc_config++; ++ aewb->update = 1; ++ cur_cfg->buf_size = h3a_aewb_get_buf_size(cur_cfg); ++ } ++} ++ ++static long h3a_aewb_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) ++{ ++ struct ispstat *stat = v4l2_get_subdevdata(sd); ++ ++ switch (cmd) { ++ case VIDIOC_OMAP3ISP_AEWB_CFG: ++ return omap3isp_stat_config(stat, arg); ++ case VIDIOC_OMAP3ISP_STAT_REQ: ++ return omap3isp_stat_request_statistics(stat, arg); ++ case VIDIOC_OMAP3ISP_STAT_EN: { ++ unsigned long *en = arg; ++ return omap3isp_stat_enable(stat, !!*en); ++ } ++ } ++ ++ return -ENOIOCTLCMD; ++} ++ ++static const struct ispstat_ops h3a_aewb_ops = { ++ .validate_params = h3a_aewb_validate_params, ++ .set_params = h3a_aewb_set_params, ++ .setup_regs = h3a_aewb_setup_regs, ++ .enable = h3a_aewb_enable, ++ .busy = h3a_aewb_busy, ++}; ++ ++static const struct v4l2_subdev_core_ops h3a_aewb_subdev_core_ops = { ++ .ioctl = h3a_aewb_ioctl, ++ .subscribe_event = omap3isp_stat_subscribe_event, ++ .unsubscribe_event = omap3isp_stat_unsubscribe_event, ++}; ++ ++static const struct v4l2_subdev_video_ops h3a_aewb_subdev_video_ops = { ++ .s_stream = omap3isp_stat_s_stream, ++}; ++ ++static const struct v4l2_subdev_ops h3a_aewb_subdev_ops = { ++ .core = &h3a_aewb_subdev_core_ops, ++ .video = &h3a_aewb_subdev_video_ops, ++}; ++ ++/* ++ * omap3isp_h3a_aewb_init - Module Initialisation. ++ */ ++int omap3isp_h3a_aewb_init(struct isp_device *isp) ++{ ++ struct ispstat *aewb = &isp->isp_aewb; ++ struct omap3isp_h3a_aewb_config *aewb_cfg; ++ struct omap3isp_h3a_aewb_config *aewb_recover_cfg; ++ int ret; ++ ++ aewb_cfg = kzalloc(sizeof(*aewb_cfg), GFP_KERNEL); ++ if (!aewb_cfg) ++ return -ENOMEM; ++ ++ memset(aewb, 0, sizeof(*aewb)); ++ aewb->ops = &h3a_aewb_ops; ++ aewb->priv = aewb_cfg; ++ aewb->dma_ch = -1; ++ aewb->event_type = V4L2_EVENT_OMAP3ISP_AEWB; ++ aewb->isp = isp; ++ ++ /* Set recover state configuration */ ++ aewb_recover_cfg = kzalloc(sizeof(*aewb_recover_cfg), GFP_KERNEL); ++ if (!aewb_recover_cfg) { ++ dev_err(aewb->isp->dev, "AEWB: cannot allocate memory for " ++ "recover configuration.\n"); ++ ret = -ENOMEM; ++ goto err_recover_alloc; ++ } ++ ++ aewb_recover_cfg->saturation_limit = OMAP3ISP_AEWB_MAX_SATURATION_LIM; ++ aewb_recover_cfg->win_height = OMAP3ISP_AEWB_MIN_WIN_H; ++ aewb_recover_cfg->win_width = OMAP3ISP_AEWB_MIN_WIN_W; ++ aewb_recover_cfg->ver_win_count = OMAP3ISP_AEWB_MIN_WINVC; ++ aewb_recover_cfg->hor_win_count = OMAP3ISP_AEWB_MIN_WINHC; ++ aewb_recover_cfg->blk_ver_win_start = aewb_recover_cfg->ver_win_start + ++ aewb_recover_cfg->win_height * aewb_recover_cfg->ver_win_count; ++ aewb_recover_cfg->blk_win_height = OMAP3ISP_AEWB_MIN_WIN_H; ++ aewb_recover_cfg->subsample_ver_inc = OMAP3ISP_AEWB_MIN_SUB_INC; ++ aewb_recover_cfg->subsample_hor_inc = OMAP3ISP_AEWB_MIN_SUB_INC; ++ ++ if (h3a_aewb_validate_params(aewb, aewb_recover_cfg)) { ++ dev_err(aewb->isp->dev, "AEWB: recover configuration is " ++ "invalid.\n"); ++ ret = -EINVAL; ++ goto err_conf; ++ } ++ ++ aewb_recover_cfg->buf_size = h3a_aewb_get_buf_size(aewb_recover_cfg); ++ aewb->recover_priv = aewb_recover_cfg; ++ ++ ret = omap3isp_stat_init(aewb, "AEWB", &h3a_aewb_subdev_ops); ++ if (ret) ++ goto err_conf; ++ ++ return 0; ++ ++err_conf: ++ kfree(aewb_recover_cfg); ++err_recover_alloc: ++ kfree(aewb_cfg); ++ ++ return ret; ++} ++ ++/* ++ * omap3isp_h3a_aewb_cleanup - Module exit. ++ */ ++void omap3isp_h3a_aewb_cleanup(struct isp_device *isp) ++{ ++ kfree(isp->isp_aewb.priv); ++ kfree(isp->isp_aewb.recover_priv); ++ omap3isp_stat_free(&isp->isp_aewb); ++} +diff --git a/drivers/media/video/isp/isph3a_af.c b/drivers/media/video/isp/isph3a_af.c +new file mode 100644 +index 0000000..c32a18e +--- /dev/null ++++ b/drivers/media/video/isp/isph3a_af.c +@@ -0,0 +1,429 @@ ++/* ++ * isph3a_af.c ++ * ++ * TI OMAP3 ISP - H3A AF module ++ * ++ * Copyright (C) 2010 Nokia Corporation ++ * Copyright (C) 2009 Texas Instruments, Inc. ++ * ++ * Contacts: David Cohen ++ * Laurent Pinchart ++ * Sakari Ailus ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++/* Linux specific include files */ ++#include ++#include ++ ++#include "isp.h" ++#include "isph3a.h" ++#include "ispstat.h" ++ ++#define IS_OUT_OF_BOUNDS(value, min, max) \ ++ (((value) < (min)) || ((value) > (max))) ++ ++static void h3a_af_setup_regs(struct ispstat *af, void *priv) ++{ ++ struct omap3isp_h3a_af_config *conf = priv; ++ u32 pcr; ++ u32 pax1; ++ u32 pax2; ++ u32 paxstart; ++ u32 coef; ++ u32 base_coef_set0; ++ u32 base_coef_set1; ++ int index; ++ ++ if (af->state == ISPSTAT_DISABLED) ++ return; ++ ++ isp_reg_writel(af->isp, af->active_buf->iommu_addr, OMAP3_ISP_IOMEM_H3A, ++ ISPH3A_AFBUFST); ++ ++ if (!af->update) ++ return; ++ ++ /* Configure Hardware Registers */ ++ pax1 = ((conf->paxel.width >> 1) - 1) << AF_PAXW_SHIFT; ++ /* Set height in AFPAX1 */ ++ pax1 |= (conf->paxel.height >> 1) - 1; ++ isp_reg_writel(af->isp, pax1, OMAP3_ISP_IOMEM_H3A, ISPH3A_AFPAX1); ++ ++ /* Configure AFPAX2 Register */ ++ /* Set Line Increment in AFPAX2 Register */ ++ pax2 = ((conf->paxel.line_inc >> 1) - 1) << AF_LINE_INCR_SHIFT; ++ /* Set Vertical Count */ ++ pax2 |= (conf->paxel.v_cnt - 1) << AF_VT_COUNT_SHIFT; ++ /* Set Horizontal Count */ ++ pax2 |= (conf->paxel.h_cnt - 1); ++ isp_reg_writel(af->isp, pax2, OMAP3_ISP_IOMEM_H3A, ISPH3A_AFPAX2); ++ ++ /* Configure PAXSTART Register */ ++ /*Configure Horizontal Start */ ++ paxstart = conf->paxel.h_start << AF_HZ_START_SHIFT; ++ /* Configure Vertical Start */ ++ paxstart |= conf->paxel.v_start; ++ isp_reg_writel(af->isp, paxstart, OMAP3_ISP_IOMEM_H3A, ++ ISPH3A_AFPAXSTART); ++ ++ /*SetIIRSH Register */ ++ isp_reg_writel(af->isp, conf->iir.h_start, ++ OMAP3_ISP_IOMEM_H3A, ISPH3A_AFIIRSH); ++ ++ base_coef_set0 = ISPH3A_AFCOEF010; ++ base_coef_set1 = ISPH3A_AFCOEF110; ++ for (index = 0; index <= 8; index += 2) { ++ /*Set IIR Filter0 Coefficients */ ++ coef = 0; ++ coef |= conf->iir.coeff_set0[index]; ++ coef |= conf->iir.coeff_set0[index + 1] << ++ AF_COEF_SHIFT; ++ isp_reg_writel(af->isp, coef, OMAP3_ISP_IOMEM_H3A, ++ base_coef_set0); ++ base_coef_set0 += AFCOEF_OFFSET; ++ ++ /*Set IIR Filter1 Coefficients */ ++ coef = 0; ++ coef |= conf->iir.coeff_set1[index]; ++ coef |= conf->iir.coeff_set1[index + 1] << ++ AF_COEF_SHIFT; ++ isp_reg_writel(af->isp, coef, OMAP3_ISP_IOMEM_H3A, ++ base_coef_set1); ++ base_coef_set1 += AFCOEF_OFFSET; ++ } ++ /* set AFCOEF0010 Register */ ++ isp_reg_writel(af->isp, conf->iir.coeff_set0[10], ++ OMAP3_ISP_IOMEM_H3A, ISPH3A_AFCOEF0010); ++ /* set AFCOEF1010 Register */ ++ isp_reg_writel(af->isp, conf->iir.coeff_set1[10], ++ OMAP3_ISP_IOMEM_H3A, ISPH3A_AFCOEF1010); ++ ++ /* PCR Register */ ++ /* Set RGB Position */ ++ pcr = conf->rgb_pos << AF_RGBPOS_SHIFT; ++ /* Set Accumulator Mode */ ++ if (conf->fvmode == OMAP3ISP_AF_MODE_PEAK) ++ pcr |= AF_FVMODE; ++ /* Set A-law */ ++ if (conf->alaw_enable) ++ pcr |= AF_ALAW_EN; ++ /* HMF Configurations */ ++ if (conf->hmf.enable) { ++ /* Enable HMF */ ++ pcr |= AF_MED_EN; ++ /* Set Median Threshold */ ++ pcr |= conf->hmf.threshold << AF_MED_TH_SHIFT; ++ } ++ /* Set PCR Register */ ++ isp_reg_clr_set(af->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR, ++ AF_PCR_MASK, pcr); ++ ++ af->update = 0; ++ af->config_counter += af->inc_config; ++ af->inc_config = 0; ++ af->buf_size = conf->buf_size; ++} ++ ++static void h3a_af_enable(struct ispstat *af, int enable) ++{ ++ if (enable) { ++ isp_reg_set(af->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR, ++ ISPH3A_PCR_AF_EN); ++ /* This bit is already set if AEWB is enabled */ ++ if (af->isp->isp_aewb.state != ISPSTAT_ENABLED) ++ isp_reg_set(af->isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, ++ ISPCTRL_H3A_CLK_EN); ++ } else { ++ isp_reg_clr(af->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR, ++ ISPH3A_PCR_AF_EN); ++ /* This bit can't be cleared if AEWB is enabled */ ++ if (af->isp->isp_aewb.state != ISPSTAT_ENABLED) ++ isp_reg_clr(af->isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, ++ ISPCTRL_H3A_CLK_EN); ++ } ++} ++ ++static int h3a_af_busy(struct ispstat *af) ++{ ++ return isp_reg_readl(af->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR) ++ & ISPH3A_PCR_BUSYAF; ++} ++ ++static u32 h3a_af_get_buf_size(struct omap3isp_h3a_af_config *conf) ++{ ++ return conf->paxel.h_cnt * conf->paxel.v_cnt * OMAP3ISP_AF_PAXEL_SIZE; ++} ++ ++/* Function to check paxel parameters */ ++static int h3a_af_validate_params(struct ispstat *af, void *new_conf) ++{ ++ struct omap3isp_h3a_af_config *user_cfg = new_conf; ++ struct omap3isp_h3a_af_paxel *paxel_cfg = &user_cfg->paxel; ++ struct omap3isp_h3a_af_iir *iir_cfg = &user_cfg->iir; ++ int index; ++ u32 buf_size; ++ ++ /* Check horizontal Count */ ++ if (IS_OUT_OF_BOUNDS(paxel_cfg->h_cnt, ++ OMAP3ISP_AF_PAXEL_HORIZONTAL_COUNT_MIN, ++ OMAP3ISP_AF_PAXEL_HORIZONTAL_COUNT_MAX)) ++ return -EINVAL; ++ ++ /* Check Vertical Count */ ++ if (IS_OUT_OF_BOUNDS(paxel_cfg->v_cnt, ++ OMAP3ISP_AF_PAXEL_VERTICAL_COUNT_MIN, ++ OMAP3ISP_AF_PAXEL_VERTICAL_COUNT_MAX)) ++ return -EINVAL; ++ ++ if (IS_OUT_OF_BOUNDS(paxel_cfg->height, OMAP3ISP_AF_PAXEL_HEIGHT_MIN, ++ OMAP3ISP_AF_PAXEL_HEIGHT_MAX) || ++ paxel_cfg->height % 2) ++ return -EINVAL; ++ ++ /* Check width */ ++ if (IS_OUT_OF_BOUNDS(paxel_cfg->width, OMAP3ISP_AF_PAXEL_WIDTH_MIN, ++ OMAP3ISP_AF_PAXEL_WIDTH_MAX) || ++ paxel_cfg->width % 2) ++ return -EINVAL; ++ ++ /* Check Line Increment */ ++ if (IS_OUT_OF_BOUNDS(paxel_cfg->line_inc, ++ OMAP3ISP_AF_PAXEL_INCREMENT_MIN, ++ OMAP3ISP_AF_PAXEL_INCREMENT_MAX) || ++ paxel_cfg->line_inc % 2) ++ return -EINVAL; ++ ++ /* Check Horizontal Start */ ++ if ((paxel_cfg->h_start < iir_cfg->h_start) || ++ IS_OUT_OF_BOUNDS(paxel_cfg->h_start, ++ OMAP3ISP_AF_PAXEL_HZSTART_MIN, ++ OMAP3ISP_AF_PAXEL_HZSTART_MAX)) ++ return -EINVAL; ++ ++ /* Check IIR */ ++ for (index = 0; index < OMAP3ISP_AF_NUM_COEF; index++) { ++ if ((iir_cfg->coeff_set0[index]) > OMAP3ISP_AF_COEF_MAX) ++ return -EINVAL; ++ ++ if ((iir_cfg->coeff_set1[index]) > OMAP3ISP_AF_COEF_MAX) ++ return -EINVAL; ++ } ++ ++ if (IS_OUT_OF_BOUNDS(iir_cfg->h_start, OMAP3ISP_AF_IIRSH_MIN, ++ OMAP3ISP_AF_IIRSH_MAX)) ++ return -EINVAL; ++ ++ /* Hack: If paxel size is 12, the 10th AF window may be corrupted */ ++ if ((paxel_cfg->h_cnt * paxel_cfg->v_cnt > 9) && ++ (paxel_cfg->width * paxel_cfg->height == 12)) ++ return -EINVAL; ++ ++ buf_size = h3a_af_get_buf_size(user_cfg); ++ if (buf_size > user_cfg->buf_size) ++ /* User buf_size request wasn't enough */ ++ user_cfg->buf_size = buf_size; ++ else if (user_cfg->buf_size > OMAP3ISP_AF_MAX_BUF_SIZE) ++ user_cfg->buf_size = OMAP3ISP_AF_MAX_BUF_SIZE; ++ ++ return 0; ++} ++ ++/* Update local parameters */ ++static void h3a_af_set_params(struct ispstat *af, void *new_conf) ++{ ++ struct omap3isp_h3a_af_config *user_cfg = new_conf; ++ struct omap3isp_h3a_af_config *cur_cfg = af->priv; ++ int update = 0; ++ int index; ++ ++ /* alaw */ ++ if (cur_cfg->alaw_enable != user_cfg->alaw_enable) { ++ update = 1; ++ goto out; ++ } ++ ++ /* hmf */ ++ if (cur_cfg->hmf.enable != user_cfg->hmf.enable) { ++ update = 1; ++ goto out; ++ } ++ if (cur_cfg->hmf.threshold != user_cfg->hmf.threshold) { ++ update = 1; ++ goto out; ++ } ++ ++ /* rgbpos */ ++ if (cur_cfg->rgb_pos != user_cfg->rgb_pos) { ++ update = 1; ++ goto out; ++ } ++ ++ /* iir */ ++ if (cur_cfg->iir.h_start != user_cfg->iir.h_start) { ++ update = 1; ++ goto out; ++ } ++ for (index = 0; index < OMAP3ISP_AF_NUM_COEF; index++) { ++ if (cur_cfg->iir.coeff_set0[index] != ++ user_cfg->iir.coeff_set0[index]) { ++ update = 1; ++ goto out; ++ } ++ if (cur_cfg->iir.coeff_set1[index] != ++ user_cfg->iir.coeff_set1[index]) { ++ update = 1; ++ goto out; ++ } ++ } ++ ++ /* paxel */ ++ if ((cur_cfg->paxel.width != user_cfg->paxel.width) || ++ (cur_cfg->paxel.height != user_cfg->paxel.height) || ++ (cur_cfg->paxel.h_start != user_cfg->paxel.h_start) || ++ (cur_cfg->paxel.v_start != user_cfg->paxel.v_start) || ++ (cur_cfg->paxel.h_cnt != user_cfg->paxel.h_cnt) || ++ (cur_cfg->paxel.v_cnt != user_cfg->paxel.v_cnt) || ++ (cur_cfg->paxel.line_inc != user_cfg->paxel.line_inc)) { ++ update = 1; ++ goto out; ++ } ++ ++ /* af_mode */ ++ if (cur_cfg->fvmode != user_cfg->fvmode) ++ update = 1; ++ ++out: ++ if (update || !af->configured) { ++ memcpy(cur_cfg, user_cfg, sizeof(*cur_cfg)); ++ af->inc_config++; ++ af->update = 1; ++ /* ++ * User might be asked for a bigger buffer than necessary for ++ * this configuration. In order to return the right amount of ++ * data during buffer request, let's calculate the size here ++ * instead of stick with user_cfg->buf_size. ++ */ ++ cur_cfg->buf_size = h3a_af_get_buf_size(cur_cfg); ++ } ++} ++ ++static long h3a_af_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) ++{ ++ struct ispstat *stat = v4l2_get_subdevdata(sd); ++ ++ switch (cmd) { ++ case VIDIOC_OMAP3ISP_AF_CFG: ++ return omap3isp_stat_config(stat, arg); ++ case VIDIOC_OMAP3ISP_STAT_REQ: ++ return omap3isp_stat_request_statistics(stat, arg); ++ case VIDIOC_OMAP3ISP_STAT_EN: { ++ int *en = arg; ++ return omap3isp_stat_enable(stat, !!*en); ++ } ++ } ++ ++ return -ENOIOCTLCMD; ++ ++} ++ ++static const struct ispstat_ops h3a_af_ops = { ++ .validate_params = h3a_af_validate_params, ++ .set_params = h3a_af_set_params, ++ .setup_regs = h3a_af_setup_regs, ++ .enable = h3a_af_enable, ++ .busy = h3a_af_busy, ++}; ++ ++static const struct v4l2_subdev_core_ops h3a_af_subdev_core_ops = { ++ .ioctl = h3a_af_ioctl, ++ .subscribe_event = omap3isp_stat_subscribe_event, ++ .unsubscribe_event = omap3isp_stat_unsubscribe_event, ++}; ++ ++static const struct v4l2_subdev_video_ops h3a_af_subdev_video_ops = { ++ .s_stream = omap3isp_stat_s_stream, ++}; ++ ++static const struct v4l2_subdev_ops h3a_af_subdev_ops = { ++ .core = &h3a_af_subdev_core_ops, ++ .video = &h3a_af_subdev_video_ops, ++}; ++ ++/* Function to register the AF character device driver. */ ++int omap3isp_h3a_af_init(struct isp_device *isp) ++{ ++ struct ispstat *af = &isp->isp_af; ++ struct omap3isp_h3a_af_config *af_cfg; ++ struct omap3isp_h3a_af_config *af_recover_cfg; ++ int ret; ++ ++ af_cfg = kzalloc(sizeof(*af_cfg), GFP_KERNEL); ++ if (af_cfg == NULL) ++ return -ENOMEM; ++ ++ memset(af, 0, sizeof(*af)); ++ af->ops = &h3a_af_ops; ++ af->priv = af_cfg; ++ af->dma_ch = -1; ++ af->event_type = V4L2_EVENT_OMAP3ISP_AF; ++ af->isp = isp; ++ ++ /* Set recover state configuration */ ++ af_recover_cfg = kzalloc(sizeof(*af_recover_cfg), GFP_KERNEL); ++ if (!af_recover_cfg) { ++ dev_err(af->isp->dev, "AF: cannot allocate memory for recover " ++ "configuration.\n"); ++ ret = -ENOMEM; ++ goto err_recover_alloc; ++ } ++ ++ af_recover_cfg->paxel.h_start = OMAP3ISP_AF_PAXEL_HZSTART_MIN; ++ af_recover_cfg->paxel.width = OMAP3ISP_AF_PAXEL_WIDTH_MIN; ++ af_recover_cfg->paxel.height = OMAP3ISP_AF_PAXEL_HEIGHT_MIN; ++ af_recover_cfg->paxel.h_cnt = OMAP3ISP_AF_PAXEL_HORIZONTAL_COUNT_MIN; ++ af_recover_cfg->paxel.v_cnt = OMAP3ISP_AF_PAXEL_VERTICAL_COUNT_MIN; ++ af_recover_cfg->paxel.line_inc = OMAP3ISP_AF_PAXEL_INCREMENT_MIN; ++ if (h3a_af_validate_params(af, af_recover_cfg)) { ++ dev_err(af->isp->dev, "AF: recover configuration is " ++ "invalid.\n"); ++ ret = -EINVAL; ++ goto err_conf; ++ } ++ ++ af_recover_cfg->buf_size = h3a_af_get_buf_size(af_recover_cfg); ++ af->recover_priv = af_recover_cfg; ++ ++ ret = omap3isp_stat_init(af, "AF", &h3a_af_subdev_ops); ++ if (ret) ++ goto err_conf; ++ ++ return 0; ++ ++err_conf: ++ kfree(af_recover_cfg); ++err_recover_alloc: ++ kfree(af_cfg); ++ ++ return ret; ++} ++ ++void omap3isp_h3a_af_cleanup(struct isp_device *isp) ++{ ++ kfree(isp->isp_af.priv); ++ kfree(isp->isp_af.recover_priv); ++ omap3isp_stat_free(&isp->isp_af); ++} +diff --git a/drivers/media/video/isp/isphist.c b/drivers/media/video/isp/isphist.c +new file mode 100644 +index 0000000..a43eb92 +--- /dev/null ++++ b/drivers/media/video/isp/isphist.c +@@ -0,0 +1,520 @@ ++/* ++ * isphist.c ++ * ++ * TI OMAP3 ISP - Histogram module ++ * ++ * Copyright (C) 2010 Nokia Corporation ++ * Copyright (C) 2009 Texas Instruments, Inc. ++ * ++ * Contacts: David Cohen ++ * Laurent Pinchart ++ * Sakari Ailus ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include "isp.h" ++#include "ispreg.h" ++#include "isphist.h" ++ ++#define HIST_CONFIG_DMA 1 ++ ++#define HIST_USING_DMA(hist) ((hist)->dma_ch >= 0) ++ ++/* ++ * hist_reset_mem - clear Histogram memory before start stats engine. ++ */ ++static void hist_reset_mem(struct ispstat *hist) ++{ ++ struct isp_device *isp = hist->isp; ++ struct omap3isp_hist_config *conf = hist->priv; ++ unsigned int i; ++ ++ isp_reg_writel(isp, 0, OMAP3_ISP_IOMEM_HIST, ISPHIST_ADDR); ++ ++ /* ++ * By setting it, the histogram internal buffer is being cleared at the ++ * same time it's being read. This bit must be cleared afterwards. ++ */ ++ isp_reg_set(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT, ISPHIST_CNT_CLEAR); ++ ++ /* ++ * We'll clear 4 words at each iteration for optimization. It avoids ++ * 3/4 of the jumps. We also know HIST_MEM_SIZE is divisible by 4. ++ */ ++ for (i = OMAP3ISP_HIST_MEM_SIZE / 4; i > 0; i--) { ++ isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA); ++ isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA); ++ isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA); ++ isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA); ++ } ++ isp_reg_clr(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT, ISPHIST_CNT_CLEAR); ++ ++ hist->wait_acc_frames = conf->num_acc_frames; ++} ++ ++static void hist_dma_config(struct ispstat *hist) ++{ ++ hist->dma_config.data_type = OMAP_DMA_DATA_TYPE_S32; ++ hist->dma_config.sync_mode = OMAP_DMA_SYNC_ELEMENT; ++ hist->dma_config.frame_count = 1; ++ hist->dma_config.src_amode = OMAP_DMA_AMODE_CONSTANT; ++ hist->dma_config.src_start = OMAP3ISP_HIST_REG_BASE + ISPHIST_DATA; ++ hist->dma_config.dst_amode = OMAP_DMA_AMODE_POST_INC; ++ hist->dma_config.src_or_dst_synch = OMAP_DMA_SRC_SYNC; ++} ++ ++/* ++ * hist_setup_regs - Helper function to update Histogram registers. ++ */ ++static void hist_setup_regs(struct ispstat *hist, void *priv) ++{ ++ struct isp_device *isp = hist->isp; ++ struct omap3isp_hist_config *conf = priv; ++ int c; ++ u32 cnt; ++ u32 wb_gain; ++ u32 reg_hor[OMAP3ISP_HIST_MAX_REGIONS]; ++ u32 reg_ver[OMAP3ISP_HIST_MAX_REGIONS]; ++ ++ if (!hist->update || hist->state == ISPSTAT_DISABLED || ++ hist->state == ISPSTAT_DISABLING) ++ return; ++ ++ cnt = conf->cfa << ISPHIST_CNT_CFA_SHIFT; ++ ++ wb_gain = conf->wg[0] << ISPHIST_WB_GAIN_WG00_SHIFT; ++ wb_gain |= conf->wg[1] << ISPHIST_WB_GAIN_WG01_SHIFT; ++ wb_gain |= conf->wg[2] << ISPHIST_WB_GAIN_WG02_SHIFT; ++ if (conf->cfa == OMAP3ISP_HIST_CFA_BAYER) ++ wb_gain |= conf->wg[3] << ISPHIST_WB_GAIN_WG03_SHIFT; ++ ++ /* Regions size and position */ ++ for (c = 0; c < OMAP3ISP_HIST_MAX_REGIONS; c++) { ++ if (c < conf->num_regions) { ++ reg_hor[c] = conf->region[c].h_start << ++ ISPHIST_REG_START_SHIFT; ++ reg_hor[c] = conf->region[c].h_end << ++ ISPHIST_REG_END_SHIFT; ++ reg_ver[c] = conf->region[c].v_start << ++ ISPHIST_REG_START_SHIFT; ++ reg_ver[c] = conf->region[c].v_end << ++ ISPHIST_REG_END_SHIFT; ++ } else { ++ reg_hor[c] = 0; ++ reg_ver[c] = 0; ++ } ++ } ++ ++ cnt |= conf->hist_bins << ISPHIST_CNT_BINS_SHIFT; ++ switch (conf->hist_bins) { ++ case OMAP3ISP_HIST_BINS_256: ++ cnt |= (ISPHIST_IN_BIT_WIDTH_CCDC - 8) << ++ ISPHIST_CNT_SHIFT_SHIFT; ++ break; ++ case OMAP3ISP_HIST_BINS_128: ++ cnt |= (ISPHIST_IN_BIT_WIDTH_CCDC - 7) << ++ ISPHIST_CNT_SHIFT_SHIFT; ++ break; ++ case OMAP3ISP_HIST_BINS_64: ++ cnt |= (ISPHIST_IN_BIT_WIDTH_CCDC - 6) << ++ ISPHIST_CNT_SHIFT_SHIFT; ++ break; ++ default: /* OMAP3ISP_HIST_BINS_32 */ ++ cnt |= (ISPHIST_IN_BIT_WIDTH_CCDC - 5) << ++ ISPHIST_CNT_SHIFT_SHIFT; ++ break; ++ } ++ ++ hist_reset_mem(hist); ++ ++ isp_reg_writel(isp, cnt, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT); ++ isp_reg_writel(isp, wb_gain, OMAP3_ISP_IOMEM_HIST, ISPHIST_WB_GAIN); ++ isp_reg_writel(isp, reg_hor[0], OMAP3_ISP_IOMEM_HIST, ISPHIST_R0_HORZ); ++ isp_reg_writel(isp, reg_ver[0], OMAP3_ISP_IOMEM_HIST, ISPHIST_R0_VERT); ++ isp_reg_writel(isp, reg_hor[1], OMAP3_ISP_IOMEM_HIST, ISPHIST_R1_HORZ); ++ isp_reg_writel(isp, reg_ver[1], OMAP3_ISP_IOMEM_HIST, ISPHIST_R1_VERT); ++ isp_reg_writel(isp, reg_hor[2], OMAP3_ISP_IOMEM_HIST, ISPHIST_R2_HORZ); ++ isp_reg_writel(isp, reg_ver[2], OMAP3_ISP_IOMEM_HIST, ISPHIST_R2_VERT); ++ isp_reg_writel(isp, reg_hor[3], OMAP3_ISP_IOMEM_HIST, ISPHIST_R3_HORZ); ++ isp_reg_writel(isp, reg_ver[3], OMAP3_ISP_IOMEM_HIST, ISPHIST_R3_VERT); ++ ++ hist->update = 0; ++ hist->config_counter += hist->inc_config; ++ hist->inc_config = 0; ++ hist->buf_size = conf->buf_size; ++} ++ ++static void hist_enable(struct ispstat *hist, int enable) ++{ ++ if (enable) { ++ isp_reg_set(hist->isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_PCR, ++ ISPHIST_PCR_ENABLE); ++ isp_reg_set(hist->isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, ++ ISPCTRL_HIST_CLK_EN); ++ } else { ++ isp_reg_clr(hist->isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_PCR, ++ ISPHIST_PCR_ENABLE); ++ isp_reg_clr(hist->isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, ++ ISPCTRL_HIST_CLK_EN); ++ } ++} ++ ++static int hist_busy(struct ispstat *hist) ++{ ++ return isp_reg_readl(hist->isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_PCR) ++ & ISPHIST_PCR_BUSY; ++} ++ ++static void hist_dma_cb(int lch, u16 ch_status, void *data) ++{ ++ struct ispstat *hist = data; ++ ++ if (ch_status & ~OMAP_DMA_BLOCK_IRQ) { ++ dev_dbg(hist->isp->dev, "hist: DMA error. status = 0x%04x\n", ++ ch_status); ++ omap_stop_dma(lch); ++ hist_reset_mem(hist); ++ atomic_set(&hist->buf_err, 1); ++ } ++ isp_reg_clr(hist->isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT, ++ ISPHIST_CNT_CLEAR); ++ ++ omap3isp_stat_dma_isr(hist); ++ if (hist->state != ISPSTAT_DISABLED) ++ omap3isp_hist_dma_done(hist->isp); ++} ++ ++static int hist_buf_dma(struct ispstat *hist) ++{ ++ dma_addr_t dma_addr = hist->active_buf->dma_addr; ++ ++ if (unlikely(!dma_addr)) { ++ dev_dbg(hist->isp->dev, "hist: invalid DMA buffer address\n"); ++ hist_reset_mem(hist); ++ return STAT_NO_BUF; ++ } ++ ++ isp_reg_writel(hist->isp, 0, OMAP3_ISP_IOMEM_HIST, ISPHIST_ADDR); ++ isp_reg_set(hist->isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT, ++ ISPHIST_CNT_CLEAR); ++ omap3isp_flush(hist->isp); ++ hist->dma_config.dst_start = dma_addr; ++ hist->dma_config.elem_count = hist->buf_size / sizeof(u32); ++ omap_set_dma_params(hist->dma_ch, &hist->dma_config); ++ ++ omap_start_dma(hist->dma_ch); ++ ++ return STAT_BUF_WAITING_DMA; ++} ++ ++static int hist_buf_pio(struct ispstat *hist) ++{ ++ struct isp_device *isp = hist->isp; ++ u32 *buf = hist->active_buf->virt_addr; ++ unsigned int i; ++ ++ if (!buf) { ++ dev_dbg(isp->dev, "hist: invalid PIO buffer address\n"); ++ hist_reset_mem(hist); ++ return STAT_NO_BUF; ++ } ++ ++ isp_reg_writel(isp, 0, OMAP3_ISP_IOMEM_HIST, ISPHIST_ADDR); ++ ++ /* ++ * By setting it, the histogram internal buffer is being cleared at the ++ * same time it's being read. This bit must be cleared just after all ++ * data is acquired. ++ */ ++ isp_reg_set(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT, ISPHIST_CNT_CLEAR); ++ ++ /* ++ * We'll read 4 times a 4-bytes-word at each iteration for ++ * optimization. It avoids 3/4 of the jumps. We also know buf_size is ++ * divisible by 16. ++ */ ++ for (i = hist->buf_size / 16; i > 0; i--) { ++ *buf++ = isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA); ++ *buf++ = isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA); ++ *buf++ = isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA); ++ *buf++ = isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA); ++ } ++ isp_reg_clr(hist->isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT, ++ ISPHIST_CNT_CLEAR); ++ ++ return STAT_BUF_DONE; ++} ++ ++/* ++ * hist_buf_process - Callback from ISP driver for HIST interrupt. ++ */ ++static int hist_buf_process(struct ispstat *hist) ++{ ++ struct omap3isp_hist_config *user_cfg = hist->priv; ++ int ret; ++ ++ if (atomic_read(&hist->buf_err) || hist->state != ISPSTAT_ENABLED) { ++ hist_reset_mem(hist); ++ return STAT_NO_BUF; ++ } ++ ++ if (--(hist->wait_acc_frames)) ++ return STAT_NO_BUF; ++ ++ if (HIST_USING_DMA(hist)) ++ ret = hist_buf_dma(hist); ++ else ++ ret = hist_buf_pio(hist); ++ ++ hist->wait_acc_frames = user_cfg->num_acc_frames; ++ ++ return ret; ++} ++ ++static u32 hist_get_buf_size(struct omap3isp_hist_config *conf) ++{ ++ return OMAP3ISP_HIST_MEM_SIZE_BINS(conf->hist_bins) * conf->num_regions; ++} ++ ++/* ++ * hist_validate_params - Helper function to check user given params. ++ * @user_cfg: Pointer to user configuration structure. ++ * ++ * Returns 0 on success configuration. ++ */ ++static int hist_validate_params(struct ispstat *hist, void *new_conf) ++{ ++ struct omap3isp_hist_config *user_cfg = new_conf; ++ int c; ++ u32 buf_size; ++ ++ if (user_cfg->cfa > OMAP3ISP_HIST_CFA_FOVEONX3) ++ return -EINVAL; ++ ++ /* Regions size and position */ ++ ++ if ((user_cfg->num_regions < OMAP3ISP_HIST_MIN_REGIONS) || ++ (user_cfg->num_regions > OMAP3ISP_HIST_MAX_REGIONS)) ++ return -EINVAL; ++ ++ /* Regions */ ++ for (c = 0; c < user_cfg->num_regions; c++) { ++ if (user_cfg->region[c].h_start & ~ISPHIST_REG_START_END_MASK) ++ return -EINVAL; ++ if (user_cfg->region[c].h_end & ~ISPHIST_REG_START_END_MASK) ++ return -EINVAL; ++ if (user_cfg->region[c].v_start & ~ISPHIST_REG_START_END_MASK) ++ return -EINVAL; ++ if (user_cfg->region[c].v_end & ~ISPHIST_REG_START_END_MASK) ++ return -EINVAL; ++ if (user_cfg->region[c].h_start > user_cfg->region[c].h_end) ++ return -EINVAL; ++ if (user_cfg->region[c].v_start > user_cfg->region[c].v_end) ++ return -EINVAL; ++ } ++ ++ switch (user_cfg->num_regions) { ++ case 1: ++ if (user_cfg->hist_bins > OMAP3ISP_HIST_BINS_256) ++ return -EINVAL; ++ break; ++ case 2: ++ if (user_cfg->hist_bins > OMAP3ISP_HIST_BINS_128) ++ return -EINVAL; ++ break; ++ default: /* 3 or 4 */ ++ if (user_cfg->hist_bins > OMAP3ISP_HIST_BINS_64) ++ return -EINVAL; ++ break; ++ } ++ ++ buf_size = hist_get_buf_size(user_cfg); ++ if (buf_size > user_cfg->buf_size) ++ /* User's buf_size request wasn't enoght */ ++ user_cfg->buf_size = buf_size; ++ else if (user_cfg->buf_size > OMAP3ISP_HIST_MAX_BUF_SIZE) ++ user_cfg->buf_size = OMAP3ISP_HIST_MAX_BUF_SIZE; ++ ++ return 0; ++} ++ ++static int hist_comp_params(struct ispstat *hist, ++ struct omap3isp_hist_config *user_cfg) ++{ ++ struct omap3isp_hist_config *cur_cfg = hist->priv; ++ int c; ++ ++ if (cur_cfg->cfa != user_cfg->cfa) ++ return 1; ++ ++ if (cur_cfg->num_acc_frames != user_cfg->num_acc_frames) ++ return 1; ++ ++ if (cur_cfg->hist_bins != user_cfg->hist_bins) ++ return 1; ++ ++ for (c = 0; c < OMAP3ISP_HIST_MAX_WG; c++) { ++ if (c == 3 && user_cfg->cfa == OMAP3ISP_HIST_CFA_FOVEONX3) ++ break; ++ else if (cur_cfg->wg[c] != user_cfg->wg[c]) ++ return 1; ++ } ++ ++ if (cur_cfg->num_regions != user_cfg->num_regions) ++ return 1; ++ ++ /* Regions */ ++ for (c = 0; c < user_cfg->num_regions; c++) { ++ if (cur_cfg->region[c].h_start != user_cfg->region[c].h_start) ++ return 1; ++ if (cur_cfg->region[c].h_end != user_cfg->region[c].h_end) ++ return 1; ++ if (cur_cfg->region[c].v_start != user_cfg->region[c].v_start) ++ return 1; ++ if (cur_cfg->region[c].v_end != user_cfg->region[c].v_end) ++ return 1; ++ } ++ ++ return 0; ++} ++ ++/* ++ * hist_update_params - Helper function to check and store user given params. ++ * @new_conf: Pointer to user configuration structure. ++ */ ++static void hist_set_params(struct ispstat *hist, void *new_conf) ++{ ++ struct omap3isp_hist_config *user_cfg = new_conf; ++ struct omap3isp_hist_config *cur_cfg = hist->priv; ++ ++ if (!hist->configured || hist_comp_params(hist, user_cfg)) { ++ memcpy(cur_cfg, user_cfg, sizeof(*user_cfg)); ++ if (user_cfg->num_acc_frames == 0) ++ user_cfg->num_acc_frames = 1; ++ hist->inc_config++; ++ hist->update = 1; ++ /* ++ * User might be asked for a bigger buffer than necessary for ++ * this configuration. In order to return the right amount of ++ * data during buffer request, let's calculate the size here ++ * instead of stick with user_cfg->buf_size. ++ */ ++ cur_cfg->buf_size = hist_get_buf_size(cur_cfg); ++ ++ } ++} ++ ++static long hist_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) ++{ ++ struct ispstat *stat = v4l2_get_subdevdata(sd); ++ ++ switch (cmd) { ++ case VIDIOC_OMAP3ISP_HIST_CFG: ++ return omap3isp_stat_config(stat, arg); ++ case VIDIOC_OMAP3ISP_STAT_REQ: ++ return omap3isp_stat_request_statistics(stat, arg); ++ case VIDIOC_OMAP3ISP_STAT_EN: { ++ int *en = arg; ++ return omap3isp_stat_enable(stat, !!*en); ++ } ++ } ++ ++ return -ENOIOCTLCMD; ++ ++} ++ ++static const struct ispstat_ops hist_ops = { ++ .validate_params = hist_validate_params, ++ .set_params = hist_set_params, ++ .setup_regs = hist_setup_regs, ++ .enable = hist_enable, ++ .busy = hist_busy, ++ .buf_process = hist_buf_process, ++}; ++ ++static const struct v4l2_subdev_core_ops hist_subdev_core_ops = { ++ .ioctl = hist_ioctl, ++ .subscribe_event = omap3isp_stat_subscribe_event, ++ .unsubscribe_event = omap3isp_stat_unsubscribe_event, ++}; ++ ++static const struct v4l2_subdev_video_ops hist_subdev_video_ops = { ++ .s_stream = omap3isp_stat_s_stream, ++}; ++ ++static const struct v4l2_subdev_ops hist_subdev_ops = { ++ .core = &hist_subdev_core_ops, ++ .video = &hist_subdev_video_ops, ++}; ++ ++/* ++ * omap3isp_hist_init - Module Initialization. ++ */ ++int omap3isp_hist_init(struct isp_device *isp) ++{ ++ struct ispstat *hist = &isp->isp_hist; ++ struct omap3isp_hist_config *hist_cfg; ++ int ret = -1; ++ ++ hist_cfg = kzalloc(sizeof(*hist_cfg), GFP_KERNEL); ++ if (hist_cfg == NULL) ++ return -ENOMEM; ++ ++ memset(hist, 0, sizeof(*hist)); ++ if (HIST_CONFIG_DMA) ++ ret = omap_request_dma(OMAP24XX_DMA_NO_DEVICE, "DMA_ISP_HIST", ++ hist_dma_cb, hist, &hist->dma_ch); ++ if (ret) { ++ if (HIST_CONFIG_DMA) ++ dev_warn(isp->dev, "hist: DMA request channel failed. " ++ "Using PIO only.\n"); ++ hist->dma_ch = -1; ++ } else { ++ dev_dbg(isp->dev, "hist: DMA channel = %d\n", hist->dma_ch); ++ hist_dma_config(hist); ++ omap_enable_dma_irq(hist->dma_ch, OMAP_DMA_BLOCK_IRQ); ++ } ++ ++ hist->ops = &hist_ops; ++ hist->priv = hist_cfg; ++ hist->event_type = V4L2_EVENT_OMAP3ISP_HIST; ++ hist->isp = isp; ++ ++ ret = omap3isp_stat_init(hist, "histogram", &hist_subdev_ops); ++ if (ret) { ++ kfree(hist_cfg); ++ if (HIST_USING_DMA(hist)) ++ omap_free_dma(hist->dma_ch); ++ } ++ ++ return ret; ++} ++ ++/* ++ * omap3isp_hist_cleanup - Module cleanup. ++ */ ++void omap3isp_hist_cleanup(struct isp_device *isp) ++{ ++ if (HIST_USING_DMA(&isp->isp_hist)) ++ omap_free_dma(isp->isp_hist.dma_ch); ++ kfree(isp->isp_hist.priv); ++ omap3isp_stat_free(&isp->isp_hist); ++} +diff --git a/drivers/media/video/isp/isphist.h b/drivers/media/video/isp/isphist.h +new file mode 100644 +index 0000000..247192b +--- /dev/null ++++ b/drivers/media/video/isp/isphist.h +@@ -0,0 +1,40 @@ ++/* ++ * isphist.h ++ * ++ * TI OMAP3 ISP - Histogram module ++ * ++ * Copyright (C) 2010 Nokia Corporation ++ * Copyright (C) 2009 Texas Instruments, Inc. ++ * ++ * Contacts: David Cohen ++ * Laurent Pinchart ++ * Sakari Ailus ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#ifndef OMAP3_ISP_HIST_H ++#define OMAP3_ISP_HIST_H ++ ++#include ++ ++#define ISPHIST_IN_BIT_WIDTH_CCDC 10 ++ ++struct isp_device; ++ ++int omap3isp_hist_init(struct isp_device *isp); ++void omap3isp_hist_cleanup(struct isp_device *isp); ++ ++#endif /* OMAP3_ISP_HIST */ +diff --git a/drivers/media/video/isp/isppreview.c b/drivers/media/video/isp/isppreview.c +new file mode 100644 +index 0000000..869583c +--- /dev/null ++++ b/drivers/media/video/isp/isppreview.c +@@ -0,0 +1,2120 @@ ++/* ++ * isppreview.c ++ * ++ * TI OMAP3 ISP driver - Preview module ++ * ++ * Copyright (C) 2010 Nokia Corporation ++ * Copyright (C) 2009 Texas Instruments, Inc. ++ * ++ * Contacts: Laurent Pinchart ++ * Sakari Ailus ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "isp.h" ++#include "ispreg.h" ++#include "isppreview.h" ++ ++/* Default values in Office Flourescent Light for RGBtoRGB Blending */ ++static struct omap3isp_prev_rgbtorgb flr_rgb2rgb = { ++ { /* RGB-RGB Matrix */ ++ {0x01E2, 0x0F30, 0x0FEE}, ++ {0x0F9B, 0x01AC, 0x0FB9}, ++ {0x0FE0, 0x0EC0, 0x0260} ++ }, /* RGB Offset */ ++ {0x0000, 0x0000, 0x0000} ++}; ++ ++/* Default values in Office Flourescent Light for RGB to YUV Conversion*/ ++static struct omap3isp_prev_csc flr_prev_csc = { ++ { /* CSC Coef Matrix */ ++ {66, 129, 25}, ++ {-38, -75, 112}, ++ {112, -94 , -18} ++ }, /* CSC Offset */ ++ {0x0, 0x0, 0x0} ++}; ++ ++/* Default values in Office Flourescent Light for CFA Gradient*/ ++#define FLR_CFA_GRADTHRS_HORZ 0x28 ++#define FLR_CFA_GRADTHRS_VERT 0x28 ++ ++/* Default values in Office Flourescent Light for Chroma Suppression*/ ++#define FLR_CSUP_GAIN 0x0D ++#define FLR_CSUP_THRES 0xEB ++ ++/* Default values in Office Flourescent Light for Noise Filter*/ ++#define FLR_NF_STRGTH 0x03 ++ ++/* Default values for White Balance */ ++#define FLR_WBAL_DGAIN 0x100 ++#define FLR_WBAL_COEF 0x20 ++ ++/* Default values in Office Flourescent Light for Black Adjustment*/ ++#define FLR_BLKADJ_BLUE 0x0 ++#define FLR_BLKADJ_GREEN 0x0 ++#define FLR_BLKADJ_RED 0x0 ++ ++#define DEF_DETECT_CORRECT_VAL 0xe ++ ++#define PREV_MIN_WIDTH 64 ++#define PREV_MIN_HEIGHT 8 ++#define PREV_MAX_HEIGHT 16384 ++ ++/* ++ * Coeficient Tables for the submodules in Preview. ++ * Array is initialised with the values from.the tables text file. ++ */ ++ ++/* ++ * CFA Filter Coefficient Table ++ * ++ */ ++static u32 cfa_coef_table[] = { ++#include "cfa_coef_table.h" ++}; ++ ++/* ++ * Default Gamma Correction Table - All components ++ */ ++static u32 gamma_table[] = { ++#include "gamma_table.h" ++}; ++ ++/* ++ * Noise Filter Threshold table ++ */ ++static u32 noise_filter_table[] = { ++#include "noise_filter_table.h" ++}; ++ ++/* ++ * Luminance Enhancement Table ++ */ ++static u32 luma_enhance_table[] = { ++#include "luma_enhance_table.h" ++}; ++ ++/* ++ * preview_enable_invalaw - Enable/Disable Inverse A-Law module in Preview. ++ * @enable: 1 - Reverse the A-Law done in CCDC. ++ */ ++static void ++preview_enable_invalaw(struct isp_prev_device *prev, u8 enable) ++{ ++ struct isp_device *isp = to_isp_device(prev); ++ ++ if (enable) ++ isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, ++ ISPPRV_PCR_WIDTH | ISPPRV_PCR_INVALAW); ++ else ++ isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, ++ ISPPRV_PCR_WIDTH | ISPPRV_PCR_INVALAW); ++} ++ ++/* ++ * preview_enable_drkframe_capture - Enable/Disable of the darkframe capture. ++ * @prev - ++ * @enable: 1 - Enable, 0 - Disable ++ * ++ * NOTE: PRV_WSDR_ADDR and PRV_WADD_OFFSET must be set also ++ * The proccess is applied for each captured frame. ++ */ ++static void ++preview_enable_drkframe_capture(struct isp_prev_device *prev, u8 enable) ++{ ++ struct isp_device *isp = to_isp_device(prev); ++ ++ if (enable) ++ isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, ++ ISPPRV_PCR_DRKFCAP); ++ else ++ isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, ++ ISPPRV_PCR_DRKFCAP); ++} ++ ++/* ++ * preview_enable_drkframe - Enable/Disable of the darkframe subtract. ++ * @enable: 1 - Acquires memory bandwidth since the pixels in each frame is ++ * subtracted with the pixels in the current frame. ++ * ++ * The proccess is applied for each captured frame. ++ */ ++static void ++preview_enable_drkframe(struct isp_prev_device *prev, u8 enable) ++{ ++ struct isp_device *isp = to_isp_device(prev); ++ ++ if (enable) ++ isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, ++ ISPPRV_PCR_DRKFEN); ++ else ++ isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, ++ ISPPRV_PCR_DRKFEN); ++} ++ ++/* ++ * preview_config_drkf_shadcomp - Configures shift value in shading comp. ++ * @scomp_shtval: 3bit value of shift used in shading compensation. ++ */ ++static void ++preview_config_drkf_shadcomp(struct isp_prev_device *prev, ++ const void *scomp_shtval) ++{ ++ struct isp_device *isp = to_isp_device(prev); ++ const u32 *shtval = scomp_shtval; ++ ++ isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, ++ ISPPRV_PCR_SCOMP_SFT_MASK, ++ *shtval << ISPPRV_PCR_SCOMP_SFT_SHIFT); ++} ++ ++/* ++ * preview_enable_hmed - Enables/Disables of the Horizontal Median Filter. ++ * @enable: 1 - Enables Horizontal Median Filter. ++ */ ++static void ++preview_enable_hmed(struct isp_prev_device *prev, u8 enable) ++{ ++ struct isp_device *isp = to_isp_device(prev); ++ ++ if (enable) ++ isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, ++ ISPPRV_PCR_HMEDEN); ++ else ++ isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, ++ ISPPRV_PCR_HMEDEN); ++} ++ ++/* ++ * preview_config_hmed - Configures the Horizontal Median Filter. ++ * @prev_hmed: Structure containing the odd and even distance between the ++ * pixels in the image along with the filter threshold. ++ */ ++static void ++preview_config_hmed(struct isp_prev_device *prev, const void *prev_hmed) ++{ ++ struct isp_device *isp = to_isp_device(prev); ++ const struct omap3isp_prev_hmed *hmed = prev_hmed; ++ ++ isp_reg_writel(isp, (hmed->odddist == 1 ? 0 : ISPPRV_HMED_ODDDIST) | ++ (hmed->evendist == 1 ? 0 : ISPPRV_HMED_EVENDIST) | ++ (hmed->thres << ISPPRV_HMED_THRESHOLD_SHIFT), ++ OMAP3_ISP_IOMEM_PREV, ISPPRV_HMED); ++} ++ ++/* ++ * preview_config_noisefilter - Configures the Noise Filter. ++ * @prev_nf: Structure containing the noisefilter table, strength to be used ++ * for the noise filter and the defect correction enable flag. ++ */ ++static void ++preview_config_noisefilter(struct isp_prev_device *prev, const void *prev_nf) ++{ ++ struct isp_device *isp = to_isp_device(prev); ++ const struct omap3isp_prev_nf *nf = prev_nf; ++ unsigned int i; ++ ++ isp_reg_writel(isp, nf->spread, OMAP3_ISP_IOMEM_PREV, ISPPRV_NF); ++ isp_reg_writel(isp, ISPPRV_NF_TABLE_ADDR, ++ OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_ADDR); ++ for (i = 0; i < OMAP3ISP_PREV_NF_TBL_SIZE; i++) { ++ isp_reg_writel(isp, nf->table[i], ++ OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_DATA); ++ } ++} ++ ++/* ++ * preview_config_dcor - Configures the defect correction ++ * @prev_dcor: Structure containing the defect correct thresholds ++ */ ++static void ++preview_config_dcor(struct isp_prev_device *prev, const void *prev_dcor) ++{ ++ struct isp_device *isp = to_isp_device(prev); ++ const struct omap3isp_prev_dcor *dcor = prev_dcor; ++ ++ isp_reg_writel(isp, dcor->detect_correct[0], ++ OMAP3_ISP_IOMEM_PREV, ISPPRV_CDC_THR0); ++ isp_reg_writel(isp, dcor->detect_correct[1], ++ OMAP3_ISP_IOMEM_PREV, ISPPRV_CDC_THR1); ++ isp_reg_writel(isp, dcor->detect_correct[2], ++ OMAP3_ISP_IOMEM_PREV, ISPPRV_CDC_THR2); ++ isp_reg_writel(isp, dcor->detect_correct[3], ++ OMAP3_ISP_IOMEM_PREV, ISPPRV_CDC_THR3); ++ isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, ++ ISPPRV_PCR_DCCOUP, ++ dcor->couplet_mode_en ? ISPPRV_PCR_DCCOUP : 0); ++} ++ ++/* ++ * preview_config_cfa - Configures the CFA Interpolation parameters. ++ * @prev_cfa: Structure containing the CFA interpolation table, CFA format ++ * in the image, vertical and horizontal gradient threshold. ++ */ ++static void ++preview_config_cfa(struct isp_prev_device *prev, const void *prev_cfa) ++{ ++ struct isp_device *isp = to_isp_device(prev); ++ const struct omap3isp_prev_cfa *cfa = prev_cfa; ++ unsigned int i; ++ ++ isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, ++ ISPPRV_PCR_CFAFMT_MASK, ++ cfa->format << ISPPRV_PCR_CFAFMT_SHIFT); ++ ++ isp_reg_writel(isp, ++ (cfa->gradthrs_vert << ISPPRV_CFA_GRADTH_VER_SHIFT) | ++ (cfa->gradthrs_horz << ISPPRV_CFA_GRADTH_HOR_SHIFT), ++ OMAP3_ISP_IOMEM_PREV, ISPPRV_CFA); ++ ++ isp_reg_writel(isp, ISPPRV_CFA_TABLE_ADDR, ++ OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_ADDR); ++ ++ for (i = 0; i < OMAP3ISP_PREV_CFA_TBL_SIZE; i++) { ++ isp_reg_writel(isp, cfa->table[i], ++ OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_DATA); ++ } ++} ++ ++/* ++ * preview_config_gammacorrn - Configures the Gamma Correction table values ++ * @gtable: Structure containing the table for red, blue, green gamma table. ++ */ ++static void ++preview_config_gammacorrn(struct isp_prev_device *prev, const void *gtable) ++{ ++ struct isp_device *isp = to_isp_device(prev); ++ const struct omap3isp_prev_gtables *gt = gtable; ++ unsigned int i; ++ ++ isp_reg_writel(isp, ISPPRV_REDGAMMA_TABLE_ADDR, ++ OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_ADDR); ++ for (i = 0; i < OMAP3ISP_PREV_GAMMA_TBL_SIZE; i++) ++ isp_reg_writel(isp, gt->red[i], OMAP3_ISP_IOMEM_PREV, ++ ISPPRV_SET_TBL_DATA); ++ ++ isp_reg_writel(isp, ISPPRV_GREENGAMMA_TABLE_ADDR, ++ OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_ADDR); ++ for (i = 0; i < OMAP3ISP_PREV_GAMMA_TBL_SIZE; i++) ++ isp_reg_writel(isp, gt->green[i], OMAP3_ISP_IOMEM_PREV, ++ ISPPRV_SET_TBL_DATA); ++ ++ isp_reg_writel(isp, ISPPRV_BLUEGAMMA_TABLE_ADDR, ++ OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_ADDR); ++ for (i = 0; i < OMAP3ISP_PREV_GAMMA_TBL_SIZE; i++) ++ isp_reg_writel(isp, gt->blue[i], OMAP3_ISP_IOMEM_PREV, ++ ISPPRV_SET_TBL_DATA); ++} ++ ++/* ++ * preview_config_luma_enhancement - Sets the Luminance Enhancement table. ++ * @ytable: Structure containing the table for Luminance Enhancement table. ++ */ ++static void ++preview_config_luma_enhancement(struct isp_prev_device *prev, ++ const void *ytable) ++{ ++ struct isp_device *isp = to_isp_device(prev); ++ const struct omap3isp_prev_luma *yt = ytable; ++ unsigned int i; ++ ++ isp_reg_writel(isp, ISPPRV_YENH_TABLE_ADDR, ++ OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_ADDR); ++ for (i = 0; i < OMAP3ISP_PREV_YENH_TBL_SIZE; i++) { ++ isp_reg_writel(isp, yt->table[i], ++ OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_DATA); ++ } ++} ++ ++/* ++ * preview_config_chroma_suppression - Configures the Chroma Suppression. ++ * @csup: Structure containing the threshold value for suppression ++ * and the hypass filter enable flag. ++ */ ++static void ++preview_config_chroma_suppression(struct isp_prev_device *prev, ++ const void *csup) ++{ ++ struct isp_device *isp = to_isp_device(prev); ++ const struct omap3isp_prev_csup *cs = csup; ++ ++ isp_reg_writel(isp, ++ cs->gain | (cs->thres << ISPPRV_CSUP_THRES_SHIFT) | ++ (cs->hypf_en << ISPPRV_CSUP_HPYF_SHIFT), ++ OMAP3_ISP_IOMEM_PREV, ISPPRV_CSUP); ++} ++ ++/* ++ * preview_enable_noisefilter - Enables/Disables the Noise Filter. ++ * @enable: 1 - Enables the Noise Filter. ++ */ ++static void ++preview_enable_noisefilter(struct isp_prev_device *prev, u8 enable) ++{ ++ struct isp_device *isp = to_isp_device(prev); ++ ++ if (enable) ++ isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, ++ ISPPRV_PCR_NFEN); ++ else ++ isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, ++ ISPPRV_PCR_NFEN); ++} ++ ++/* ++ * preview_enable_dcor - Enables/Disables the defect correction. ++ * @enable: 1 - Enables the defect correction. ++ */ ++static void ++preview_enable_dcor(struct isp_prev_device *prev, u8 enable) ++{ ++ struct isp_device *isp = to_isp_device(prev); ++ ++ if (enable) ++ isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, ++ ISPPRV_PCR_DCOREN); ++ else ++ isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, ++ ISPPRV_PCR_DCOREN); ++} ++ ++/* ++ * preview_enable_cfa - Enable/Disable the CFA Interpolation. ++ * @enable: 1 - Enables the CFA. ++ */ ++static void ++preview_enable_cfa(struct isp_prev_device *prev, u8 enable) ++{ ++ struct isp_device *isp = to_isp_device(prev); ++ ++ if (enable) ++ isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, ++ ISPPRV_PCR_CFAEN); ++ else ++ isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, ++ ISPPRV_PCR_CFAEN); ++} ++ ++/* ++ * preview_enable_gammabypass - Enables/Disables the GammaByPass ++ * @enable: 1 - Bypasses Gamma - 10bit input is cropped to 8MSB. ++ * 0 - Goes through Gamma Correction. input and output is 10bit. ++ */ ++static void ++preview_enable_gammabypass(struct isp_prev_device *prev, u8 enable) ++{ ++ struct isp_device *isp = to_isp_device(prev); ++ ++ if (enable) ++ isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, ++ ISPPRV_PCR_GAMMA_BYPASS); ++ else ++ isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, ++ ISPPRV_PCR_GAMMA_BYPASS); ++} ++ ++/* ++ * preview_enable_luma_enhancement - Enables/Disables Luminance Enhancement ++ * @enable: 1 - Enable the Luminance Enhancement. ++ */ ++static void ++preview_enable_luma_enhancement(struct isp_prev_device *prev, u8 enable) ++{ ++ struct isp_device *isp = to_isp_device(prev); ++ ++ if (enable) ++ isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, ++ ISPPRV_PCR_YNENHEN); ++ else ++ isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, ++ ISPPRV_PCR_YNENHEN); ++} ++ ++/* ++ * preview_enable_chroma_suppression - Enables/Disables Chrominance Suppr. ++ * @enable: 1 - Enable the Chrominance Suppression. ++ */ ++static void ++preview_enable_chroma_suppression(struct isp_prev_device *prev, u8 enable) ++{ ++ struct isp_device *isp = to_isp_device(prev); ++ ++ if (enable) ++ isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, ++ ISPPRV_PCR_SUPEN); ++ else ++ isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, ++ ISPPRV_PCR_SUPEN); ++} ++ ++/* ++ * preview_config_whitebalance - Configures the White Balance parameters. ++ * @prev_wbal: Structure containing the digital gain and white balance ++ * coefficient. ++ * ++ * Coefficient matrix always with default values. ++ */ ++static void ++preview_config_whitebalance(struct isp_prev_device *prev, const void *prev_wbal) ++{ ++ struct isp_device *isp = to_isp_device(prev); ++ const struct omap3isp_prev_wbal *wbal = prev_wbal; ++ u32 val; ++ ++ isp_reg_writel(isp, wbal->dgain, OMAP3_ISP_IOMEM_PREV, ISPPRV_WB_DGAIN); ++ ++ val = wbal->coef0 << ISPPRV_WBGAIN_COEF0_SHIFT; ++ val |= wbal->coef1 << ISPPRV_WBGAIN_COEF1_SHIFT; ++ val |= wbal->coef2 << ISPPRV_WBGAIN_COEF2_SHIFT; ++ val |= wbal->coef3 << ISPPRV_WBGAIN_COEF3_SHIFT; ++ isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_WBGAIN); ++ ++ isp_reg_writel(isp, ++ ISPPRV_WBSEL_COEF0 << ISPPRV_WBSEL_N0_0_SHIFT | ++ ISPPRV_WBSEL_COEF1 << ISPPRV_WBSEL_N0_1_SHIFT | ++ ISPPRV_WBSEL_COEF0 << ISPPRV_WBSEL_N0_2_SHIFT | ++ ISPPRV_WBSEL_COEF1 << ISPPRV_WBSEL_N0_3_SHIFT | ++ ISPPRV_WBSEL_COEF2 << ISPPRV_WBSEL_N1_0_SHIFT | ++ ISPPRV_WBSEL_COEF3 << ISPPRV_WBSEL_N1_1_SHIFT | ++ ISPPRV_WBSEL_COEF2 << ISPPRV_WBSEL_N1_2_SHIFT | ++ ISPPRV_WBSEL_COEF3 << ISPPRV_WBSEL_N1_3_SHIFT | ++ ISPPRV_WBSEL_COEF0 << ISPPRV_WBSEL_N2_0_SHIFT | ++ ISPPRV_WBSEL_COEF1 << ISPPRV_WBSEL_N2_1_SHIFT | ++ ISPPRV_WBSEL_COEF0 << ISPPRV_WBSEL_N2_2_SHIFT | ++ ISPPRV_WBSEL_COEF1 << ISPPRV_WBSEL_N2_3_SHIFT | ++ ISPPRV_WBSEL_COEF2 << ISPPRV_WBSEL_N3_0_SHIFT | ++ ISPPRV_WBSEL_COEF3 << ISPPRV_WBSEL_N3_1_SHIFT | ++ ISPPRV_WBSEL_COEF2 << ISPPRV_WBSEL_N3_2_SHIFT | ++ ISPPRV_WBSEL_COEF3 << ISPPRV_WBSEL_N3_3_SHIFT, ++ OMAP3_ISP_IOMEM_PREV, ISPPRV_WBSEL); ++} ++ ++/* ++ * preview_config_blkadj - Configures the Black Adjustment parameters. ++ * @prev_blkadj: Structure containing the black adjustment towards red, green, ++ * blue. ++ */ ++static void ++preview_config_blkadj(struct isp_prev_device *prev, const void *prev_blkadj) ++{ ++ struct isp_device *isp = to_isp_device(prev); ++ const struct omap3isp_prev_blkadj *blkadj = prev_blkadj; ++ ++ isp_reg_writel(isp, (blkadj->blue << ISPPRV_BLKADJOFF_B_SHIFT) | ++ (blkadj->green << ISPPRV_BLKADJOFF_G_SHIFT) | ++ (blkadj->red << ISPPRV_BLKADJOFF_R_SHIFT), ++ OMAP3_ISP_IOMEM_PREV, ISPPRV_BLKADJOFF); ++} ++ ++/* ++ * preview_config_rgb_blending - Configures the RGB-RGB Blending matrix. ++ * @rgb2rgb: Structure containing the rgb to rgb blending matrix and the rgb ++ * offset. ++ */ ++static void ++preview_config_rgb_blending(struct isp_prev_device *prev, const void *rgb2rgb) ++{ ++ struct isp_device *isp = to_isp_device(prev); ++ const struct omap3isp_prev_rgbtorgb *rgbrgb = rgb2rgb; ++ u32 val; ++ ++ val = (rgbrgb->matrix[0][0] & 0xfff) << ISPPRV_RGB_MAT1_MTX_RR_SHIFT; ++ val |= (rgbrgb->matrix[0][1] & 0xfff) << ISPPRV_RGB_MAT1_MTX_GR_SHIFT; ++ isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_MAT1); ++ ++ val = (rgbrgb->matrix[0][2] & 0xfff) << ISPPRV_RGB_MAT2_MTX_BR_SHIFT; ++ val |= (rgbrgb->matrix[1][0] & 0xfff) << ISPPRV_RGB_MAT2_MTX_RG_SHIFT; ++ isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_MAT2); ++ ++ val = (rgbrgb->matrix[1][1] & 0xfff) << ISPPRV_RGB_MAT3_MTX_GG_SHIFT; ++ val |= (rgbrgb->matrix[1][2] & 0xfff) << ISPPRV_RGB_MAT3_MTX_BG_SHIFT; ++ isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_MAT3); ++ ++ val = (rgbrgb->matrix[2][0] & 0xfff) << ISPPRV_RGB_MAT4_MTX_RB_SHIFT; ++ val |= (rgbrgb->matrix[2][1] & 0xfff) << ISPPRV_RGB_MAT4_MTX_GB_SHIFT; ++ isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_MAT4); ++ ++ val = (rgbrgb->matrix[2][2] & 0xfff) << ISPPRV_RGB_MAT5_MTX_BB_SHIFT; ++ isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_MAT5); ++ ++ val = (rgbrgb->offset[0] & 0x3ff) << ISPPRV_RGB_OFF1_MTX_OFFR_SHIFT; ++ val |= (rgbrgb->offset[1] & 0x3ff) << ISPPRV_RGB_OFF1_MTX_OFFG_SHIFT; ++ isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_OFF1); ++ ++ val = (rgbrgb->offset[2] & 0x3ff) << ISPPRV_RGB_OFF2_MTX_OFFB_SHIFT; ++ isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_OFF2); ++} ++ ++/* ++ * Configures the RGB-YCbYCr conversion matrix ++ * @prev_csc: Structure containing the RGB to YCbYCr matrix and the ++ * YCbCr offset. ++ */ ++static void ++preview_config_rgb_to_ycbcr(struct isp_prev_device *prev, const void *prev_csc) ++{ ++ struct isp_device *isp = to_isp_device(prev); ++ const struct omap3isp_prev_csc *csc = prev_csc; ++ u32 val; ++ ++ val = (csc->matrix[0][0] & 0x3ff) << ISPPRV_CSC0_RY_SHIFT; ++ val |= (csc->matrix[0][1] & 0x3ff) << ISPPRV_CSC0_GY_SHIFT; ++ val |= (csc->matrix[0][2] & 0x3ff) << ISPPRV_CSC0_BY_SHIFT; ++ isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_CSC0); ++ ++ val = (csc->matrix[1][0] & 0x3ff) << ISPPRV_CSC1_RCB_SHIFT; ++ val |= (csc->matrix[1][1] & 0x3ff) << ISPPRV_CSC1_GCB_SHIFT; ++ val |= (csc->matrix[1][2] & 0x3ff) << ISPPRV_CSC1_BCB_SHIFT; ++ isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_CSC1); ++ ++ val = (csc->matrix[2][0] & 0x3ff) << ISPPRV_CSC2_RCR_SHIFT; ++ val |= (csc->matrix[2][1] & 0x3ff) << ISPPRV_CSC2_GCR_SHIFT; ++ val |= (csc->matrix[2][2] & 0x3ff) << ISPPRV_CSC2_BCR_SHIFT; ++ isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_CSC2); ++ ++ val = (csc->offset[0] & 0xff) << ISPPRV_CSC_OFFSET_Y_SHIFT; ++ val |= (csc->offset[1] & 0xff) << ISPPRV_CSC_OFFSET_CB_SHIFT; ++ val |= (csc->offset[2] & 0xff) << ISPPRV_CSC_OFFSET_CR_SHIFT; ++ isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_CSC_OFFSET); ++} ++ ++/* ++ * preview_update_contrast - Updates the contrast. ++ * @contrast: Pointer to hold the current programmed contrast value. ++ * ++ * Value should be programmed before enabling the module. ++ */ ++static void ++preview_update_contrast(struct isp_prev_device *prev, u8 contrast) ++{ ++ struct prev_params *params = &prev->params; ++ ++ if (params->contrast != (contrast * ISPPRV_CONTRAST_UNITS)) { ++ params->contrast = contrast * ISPPRV_CONTRAST_UNITS; ++ prev->update |= PREV_CONTRAST; ++ } ++} ++ ++/* ++ * preview_config_contrast - Configures the Contrast. ++ * @params: Contrast value (u8 pointer, U8Q0 format). ++ * ++ * Value should be programmed before enabling the module. ++ */ ++static void ++preview_config_contrast(struct isp_prev_device *prev, const void *params) ++{ ++ struct isp_device *isp = to_isp_device(prev); ++ ++ isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_CNT_BRT, ++ 0xff << ISPPRV_CNT_BRT_CNT_SHIFT, ++ *(u8 *)params << ISPPRV_CNT_BRT_CNT_SHIFT); ++} ++ ++/* ++ * preview_update_brightness - Updates the brightness in preview module. ++ * @brightness: Pointer to hold the current programmed brightness value. ++ * ++ */ ++static void ++preview_update_brightness(struct isp_prev_device *prev, u8 brightness) ++{ ++ struct prev_params *params = &prev->params; ++ ++ if (params->brightness != (brightness * ISPPRV_BRIGHT_UNITS)) { ++ params->brightness = brightness * ISPPRV_BRIGHT_UNITS; ++ prev->update |= PREV_BRIGHTNESS; ++ } ++} ++ ++/* ++ * preview_config_brightness - Configures the brightness. ++ * @params: Brightness value (u8 pointer, U8Q0 format). ++ */ ++static void ++preview_config_brightness(struct isp_prev_device *prev, const void *params) ++{ ++ struct isp_device *isp = to_isp_device(prev); ++ ++ isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_CNT_BRT, ++ 0xff << ISPPRV_CNT_BRT_BRT_SHIFT, ++ *(u8 *)params << ISPPRV_CNT_BRT_BRT_SHIFT); ++} ++ ++/* ++ * preview_config_yc_range - Configures the max and min Y and C values. ++ * @yclimit: Structure containing the range of Y and C values. ++ */ ++static void ++preview_config_yc_range(struct isp_prev_device *prev, const void *yclimit) ++{ ++ struct isp_device *isp = to_isp_device(prev); ++ const struct omap3isp_prev_yclimit *yc = yclimit; ++ ++ isp_reg_writel(isp, ++ yc->maxC << ISPPRV_SETUP_YC_MAXC_SHIFT | ++ yc->maxY << ISPPRV_SETUP_YC_MAXY_SHIFT | ++ yc->minC << ISPPRV_SETUP_YC_MINC_SHIFT | ++ yc->minY << ISPPRV_SETUP_YC_MINY_SHIFT, ++ OMAP3_ISP_IOMEM_PREV, ISPPRV_SETUP_YC); ++} ++ ++/* preview parameters update structure */ ++struct preview_update { ++ int cfg_bit; ++ int feature_bit; ++ void (*config)(struct isp_prev_device *, const void *); ++ void (*enable)(struct isp_prev_device *, u8); ++}; ++ ++static struct preview_update update_attrs[] = { ++ {OMAP3ISP_PREV_LUMAENH, PREV_LUMA_ENHANCE, ++ preview_config_luma_enhancement, ++ preview_enable_luma_enhancement}, ++ {OMAP3ISP_PREV_INVALAW, PREV_INVERSE_ALAW, ++ NULL, ++ preview_enable_invalaw}, ++ {OMAP3ISP_PREV_HRZ_MED, PREV_HORZ_MEDIAN_FILTER, ++ preview_config_hmed, ++ preview_enable_hmed}, ++ {OMAP3ISP_PREV_CFA, PREV_CFA, ++ preview_config_cfa, ++ preview_enable_cfa}, ++ {OMAP3ISP_PREV_CHROMA_SUPP, PREV_CHROMA_SUPPRESS, ++ preview_config_chroma_suppression, ++ preview_enable_chroma_suppression}, ++ {OMAP3ISP_PREV_WB, PREV_WB, ++ preview_config_whitebalance, ++ NULL}, ++ {OMAP3ISP_PREV_BLKADJ, PREV_BLKADJ, ++ preview_config_blkadj, ++ NULL}, ++ {OMAP3ISP_PREV_RGB2RGB, PREV_RGB2RGB, ++ preview_config_rgb_blending, ++ NULL}, ++ {OMAP3ISP_PREV_COLOR_CONV, PREV_COLOR_CONV, ++ preview_config_rgb_to_ycbcr, ++ NULL}, ++ {OMAP3ISP_PREV_YC_LIMIT, PREV_YCLIMITS, ++ preview_config_yc_range, ++ NULL}, ++ {OMAP3ISP_PREV_DEFECT_COR, PREV_DEFECT_COR, ++ preview_config_dcor, ++ preview_enable_dcor}, ++ {OMAP3ISP_PREV_GAMMABYPASS, PREV_GAMMA_BYPASS, ++ NULL, ++ preview_enable_gammabypass}, ++ {OMAP3ISP_PREV_DRK_FRM_CAPTURE, PREV_DARK_FRAME_CAPTURE, ++ NULL, ++ preview_enable_drkframe_capture}, ++ {OMAP3ISP_PREV_DRK_FRM_SUBTRACT, PREV_DARK_FRAME_SUBTRACT, ++ NULL, ++ preview_enable_drkframe}, ++ {OMAP3ISP_PREV_LENS_SHADING, PREV_LENS_SHADING, ++ preview_config_drkf_shadcomp, ++ preview_enable_drkframe}, ++ {OMAP3ISP_PREV_NF, PREV_NOISE_FILTER, ++ preview_config_noisefilter, ++ preview_enable_noisefilter}, ++ {OMAP3ISP_PREV_GAMMA, PREV_GAMMA, ++ preview_config_gammacorrn, ++ NULL}, ++ {-1, PREV_CONTRAST, ++ preview_config_contrast, ++ NULL}, ++ {-1, PREV_BRIGHTNESS, ++ preview_config_brightness, ++ NULL}, ++}; ++ ++/* ++ * __preview_get_ptrs - helper function which return pointers to members ++ * of params and config structures. ++ * @params - pointer to preview_params structure. ++ * @param - return pointer to appropriate structure field. ++ * @configs - pointer to update config structure. ++ * @config - return pointer to appropriate structure field. ++ * @bit - for which feature to return pointers. ++ * Return size of coresponding prev_params member ++ */ ++static u32 ++__preview_get_ptrs(struct prev_params *params, void **param, ++ struct omap3isp_prev_update_config *configs, ++ void __user **config, u32 bit) ++{ ++#define CHKARG(cfgs, cfg, field) \ ++ if (cfgs && cfg) { \ ++ *(cfg) = (cfgs)->field; \ ++ } ++ ++ switch (bit) { ++ case PREV_HORZ_MEDIAN_FILTER: ++ *param = ¶ms->hmed; ++ CHKARG(configs, config, hmed) ++ return sizeof(params->hmed); ++ case PREV_NOISE_FILTER: ++ *param = ¶ms->nf; ++ CHKARG(configs, config, nf) ++ return sizeof(params->nf); ++ break; ++ case PREV_CFA: ++ *param = ¶ms->cfa; ++ CHKARG(configs, config, cfa) ++ return sizeof(params->cfa); ++ case PREV_LUMA_ENHANCE: ++ *param = ¶ms->luma; ++ CHKARG(configs, config, luma) ++ return sizeof(params->luma); ++ case PREV_CHROMA_SUPPRESS: ++ *param = ¶ms->csup; ++ CHKARG(configs, config, csup) ++ return sizeof(params->csup); ++ case PREV_DEFECT_COR: ++ *param = ¶ms->dcor; ++ CHKARG(configs, config, dcor) ++ return sizeof(params->dcor); ++ case PREV_BLKADJ: ++ *param = ¶ms->blk_adj; ++ CHKARG(configs, config, blkadj) ++ return sizeof(params->blk_adj); ++ case PREV_YCLIMITS: ++ *param = ¶ms->yclimit; ++ CHKARG(configs, config, yclimit) ++ return sizeof(params->yclimit); ++ case PREV_RGB2RGB: ++ *param = ¶ms->rgb2rgb; ++ CHKARG(configs, config, rgb2rgb) ++ return sizeof(params->rgb2rgb); ++ case PREV_COLOR_CONV: ++ *param = ¶ms->rgb2ycbcr; ++ CHKARG(configs, config, csc) ++ return sizeof(params->rgb2ycbcr); ++ case PREV_WB: ++ *param = ¶ms->wbal; ++ CHKARG(configs, config, wbal) ++ return sizeof(params->wbal); ++ case PREV_GAMMA: ++ *param = ¶ms->gamma; ++ CHKARG(configs, config, gamma) ++ return sizeof(params->gamma); ++ case PREV_CONTRAST: ++ *param = ¶ms->contrast; ++ return 0; ++ case PREV_BRIGHTNESS: ++ *param = ¶ms->brightness; ++ return 0; ++ default: ++ *param = NULL; ++ *config = NULL; ++ break; ++ } ++ return 0; ++} ++ ++/* ++ * preview_config - Copy and update local structure with userspace preview ++ * configuration. ++ * @prev: ISP preview engine ++ * @cfg: Configuration ++ * ++ * Return zero if success or -EFAULT if the configuration can't be copied from ++ * userspace. ++ */ ++static int preview_config(struct isp_prev_device *prev, ++ struct omap3isp_prev_update_config *cfg) ++{ ++ struct prev_params *params; ++ struct preview_update *attr; ++ int i, bit, rval = 0; ++ ++ params = &prev->params; ++ ++ if (prev->state != ISP_PIPELINE_STREAM_STOPPED) { ++ unsigned long flags; ++ ++ spin_lock_irqsave(&prev->lock, flags); ++ prev->shadow_update = 1; ++ spin_unlock_irqrestore(&prev->lock, flags); ++ } ++ ++ for (i = 0; i < ARRAY_SIZE(update_attrs); i++) { ++ attr = &update_attrs[i]; ++ bit = 0; ++ ++ if (!(cfg->update & attr->cfg_bit)) ++ continue; ++ ++ bit = cfg->flag & attr->cfg_bit; ++ if (bit) { ++ void *to = NULL, __user *from = NULL; ++ unsigned long sz = 0; ++ ++ sz = __preview_get_ptrs(params, &to, cfg, &from, ++ bit); ++ if (to && from && sz) { ++ if (copy_from_user(to, from, sz)) { ++ rval = -EFAULT; ++ break; ++ } ++ } ++ params->features |= attr->feature_bit; ++ } else { ++ params->features &= ~attr->feature_bit; ++ } ++ ++ prev->update |= attr->feature_bit; ++ } ++ ++ prev->shadow_update = 0; ++ return rval; ++} ++ ++/* ++ * preview_setup_hw - Setup preview registers and/or internal memory ++ * @prev: pointer to preview private structure ++ * Note: can be called from interrupt context ++ * Return none ++ */ ++static void preview_setup_hw(struct isp_prev_device *prev) ++{ ++ struct prev_params *params = &prev->params; ++ struct preview_update *attr; ++ int i, bit; ++ void *param_ptr; ++ ++ for (i = 0; i < ARRAY_SIZE(update_attrs); i++) { ++ attr = &update_attrs[i]; ++ ++ if (!(prev->update & attr->feature_bit)) ++ continue; ++ bit = params->features & attr->feature_bit; ++ if (bit) { ++ if (attr->config) { ++ __preview_get_ptrs(params, ¶m_ptr, NULL, ++ NULL, bit); ++ attr->config(prev, param_ptr); ++ } ++ if (attr->enable) ++ attr->enable(prev, 1); ++ } else ++ if (attr->enable) ++ attr->enable(prev, 0); ++ ++ prev->update &= ~attr->feature_bit; ++ } ++} ++ ++/* ++ * preview_config_ycpos - Configure byte layout of YUV image. ++ * @mode: Indicates the required byte layout. ++ */ ++static void ++preview_config_ycpos(struct isp_prev_device *prev, ++ enum v4l2_mbus_pixelcode pixelcode) ++{ ++ struct isp_device *isp = to_isp_device(prev); ++ enum preview_ycpos_mode mode; ++ ++ switch (pixelcode) { ++ case V4L2_MBUS_FMT_YUYV8_1X16: ++ mode = YCPOS_CrYCbY; ++ break; ++ case V4L2_MBUS_FMT_UYVY8_1X16: ++ mode = YCPOS_YCrYCb; ++ break; ++ default: ++ return; ++ } ++ ++ isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, ++ ISPPRV_PCR_YCPOS_CrYCbY, ++ mode << ISPPRV_PCR_YCPOS_SHIFT); ++} ++ ++/* ++ * preview_config_averager - Enable / disable / configure averager ++ * @average: Average value to be configured. ++ */ ++static void preview_config_averager(struct isp_prev_device *prev, u8 average) ++{ ++ struct isp_device *isp = to_isp_device(prev); ++ int reg = 0; ++ ++ if (prev->params.cfa.format == OMAP3ISP_CFAFMT_BAYER) ++ reg = ISPPRV_AVE_EVENDIST_2 << ISPPRV_AVE_EVENDIST_SHIFT | ++ ISPPRV_AVE_ODDDIST_2 << ISPPRV_AVE_ODDDIST_SHIFT | ++ average; ++ else if (prev->params.cfa.format == OMAP3ISP_CFAFMT_RGBFOVEON) ++ reg = ISPPRV_AVE_EVENDIST_3 << ISPPRV_AVE_EVENDIST_SHIFT | ++ ISPPRV_AVE_ODDDIST_3 << ISPPRV_AVE_ODDDIST_SHIFT | ++ average; ++ isp_reg_writel(isp, reg, OMAP3_ISP_IOMEM_PREV, ISPPRV_AVE); ++} ++ ++/* ++ * preview_config_input_size - Configure the input frame size ++ * ++ * The preview engine crops several rows and columns internally depending on ++ * which processing blocks are enabled. The driver assumes all those blocks are ++ * enabled when reporting source pad formats to userspace. If this assumption is ++ * not true, rows and columns must be manually cropped at the preview engine ++ * input to avoid overflows at the end of lines and frames. ++ */ ++static void preview_config_input_size(struct isp_prev_device *prev) ++{ ++ struct isp_device *isp = to_isp_device(prev); ++ struct prev_params *params = &prev->params; ++ struct v4l2_mbus_framefmt *format = &prev->formats[PREV_PAD_SINK]; ++ unsigned int sph = 0; ++ unsigned int eph = format->width - 1; ++ unsigned int slv = 0; ++ unsigned int elv = format->height - 1; ++ ++ if (prev->input == PREVIEW_INPUT_CCDC) { ++ sph += 2; ++ eph -= 2; ++ } ++ ++ /* ++ * Median filter 4 pixels ++ * Noise filter 4 pixels, 4 lines ++ * or faulty pixels correction ++ * CFA filter 4 pixels, 4 lines in Bayer mode ++ * 2 lines in other modes ++ * Color suppression 2 pixels ++ * or luma enhancement ++ * ------------------------------------------------------------- ++ * Maximum total 14 pixels, 8 lines ++ */ ++ ++ if (!(params->features & PREV_CFA)) { ++ sph += 2; ++ eph -= 2; ++ slv += 2; ++ elv -= 2; ++ } ++ if (!(params->features & (PREV_DEFECT_COR | PREV_NOISE_FILTER))) { ++ sph += 2; ++ eph -= 2; ++ slv += 2; ++ elv -= 2; ++ } ++ if (!(params->features & PREV_HORZ_MEDIAN_FILTER)) { ++ sph += 2; ++ eph -= 2; ++ } ++ if (!(params->features & (PREV_CHROMA_SUPPRESS | PREV_LUMA_ENHANCE))) ++ sph += 2; ++ ++ isp_reg_writel(isp, (sph << ISPPRV_HORZ_INFO_SPH_SHIFT) | eph, ++ OMAP3_ISP_IOMEM_PREV, ISPPRV_HORZ_INFO); ++ isp_reg_writel(isp, (slv << ISPPRV_VERT_INFO_SLV_SHIFT) | elv, ++ OMAP3_ISP_IOMEM_PREV, ISPPRV_VERT_INFO); ++} ++ ++/* ++ * preview_config_inlineoffset - Configures the Read address line offset. ++ * @prev: Preview module ++ * @offset: Line offset ++ * ++ * According to the TRM, the line offset must be aligned on a 32 bytes boundary. ++ * However, a hardware bug requires the memory start address to be aligned on a ++ * 64 bytes boundary, so the offset probably should be aligned on 64 bytes as ++ * well. ++ */ ++static void ++preview_config_inlineoffset(struct isp_prev_device *prev, u32 offset) ++{ ++ struct isp_device *isp = to_isp_device(prev); ++ ++ isp_reg_writel(isp, offset & 0xffff, OMAP3_ISP_IOMEM_PREV, ++ ISPPRV_RADR_OFFSET); ++} ++ ++/* ++ * preview_set_inaddr - Sets memory address of input frame. ++ * @addr: 32bit memory address aligned on 32byte boundary. ++ * ++ * Configures the memory address from which the input frame is to be read. ++ */ ++static void preview_set_inaddr(struct isp_prev_device *prev, u32 addr) ++{ ++ struct isp_device *isp = to_isp_device(prev); ++ ++ isp_reg_writel(isp, addr, OMAP3_ISP_IOMEM_PREV, ISPPRV_RSDR_ADDR); ++} ++ ++/* ++ * preview_config_outlineoffset - Configures the Write address line offset. ++ * @offset: Line Offset for the preview output. ++ * ++ * The offset must be a multiple of 32 bytes. ++ */ ++static void preview_config_outlineoffset(struct isp_prev_device *prev, ++ u32 offset) ++{ ++ struct isp_device *isp = to_isp_device(prev); ++ ++ isp_reg_writel(isp, offset & 0xffff, OMAP3_ISP_IOMEM_PREV, ++ ISPPRV_WADD_OFFSET); ++} ++ ++/* ++ * preview_set_outaddr - Sets the memory address to store output frame ++ * @addr: 32bit memory address aligned on 32byte boundary. ++ * ++ * Configures the memory address to which the output frame is written. ++ */ ++static void preview_set_outaddr(struct isp_prev_device *prev, u32 addr) ++{ ++ struct isp_device *isp = to_isp_device(prev); ++ ++ isp_reg_writel(isp, addr, OMAP3_ISP_IOMEM_PREV, ISPPRV_WSDR_ADDR); ++} ++ ++static void preview_adjust_bandwidth(struct isp_prev_device *prev) ++{ ++ struct isp_pipeline *pipe = to_isp_pipeline(&prev->subdev.entity); ++ struct isp_device *isp = to_isp_device(prev); ++ const struct v4l2_mbus_framefmt *ifmt = &prev->formats[PREV_PAD_SINK]; ++ unsigned long l3_ick = pipe->l3_ick; ++ struct v4l2_fract *timeperframe; ++ unsigned int cycles_per_frame; ++ unsigned int requests_per_frame; ++ unsigned int cycles_per_request; ++ unsigned int minimum; ++ unsigned int maximum; ++ unsigned int value; ++ ++ if (prev->input != PREVIEW_INPUT_MEMORY) { ++ isp_reg_clr(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_SDR_REQ_EXP, ++ ISPSBL_SDR_REQ_PRV_EXP_MASK); ++ return; ++ } ++ ++ /* Compute the minimum number of cycles per request, based on the ++ * pipeline maximum data rate. This is an absolute lower bound if we ++ * don't want SBL overflows, so round the value up. ++ */ ++ cycles_per_request = div_u64((u64)l3_ick / 2 * 256 + pipe->max_rate - 1, ++ pipe->max_rate); ++ minimum = DIV_ROUND_UP(cycles_per_request, 32); ++ ++ /* Compute the maximum number of cycles per request, based on the ++ * requested frame rate. This is a soft upper bound to achieve a frame ++ * rate equal or higher than the requested value, so round the value ++ * down. ++ */ ++ timeperframe = &pipe->max_timeperframe; ++ ++ requests_per_frame = DIV_ROUND_UP(ifmt->width * 2, 256) * ifmt->height; ++ cycles_per_frame = div_u64((u64)l3_ick * timeperframe->numerator, ++ timeperframe->denominator); ++ cycles_per_request = cycles_per_frame / requests_per_frame; ++ ++ maximum = cycles_per_request / 32; ++ ++ value = max(minimum, maximum); ++ ++ dev_dbg(isp->dev, "%s: cycles per request = %u\n", __func__, value); ++ isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_SDR_REQ_EXP, ++ ISPSBL_SDR_REQ_PRV_EXP_MASK, ++ value << ISPSBL_SDR_REQ_PRV_EXP_SHIFT); ++} ++ ++/* ++ * omap3isp_preview_busy - Gets busy state of preview module. ++ */ ++int omap3isp_preview_busy(struct isp_prev_device *prev) ++{ ++ struct isp_device *isp = to_isp_device(prev); ++ ++ return isp_reg_readl(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR) ++ & ISPPRV_PCR_BUSY; ++} ++ ++/* ++ * omap3isp_preview_restore_context - Restores the values of preview registers ++ */ ++void omap3isp_preview_restore_context(struct isp_device *isp) ++{ ++ isp->isp_prev.update = PREV_FEATURES_END - 1; ++ preview_setup_hw(&isp->isp_prev); ++} ++ ++/* ++ * preview_print_status - Dump preview module registers to the kernel log ++ */ ++#define PREV_PRINT_REGISTER(isp, name)\ ++ dev_dbg(isp->dev, "###PRV " #name "=0x%08x\n", \ ++ isp_reg_readl(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_##name)) ++ ++static void preview_print_status(struct isp_prev_device *prev) ++{ ++ struct isp_device *isp = to_isp_device(prev); ++ ++ dev_dbg(isp->dev, "-------------Preview Register dump----------\n"); ++ ++ PREV_PRINT_REGISTER(isp, PCR); ++ PREV_PRINT_REGISTER(isp, HORZ_INFO); ++ PREV_PRINT_REGISTER(isp, VERT_INFO); ++ PREV_PRINT_REGISTER(isp, RSDR_ADDR); ++ PREV_PRINT_REGISTER(isp, RADR_OFFSET); ++ PREV_PRINT_REGISTER(isp, DSDR_ADDR); ++ PREV_PRINT_REGISTER(isp, DRKF_OFFSET); ++ PREV_PRINT_REGISTER(isp, WSDR_ADDR); ++ PREV_PRINT_REGISTER(isp, WADD_OFFSET); ++ PREV_PRINT_REGISTER(isp, AVE); ++ PREV_PRINT_REGISTER(isp, HMED); ++ PREV_PRINT_REGISTER(isp, NF); ++ PREV_PRINT_REGISTER(isp, WB_DGAIN); ++ PREV_PRINT_REGISTER(isp, WBGAIN); ++ PREV_PRINT_REGISTER(isp, WBSEL); ++ PREV_PRINT_REGISTER(isp, CFA); ++ PREV_PRINT_REGISTER(isp, BLKADJOFF); ++ PREV_PRINT_REGISTER(isp, RGB_MAT1); ++ PREV_PRINT_REGISTER(isp, RGB_MAT2); ++ PREV_PRINT_REGISTER(isp, RGB_MAT3); ++ PREV_PRINT_REGISTER(isp, RGB_MAT4); ++ PREV_PRINT_REGISTER(isp, RGB_MAT5); ++ PREV_PRINT_REGISTER(isp, RGB_OFF1); ++ PREV_PRINT_REGISTER(isp, RGB_OFF2); ++ PREV_PRINT_REGISTER(isp, CSC0); ++ PREV_PRINT_REGISTER(isp, CSC1); ++ PREV_PRINT_REGISTER(isp, CSC2); ++ PREV_PRINT_REGISTER(isp, CSC_OFFSET); ++ PREV_PRINT_REGISTER(isp, CNT_BRT); ++ PREV_PRINT_REGISTER(isp, CSUP); ++ PREV_PRINT_REGISTER(isp, SETUP_YC); ++ PREV_PRINT_REGISTER(isp, SET_TBL_ADDR); ++ PREV_PRINT_REGISTER(isp, CDC_THR0); ++ PREV_PRINT_REGISTER(isp, CDC_THR1); ++ PREV_PRINT_REGISTER(isp, CDC_THR2); ++ PREV_PRINT_REGISTER(isp, CDC_THR3); ++ ++ dev_dbg(isp->dev, "--------------------------------------------\n"); ++} ++ ++/* ++ * preview_init_params - init image processing parameters. ++ * @prev: pointer to previewer private structure ++ * return none ++ */ ++static void preview_init_params(struct isp_prev_device *prev) ++{ ++ struct prev_params *params = &prev->params; ++ int i = 0; ++ ++ /* Init values */ ++ params->contrast = ISPPRV_CONTRAST_DEF * ISPPRV_CONTRAST_UNITS; ++ params->brightness = ISPPRV_BRIGHT_DEF * ISPPRV_BRIGHT_UNITS; ++ params->average = NO_AVE; ++ params->cfa.format = OMAP3ISP_CFAFMT_BAYER; ++ memcpy(params->cfa.table, cfa_coef_table, ++ sizeof(params->cfa.table)); ++ params->cfa.gradthrs_horz = FLR_CFA_GRADTHRS_HORZ; ++ params->cfa.gradthrs_vert = FLR_CFA_GRADTHRS_VERT; ++ params->csup.gain = FLR_CSUP_GAIN; ++ params->csup.thres = FLR_CSUP_THRES; ++ params->csup.hypf_en = 0; ++ memcpy(params->luma.table, luma_enhance_table, ++ sizeof(params->luma.table)); ++ params->nf.spread = FLR_NF_STRGTH; ++ memcpy(params->nf.table, noise_filter_table, sizeof(params->nf.table)); ++ params->dcor.couplet_mode_en = 1; ++ for (i = 0; i < OMAP3ISP_PREV_DETECT_CORRECT_CHANNELS; i++) ++ params->dcor.detect_correct[i] = DEF_DETECT_CORRECT_VAL; ++ memcpy(params->gamma.blue, gamma_table, sizeof(params->gamma.blue)); ++ memcpy(params->gamma.green, gamma_table, sizeof(params->gamma.green)); ++ memcpy(params->gamma.red, gamma_table, sizeof(params->gamma.red)); ++ params->wbal.dgain = FLR_WBAL_DGAIN; ++ params->wbal.coef0 = FLR_WBAL_COEF; ++ params->wbal.coef1 = FLR_WBAL_COEF; ++ params->wbal.coef2 = FLR_WBAL_COEF; ++ params->wbal.coef3 = FLR_WBAL_COEF; ++ params->blk_adj.red = FLR_BLKADJ_RED; ++ params->blk_adj.green = FLR_BLKADJ_GREEN; ++ params->blk_adj.blue = FLR_BLKADJ_BLUE; ++ params->rgb2rgb = flr_rgb2rgb; ++ params->rgb2ycbcr = flr_prev_csc; ++ params->yclimit.minC = ISPPRV_YC_MIN; ++ params->yclimit.maxC = ISPPRV_YC_MAX; ++ params->yclimit.minY = ISPPRV_YC_MIN; ++ params->yclimit.maxY = ISPPRV_YC_MAX; ++ ++ params->features = PREV_CFA | PREV_DEFECT_COR | PREV_NOISE_FILTER ++ | PREV_GAMMA | PREV_BLKADJ | PREV_YCLIMITS ++ | PREV_RGB2RGB | PREV_COLOR_CONV | PREV_WB ++ | PREV_BRIGHTNESS | PREV_CONTRAST; ++ ++ prev->update = PREV_FEATURES_END - 1; ++} ++ ++/* ++ * preview_max_out_width - Handle previewer hardware ouput limitations ++ * @isp_revision : ISP revision ++ * returns maximum width output for current isp revision ++ */ ++static unsigned int preview_max_out_width(struct isp_prev_device *prev) ++{ ++ struct isp_device *isp = to_isp_device(prev); ++ ++ switch (isp->revision) { ++ case ISP_REVISION_1_0: ++ return ISPPRV_MAXOUTPUT_WIDTH; ++ ++ case ISP_REVISION_2_0: ++ default: ++ return ISPPRV_MAXOUTPUT_WIDTH_ES2; ++ ++ case ISP_REVISION_15_0: ++ return ISPPRV_MAXOUTPUT_WIDTH_3630; ++ } ++} ++ ++static void preview_configure(struct isp_prev_device *prev) ++{ ++ struct isp_device *isp = to_isp_device(prev); ++ struct v4l2_mbus_framefmt *format; ++ unsigned int max_out_width; ++ unsigned int format_avg; ++ ++ preview_setup_hw(prev); ++ ++ if (prev->output & PREVIEW_OUTPUT_MEMORY) ++ isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, ++ ISPPRV_PCR_SDRPORT); ++ else ++ isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, ++ ISPPRV_PCR_SDRPORT); ++ ++ if (prev->output & PREVIEW_OUTPUT_RESIZER) ++ isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, ++ ISPPRV_PCR_RSZPORT); ++ else ++ isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, ++ ISPPRV_PCR_RSZPORT); ++ ++ /* PREV_PAD_SINK */ ++ format = &prev->formats[PREV_PAD_SINK]; ++ ++ preview_adjust_bandwidth(prev); ++ ++ preview_config_input_size(prev); ++ ++ if (prev->input == PREVIEW_INPUT_CCDC) ++ preview_config_inlineoffset(prev, 0); ++ else ++ preview_config_inlineoffset(prev, ++ ALIGN(format->width, 0x20) * 2); ++ ++ /* PREV_PAD_SOURCE */ ++ format = &prev->formats[PREV_PAD_SOURCE]; ++ ++ if (prev->output & PREVIEW_OUTPUT_MEMORY) ++ preview_config_outlineoffset(prev, ++ ALIGN(format->width, 0x10) * 2); ++ ++ max_out_width = preview_max_out_width(prev); ++ ++ format_avg = fls(DIV_ROUND_UP(format->width, max_out_width) - 1); ++ preview_config_averager(prev, format_avg); ++ preview_config_ycpos(prev, format->code); ++} ++ ++/* ----------------------------------------------------------------------------- ++ * Interrupt handling ++ */ ++ ++static void preview_enable_oneshot(struct isp_prev_device *prev) ++{ ++ struct isp_device *isp = to_isp_device(prev); ++ ++ /* The PCR.SOURCE bit is automatically reset to 0 when the PCR.ENABLE ++ * bit is set. As the preview engine is used in single-shot mode, we ++ * need to set PCR.SOURCE before enabling the preview engine. ++ */ ++ if (prev->input == PREVIEW_INPUT_MEMORY) ++ isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, ++ ISPPRV_PCR_SOURCE); ++ ++ isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, ++ ISPPRV_PCR_EN | ISPPRV_PCR_ONESHOT); ++} ++ ++void omap3isp_preview_isr_frame_sync(struct isp_prev_device *prev) ++{ ++ /* ++ * If ISP_VIDEO_DMAQUEUE_QUEUED is set, DMA queue had an underrun ++ * condition, the module was paused and now we have a buffer queued ++ * on the output again. Restart the pipeline if running in continuous ++ * mode. ++ */ ++ if (prev->state == ISP_PIPELINE_STREAM_CONTINUOUS && ++ prev->video_out.dmaqueue_flags & ISP_VIDEO_DMAQUEUE_QUEUED) { ++ preview_enable_oneshot(prev); ++ isp_video_dmaqueue_flags_clr(&prev->video_out); ++ } ++} ++ ++static void preview_isr_buffer(struct isp_prev_device *prev) ++{ ++ struct isp_pipeline *pipe = to_isp_pipeline(&prev->subdev.entity); ++ struct isp_buffer *buffer; ++ int restart = 0; ++ ++ if (prev->input == PREVIEW_INPUT_MEMORY) { ++ buffer = omap3isp_video_buffer_next(&prev->video_in, ++ prev->error); ++ if (buffer != NULL) ++ preview_set_inaddr(prev, buffer->isp_addr); ++ pipe->state |= ISP_PIPELINE_IDLE_INPUT; ++ } ++ ++ if (prev->output & PREVIEW_OUTPUT_MEMORY) { ++ buffer = omap3isp_video_buffer_next(&prev->video_out, ++ prev->error); ++ if (buffer != NULL) { ++ preview_set_outaddr(prev, buffer->isp_addr); ++ restart = 1; ++ } ++ pipe->state |= ISP_PIPELINE_IDLE_OUTPUT; ++ } ++ ++ switch (prev->state) { ++ case ISP_PIPELINE_STREAM_SINGLESHOT: ++ if (isp_pipeline_ready(pipe)) ++ omap3isp_pipeline_set_stream(pipe, ++ ISP_PIPELINE_STREAM_SINGLESHOT); ++ break; ++ ++ case ISP_PIPELINE_STREAM_CONTINUOUS: ++ /* If an underrun occurs, the video queue operation handler will ++ * restart the preview engine. Otherwise restart it immediately. ++ */ ++ if (restart) ++ preview_enable_oneshot(prev); ++ break; ++ ++ case ISP_PIPELINE_STREAM_STOPPED: ++ default: ++ return; ++ } ++ ++ prev->error = 0; ++} ++ ++/* ++ * omap3isp_preview_isr - ISP preview engine interrupt handler ++ * ++ * Manage the preview engine video buffers and configure shadowed registers. ++ */ ++void omap3isp_preview_isr(struct isp_prev_device *prev) ++{ ++ unsigned long flags; ++ ++ if (omap3isp_module_sync_is_stopping(&prev->wait, &prev->stopping)) ++ return; ++ ++ spin_lock_irqsave(&prev->lock, flags); ++ if (prev->shadow_update) ++ goto done; ++ ++ preview_setup_hw(prev); ++ preview_config_input_size(prev); ++ ++done: ++ spin_unlock_irqrestore(&prev->lock, flags); ++ ++ if (prev->input == PREVIEW_INPUT_MEMORY || ++ prev->output & PREVIEW_OUTPUT_MEMORY) ++ preview_isr_buffer(prev); ++ else if (prev->state == ISP_PIPELINE_STREAM_CONTINUOUS) ++ preview_enable_oneshot(prev); ++} ++ ++/* ----------------------------------------------------------------------------- ++ * ISP video operations ++ */ ++ ++static int preview_video_queue(struct isp_video *video, ++ struct isp_buffer *buffer) ++{ ++ struct isp_prev_device *prev = &video->isp->isp_prev; ++ ++ if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) ++ preview_set_inaddr(prev, buffer->isp_addr); ++ ++ if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) ++ preview_set_outaddr(prev, buffer->isp_addr); ++ ++ return 0; ++} ++ ++static const struct isp_video_operations preview_video_ops = { ++ .queue = preview_video_queue, ++}; ++ ++/* ----------------------------------------------------------------------------- ++ * V4L2 subdev operations ++ */ ++ ++/* ++ * preview_s_ctrl - Handle set control subdev method ++ * @ctrl: pointer to v4l2 control structure ++ */ ++static int preview_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct isp_prev_device *prev = ++ container_of(ctrl->handler, struct isp_prev_device, ctrls); ++ ++ switch (ctrl->id) { ++ case V4L2_CID_BRIGHTNESS: ++ preview_update_brightness(prev, ctrl->val); ++ break; ++ case V4L2_CID_CONTRAST: ++ preview_update_contrast(prev, ctrl->val); ++ break; ++ } ++ ++ return 0; ++} ++ ++static const struct v4l2_ctrl_ops preview_ctrl_ops = { ++ .s_ctrl = preview_s_ctrl, ++}; ++ ++/* ++ * preview_ioctl - Handle preview module private ioctl's ++ * @prev: pointer to preview context structure ++ * @cmd: configuration command ++ * @arg: configuration argument ++ * return -EINVAL or zero on success ++ */ ++static long preview_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) ++{ ++ struct isp_prev_device *prev = v4l2_get_subdevdata(sd); ++ ++ switch (cmd) { ++ case VIDIOC_OMAP3ISP_PRV_CFG: ++ return preview_config(prev, arg); ++ ++ default: ++ return -ENOIOCTLCMD; ++ } ++} ++ ++/* ++ * preview_set_stream - Enable/Disable streaming on preview subdev ++ * @sd : pointer to v4l2 subdev structure ++ * @enable: 1 == Enable, 0 == Disable ++ * return -EINVAL or zero on sucess ++ */ ++static int preview_set_stream(struct v4l2_subdev *sd, int enable) ++{ ++ struct isp_prev_device *prev = v4l2_get_subdevdata(sd); ++ struct isp_video *video_out = &prev->video_out; ++ struct isp_device *isp = to_isp_device(prev); ++ struct device *dev = to_device(prev); ++ unsigned long flags; ++ ++ if (prev->state == ISP_PIPELINE_STREAM_STOPPED) { ++ if (enable == ISP_PIPELINE_STREAM_STOPPED) ++ return 0; ++ ++ omap3isp_subclk_enable(isp, OMAP3_ISP_SUBCLK_PREVIEW); ++ preview_configure(prev); ++ atomic_set(&prev->stopping, 0); ++ prev->error = 0; ++ preview_print_status(prev); ++ } ++ ++ switch (enable) { ++ case ISP_PIPELINE_STREAM_CONTINUOUS: ++ if (prev->output & PREVIEW_OUTPUT_MEMORY) ++ omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_PREVIEW_WRITE); ++ ++ if (video_out->dmaqueue_flags & ISP_VIDEO_DMAQUEUE_QUEUED || ++ !(prev->output & PREVIEW_OUTPUT_MEMORY)) ++ preview_enable_oneshot(prev); ++ ++ isp_video_dmaqueue_flags_clr(video_out); ++ break; ++ ++ case ISP_PIPELINE_STREAM_SINGLESHOT: ++ if (prev->input == PREVIEW_INPUT_MEMORY) ++ omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_PREVIEW_READ); ++ if (prev->output & PREVIEW_OUTPUT_MEMORY) ++ omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_PREVIEW_WRITE); ++ ++ preview_enable_oneshot(prev); ++ break; ++ ++ case ISP_PIPELINE_STREAM_STOPPED: ++ if (omap3isp_module_sync_idle(&sd->entity, &prev->wait, ++ &prev->stopping)) ++ dev_dbg(dev, "%s: stop timeout.\n", sd->name); ++ spin_lock_irqsave(&prev->lock, flags); ++ omap3isp_sbl_disable(isp, OMAP3_ISP_SBL_PREVIEW_READ); ++ omap3isp_sbl_disable(isp, OMAP3_ISP_SBL_PREVIEW_WRITE); ++ omap3isp_subclk_disable(isp, OMAP3_ISP_SUBCLK_PREVIEW); ++ spin_unlock_irqrestore(&prev->lock, flags); ++ isp_video_dmaqueue_flags_clr(video_out); ++ break; ++ } ++ ++ prev->state = enable; ++ return 0; ++} ++ ++static struct v4l2_mbus_framefmt * ++__preview_get_format(struct isp_prev_device *prev, struct v4l2_subdev_fh *fh, ++ unsigned int pad, enum v4l2_subdev_format_whence which) ++{ ++ if (which == V4L2_SUBDEV_FORMAT_TRY) ++ return v4l2_subdev_get_try_format(fh, pad); ++ else ++ return &prev->formats[pad]; ++} ++ ++/* previewer format descriptions */ ++static const unsigned int preview_input_fmts[] = { ++ V4L2_MBUS_FMT_SGRBG10_1X10, ++ V4L2_MBUS_FMT_SRGGB10_1X10, ++ V4L2_MBUS_FMT_SBGGR10_1X10, ++ V4L2_MBUS_FMT_SGBRG10_1X10, ++}; ++ ++static const unsigned int preview_output_fmts[] = { ++ V4L2_MBUS_FMT_UYVY8_1X16, ++ V4L2_MBUS_FMT_YUYV8_1X16, ++}; ++ ++/* ++ * preview_try_format - Handle try format by pad subdev method ++ * @prev: ISP preview device ++ * @fh : V4L2 subdev file handle ++ * @pad: pad num ++ * @fmt: pointer to v4l2 format structure ++ */ ++static void preview_try_format(struct isp_prev_device *prev, ++ struct v4l2_subdev_fh *fh, unsigned int pad, ++ struct v4l2_mbus_framefmt *fmt, ++ enum v4l2_subdev_format_whence which) ++{ ++ struct v4l2_mbus_framefmt *format; ++ unsigned int max_out_width; ++ enum v4l2_mbus_pixelcode pixelcode; ++ unsigned int i; ++ ++ max_out_width = preview_max_out_width(prev); ++ ++ switch (pad) { ++ case PREV_PAD_SINK: ++ /* When reading data from the CCDC, the input size has already ++ * been mangled by the CCDC output pad so it can be accepted ++ * as-is. ++ * ++ * When reading data from memory, clamp the requested width and ++ * height. The TRM doesn't specify a minimum input height, make ++ * sure we got enough lines to enable the noise filter and color ++ * filter array interpolation. ++ */ ++ if (prev->input == PREVIEW_INPUT_MEMORY) { ++ fmt->width = clamp_t(u32, fmt->width, PREV_MIN_WIDTH, ++ max_out_width * 8); ++ fmt->height = clamp_t(u32, fmt->height, PREV_MIN_HEIGHT, ++ PREV_MAX_HEIGHT); ++ } ++ ++ fmt->colorspace = V4L2_COLORSPACE_SRGB; ++ ++ for (i = 0; i < ARRAY_SIZE(preview_input_fmts); i++) { ++ if (fmt->code == preview_input_fmts[i]) ++ break; ++ } ++ ++ /* If not found, use SGRBG10 as default */ ++ if (i >= ARRAY_SIZE(preview_input_fmts)) ++ fmt->code = V4L2_MBUS_FMT_SGRBG10_1X10; ++ break; ++ ++ case PREV_PAD_SOURCE: ++ pixelcode = fmt->code; ++ format = __preview_get_format(prev, fh, PREV_PAD_SINK, which); ++ memcpy(fmt, format, sizeof(*fmt)); ++ ++ /* The preview module output size is configurable through the ++ * input interface (horizontal and vertical cropping) and the ++ * averager (horizontal scaling by 1/1, 1/2, 1/4 or 1/8). In ++ * spite of this, hardcode the output size to the biggest ++ * possible value for simplicity reasons. ++ */ ++ switch (pixelcode) { ++ case V4L2_MBUS_FMT_YUYV8_1X16: ++ case V4L2_MBUS_FMT_UYVY8_1X16: ++ fmt->code = pixelcode; ++ break; ++ ++ default: ++ fmt->code = V4L2_MBUS_FMT_YUYV8_1X16; ++ break; ++ } ++ ++ /* The TRM states (12.1.4.7.1.2) that 2 pixels must be cropped ++ * from the left and right sides when the input source is the ++ * CCDC. This seems not to be needed in practice, investigation ++ * is required. ++ */ ++ if (prev->input == PREVIEW_INPUT_CCDC) ++ fmt->width -= 4; ++ ++ /* The preview module can output a maximum of 3312 pixels ++ * horizontally due to fixed memory-line sizes. Compute the ++ * horizontal averaging factor accordingly. Note that the limit ++ * applies to the noise filter and CFA interpolation blocks, so ++ * it doesn't take cropping by further blocks into account. ++ * ++ * ES 1.0 hardware revision is limited to 1280 pixels ++ * horizontally. ++ */ ++ fmt->width >>= fls(DIV_ROUND_UP(fmt->width, max_out_width) - 1); ++ ++ /* Assume that all blocks are enabled and crop pixels and lines ++ * accordingly. See preview_config_input_size() for more ++ * information. ++ */ ++ fmt->width -= 14; ++ fmt->height -= 8; ++ ++ fmt->colorspace = V4L2_COLORSPACE_JPEG; ++ break; ++ } ++ ++ fmt->field = V4L2_FIELD_NONE; ++} ++ ++/* ++ * preview_enum_mbus_code - Handle pixel format enumeration ++ * @sd : pointer to v4l2 subdev structure ++ * @fh : V4L2 subdev file handle ++ * @code : pointer to v4l2_subdev_mbus_code_enum structure ++ * return -EINVAL or zero on success ++ */ ++static int preview_enum_mbus_code(struct v4l2_subdev *sd, ++ struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_mbus_code_enum *code) ++{ ++ switch (code->pad) { ++ case PREV_PAD_SINK: ++ if (code->index >= ARRAY_SIZE(preview_input_fmts)) ++ return -EINVAL; ++ ++ code->code = preview_input_fmts[code->index]; ++ break; ++ case PREV_PAD_SOURCE: ++ if (code->index >= ARRAY_SIZE(preview_output_fmts)) ++ return -EINVAL; ++ ++ code->code = preview_output_fmts[code->index]; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int preview_enum_frame_size(struct v4l2_subdev *sd, ++ struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_frame_size_enum *fse) ++{ ++ struct isp_prev_device *prev = v4l2_get_subdevdata(sd); ++ struct v4l2_mbus_framefmt format; ++ ++ if (fse->index != 0) ++ return -EINVAL; ++ ++ format.code = fse->code; ++ format.width = 1; ++ format.height = 1; ++ preview_try_format(prev, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); ++ fse->min_width = format.width; ++ fse->min_height = format.height; ++ ++ if (format.code != fse->code) ++ return -EINVAL; ++ ++ format.code = fse->code; ++ format.width = -1; ++ format.height = -1; ++ preview_try_format(prev, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); ++ fse->max_width = format.width; ++ fse->max_height = format.height; ++ ++ return 0; ++} ++ ++/* ++ * preview_get_format - Handle get format by pads subdev method ++ * @sd : pointer to v4l2 subdev structure ++ * @fh : V4L2 subdev file handle ++ * @fmt: pointer to v4l2 subdev format structure ++ * return -EINVAL or zero on sucess ++ */ ++static int preview_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_format *fmt) ++{ ++ struct isp_prev_device *prev = v4l2_get_subdevdata(sd); ++ struct v4l2_mbus_framefmt *format; ++ ++ format = __preview_get_format(prev, fh, fmt->pad, fmt->which); ++ if (format == NULL) ++ return -EINVAL; ++ ++ fmt->format = *format; ++ return 0; ++} ++ ++/* ++ * preview_set_format - Handle set format by pads subdev method ++ * @sd : pointer to v4l2 subdev structure ++ * @fh : V4L2 subdev file handle ++ * @fmt: pointer to v4l2 subdev format structure ++ * return -EINVAL or zero on success ++ */ ++static int preview_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_format *fmt) ++{ ++ struct isp_prev_device *prev = v4l2_get_subdevdata(sd); ++ struct v4l2_mbus_framefmt *format; ++ ++ format = __preview_get_format(prev, fh, fmt->pad, fmt->which); ++ if (format == NULL) ++ return -EINVAL; ++ ++ preview_try_format(prev, fh, fmt->pad, &fmt->format, fmt->which); ++ *format = fmt->format; ++ ++ /* Propagate the format from sink to source */ ++ if (fmt->pad == PREV_PAD_SINK) { ++ format = __preview_get_format(prev, fh, PREV_PAD_SOURCE, ++ fmt->which); ++ *format = fmt->format; ++ preview_try_format(prev, fh, PREV_PAD_SOURCE, format, ++ fmt->which); ++ } ++ ++ return 0; ++} ++ ++/* ++ * preview_init_formats - Initialize formats on all pads ++ * @sd: ISP preview V4L2 subdevice ++ * @fh: V4L2 subdev file handle ++ * ++ * Initialize all pad formats with default values. If fh is not NULL, try ++ * formats are initialized on the file handle. Otherwise active formats are ++ * initialized on the device. ++ */ ++static int preview_init_formats(struct v4l2_subdev *sd, ++ struct v4l2_subdev_fh *fh) ++{ ++ struct v4l2_subdev_format format; ++ ++ memset(&format, 0, sizeof(format)); ++ format.pad = PREV_PAD_SINK; ++ format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; ++ format.format.code = V4L2_MBUS_FMT_SGRBG10_1X10; ++ format.format.width = 4096; ++ format.format.height = 4096; ++ preview_set_format(sd, fh, &format); ++ ++ return 0; ++} ++ ++/* subdev core operations */ ++static const struct v4l2_subdev_core_ops preview_v4l2_core_ops = { ++ .queryctrl = v4l2_subdev_queryctrl, ++ .querymenu = v4l2_subdev_querymenu, ++ .g_ctrl = v4l2_subdev_g_ctrl, ++ .s_ctrl = v4l2_subdev_s_ctrl, ++ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, ++ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, ++ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, ++ .ioctl = preview_ioctl, ++}; ++ ++/* subdev file operations */ ++static const struct v4l2_subdev_file_ops preview_v4l2_file_ops = { ++ .open = preview_init_formats, ++}; ++ ++/* subdev video operations */ ++static const struct v4l2_subdev_video_ops preview_v4l2_video_ops = { ++ .s_stream = preview_set_stream, ++}; ++ ++/* subdev pad operations */ ++static const struct v4l2_subdev_pad_ops preview_v4l2_pad_ops = { ++ .enum_mbus_code = preview_enum_mbus_code, ++ .enum_frame_size = preview_enum_frame_size, ++ .get_fmt = preview_get_format, ++ .set_fmt = preview_set_format, ++}; ++ ++/* subdev operations */ ++static const struct v4l2_subdev_ops preview_v4l2_ops = { ++ .core = &preview_v4l2_core_ops, ++ .file = &preview_v4l2_file_ops, ++ .video = &preview_v4l2_video_ops, ++ .pad = &preview_v4l2_pad_ops, ++}; ++ ++/* ----------------------------------------------------------------------------- ++ * Media entity operations ++ */ ++ ++/* ++ * preview_link_setup - Setup previewer connections. ++ * @entity : Pointer to media entity structure ++ * @local : Pointer to local pad array ++ * @remote : Pointer to remote pad array ++ * @flags : Link flags ++ * return -EINVAL or zero on success ++ */ ++static int preview_link_setup(struct media_entity *entity, ++ const struct media_pad *local, ++ const struct media_pad *remote, u32 flags) ++{ ++ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); ++ struct isp_prev_device *prev = v4l2_get_subdevdata(sd); ++ ++ switch (local->index | media_entity_type(remote->entity)) { ++ case PREV_PAD_SINK | MEDIA_ENT_T_DEVNODE: ++ /* read from memory */ ++ if (flags & MEDIA_LNK_FL_ENABLED) { ++ if (prev->input == PREVIEW_INPUT_CCDC) ++ return -EBUSY; ++ prev->input = PREVIEW_INPUT_MEMORY; ++ } else { ++ if (prev->input == PREVIEW_INPUT_MEMORY) ++ prev->input = PREVIEW_INPUT_NONE; ++ } ++ break; ++ ++ case PREV_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: ++ /* read from ccdc */ ++ if (flags & MEDIA_LNK_FL_ENABLED) { ++ if (prev->input == PREVIEW_INPUT_MEMORY) ++ return -EBUSY; ++ prev->input = PREVIEW_INPUT_CCDC; ++ } else { ++ if (prev->input == PREVIEW_INPUT_CCDC) ++ prev->input = PREVIEW_INPUT_NONE; ++ } ++ break; ++ ++ /* ++ * The ISP core doesn't support pipelines with multiple video outputs. ++ * Revisit this when it will be implemented, and return -EBUSY for now. ++ */ ++ ++ case PREV_PAD_SOURCE | MEDIA_ENT_T_DEVNODE: ++ /* write to memory */ ++ if (flags & MEDIA_LNK_FL_ENABLED) { ++ if (prev->output & ~PREVIEW_OUTPUT_MEMORY) ++ return -EBUSY; ++ prev->output |= PREVIEW_OUTPUT_MEMORY; ++ } else { ++ prev->output &= ~PREVIEW_OUTPUT_MEMORY; ++ } ++ break; ++ ++ case PREV_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV: ++ /* write to resizer */ ++ if (flags & MEDIA_LNK_FL_ENABLED) { ++ if (prev->output & ~PREVIEW_OUTPUT_RESIZER) ++ return -EBUSY; ++ prev->output |= PREVIEW_OUTPUT_RESIZER; ++ } else { ++ prev->output &= ~PREVIEW_OUTPUT_RESIZER; ++ } ++ break; ++ ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++/* media operations */ ++static const struct media_entity_operations preview_media_ops = { ++ .link_setup = preview_link_setup, ++}; ++ ++/* ++ * review_init_entities - Initialize subdev and media entity. ++ * @prev : Pointer to preview structure ++ * return -ENOMEM or zero on success ++ */ ++static int preview_init_entities(struct isp_prev_device *prev) ++{ ++ struct v4l2_subdev *sd = &prev->subdev; ++ struct media_pad *pads = prev->pads; ++ struct media_entity *me = &sd->entity; ++ int ret; ++ ++ prev->input = PREVIEW_INPUT_NONE; ++ ++ v4l2_subdev_init(sd, &preview_v4l2_ops); ++ strlcpy(sd->name, "OMAP3 ISP preview", sizeof(sd->name)); ++ sd->grp_id = 1 << 16; /* group ID for isp subdevs */ ++ v4l2_set_subdevdata(sd, prev); ++ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; ++ ++ v4l2_ctrl_handler_init(&prev->ctrls, 3); ++ v4l2_ctrl_new_std(&prev->ctrls, &preview_ctrl_ops, V4L2_CID_BRIGHTNESS, ++ ISPPRV_BRIGHT_LOW, ISPPRV_BRIGHT_HIGH, ++ ISPPRV_BRIGHT_STEP, ISPPRV_BRIGHT_DEF); ++ v4l2_ctrl_new_std(&prev->ctrls, &preview_ctrl_ops, V4L2_CID_CONTRAST, ++ ISPPRV_CONTRAST_LOW, ISPPRV_CONTRAST_HIGH, ++ ISPPRV_CONTRAST_STEP, ISPPRV_CONTRAST_DEF); ++ v4l2_ctrl_handler_setup(&prev->ctrls); ++ sd->ctrl_handler = &prev->ctrls; ++ ++ pads[PREV_PAD_SINK].flags = MEDIA_PAD_FL_INPUT; ++ pads[PREV_PAD_SOURCE].flags = MEDIA_PAD_FL_OUTPUT; ++ ++ me->ops = &preview_media_ops; ++ ret = media_entity_init(me, PREV_PADS_NUM, pads, 0); ++ if (ret < 0) ++ return ret; ++ ++ preview_init_formats(sd, NULL); ++ ++ /* According to the OMAP34xx TRM, video buffers need to be aligned on a ++ * 32 bytes boundary. However, an undocumented hardware bug requires a ++ * 64 bytes boundary at the preview engine input. ++ */ ++ prev->video_in.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; ++ prev->video_in.ops = &preview_video_ops; ++ prev->video_in.isp = to_isp_device(prev); ++ prev->video_in.capture_mem = PAGE_ALIGN(4096 * 4096) * 2 * 3; ++ prev->video_in.bpl_alignment = 64; ++ prev->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ prev->video_out.ops = &preview_video_ops; ++ prev->video_out.isp = to_isp_device(prev); ++ prev->video_out.capture_mem = PAGE_ALIGN(4096 * 4096) * 2 * 3; ++ prev->video_out.bpl_alignment = 32; ++ ++ ret = omap3isp_video_init(&prev->video_in, "preview"); ++ if (ret < 0) ++ return ret; ++ ++ ret = omap3isp_video_init(&prev->video_out, "preview"); ++ if (ret < 0) ++ return ret; ++ ++ /* Connect the video nodes to the previewer subdev. */ ++ ret = media_entity_create_link(&prev->video_in.video.entity, 0, ++ &prev->subdev.entity, PREV_PAD_SINK, 0); ++ if (ret < 0) ++ return ret; ++ ++ ret = media_entity_create_link(&prev->subdev.entity, PREV_PAD_SOURCE, ++ &prev->video_out.video.entity, 0, 0); ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++void omap3isp_preview_unregister_entities(struct isp_prev_device *prev) ++{ ++ media_entity_cleanup(&prev->subdev.entity); ++ ++ v4l2_device_unregister_subdev(&prev->subdev); ++ v4l2_ctrl_handler_free(&prev->ctrls); ++ omap3isp_video_unregister(&prev->video_in); ++ omap3isp_video_unregister(&prev->video_out); ++} ++ ++int omap3isp_preview_register_entities(struct isp_prev_device *prev, ++ struct v4l2_device *vdev) ++{ ++ int ret; ++ ++ /* Register the subdev and video nodes. */ ++ ret = v4l2_device_register_subdev(vdev, &prev->subdev); ++ if (ret < 0) ++ goto error; ++ ++ ret = omap3isp_video_register(&prev->video_in, vdev); ++ if (ret < 0) ++ goto error; ++ ++ ret = omap3isp_video_register(&prev->video_out, vdev); ++ if (ret < 0) ++ goto error; ++ ++ return 0; ++ ++error: ++ omap3isp_preview_unregister_entities(prev); ++ return ret; ++} ++ ++/* ----------------------------------------------------------------------------- ++ * ISP previewer initialisation and cleanup ++ */ ++ ++void omap3isp_preview_cleanup(struct isp_device *isp) ++{ ++} ++ ++/* ++ * isp_preview_init - Previewer initialization. ++ * @dev : Pointer to ISP device ++ * return -ENOMEM or zero on success ++ */ ++int omap3isp_preview_init(struct isp_device *isp) ++{ ++ struct isp_prev_device *prev = &isp->isp_prev; ++ int ret; ++ ++ spin_lock_init(&prev->lock); ++ init_waitqueue_head(&prev->wait); ++ preview_init_params(prev); ++ ++ ret = preview_init_entities(prev); ++ if (ret < 0) ++ goto out; ++ ++out: ++ if (ret) ++ omap3isp_preview_cleanup(isp); ++ ++ return ret; ++} +diff --git a/drivers/media/video/isp/isppreview.h b/drivers/media/video/isp/isppreview.h +new file mode 100644 +index 0000000..e20c7c6 +--- /dev/null ++++ b/drivers/media/video/isp/isppreview.h +@@ -0,0 +1,214 @@ ++/* ++ * isppreview.h ++ * ++ * TI OMAP3 ISP - Preview module ++ * ++ * Copyright (C) 2010 Nokia Corporation ++ * Copyright (C) 2009 Texas Instruments, Inc. ++ * ++ * Contacts: Laurent Pinchart ++ * Sakari Ailus ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#ifndef OMAP3_ISP_PREVIEW_H ++#define OMAP3_ISP_PREVIEW_H ++ ++#include ++#include ++#include ++ ++#include "ispvideo.h" ++ ++#define ISPPRV_BRIGHT_STEP 0x1 ++#define ISPPRV_BRIGHT_DEF 0x0 ++#define ISPPRV_BRIGHT_LOW 0x0 ++#define ISPPRV_BRIGHT_HIGH 0xFF ++#define ISPPRV_BRIGHT_UNITS 0x1 ++ ++#define ISPPRV_CONTRAST_STEP 0x1 ++#define ISPPRV_CONTRAST_DEF 0x10 ++#define ISPPRV_CONTRAST_LOW 0x0 ++#define ISPPRV_CONTRAST_HIGH 0xFF ++#define ISPPRV_CONTRAST_UNITS 0x1 ++ ++#define NO_AVE 0x0 ++#define AVE_2_PIX 0x1 ++#define AVE_4_PIX 0x2 ++#define AVE_8_PIX 0x3 ++ ++/* Features list */ ++#define PREV_LUMA_ENHANCE OMAP3ISP_PREV_LUMAENH ++#define PREV_INVERSE_ALAW OMAP3ISP_PREV_INVALAW ++#define PREV_HORZ_MEDIAN_FILTER OMAP3ISP_PREV_HRZ_MED ++#define PREV_CFA OMAP3ISP_PREV_CFA ++#define PREV_CHROMA_SUPPRESS OMAP3ISP_PREV_CHROMA_SUPP ++#define PREV_WB OMAP3ISP_PREV_WB ++#define PREV_BLKADJ OMAP3ISP_PREV_BLKADJ ++#define PREV_RGB2RGB OMAP3ISP_PREV_RGB2RGB ++#define PREV_COLOR_CONV OMAP3ISP_PREV_COLOR_CONV ++#define PREV_YCLIMITS OMAP3ISP_PREV_YC_LIMIT ++#define PREV_DEFECT_COR OMAP3ISP_PREV_DEFECT_COR ++#define PREV_GAMMA_BYPASS OMAP3ISP_PREV_GAMMABYPASS ++#define PREV_DARK_FRAME_CAPTURE OMAP3ISP_PREV_DRK_FRM_CAPTURE ++#define PREV_DARK_FRAME_SUBTRACT OMAP3ISP_PREV_DRK_FRM_SUBTRACT ++#define PREV_LENS_SHADING OMAP3ISP_PREV_LENS_SHADING ++#define PREV_NOISE_FILTER OMAP3ISP_PREV_NF ++#define PREV_GAMMA OMAP3ISP_PREV_GAMMA ++ ++#define PREV_CONTRAST (1 << 17) ++#define PREV_BRIGHTNESS (1 << 18) ++#define PREV_AVERAGER (1 << 19) ++#define PREV_FEATURES_END (1 << 20) ++ ++enum preview_input_entity { ++ PREVIEW_INPUT_NONE, ++ PREVIEW_INPUT_CCDC, ++ PREVIEW_INPUT_MEMORY, ++}; ++ ++#define PREVIEW_OUTPUT_RESIZER (1 << 1) ++#define PREVIEW_OUTPUT_MEMORY (1 << 2) ++ ++/* Configure byte layout of YUV image */ ++enum preview_ycpos_mode { ++ YCPOS_YCrYCb = 0, ++ YCPOS_YCbYCr = 1, ++ YCPOS_CbYCrY = 2, ++ YCPOS_CrYCbY = 3 ++}; ++ ++/* ++ * struct prev_params - Structure for all configuration ++ * @features: Set of features enabled. ++ * @cfa: CFA coefficients. ++ * @csup: Chroma suppression coefficients. ++ * @luma: Luma enhancement coefficients. ++ * @nf: Noise filter coefficients. ++ * @dcor: Noise filter coefficients. ++ * @gamma: Gamma coefficients. ++ * @wbal: White Balance parameters. ++ * @blk_adj: Black adjustment parameters. ++ * @rgb2rgb: RGB blending parameters. ++ * @rgb2ycbcr: RGB to ycbcr parameters. ++ * @hmed: Horizontal median filter. ++ * @yclimit: YC limits parameters. ++ * @average: Downsampling rate for averager. ++ * @contrast: Contrast. ++ * @brightness: Brightness. ++ */ ++struct prev_params { ++ u32 features; ++ struct omap3isp_prev_cfa cfa; ++ struct omap3isp_prev_csup csup; ++ struct omap3isp_prev_luma luma; ++ struct omap3isp_prev_nf nf; ++ struct omap3isp_prev_dcor dcor; ++ struct omap3isp_prev_gtables gamma; ++ struct omap3isp_prev_wbal wbal; ++ struct omap3isp_prev_blkadj blk_adj; ++ struct omap3isp_prev_rgbtorgb rgb2rgb; ++ struct omap3isp_prev_csc rgb2ycbcr; ++ struct omap3isp_prev_hmed hmed; ++ struct omap3isp_prev_yclimit yclimit; ++ u8 average; ++ u8 contrast; ++ u8 brightness; ++}; ++ ++/* ++ * struct isptables_update - Structure for Table Configuration. ++ * @update: Specifies which tables should be updated. ++ * @flag: Specifies which tables should be enabled. ++ * @nf: Pointer to structure for Noise Filter ++ * @lsc: Pointer to LSC gain table. (currently not used) ++ * @gamma: Pointer to gamma correction tables. ++ * @cfa: Pointer to color filter array configuration. ++ * @wbal: Pointer to colour and digital gain configuration. ++ */ ++struct isptables_update { ++ u32 update; ++ u32 flag; ++ struct omap3isp_prev_nf *nf; ++ u32 *lsc; ++ struct omap3isp_prev_gtables *gamma; ++ struct omap3isp_prev_cfa *cfa; ++ struct omap3isp_prev_wbal *wbal; ++}; ++ ++/* Sink and source previewer pads */ ++#define PREV_PAD_SINK 0 ++#define PREV_PAD_SOURCE 1 ++#define PREV_PADS_NUM 2 ++ ++/* ++ * struct isp_prev_device - Structure for storing ISP Preview module information ++ * @subdev: V4L2 subdevice ++ * @pads: Media entity pads ++ * @formats: Active formats at the subdev pad ++ * @input: Module currently connected to the input pad ++ * @output: Bitmask of the active output ++ * @video_in: Input video entity ++ * @video_out: Output video entity ++ * @error: A hardware error occured during capture ++ * @params: Module configuration data ++ * @shadow_update: If set, update the hardware configured in the next interrupt ++ * @underrun: Whether the preview entity has queued buffers on the output ++ * @state: Current preview pipeline state ++ * @lock: Shadow update lock ++ * @update: Bitmask of the parameters to be updated ++ * ++ * This structure is used to store the OMAP ISP Preview module Information. ++ */ ++struct isp_prev_device { ++ struct v4l2_subdev subdev; ++ struct media_pad pads[PREV_PADS_NUM]; ++ struct v4l2_mbus_framefmt formats[PREV_PADS_NUM]; ++ ++ struct v4l2_ctrl_handler ctrls; ++ ++ enum preview_input_entity input; ++ unsigned int output; ++ struct isp_video video_in; ++ struct isp_video video_out; ++ unsigned int error; ++ ++ struct prev_params params; ++ unsigned int shadow_update:1; ++ enum isp_pipeline_stream_state state; ++ wait_queue_head_t wait; ++ atomic_t stopping; ++ spinlock_t lock; ++ u32 update; ++}; ++ ++struct isp_device; ++ ++int omap3isp_preview_init(struct isp_device *isp); ++void omap3isp_preview_cleanup(struct isp_device *isp); ++ ++int omap3isp_preview_register_entities(struct isp_prev_device *prv, ++ struct v4l2_device *vdev); ++void omap3isp_preview_unregister_entities(struct isp_prev_device *prv); ++ ++void omap3isp_preview_isr_frame_sync(struct isp_prev_device *prev); ++void omap3isp_preview_isr(struct isp_prev_device *prev); ++ ++int omap3isp_preview_busy(struct isp_prev_device *isp_prev); ++ ++void omap3isp_preview_restore_context(struct isp_device *isp); ++ ++#endif /* OMAP3_ISP_PREVIEW_H */ +diff --git a/drivers/media/video/isp/ispqueue.c b/drivers/media/video/isp/ispqueue.c +new file mode 100644 +index 0000000..af78c19 +--- /dev/null ++++ b/drivers/media/video/isp/ispqueue.c +@@ -0,0 +1,1136 @@ ++/* ++ * ispqueue.c ++ * ++ * TI OMAP3 ISP - Video buffers queue handling ++ * ++ * Copyright (C) 2010 Nokia Corporation ++ * ++ * Contacts: Laurent Pinchart ++ * Sakari Ailus ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "ispqueue.h" ++ ++/* ----------------------------------------------------------------------------- ++ * Video buffers management ++ */ ++ ++/* ++ * isp_video_buffer_cache_sync - Keep the buffers coherent between CPU and ISP ++ * ++ * The typical operation required here is Cache Invalidation across ++ * the (user space) buffer address range. And this _must_ be done ++ * at QBUF stage (and *only* at QBUF). ++ * ++ * We try to use optimal cache invalidation function: ++ * - dmac_map_area: ++ * - used when the number of pages are _low_. ++ * - it becomes quite slow as the number of pages increase. ++ * - for 648x492 viewfinder (150 pages) it takes 1.3 ms. ++ * - for 5 Mpix buffer (2491 pages) it takes between 25-50 ms. ++ * ++ * - flush_cache_all: ++ * - used when the number of pages are _high_. ++ * - time taken in the range of 500-900 us. ++ * - has a higher penalty but, as whole dcache + icache is invalidated ++ */ ++/* ++ * FIXME: dmac_inv_range crashes randomly on the user space buffer ++ * address. Fall back to flush_cache_all for now. ++ */ ++#define ISP_CACHE_FLUSH_PAGES_MAX 0 ++ ++static void isp_video_buffer_cache_sync(struct isp_video_buffer *buf) ++{ ++ if (buf->vbuf.m.userptr == 0 || buf->npages == 0 || ++ buf->npages > ISP_CACHE_FLUSH_PAGES_MAX) ++ flush_cache_all(); ++ else { ++ dmac_map_area((void *)buf->vbuf.m.userptr, buf->vbuf.length, ++ DMA_FROM_DEVICE); ++ outer_inv_range(buf->vbuf.m.userptr, ++ buf->vbuf.m.userptr + buf->vbuf.length); ++ } ++} ++ ++/* ++ * isp_video_buffer_lock_vma - Prevent VMAs from being unmapped ++ * ++ * Lock the VMAs underlying the given buffer into memory. This avoids the ++ * userspace buffer mapping from being swapped out, making VIPT cache handling ++ * easier. ++ * ++ * Note that the pages will not be freed as the buffers have been locked to ++ * memory using by a call to get_user_pages(), but the userspace mapping could ++ * still disappear if the VMAs are not locked. This is caused by the memory ++ * management code trying to be as lock-less as possible, which results in the ++ * userspace mapping manager not finding out that the pages are locked under ++ * some conditions. ++ */ ++static int isp_video_buffer_lock_vma(struct isp_video_buffer *buf, int lock) ++{ ++ struct vm_area_struct *vma; ++ unsigned long start; ++ unsigned long end; ++ int ret = 0; ++ ++ if (buf->vbuf.memory == V4L2_MEMORY_MMAP) ++ return 0; ++ ++ /* We can be called from workqueue context if the current task dies to ++ * unlock the VMAs. In that case there's no current memory management ++ * context so unlocking can't be performed, but the VMAs have been or ++ * are getting destroyed anyway so it doesn't really matter. ++ */ ++ if (!current || !current->mm) ++ return lock ? -EINVAL : 0; ++ ++ start = buf->vbuf.m.userptr; ++ end = buf->vbuf.m.userptr + buf->vbuf.length - 1; ++ ++ down_write(¤t->mm->mmap_sem); ++ spin_lock(¤t->mm->page_table_lock); ++ ++ do { ++ vma = find_vma(current->mm, start); ++ if (vma == NULL) { ++ ret = -EFAULT; ++ goto out; ++ } ++ ++ if (lock) ++ vma->vm_flags |= VM_LOCKED; ++ else ++ vma->vm_flags &= ~VM_LOCKED; ++ ++ start = vma->vm_end + 1; ++ } while (vma->vm_end < end); ++ ++ if (lock) ++ buf->vm_flags |= VM_LOCKED; ++ else ++ buf->vm_flags &= ~VM_LOCKED; ++ ++out: ++ spin_unlock(¤t->mm->page_table_lock); ++ up_write(¤t->mm->mmap_sem); ++ return ret; ++} ++ ++/* ++ * isp_video_buffer_sglist_kernel - Build a scatter list for a vmalloc'ed buffer ++ * ++ * Iterate over the vmalloc'ed area and create a scatter list entry for every ++ * page. ++ */ ++static int isp_video_buffer_sglist_kernel(struct isp_video_buffer *buf) ++{ ++ struct scatterlist *sglist; ++ unsigned int npages; ++ unsigned int i; ++ void *addr; ++ ++ addr = buf->vaddr; ++ npages = PAGE_ALIGN(buf->vbuf.length) >> PAGE_SHIFT; ++ ++ sglist = vmalloc(npages * sizeof(*sglist)); ++ if (sglist == NULL) ++ return -ENOMEM; ++ ++ sg_init_table(sglist, npages); ++ ++ for (i = 0; i < npages; ++i, addr += PAGE_SIZE) { ++ struct page *page = vmalloc_to_page(addr); ++ ++ if (page == NULL || PageHighMem(page)) { ++ vfree(sglist); ++ return -EINVAL; ++ } ++ ++ sg_set_page(&sglist[i], page, PAGE_SIZE, 0); ++ } ++ ++ buf->sglen = npages; ++ buf->sglist = sglist; ++ ++ return 0; ++} ++ ++/* ++ * isp_video_buffer_sglist_user - Build a scatter list for a userspace buffer ++ * ++ * Walk the buffer pages list and create a 1:1 mapping to a scatter list. ++ */ ++static int isp_video_buffer_sglist_user(struct isp_video_buffer *buf) ++{ ++ struct scatterlist *sglist; ++ unsigned int offset = buf->offset; ++ unsigned int i; ++ ++ sglist = vmalloc(buf->npages * sizeof(*sglist)); ++ if (sglist == NULL) ++ return -ENOMEM; ++ ++ sg_init_table(sglist, buf->npages); ++ ++ for (i = 0; i < buf->npages; ++i) { ++ if (PageHighMem(buf->pages[i])) { ++ vfree(sglist); ++ return -EINVAL; ++ } ++ ++ sg_set_page(&sglist[i], buf->pages[i], PAGE_SIZE - offset, ++ offset); ++ offset = 0; ++ } ++ ++ buf->sglen = buf->npages; ++ buf->sglist = sglist; ++ ++ return 0; ++} ++ ++/* ++ * isp_video_buffer_sglist_pfnmap - Build a scatter list for a VM_PFNMAP buffer ++ * ++ * Create a scatter list of physically contiguous pages starting at the buffer ++ * memory physical address. ++ */ ++static int isp_video_buffer_sglist_pfnmap(struct isp_video_buffer *buf) ++{ ++ struct scatterlist *sglist; ++ unsigned int offset = buf->offset; ++ unsigned long pfn = buf->paddr >> PAGE_SHIFT; ++ unsigned int i; ++ ++ sglist = vmalloc(buf->npages * sizeof(*sglist)); ++ if (sglist == NULL) ++ return -ENOMEM; ++ ++ sg_init_table(sglist, buf->npages); ++ ++ for (i = 0; i < buf->npages; ++i, ++pfn) { ++ sg_set_page(&sglist[i], pfn_to_page(pfn), PAGE_SIZE - offset, ++ offset); ++ /* PFNMAP buffers will not get DMA-mapped, set the DMA address ++ * manually. ++ */ ++ sg_dma_address(&sglist[i]) = (pfn << PAGE_SHIFT) + offset; ++ offset = 0; ++ } ++ ++ buf->sglen = buf->npages; ++ buf->sglist = sglist; ++ ++ return 0; ++} ++ ++/* ++ * isp_video_buffer_cleanup - Release pages for a userspace VMA. ++ * ++ * Release pages locked by a call isp_video_buffer_prepare_user and free the ++ * pages table. ++ */ ++static void isp_video_buffer_cleanup(struct isp_video_buffer *buf) ++{ ++ enum dma_data_direction direction; ++ unsigned int i; ++ ++ if (buf->queue->ops->buffer_cleanup) ++ buf->queue->ops->buffer_cleanup(buf); ++ ++ if (!(buf->vm_flags & VM_PFNMAP)) { ++ direction = buf->vbuf.type == V4L2_BUF_TYPE_VIDEO_CAPTURE ++ ? DMA_FROM_DEVICE : DMA_TO_DEVICE; ++ dma_unmap_sg(buf->queue->dev, buf->sglist, buf->sglen, ++ direction); ++ } ++ ++ vfree(buf->sglist); ++ buf->sglist = NULL; ++ buf->sglen = 0; ++ ++ if (buf->pages != NULL) { ++ isp_video_buffer_lock_vma(buf, 0); ++ ++ for (i = 0; i < buf->npages; ++i) ++ page_cache_release(buf->pages[i]); ++ ++ vfree(buf->pages); ++ buf->pages = NULL; ++ } ++ ++ buf->npages = 0; ++} ++ ++/* ++ * isp_video_buffer_prepare_user - Pin userspace VMA pages to memory. ++ * ++ * This function creates a list of pages for a userspace VMA. The number of ++ * pages is first computed based on the buffer size, and pages are then ++ * retrieved by a call to get_user_pages. ++ * ++ * Pages are pinned to memory by get_user_pages, making them available for DMA ++ * transfers. However, due to memory management optimization, it seems the ++ * get_user_pages doesn't guarantee that the pinned pages will not be written ++ * to swap and removed from the userspace mapping(s). When this happens, a page ++ * fault can be generated when accessing those unmapped pages. ++ * ++ * If the fault is triggered by a page table walk caused by VIPT cache ++ * management operations, the page fault handler might oops if the MM semaphore ++ * is held, as it can't handle kernel page faults in that case. To fix that, a ++ * fixup entry needs to be added to the cache management code, or the userspace ++ * VMA must be locked to avoid removing pages from the userspace mapping in the ++ * first place. ++ * ++ * If the number of pages retrieved is smaller than the number required by the ++ * buffer size, the function returns -EFAULT. ++ */ ++static int isp_video_buffer_prepare_user(struct isp_video_buffer *buf) ++{ ++ unsigned long data; ++ unsigned int first; ++ unsigned int last; ++ int ret; ++ ++ data = buf->vbuf.m.userptr; ++ first = (data & PAGE_MASK) >> PAGE_SHIFT; ++ last = ((data + buf->vbuf.length - 1) & PAGE_MASK) >> PAGE_SHIFT; ++ ++ buf->offset = data & ~PAGE_MASK; ++ buf->npages = last - first + 1; ++ buf->pages = vmalloc(buf->npages * sizeof(buf->pages[0])); ++ if (buf->pages == NULL) ++ return -ENOMEM; ++ ++ down_read(¤t->mm->mmap_sem); ++ ret = get_user_pages(current, current->mm, data & PAGE_MASK, ++ buf->npages, ++ buf->vbuf.type == V4L2_BUF_TYPE_VIDEO_CAPTURE, 0, ++ buf->pages, NULL); ++ up_read(¤t->mm->mmap_sem); ++ ++ if (ret != buf->npages) { ++ buf->npages = ret; ++ isp_video_buffer_cleanup(buf); ++ return -EFAULT; ++ } ++ ++ ret = isp_video_buffer_lock_vma(buf, 1); ++ if (ret < 0) ++ isp_video_buffer_cleanup(buf); ++ ++ return ret; ++} ++ ++/* ++ * isp_video_buffer_prepare_pfnmap - Validate a VM_PFNMAP userspace buffer ++ * ++ * Userspace VM_PFNMAP buffers are supported only if they are contiguous in ++ * memory and if they span a single VMA. ++ * ++ * Return 0 if the buffer is valid, or -EFAULT otherwise. ++ */ ++static int isp_video_buffer_prepare_pfnmap(struct isp_video_buffer *buf) ++{ ++ struct vm_area_struct *vma; ++ unsigned long prev_pfn; ++ unsigned long this_pfn; ++ unsigned long start; ++ unsigned long end; ++ dma_addr_t pa; ++ int ret = -EFAULT; ++ ++ start = buf->vbuf.m.userptr; ++ end = buf->vbuf.m.userptr + buf->vbuf.length - 1; ++ ++ buf->offset = start & ~PAGE_MASK; ++ buf->npages = (end >> PAGE_SHIFT) - (start >> PAGE_SHIFT) + 1; ++ buf->pages = NULL; ++ ++ down_read(¤t->mm->mmap_sem); ++ vma = find_vma(current->mm, start); ++ if (vma == NULL || vma->vm_end < end) ++ goto done; ++ ++ for (prev_pfn = 0; start <= end; start += PAGE_SIZE) { ++ ret = follow_pfn(vma, start, &this_pfn); ++ if (ret) ++ goto done; ++ ++ if (prev_pfn == 0) ++ pa = this_pfn << PAGE_SHIFT; ++ else if (this_pfn != prev_pfn + 1) { ++ ret = -EFAULT; ++ goto done; ++ } ++ ++ prev_pfn = this_pfn; ++ } ++ ++ buf->paddr = pa + buf->offset; ++ ret = 0; ++ ++done: ++ up_read(¤t->mm->mmap_sem); ++ return ret; ++} ++ ++/* ++ * isp_video_buffer_prepare_vm_flags - Get VMA flags for a userspace address ++ * ++ * This function locates the VMAs for the buffer's userspace address and checks ++ * that their flags match. The onlflag that we need to care for at the moment is ++ * VM_PFNMAP. ++ * ++ * The buffer vm_flags field is set to the first VMA flags. ++ * ++ * Return -EFAULT if no VMA can be found for part of the buffer, or if the VMAs ++ * have incompatible flags. ++ */ ++static int isp_video_buffer_prepare_vm_flags(struct isp_video_buffer *buf) ++{ ++ struct vm_area_struct *vma; ++ unsigned long start; ++ unsigned long end; ++ int ret = -EFAULT; ++ ++ start = buf->vbuf.m.userptr; ++ end = buf->vbuf.m.userptr + buf->vbuf.length - 1; ++ ++ down_read(¤t->mm->mmap_sem); ++ ++ do { ++ vma = find_vma(current->mm, start); ++ if (vma == NULL) ++ goto done; ++ ++ if (start == buf->vbuf.m.userptr) ++ buf->vm_flags = vma->vm_flags; ++ ++ if ((buf->vm_flags ^ vma->vm_flags) & VM_PFNMAP) ++ goto done; ++ ++ start = vma->vm_end + 1; ++ } while (vma->vm_end < end); ++ ++ ret = 0; ++ ++done: ++ up_read(¤t->mm->mmap_sem); ++ return ret; ++} ++ ++/* ++ * isp_video_buffer_prepare - Make a buffer ready for operation ++ * ++ * Preparing a buffer involves: ++ * ++ * - validating VMAs (userspace buffers only) ++ * - locking pages and VMAs into memory (userspace buffers only) ++ * - building page and scatter-gather lists ++ * - mapping buffers for DMA operation ++ * - performing driver-specific preparation ++ * ++ * The function must be called in userspace context with a valid mm context ++ * (this excludes cleanup paths such as sys_close when the userspace process ++ * segfaults). ++ */ ++static int isp_video_buffer_prepare(struct isp_video_buffer *buf) ++{ ++ enum dma_data_direction direction; ++ int ret; ++ ++ switch (buf->vbuf.memory) { ++ case V4L2_MEMORY_MMAP: ++ ret = isp_video_buffer_sglist_kernel(buf); ++ break; ++ ++ case V4L2_MEMORY_USERPTR: ++ ret = isp_video_buffer_prepare_vm_flags(buf); ++ if (ret < 0) ++ return ret; ++ ++ if (buf->vm_flags & VM_PFNMAP) { ++ ret = isp_video_buffer_prepare_pfnmap(buf); ++ if (ret < 0) ++ return ret; ++ ++ ret = isp_video_buffer_sglist_pfnmap(buf); ++ } else { ++ ret = isp_video_buffer_prepare_user(buf); ++ if (ret < 0) ++ return ret; ++ ++ ret = isp_video_buffer_sglist_user(buf); ++ } ++ break; ++ ++ default: ++ return -EINVAL; ++ } ++ ++ if (ret < 0) ++ goto done; ++ ++ if (!(buf->vm_flags & VM_PFNMAP)) { ++ direction = buf->vbuf.type == V4L2_BUF_TYPE_VIDEO_CAPTURE ++ ? DMA_FROM_DEVICE : DMA_TO_DEVICE; ++ ret = dma_map_sg(buf->queue->dev, buf->sglist, buf->sglen, ++ direction); ++ if (ret != buf->sglen) { ++ ret = -EFAULT; ++ goto done; ++ } ++ } ++ ++ if (buf->queue->ops->buffer_prepare) ++ ret = buf->queue->ops->buffer_prepare(buf); ++ ++done: ++ if (ret < 0) { ++ isp_video_buffer_cleanup(buf); ++ return ret; ++ } ++ ++ return ret; ++} ++ ++/* ++ * isp_video_queue_query - Query the status of a given buffer ++ * ++ * Locking: must be called with the queue lock held. ++ */ ++static void isp_video_buffer_query(struct isp_video_buffer *buf, ++ struct v4l2_buffer *vbuf) ++{ ++ memcpy(vbuf, &buf->vbuf, sizeof(*vbuf)); ++ ++ if (buf->vma_use_count) ++ vbuf->flags |= V4L2_BUF_FLAG_MAPPED; ++ ++ switch (buf->state) { ++ case ISP_BUF_STATE_ERROR: ++ vbuf->flags |= V4L2_BUF_FLAG_ERROR; ++ case ISP_BUF_STATE_DONE: ++ vbuf->flags |= V4L2_BUF_FLAG_DONE; ++ case ISP_BUF_STATE_QUEUED: ++ case ISP_BUF_STATE_ACTIVE: ++ vbuf->flags |= V4L2_BUF_FLAG_QUEUED; ++ break; ++ case ISP_BUF_STATE_IDLE: ++ default: ++ break; ++ } ++} ++ ++/* ++ * isp_video_buffer_wait - Wait for a buffer to be ready ++ * ++ * In non-blocking mode, return immediately with 0 if the buffer is ready or ++ * -EAGAIN if the buffer is in the QUEUED or ACTIVE state. ++ * ++ * In blocking mode, wait (interruptibly but with no timeout) on the buffer wait ++ * queue using the same condition. ++ */ ++static int isp_video_buffer_wait(struct isp_video_buffer *buf, int nonblocking) ++{ ++ if (nonblocking) { ++ return (buf->state != ISP_BUF_STATE_QUEUED && ++ buf->state != ISP_BUF_STATE_ACTIVE) ++ ? 0 : -EAGAIN; ++ } ++ ++ return wait_event_interruptible(buf->wait, ++ buf->state != ISP_BUF_STATE_QUEUED && ++ buf->state != ISP_BUF_STATE_ACTIVE); ++} ++ ++/* ----------------------------------------------------------------------------- ++ * Queue management ++ */ ++ ++/* ++ * isp_video_queue_free - Free video buffers memory ++ * ++ * Buffers can only be freed if the queue isn't streaming and if no buffer is ++ * mapped to userspace. Return -EBUSY if those conditions aren't statisfied. ++ * ++ * This function must be called with the queue lock held. ++ */ ++static int isp_video_queue_free(struct isp_video_queue *queue) ++{ ++ unsigned int i; ++ ++ if (queue->streaming) ++ return -EBUSY; ++ ++ for (i = 0; i < queue->count; ++i) { ++ if (queue->buffers[i]->vma_use_count != 0) ++ return -EBUSY; ++ } ++ ++ for (i = 0; i < queue->count; ++i) { ++ struct isp_video_buffer *buf = queue->buffers[i]; ++ ++ isp_video_buffer_cleanup(buf); ++ ++ vfree(buf->vaddr); ++ buf->vaddr = NULL; ++ ++ kfree(buf); ++ queue->buffers[i] = NULL; ++ } ++ ++ INIT_LIST_HEAD(&queue->queue); ++ queue->count = 0; ++ return 0; ++} ++ ++/* ++ * isp_video_queue_alloc - Allocate video buffers memory ++ * ++ * This function must be called with the queue lock held. ++ */ ++static int isp_video_queue_alloc(struct isp_video_queue *queue, ++ unsigned int nbuffers, ++ unsigned int size, enum v4l2_memory memory) ++{ ++ struct isp_video_buffer *buf; ++ unsigned int i; ++ void *mem; ++ int ret; ++ ++ /* Start by freeing the buffers. */ ++ ret = isp_video_queue_free(queue); ++ if (ret < 0) ++ return ret; ++ ++ /* Bail out of no buffers should be allocated. */ ++ if (nbuffers == 0) ++ return 0; ++ ++ /* Initialize the allocated buffers. */ ++ for (i = 0; i < nbuffers; ++i) { ++ buf = kzalloc(queue->bufsize, GFP_KERNEL); ++ if (buf == NULL) ++ break; ++ ++ if (memory == V4L2_MEMORY_MMAP) { ++ /* Allocate video buffers memory for mmap mode. Align ++ * the size to the page size. ++ */ ++ mem = vmalloc_32_user(PAGE_ALIGN(size)); ++ if (mem == NULL) { ++ kfree(buf); ++ break; ++ } ++ ++ buf->vbuf.m.offset = i * PAGE_ALIGN(size); ++ buf->vaddr = mem; ++ } ++ ++ buf->vbuf.index = i; ++ buf->vbuf.length = size; ++ buf->vbuf.type = queue->type; ++ buf->vbuf.field = V4L2_FIELD_NONE; ++ buf->vbuf.memory = memory; ++ ++ buf->queue = queue; ++ init_waitqueue_head(&buf->wait); ++ ++ queue->buffers[i] = buf; ++ } ++ ++ if (i == 0) ++ return -ENOMEM; ++ ++ queue->count = i; ++ return nbuffers; ++} ++ ++/** ++ * omap3isp_video_queue_cleanup - Clean up the video buffers queue ++ * @queue: Video buffers queue ++ * ++ * Free all allocated resources and clean up the video buffers queue. The queue ++ * must not be busy (no ongoing video stream) and buffers must have been ++ * unmapped. ++ * ++ * Return 0 on success or -EBUSY if the queue is busy or buffers haven't been ++ * unmapped. ++ */ ++int omap3isp_video_queue_cleanup(struct isp_video_queue *queue) ++{ ++ return isp_video_queue_free(queue); ++} ++ ++/** ++ * omap3isp_video_queue_init - Initialize the video buffers queue ++ * @queue: Video buffers queue ++ * @type: V4L2 buffer type (capture or output) ++ * @ops: Driver-specific queue operations ++ * @dev: Device used for DMA operations ++ * @bufsize: Size of the driver-specific buffer structure ++ * ++ * Initialize the video buffers queue with the supplied parameters. ++ * ++ * The queue type must be one of V4L2_BUF_TYPE_VIDEO_CAPTURE or ++ * V4L2_BUF_TYPE_VIDEO_OUTPUT. Other buffer types are not supported yet. ++ * ++ * Buffer objects will be allocated using the given buffer size to allow room ++ * for driver-specific fields. Driver-specific buffer structures must start ++ * with a struct isp_video_buffer field. Drivers with no driver-specific buffer ++ * structure must pass the size of the isp_video_buffer structure in the bufsize ++ * parameter. ++ * ++ * Return 0 on success. ++ */ ++int omap3isp_video_queue_init(struct isp_video_queue *queue, ++ enum v4l2_buf_type type, ++ const struct isp_video_queue_operations *ops, ++ struct device *dev, unsigned int bufsize) ++{ ++ INIT_LIST_HEAD(&queue->queue); ++ mutex_init(&queue->lock); ++ spin_lock_init(&queue->irqlock); ++ ++ queue->type = type; ++ queue->ops = ops; ++ queue->dev = dev; ++ queue->bufsize = bufsize; ++ ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------------- ++ * V4L2 operations ++ */ ++ ++/** ++ * omap3isp_video_queue_reqbufs - Allocate video buffers memory ++ * ++ * This function is intended to be used as a VIDIOC_REQBUFS ioctl handler. It ++ * allocated video buffer objects and, for MMAP buffers, buffer memory. ++ * ++ * If the number of buffers is 0, all buffers are freed and the function returns ++ * without performing any allocation. ++ * ++ * If the number of buffers is not 0, currently allocated buffers (if any) are ++ * freed and the requested number of buffers are allocated. Depending on ++ * driver-specific requirements and on memory availability, a number of buffer ++ * smaller or bigger than requested can be allocated. This isn't considered as ++ * an error. ++ * ++ * Return 0 on success or one of the following error codes: ++ * ++ * -EINVAL if the buffer type or index are invalid ++ * -EBUSY if the queue is busy (streaming or buffers mapped) ++ * -ENOMEM if the buffers can't be allocated due to an out-of-memory condition ++ */ ++int omap3isp_video_queue_reqbufs(struct isp_video_queue *queue, ++ struct v4l2_requestbuffers *rb) ++{ ++ unsigned int nbuffers = rb->count; ++ unsigned int size; ++ int ret; ++ ++ if (rb->type != queue->type) ++ return -EINVAL; ++ ++ queue->ops->queue_prepare(queue, &nbuffers, &size); ++ if (size == 0) ++ return -EINVAL; ++ ++ nbuffers = min_t(unsigned int, nbuffers, ISP_VIDEO_MAX_BUFFERS); ++ ++ mutex_lock(&queue->lock); ++ ++ ret = isp_video_queue_alloc(queue, nbuffers, size, rb->memory); ++ if (ret < 0) ++ goto done; ++ ++ rb->count = ret; ++ ret = 0; ++ ++done: ++ mutex_unlock(&queue->lock); ++ return ret; ++} ++ ++/** ++ * omap3isp_video_queue_querybuf - Query the status of a buffer in a queue ++ * ++ * This function is intended to be used as a VIDIOC_QUERYBUF ioctl handler. It ++ * returns the status of a given video buffer. ++ * ++ * Return 0 on success or -EINVAL if the buffer type or index are invalid. ++ */ ++int omap3isp_video_queue_querybuf(struct isp_video_queue *queue, ++ struct v4l2_buffer *vbuf) ++{ ++ struct isp_video_buffer *buf; ++ int ret = 0; ++ ++ if (vbuf->type != queue->type) ++ return -EINVAL; ++ ++ mutex_lock(&queue->lock); ++ ++ if (vbuf->index >= queue->count) { ++ ret = -EINVAL; ++ goto done; ++ } ++ ++ buf = queue->buffers[vbuf->index]; ++ isp_video_buffer_query(buf, vbuf); ++ ++done: ++ mutex_unlock(&queue->lock); ++ return ret; ++} ++ ++/** ++ * omap3isp_video_queue_qbuf - Queue a buffer ++ * ++ * This function is intended to be used as a VIDIOC_QBUF ioctl handler. ++ * ++ * The v4l2_buffer structure passed from userspace is first sanity tested. If ++ * sane, the buffer is then processed and added to the main queue and, if the ++ * queue is streaming, to the IRQ queue. ++ * ++ * Before being enqueued, USERPTR buffers are checked for address changes. If ++ * the buffer has a different userspace address, the old memory area is unlocked ++ * and the new memory area is locked. ++ */ ++int omap3isp_video_queue_qbuf(struct isp_video_queue *queue, ++ struct v4l2_buffer *vbuf) ++{ ++ struct isp_video_buffer *buf; ++ unsigned long flags; ++ int ret = -EINVAL; ++ ++ if (vbuf->type != queue->type) ++ goto done; ++ ++ mutex_lock(&queue->lock); ++ ++ if (vbuf->index >= queue->count) ++ goto done; ++ ++ buf = queue->buffers[vbuf->index]; ++ ++ if (vbuf->memory != buf->vbuf.memory) ++ goto done; ++ ++ if (buf->state != ISP_BUF_STATE_IDLE) ++ goto done; ++ ++ if (vbuf->memory == V4L2_MEMORY_USERPTR && ++ vbuf->m.userptr != buf->vbuf.m.userptr) { ++ isp_video_buffer_cleanup(buf); ++ buf->vbuf.m.userptr = vbuf->m.userptr; ++ buf->prepared = 0; ++ } ++ ++ if (!buf->prepared) { ++ ret = isp_video_buffer_prepare(buf); ++ if (ret < 0) ++ goto done; ++ buf->prepared = 1; ++ } ++ ++ isp_video_buffer_cache_sync(buf); ++ ++ buf->state = ISP_BUF_STATE_QUEUED; ++ list_add_tail(&buf->stream, &queue->queue); ++ ++ if (queue->streaming) { ++ spin_lock_irqsave(&queue->irqlock, flags); ++ queue->ops->buffer_queue(buf); ++ spin_unlock_irqrestore(&queue->irqlock, flags); ++ } ++ ++ ret = 0; ++ ++done: ++ mutex_unlock(&queue->lock); ++ return ret; ++} ++ ++/** ++ * omap3isp_video_queue_dqbuf - Dequeue a buffer ++ * ++ * This function is intended to be used as a VIDIOC_DQBUF ioctl handler. ++ * ++ * The v4l2_buffer structure passed from userspace is first sanity tested. If ++ * sane, the buffer is then processed and added to the main queue and, if the ++ * queue is streaming, to the IRQ queue. ++ * ++ * Before being enqueued, USERPTR buffers are checked for address changes. If ++ * the buffer has a different userspace address, the old memory area is unlocked ++ * and the new memory area is locked. ++ */ ++int omap3isp_video_queue_dqbuf(struct isp_video_queue *queue, ++ struct v4l2_buffer *vbuf, int nonblocking) ++{ ++ struct isp_video_buffer *buf; ++ int ret; ++ ++ if (vbuf->type != queue->type) ++ return -EINVAL; ++ ++ mutex_lock(&queue->lock); ++ ++ if (list_empty(&queue->queue)) { ++ ret = -EINVAL; ++ goto done; ++ } ++ ++ buf = list_first_entry(&queue->queue, struct isp_video_buffer, stream); ++ ret = isp_video_buffer_wait(buf, nonblocking); ++ if (ret < 0) ++ goto done; ++ ++ list_del(&buf->stream); ++ ++ isp_video_buffer_query(buf, vbuf); ++ buf->state = ISP_BUF_STATE_IDLE; ++ vbuf->flags &= ~V4L2_BUF_FLAG_QUEUED; ++ ++done: ++ mutex_unlock(&queue->lock); ++ return ret; ++} ++ ++/** ++ * omap3isp_video_queue_streamon - Start streaming ++ * ++ * This function is intended to be used as a VIDIOC_STREAMON ioctl handler. It ++ * starts streaming on the queue and calls the buffer_queue operation for all ++ * queued buffers. ++ * ++ * Return 0 on success. ++ */ ++int omap3isp_video_queue_streamon(struct isp_video_queue *queue) ++{ ++ struct isp_video_buffer *buf; ++ unsigned long flags; ++ ++ mutex_lock(&queue->lock); ++ ++ if (queue->streaming) ++ goto done; ++ ++ queue->streaming = 1; ++ ++ spin_lock_irqsave(&queue->irqlock, flags); ++ list_for_each_entry(buf, &queue->queue, stream) ++ queue->ops->buffer_queue(buf); ++ spin_unlock_irqrestore(&queue->irqlock, flags); ++ ++done: ++ mutex_unlock(&queue->lock); ++ return 0; ++} ++ ++/** ++ * omap3isp_video_queue_streamoff - Stop streaming ++ * ++ * This function is intended to be used as a VIDIOC_STREAMOFF ioctl handler. It ++ * stops streaming on the queue and wakes up all the buffers. ++ * ++ * Drivers must stop the hardware and synchronize with interrupt handlers and/or ++ * delayed works before calling this function to make sure no buffer will be ++ * touched by the driver and/or hardware. ++ */ ++void omap3isp_video_queue_streamoff(struct isp_video_queue *queue) ++{ ++ struct isp_video_buffer *buf; ++ unsigned long flags; ++ unsigned int i; ++ ++ mutex_lock(&queue->lock); ++ ++ if (!queue->streaming) ++ goto done; ++ ++ queue->streaming = 0; ++ ++ spin_lock_irqsave(&queue->irqlock, flags); ++ for (i = 0; i < queue->count; ++i) { ++ buf = queue->buffers[i]; ++ ++ if (buf->state == ISP_BUF_STATE_ACTIVE) ++ wake_up(&buf->wait); ++ ++ buf->state = ISP_BUF_STATE_IDLE; ++ } ++ spin_unlock_irqrestore(&queue->irqlock, flags); ++ ++ INIT_LIST_HEAD(&queue->queue); ++ ++done: ++ mutex_unlock(&queue->lock); ++} ++ ++/** ++ * omap3isp_video_queue_discard_done - Discard all buffers marked as DONE ++ * ++ * This function is intended to be used with suspend/resume operations. It ++ * discards all 'done' buffers as they would be too old to be requested after ++ * resume. ++ * ++ * Drivers must stop the hardware and synchronize with interrupt handlers and/or ++ * delayed works before calling this function to make sure no buffer will be ++ * touched by the driver and/or hardware. ++ */ ++void omap3isp_video_queue_discard_done(struct isp_video_queue *queue) ++{ ++ struct isp_video_buffer *buf; ++ unsigned int i; ++ ++ mutex_lock(&queue->lock); ++ ++ if (!queue->streaming) ++ goto done; ++ ++ for (i = 0; i < queue->count; ++i) { ++ buf = queue->buffers[i]; ++ ++ if (buf->state == ISP_BUF_STATE_DONE) ++ buf->state = ISP_BUF_STATE_ERROR; ++ } ++ ++done: ++ mutex_unlock(&queue->lock); ++} ++ ++static void isp_video_queue_vm_open(struct vm_area_struct *vma) ++{ ++ struct isp_video_buffer *buf = vma->vm_private_data; ++ ++ buf->vma_use_count++; ++} ++ ++static void isp_video_queue_vm_close(struct vm_area_struct *vma) ++{ ++ struct isp_video_buffer *buf = vma->vm_private_data; ++ ++ buf->vma_use_count--; ++} ++ ++static const struct vm_operations_struct isp_video_queue_vm_ops = { ++ .open = isp_video_queue_vm_open, ++ .close = isp_video_queue_vm_close, ++}; ++ ++/** ++ * omap3isp_video_queue_mmap - Map buffers to userspace ++ * ++ * This function is intended to be used as an mmap() file operation handler. It ++ * maps a buffer to userspace based on the VMA offset. ++ * ++ * Only buffers of memory type MMAP are supported. ++ */ ++int omap3isp_video_queue_mmap(struct isp_video_queue *queue, ++ struct vm_area_struct *vma) ++{ ++ struct isp_video_buffer *uninitialized_var(buf); ++ unsigned long size; ++ unsigned int i; ++ int ret = 0; ++ ++ mutex_lock(&queue->lock); ++ ++ for (i = 0; i < queue->count; ++i) { ++ buf = queue->buffers[i]; ++ if ((buf->vbuf.m.offset >> PAGE_SHIFT) == vma->vm_pgoff) ++ break; ++ } ++ ++ if (i == queue->count) { ++ ret = -EINVAL; ++ goto done; ++ } ++ ++ size = vma->vm_end - vma->vm_start; ++ ++ if (buf->vbuf.memory != V4L2_MEMORY_MMAP || ++ size != PAGE_ALIGN(buf->vbuf.length)) { ++ ret = -EINVAL; ++ goto done; ++ } ++ ++ ret = remap_vmalloc_range(vma, buf->vaddr, 0); ++ if (ret < 0) ++ goto done; ++ ++ vma->vm_ops = &isp_video_queue_vm_ops; ++ vma->vm_private_data = buf; ++ isp_video_queue_vm_open(vma); ++ ++done: ++ mutex_unlock(&queue->lock); ++ return ret; ++} ++ ++/** ++ * omap3isp_video_queue_poll - Poll video queue state ++ * ++ * This function is intended to be used as a poll() file operation handler. It ++ * polls the state of the video buffer at the front of the queue and returns an ++ * events mask. ++ * ++ * If no buffer is present at the front of the queue, POLLERR is returned. ++ */ ++unsigned int omap3isp_video_queue_poll(struct isp_video_queue *queue, ++ struct file *file, poll_table *wait) ++{ ++ struct isp_video_buffer *buf; ++ unsigned int mask = 0; ++ ++ mutex_lock(&queue->lock); ++ if (list_empty(&queue->queue)) { ++ mask |= POLLERR; ++ goto done; ++ } ++ buf = list_first_entry(&queue->queue, struct isp_video_buffer, stream); ++ ++ poll_wait(file, &buf->wait, wait); ++ if (buf->state == ISP_BUF_STATE_DONE || ++ buf->state == ISP_BUF_STATE_ERROR) { ++ if (queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) ++ mask |= POLLIN | POLLRDNORM; ++ else ++ mask |= POLLOUT | POLLWRNORM; ++ } ++ ++done: ++ mutex_unlock(&queue->lock); ++ return mask; ++} +diff --git a/drivers/media/video/isp/ispqueue.h b/drivers/media/video/isp/ispqueue.h +new file mode 100644 +index 0000000..f05aba3 +--- /dev/null ++++ b/drivers/media/video/isp/ispqueue.h +@@ -0,0 +1,185 @@ ++/* ++ * ispqueue.h ++ * ++ * TI OMAP3 ISP - Video buffers queue handling ++ * ++ * Copyright (C) 2010 Nokia Corporation ++ * ++ * Contacts: Laurent Pinchart ++ * Sakari Ailus ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#ifndef OMAP3_ISP_QUEUE_H ++#define OMAP3_ISP_QUEUE_H ++ ++#include ++#include ++#include ++#include ++#include ++ ++struct isp_video_queue; ++struct page; ++struct scatterlist; ++ ++#define ISP_VIDEO_MAX_BUFFERS 16 ++ ++/** ++ * enum isp_video_buffer_state - ISP video buffer state ++ * @ISP_BUF_STATE_IDLE: The buffer is under userspace control (dequeued ++ * or not queued yet). ++ * @ISP_BUF_STATE_QUEUED: The buffer has been queued but isn't used by the ++ * device yet. ++ * @ISP_BUF_STATE_ACTIVE: The buffer is in use for an active video transfer. ++ * @ISP_BUF_STATE_ERROR: The device is done with the buffer and an error ++ * occured. For capture device the buffer likely contains corrupted data or ++ * no data at all. ++ * @ISP_BUF_STATE_DONE: The device is done with the buffer and no error occured. ++ * For capture devices the buffer contains valid data. ++ */ ++enum isp_video_buffer_state { ++ ISP_BUF_STATE_IDLE, ++ ISP_BUF_STATE_QUEUED, ++ ISP_BUF_STATE_ACTIVE, ++ ISP_BUF_STATE_ERROR, ++ ISP_BUF_STATE_DONE, ++}; ++ ++/** ++ * struct isp_video_buffer - ISP video buffer ++ * @vma_use_count: Number of times the buffer is mmap'ed to userspace ++ * @stream: List head for insertion into main queue ++ * @queue: ISP buffers queue this buffer belongs to ++ * @prepared: Whether the buffer has been prepared ++ * @vaddr: Memory virtual address (for kernel buffers) ++ * @vm_flags: Buffer VMA flags (for userspace buffers) ++ * @offset: Offset inside the first page (for userspace buffers) ++ * @npages: Number of pages (for userspace buffers) ++ * @pages: Pages table (for userspace non-VM_PFNMAP buffers) ++ * @paddr: Memory physical address (for userspace VM_PFNMAP buffers) ++ * @sglen: Number of elements in the scatter list (for non-VM_PFNMAP buffers) ++ * @sglist: Scatter list (for non-VM_PFNMAP buffers) ++ * @vbuf: V4L2 buffer ++ * @irqlist: List head for insertion into IRQ queue ++ * @state: Current buffer state ++ * @wait: Wait queue to signal buffer completion ++ */ ++struct isp_video_buffer { ++ unsigned long vma_use_count; ++ struct list_head stream; ++ struct isp_video_queue *queue; ++ unsigned int prepared:1; ++ ++ /* For kernel buffers. */ ++ void *vaddr; ++ ++ /* For userspace buffers. */ ++ unsigned long vm_flags; ++ unsigned long offset; ++ unsigned int npages; ++ struct page **pages; ++ dma_addr_t paddr; ++ ++ /* For all buffers except VM_PFNMAP. */ ++ unsigned int sglen; ++ struct scatterlist *sglist; ++ ++ /* Touched by the interrupt handler. */ ++ struct v4l2_buffer vbuf; ++ struct list_head irqlist; ++ enum isp_video_buffer_state state; ++ wait_queue_head_t wait; ++}; ++ ++#define to_isp_video_buffer(vb) container_of(vb, struct isp_video_buffer, vb) ++ ++/** ++ * struct isp_video_queue_operations - Driver-specific operations ++ * @queue_prepare: Called before allocating buffers. Drivers should clamp the ++ * number of buffers according to their requirements, and must return the ++ * buffer size in bytes. ++ * @buffer_prepare: Called the first time a buffer is queued, or after changing ++ * the userspace memory address for a USERPTR buffer, with the queue lock ++ * held. Drivers should perform device-specific buffer preparation (such as ++ * mapping the buffer memory in an IOMMU). This operation is optional. ++ * @buffer_queue: Called when a buffer is being added to the queue with the ++ * queue irqlock spinlock held. ++ * @buffer_cleanup: Called before freeing buffers, or before changing the ++ * userspace memory address for a USERPTR buffer, with the queue lock held. ++ * Drivers must perform cleanup operations required to undo the ++ * buffer_prepare call. This operation is optional. ++ */ ++struct isp_video_queue_operations { ++ void (*queue_prepare)(struct isp_video_queue *queue, ++ unsigned int *nbuffers, unsigned int *size); ++ int (*buffer_prepare)(struct isp_video_buffer *buf); ++ void (*buffer_queue)(struct isp_video_buffer *buf); ++ void (*buffer_cleanup)(struct isp_video_buffer *buf); ++}; ++ ++/** ++ * struct isp_video_queue - ISP video buffers queue ++ * @type: Type of video buffers handled by this queue ++ * @ops: Queue operations ++ * @dev: Device used for DMA operations ++ * @bufsize: Size of a driver-specific buffer object ++ * @count: Number of currently allocated buffers ++ * @buffers: ISP video buffers ++ * @lock: Mutex to protect access to the buffers, main queue and state ++ * @irqlock: Spinlock to protect access to the IRQ queue ++ * @streaming: Queue state, indicates whether the queue is streaming ++ * @queue: List of all queued buffers ++ */ ++struct isp_video_queue { ++ enum v4l2_buf_type type; ++ const struct isp_video_queue_operations *ops; ++ struct device *dev; ++ unsigned int bufsize; ++ ++ unsigned int count; ++ struct isp_video_buffer *buffers[ISP_VIDEO_MAX_BUFFERS]; ++ struct mutex lock; ++ spinlock_t irqlock; ++ ++ unsigned int streaming:1; ++ ++ struct list_head queue; ++}; ++ ++int omap3isp_video_queue_cleanup(struct isp_video_queue *queue); ++int omap3isp_video_queue_init(struct isp_video_queue *queue, ++ enum v4l2_buf_type type, ++ const struct isp_video_queue_operations *ops, ++ struct device *dev, unsigned int bufsize); ++ ++int omap3isp_video_queue_reqbufs(struct isp_video_queue *queue, ++ struct v4l2_requestbuffers *rb); ++int omap3isp_video_queue_querybuf(struct isp_video_queue *queue, ++ struct v4l2_buffer *vbuf); ++int omap3isp_video_queue_qbuf(struct isp_video_queue *queue, ++ struct v4l2_buffer *vbuf); ++int omap3isp_video_queue_dqbuf(struct isp_video_queue *queue, ++ struct v4l2_buffer *vbuf, int nonblocking); ++int omap3isp_video_queue_streamon(struct isp_video_queue *queue); ++void omap3isp_video_queue_streamoff(struct isp_video_queue *queue); ++void omap3isp_video_queue_discard_done(struct isp_video_queue *queue); ++int omap3isp_video_queue_mmap(struct isp_video_queue *queue, ++ struct vm_area_struct *vma); ++unsigned int omap3isp_video_queue_poll(struct isp_video_queue *queue, ++ struct file *file, poll_table *wait); ++ ++#endif /* OMAP3_ISP_QUEUE_H */ +diff --git a/drivers/media/video/isp/ispreg.h b/drivers/media/video/isp/ispreg.h +new file mode 100644 +index 0000000..e78c7e3 +--- /dev/null ++++ b/drivers/media/video/isp/ispreg.h +@@ -0,0 +1,1589 @@ ++/* ++ * ispreg.h ++ * ++ * TI OMAP3 ISP - Registers definitions ++ * ++ * Copyright (C) 2010 Nokia Corporation ++ * Copyright (C) 2009 Texas Instruments, Inc ++ * ++ * Contacts: Laurent Pinchart ++ * Sakari Ailus ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#ifndef OMAP3_ISP_REG_H ++#define OMAP3_ISP_REG_H ++ ++#include ++ ++ ++#define CM_CAM_MCLK_HZ 172800000 /* Hz */ ++ ++/* ISP Submodules offset */ ++ ++#define OMAP3ISP_REG_BASE OMAP3430_ISP_BASE ++#define OMAP3ISP_REG(offset) (OMAP3ISP_REG_BASE + (offset)) ++ ++#define OMAP3ISP_CCP2_REG_OFFSET 0x0400 ++#define OMAP3ISP_CCP2_REG_BASE (OMAP3ISP_REG_BASE + \ ++ OMAP3ISP_CCP2_REG_OFFSET) ++#define OMAP3ISP_CCP2_REG(offset) (OMAP3ISP_CCP2_REG_BASE + (offset)) ++ ++#define OMAP3ISP_CCDC_REG_OFFSET 0x0600 ++#define OMAP3ISP_CCDC_REG_BASE (OMAP3ISP_REG_BASE + \ ++ OMAP3ISP_CCDC_REG_OFFSET) ++#define OMAP3ISP_CCDC_REG(offset) (OMAP3ISP_CCDC_REG_BASE + (offset)) ++ ++#define OMAP3ISP_HIST_REG_OFFSET 0x0A00 ++#define OMAP3ISP_HIST_REG_BASE (OMAP3ISP_REG_BASE + \ ++ OMAP3ISP_HIST_REG_OFFSET) ++#define OMAP3ISP_HIST_REG(offset) (OMAP3ISP_HIST_REG_BASE + (offset)) ++ ++#define OMAP3ISP_H3A_REG_OFFSET 0x0C00 ++#define OMAP3ISP_H3A_REG_BASE (OMAP3ISP_REG_BASE + \ ++ OMAP3ISP_H3A_REG_OFFSET) ++#define OMAP3ISP_H3A_REG(offset) (OMAP3ISP_H3A_REG_BASE + (offset)) ++ ++#define OMAP3ISP_PREV_REG_OFFSET 0x0E00 ++#define OMAP3ISP_PREV_REG_BASE (OMAP3ISP_REG_BASE + \ ++ OMAP3ISP_PREV_REG_OFFSET) ++#define OMAP3ISP_PREV_REG(offset) (OMAP3ISP_PREV_REG_BASE + (offset)) ++ ++#define OMAP3ISP_RESZ_REG_OFFSET 0x1000 ++#define OMAP3ISP_RESZ_REG_BASE (OMAP3ISP_REG_BASE + \ ++ OMAP3ISP_RESZ_REG_OFFSET) ++#define OMAP3ISP_RESZ_REG(offset) (OMAP3ISP_RESZ_REG_BASE + (offset)) ++ ++#define OMAP3ISP_SBL_REG_OFFSET 0x1200 ++#define OMAP3ISP_SBL_REG_BASE (OMAP3ISP_REG_BASE + \ ++ OMAP3ISP_SBL_REG_OFFSET) ++#define OMAP3ISP_SBL_REG(offset) (OMAP3ISP_SBL_REG_BASE + (offset)) ++ ++#define OMAP3ISP_CSI2A_REGS1_REG_OFFSET 0x1800 ++#define OMAP3ISP_CSI2A_REGS1_REG_BASE (OMAP3ISP_REG_BASE + \ ++ OMAP3ISP_CSI2A_REGS1_REG_OFFSET) ++#define OMAP3ISP_CSI2A_REGS1_REG(offset) \ ++ (OMAP3ISP_CSI2A_REGS1_REG_BASE + (offset)) ++ ++#define OMAP3ISP_CSIPHY2_REG_OFFSET 0x1970 ++#define OMAP3ISP_CSIPHY2_REG_BASE (OMAP3ISP_REG_BASE + \ ++ OMAP3ISP_CSIPHY2_REG_OFFSET) ++#define OMAP3ISP_CSIPHY2_REG(offset) (OMAP3ISP_CSIPHY2_REG_BASE + (offset)) ++ ++#define OMAP3ISP_CSI2A_REGS2_REG_OFFSET 0x19C0 ++#define OMAP3ISP_CSI2A_REGS2_REG_BASE (OMAP3ISP_REG_BASE + \ ++ OMAP3ISP_CSI2A_REGS2_REG_OFFSET) ++#define OMAP3ISP_CSI2A_REGS2_REG(offset) \ ++ (OMAP3ISP_CSI2A_REGS2_REG_BASE + (offset)) ++ ++#define OMAP3ISP_CSI2C_REGS1_REG_OFFSET 0x1C00 ++#define OMAP3ISP_CSI2C_REGS1_REG_BASE (OMAP3ISP_REG_BASE + \ ++ OMAP3ISP_CSI2C_REGS1_REG_OFFSET) ++#define OMAP3ISP_CSI2C_REGS1_REG(offset) \ ++ (OMAP3ISP_CSI2C_REGS1_REG_BASE + (offset)) ++ ++#define OMAP3ISP_CSIPHY1_REG_OFFSET 0x1D70 ++#define OMAP3ISP_CSIPHY1_REG_BASE (OMAP3ISP_REG_BASE + \ ++ OMAP3ISP_CSIPHY1_REG_OFFSET) ++#define OMAP3ISP_CSIPHY1_REG(offset) (OMAP3ISP_CSIPHY1_REG_BASE + (offset)) ++ ++#define OMAP3ISP_CSI2C_REGS2_REG_OFFSET 0x1DC0 ++#define OMAP3ISP_CSI2C_REGS2_REG_BASE (OMAP3ISP_REG_BASE + \ ++ OMAP3ISP_CSI2C_REGS2_REG_OFFSET) ++#define OMAP3ISP_CSI2C_REGS2_REG(offset) \ ++ (OMAP3ISP_CSI2C_REGS2_REG_BASE + (offset)) ++ ++/* ISP module register offset */ ++ ++#define ISP_REVISION (0x000) ++#define ISP_SYSCONFIG (0x004) ++#define ISP_SYSSTATUS (0x008) ++#define ISP_IRQ0ENABLE (0x00C) ++#define ISP_IRQ0STATUS (0x010) ++#define ISP_IRQ1ENABLE (0x014) ++#define ISP_IRQ1STATUS (0x018) ++#define ISP_TCTRL_GRESET_LENGTH (0x030) ++#define ISP_TCTRL_PSTRB_REPLAY (0x034) ++#define ISP_CTRL (0x040) ++#define ISP_SECURE (0x044) ++#define ISP_TCTRL_CTRL (0x050) ++#define ISP_TCTRL_FRAME (0x054) ++#define ISP_TCTRL_PSTRB_DELAY (0x058) ++#define ISP_TCTRL_STRB_DELAY (0x05C) ++#define ISP_TCTRL_SHUT_DELAY (0x060) ++#define ISP_TCTRL_PSTRB_LENGTH (0x064) ++#define ISP_TCTRL_STRB_LENGTH (0x068) ++#define ISP_TCTRL_SHUT_LENGTH (0x06C) ++#define ISP_PING_PONG_ADDR (0x070) ++#define ISP_PING_PONG_MEM_RANGE (0x074) ++#define ISP_PING_PONG_BUF_SIZE (0x078) ++ ++/* CCP2 receiver registers */ ++ ++#define ISPCCP2_REVISION (0x000) ++#define ISPCCP2_SYSCONFIG (0x004) ++#define ISPCCP2_SYSCONFIG_SOFT_RESET (1 << 1) ++#define ISPCCP2_SYSCONFIG_AUTO_IDLE 0x1 ++#define ISPCCP2_SYSCONFIG_MSTANDBY_MODE_SHIFT 12 ++#define ISPCCP2_SYSCONFIG_MSTANDBY_MODE_FORCE \ ++ (0x0 << ISPCCP2_SYSCONFIG_MSTANDBY_MODE_SHIFT) ++#define ISPCCP2_SYSCONFIG_MSTANDBY_MODE_NO \ ++ (0x1 << ISPCCP2_SYSCONFIG_MSTANDBY_MODE_SHIFT) ++#define ISPCCP2_SYSCONFIG_MSTANDBY_MODE_SMART \ ++ (0x2 << ISPCCP2_SYSCONFIG_MSTANDBY_MODE_SHIFT) ++#define ISPCCP2_SYSSTATUS (0x008) ++#define ISPCCP2_SYSSTATUS_RESET_DONE (1 << 0) ++#define ISPCCP2_LC01_IRQENABLE (0x00C) ++#define ISPCCP2_LC01_IRQSTATUS (0x010) ++#define ISPCCP2_LC01_IRQSTATUS_LC0_FS_IRQ (1 << 11) ++#define ISPCCP2_LC01_IRQSTATUS_LC0_LE_IRQ (1 << 10) ++#define ISPCCP2_LC01_IRQSTATUS_LC0_LS_IRQ (1 << 9) ++#define ISPCCP2_LC01_IRQSTATUS_LC0_FE_IRQ (1 << 8) ++#define ISPCCP2_LC01_IRQSTATUS_LC0_COUNT_IRQ (1 << 7) ++#define ISPCCP2_LC01_IRQSTATUS_LC0_FIFO_OVF_IRQ (1 << 5) ++#define ISPCCP2_LC01_IRQSTATUS_LC0_CRC_IRQ (1 << 4) ++#define ISPCCP2_LC01_IRQSTATUS_LC0_FSP_IRQ (1 << 3) ++#define ISPCCP2_LC01_IRQSTATUS_LC0_FW_IRQ (1 << 2) ++#define ISPCCP2_LC01_IRQSTATUS_LC0_FSC_IRQ (1 << 1) ++#define ISPCCP2_LC01_IRQSTATUS_LC0_SSC_IRQ (1 << 0) ++ ++#define ISPCCP2_LC23_IRQENABLE (0x014) ++#define ISPCCP2_LC23_IRQSTATUS (0x018) ++#define ISPCCP2_LCM_IRQENABLE (0x02C) ++#define ISPCCP2_LCM_IRQSTATUS_EOF_IRQ (1 << 0) ++#define ISPCCP2_LCM_IRQSTATUS_OCPERROR_IRQ (1 << 1) ++#define ISPCCP2_LCM_IRQSTATUS (0x030) ++#define ISPCCP2_CTRL (0x040) ++#define ISPCCP2_CTRL_IF_EN (1 << 0) ++#define ISPCCP2_CTRL_PHY_SEL (1 << 1) ++#define ISPCCP2_CTRL_PHY_SEL_CLOCK (0 << 1) ++#define ISPCCP2_CTRL_PHY_SEL_STROBE (1 << 1) ++#define ISPCCP2_CTRL_PHY_SEL_MASK 0x1 ++#define ISPCCP2_CTRL_PHY_SEL_SHIFT 1 ++#define ISPCCP2_CTRL_IO_OUT_SEL (1 << 2) ++#define ISPCCP2_CTRL_MODE (1 << 4) ++#define ISPCCP2_CTRL_VP_CLK_FORCE_ON (1 << 9) ++#define ISPCCP2_CTRL_INV (1 << 10) ++#define ISPCCP2_CTRL_INV_MASK 0x1 ++#define ISPCCP2_CTRL_INV_SHIFT 10 ++#define ISPCCP2_CTRL_VP_ONLY_EN (1 << 11) ++#define ISPCCP2_CTRL_VP_CLK_POL (1 << 12) ++#define ISPCCP2_CTRL_VPCLK_DIV_SHIFT 15 ++#define ISPCCP2_CTRL_VPCLK_DIV_MASK 0x1ffff /* [31:15] */ ++#define ISPCCP2_CTRL_VP_OUT_CTRL_SHIFT 8 /* 3430 bits */ ++#define ISPCCP2_CTRL_VP_OUT_CTRL_MASK 0x3 /* 3430 bits */ ++#define ISPCCP2_DBG (0x044) ++#define ISPCCP2_GNQ (0x048) ++#define ISPCCP2_LCx_CTRL(x) ((0x050)+0x30*(x)) ++#define ISPCCP2_LCx_CTRL_CHAN_EN (1 << 0) ++#define ISPCCP2_LCx_CTRL_CRC_EN (1 << 19) ++#define ISPCCP2_LCx_CTRL_CRC_MASK 0x1 ++#define ISPCCP2_LCx_CTRL_CRC_SHIFT 2 ++#define ISPCCP2_LCx_CTRL_CRC_SHIFT_15_0 19 ++#define ISPCCP2_LCx_CTRL_REGION_EN (1 << 1) ++#define ISPCCP2_LCx_CTRL_REGION_MASK 0x1 ++#define ISPCCP2_LCx_CTRL_REGION_SHIFT 1 ++#define ISPCCP2_LCx_CTRL_FORMAT_MASK_15_0 0x3f ++#define ISPCCP2_LCx_CTRL_FORMAT_SHIFT_15_0 0x2 ++#define ISPCCP2_LCx_CTRL_FORMAT_MASK 0x1f ++#define ISPCCP2_LCx_CTRL_FORMAT_SHIFT 0x3 ++#define ISPCCP2_LCx_CODE(x) ((0x054)+0x30*(x)) ++#define ISPCCP2_LCx_STAT_START(x) ((0x058)+0x30*(x)) ++#define ISPCCP2_LCx_STAT_SIZE(x) ((0x05C)+0x30*(x)) ++#define ISPCCP2_LCx_SOF_ADDR(x) ((0x060)+0x30*(x)) ++#define ISPCCP2_LCx_EOF_ADDR(x) ((0x064)+0x30*(x)) ++#define ISPCCP2_LCx_DAT_START(x) ((0x068)+0x30*(x)) ++#define ISPCCP2_LCx_DAT_SIZE(x) ((0x06C)+0x30*(x)) ++#define ISPCCP2_LCx_DAT_MASK 0xFFF ++#define ISPCCP2_LCx_DAT_SHIFT 16 ++#define ISPCCP2_LCx_DAT_PING_ADDR(x) ((0x070)+0x30*(x)) ++#define ISPCCP2_LCx_DAT_PONG_ADDR(x) ((0x074)+0x30*(x)) ++#define ISPCCP2_LCx_DAT_OFST(x) ((0x078)+0x30*(x)) ++#define ISPCCP2_LCM_CTRL (0x1D0) ++#define ISPCCP2_LCM_CTRL_CHAN_EN (1 << 0) ++#define ISPCCP2_LCM_CTRL_DST_PORT (1 << 2) ++#define ISPCCP2_LCM_CTRL_DST_PORT_SHIFT 2 ++#define ISPCCP2_LCM_CTRL_READ_THROTTLE_SHIFT 3 ++#define ISPCCP2_LCM_CTRL_READ_THROTTLE_MASK 0x11 ++#define ISPCCP2_LCM_CTRL_BURST_SIZE_SHIFT 5 ++#define ISPCCP2_LCM_CTRL_BURST_SIZE_MASK 0x7 ++#define ISPCCP2_LCM_CTRL_SRC_FORMAT_SHIFT 16 ++#define ISPCCP2_LCM_CTRL_SRC_FORMAT_MASK 0x7 ++#define ISPCCP2_LCM_CTRL_SRC_DECOMPR_SHIFT 20 ++#define ISPCCP2_LCM_CTRL_SRC_DECOMPR_MASK 0x3 ++#define ISPCCP2_LCM_CTRL_SRC_DPCM_PRED (1 << 22) ++#define ISPCCP2_LCM_CTRL_SRC_PACK (1 << 23) ++#define ISPCCP2_LCM_CTRL_DST_FORMAT_SHIFT 24 ++#define ISPCCP2_LCM_CTRL_DST_FORMAT_MASK 0x7 ++#define ISPCCP2_LCM_VSIZE (0x1D4) ++#define ISPCCP2_LCM_VSIZE_SHIFT 16 ++#define ISPCCP2_LCM_HSIZE (0x1D8) ++#define ISPCCP2_LCM_HSIZE_SHIFT 16 ++#define ISPCCP2_LCM_PREFETCH (0x1DC) ++#define ISPCCP2_LCM_PREFETCH_SHIFT 3 ++#define ISPCCP2_LCM_SRC_ADDR (0x1E0) ++#define ISPCCP2_LCM_SRC_OFST (0x1E4) ++#define ISPCCP2_LCM_DST_ADDR (0x1E8) ++#define ISPCCP2_LCM_DST_OFST (0x1EC) ++ ++/* CCDC module register offset */ ++ ++#define ISPCCDC_PID (0x000) ++#define ISPCCDC_PCR (0x004) ++#define ISPCCDC_SYN_MODE (0x008) ++#define ISPCCDC_HD_VD_WID (0x00C) ++#define ISPCCDC_PIX_LINES (0x010) ++#define ISPCCDC_HORZ_INFO (0x014) ++#define ISPCCDC_VERT_START (0x018) ++#define ISPCCDC_VERT_LINES (0x01C) ++#define ISPCCDC_CULLING (0x020) ++#define ISPCCDC_HSIZE_OFF (0x024) ++#define ISPCCDC_SDOFST (0x028) ++#define ISPCCDC_SDR_ADDR (0x02C) ++#define ISPCCDC_CLAMP (0x030) ++#define ISPCCDC_DCSUB (0x034) ++#define ISPCCDC_COLPTN (0x038) ++#define ISPCCDC_BLKCMP (0x03C) ++#define ISPCCDC_FPC (0x040) ++#define ISPCCDC_FPC_ADDR (0x044) ++#define ISPCCDC_VDINT (0x048) ++#define ISPCCDC_ALAW (0x04C) ++#define ISPCCDC_REC656IF (0x050) ++#define ISPCCDC_CFG (0x054) ++#define ISPCCDC_FMTCFG (0x058) ++#define ISPCCDC_FMT_HORZ (0x05C) ++#define ISPCCDC_FMT_VERT (0x060) ++#define ISPCCDC_FMT_ADDR0 (0x064) ++#define ISPCCDC_FMT_ADDR1 (0x068) ++#define ISPCCDC_FMT_ADDR2 (0x06C) ++#define ISPCCDC_FMT_ADDR3 (0x070) ++#define ISPCCDC_FMT_ADDR4 (0x074) ++#define ISPCCDC_FMT_ADDR5 (0x078) ++#define ISPCCDC_FMT_ADDR6 (0x07C) ++#define ISPCCDC_FMT_ADDR7 (0x080) ++#define ISPCCDC_PRGEVEN0 (0x084) ++#define ISPCCDC_PRGEVEN1 (0x088) ++#define ISPCCDC_PRGODD0 (0x08C) ++#define ISPCCDC_PRGODD1 (0x090) ++#define ISPCCDC_VP_OUT (0x094) ++ ++#define ISPCCDC_LSC_CONFIG (0x098) ++#define ISPCCDC_LSC_INITIAL (0x09C) ++#define ISPCCDC_LSC_TABLE_BASE (0x0A0) ++#define ISPCCDC_LSC_TABLE_OFFSET (0x0A4) ++ ++/* SBL */ ++#define ISPSBL_PCR 0x4 ++#define ISPSBL_PCR_H3A_AEAWB_WBL_OVF (1 << 16) ++#define ISPSBL_PCR_H3A_AF_WBL_OVF (1 << 17) ++#define ISPSBL_PCR_RSZ4_WBL_OVF (1 << 18) ++#define ISPSBL_PCR_RSZ3_WBL_OVF (1 << 19) ++#define ISPSBL_PCR_RSZ2_WBL_OVF (1 << 20) ++#define ISPSBL_PCR_RSZ1_WBL_OVF (1 << 21) ++#define ISPSBL_PCR_PRV_WBL_OVF (1 << 22) ++#define ISPSBL_PCR_CCDC_WBL_OVF (1 << 23) ++#define ISPSBL_PCR_CCDCPRV_2_RSZ_OVF (1 << 24) ++#define ISPSBL_PCR_CSIA_WBL_OVF (1 << 25) ++#define ISPSBL_PCR_CSIB_WBL_OVF (1 << 26) ++#define ISPSBL_CCDC_WR_0 (0x028) ++#define ISPSBL_CCDC_WR_0_DATA_READY (1 << 21) ++#define ISPSBL_CCDC_WR_1 (0x02C) ++#define ISPSBL_CCDC_WR_2 (0x030) ++#define ISPSBL_CCDC_WR_3 (0x034) ++ ++#define ISPSBL_SDR_REQ_EXP 0xF8 ++#define ISPSBL_SDR_REQ_HIST_EXP_SHIFT 0 ++#define ISPSBL_SDR_REQ_HIST_EXP_MASK (0x3FF) ++#define ISPSBL_SDR_REQ_RSZ_EXP_SHIFT 10 ++#define ISPSBL_SDR_REQ_RSZ_EXP_MASK (0x3FF << ISPSBL_SDR_REQ_RSZ_EXP_SHIFT) ++#define ISPSBL_SDR_REQ_PRV_EXP_SHIFT 20 ++#define ISPSBL_SDR_REQ_PRV_EXP_MASK (0x3FF << ISPSBL_SDR_REQ_PRV_EXP_SHIFT) ++ ++/* Histogram registers */ ++#define ISPHIST_PID (0x000) ++#define ISPHIST_PCR (0x004) ++#define ISPHIST_CNT (0x008) ++#define ISPHIST_WB_GAIN (0x00C) ++#define ISPHIST_R0_HORZ (0x010) ++#define ISPHIST_R0_VERT (0x014) ++#define ISPHIST_R1_HORZ (0x018) ++#define ISPHIST_R1_VERT (0x01C) ++#define ISPHIST_R2_HORZ (0x020) ++#define ISPHIST_R2_VERT (0x024) ++#define ISPHIST_R3_HORZ (0x028) ++#define ISPHIST_R3_VERT (0x02C) ++#define ISPHIST_ADDR (0x030) ++#define ISPHIST_DATA (0x034) ++#define ISPHIST_RADD (0x038) ++#define ISPHIST_RADD_OFF (0x03C) ++#define ISPHIST_H_V_INFO (0x040) ++ ++/* H3A module registers */ ++#define ISPH3A_PID (0x000) ++#define ISPH3A_PCR (0x004) ++#define ISPH3A_AEWWIN1 (0x04C) ++#define ISPH3A_AEWINSTART (0x050) ++#define ISPH3A_AEWINBLK (0x054) ++#define ISPH3A_AEWSUBWIN (0x058) ++#define ISPH3A_AEWBUFST (0x05C) ++#define ISPH3A_AFPAX1 (0x008) ++#define ISPH3A_AFPAX2 (0x00C) ++#define ISPH3A_AFPAXSTART (0x010) ++#define ISPH3A_AFIIRSH (0x014) ++#define ISPH3A_AFBUFST (0x018) ++#define ISPH3A_AFCOEF010 (0x01C) ++#define ISPH3A_AFCOEF032 (0x020) ++#define ISPH3A_AFCOEF054 (0x024) ++#define ISPH3A_AFCOEF076 (0x028) ++#define ISPH3A_AFCOEF098 (0x02C) ++#define ISPH3A_AFCOEF0010 (0x030) ++#define ISPH3A_AFCOEF110 (0x034) ++#define ISPH3A_AFCOEF132 (0x038) ++#define ISPH3A_AFCOEF154 (0x03C) ++#define ISPH3A_AFCOEF176 (0x040) ++#define ISPH3A_AFCOEF198 (0x044) ++#define ISPH3A_AFCOEF1010 (0x048) ++ ++#define ISPPRV_PCR (0x004) ++#define ISPPRV_HORZ_INFO (0x008) ++#define ISPPRV_VERT_INFO (0x00C) ++#define ISPPRV_RSDR_ADDR (0x010) ++#define ISPPRV_RADR_OFFSET (0x014) ++#define ISPPRV_DSDR_ADDR (0x018) ++#define ISPPRV_DRKF_OFFSET (0x01C) ++#define ISPPRV_WSDR_ADDR (0x020) ++#define ISPPRV_WADD_OFFSET (0x024) ++#define ISPPRV_AVE (0x028) ++#define ISPPRV_HMED (0x02C) ++#define ISPPRV_NF (0x030) ++#define ISPPRV_WB_DGAIN (0x034) ++#define ISPPRV_WBGAIN (0x038) ++#define ISPPRV_WBSEL (0x03C) ++#define ISPPRV_CFA (0x040) ++#define ISPPRV_BLKADJOFF (0x044) ++#define ISPPRV_RGB_MAT1 (0x048) ++#define ISPPRV_RGB_MAT2 (0x04C) ++#define ISPPRV_RGB_MAT3 (0x050) ++#define ISPPRV_RGB_MAT4 (0x054) ++#define ISPPRV_RGB_MAT5 (0x058) ++#define ISPPRV_RGB_OFF1 (0x05C) ++#define ISPPRV_RGB_OFF2 (0x060) ++#define ISPPRV_CSC0 (0x064) ++#define ISPPRV_CSC1 (0x068) ++#define ISPPRV_CSC2 (0x06C) ++#define ISPPRV_CSC_OFFSET (0x070) ++#define ISPPRV_CNT_BRT (0x074) ++#define ISPPRV_CSUP (0x078) ++#define ISPPRV_SETUP_YC (0x07C) ++#define ISPPRV_SET_TBL_ADDR (0x080) ++#define ISPPRV_SET_TBL_DATA (0x084) ++#define ISPPRV_CDC_THR0 (0x090) ++#define ISPPRV_CDC_THR1 (ISPPRV_CDC_THR0 + (0x4)) ++#define ISPPRV_CDC_THR2 (ISPPRV_CDC_THR0 + (0x4) * 2) ++#define ISPPRV_CDC_THR3 (ISPPRV_CDC_THR0 + (0x4) * 3) ++ ++#define ISPPRV_REDGAMMA_TABLE_ADDR 0x0000 ++#define ISPPRV_GREENGAMMA_TABLE_ADDR 0x0400 ++#define ISPPRV_BLUEGAMMA_TABLE_ADDR 0x0800 ++#define ISPPRV_NF_TABLE_ADDR 0x0C00 ++#define ISPPRV_YENH_TABLE_ADDR 0x1000 ++#define ISPPRV_CFA_TABLE_ADDR 0x1400 ++ ++#define ISPPRV_MAXOUTPUT_WIDTH 1280 ++#define ISPPRV_MAXOUTPUT_WIDTH_ES2 3300 ++#define ISPPRV_MAXOUTPUT_WIDTH_3630 4096 ++#define ISPRSZ_MIN_OUTPUT 64 ++#define ISPRSZ_MAX_OUTPUT 3312 ++ ++/* Resizer module register offset */ ++#define ISPRSZ_PID (0x000) ++#define ISPRSZ_PCR (0x004) ++#define ISPRSZ_CNT (0x008) ++#define ISPRSZ_OUT_SIZE (0x00C) ++#define ISPRSZ_IN_START (0x010) ++#define ISPRSZ_IN_SIZE (0x014) ++#define ISPRSZ_SDR_INADD (0x018) ++#define ISPRSZ_SDR_INOFF (0x01C) ++#define ISPRSZ_SDR_OUTADD (0x020) ++#define ISPRSZ_SDR_OUTOFF (0x024) ++#define ISPRSZ_HFILT10 (0x028) ++#define ISPRSZ_HFILT32 (0x02C) ++#define ISPRSZ_HFILT54 (0x030) ++#define ISPRSZ_HFILT76 (0x034) ++#define ISPRSZ_HFILT98 (0x038) ++#define ISPRSZ_HFILT1110 (0x03C) ++#define ISPRSZ_HFILT1312 (0x040) ++#define ISPRSZ_HFILT1514 (0x044) ++#define ISPRSZ_HFILT1716 (0x048) ++#define ISPRSZ_HFILT1918 (0x04C) ++#define ISPRSZ_HFILT2120 (0x050) ++#define ISPRSZ_HFILT2322 (0x054) ++#define ISPRSZ_HFILT2524 (0x058) ++#define ISPRSZ_HFILT2726 (0x05C) ++#define ISPRSZ_HFILT2928 (0x060) ++#define ISPRSZ_HFILT3130 (0x064) ++#define ISPRSZ_VFILT10 (0x068) ++#define ISPRSZ_VFILT32 (0x06C) ++#define ISPRSZ_VFILT54 (0x070) ++#define ISPRSZ_VFILT76 (0x074) ++#define ISPRSZ_VFILT98 (0x078) ++#define ISPRSZ_VFILT1110 (0x07C) ++#define ISPRSZ_VFILT1312 (0x080) ++#define ISPRSZ_VFILT1514 (0x084) ++#define ISPRSZ_VFILT1716 (0x088) ++#define ISPRSZ_VFILT1918 (0x08C) ++#define ISPRSZ_VFILT2120 (0x090) ++#define ISPRSZ_VFILT2322 (0x094) ++#define ISPRSZ_VFILT2524 (0x098) ++#define ISPRSZ_VFILT2726 (0x09C) ++#define ISPRSZ_VFILT2928 (0x0A0) ++#define ISPRSZ_VFILT3130 (0x0A4) ++#define ISPRSZ_YENH (0x0A8) ++ ++#define ISP_INT_CLR 0xFF113F11 ++#define ISPPRV_PCR_EN 1 ++#define ISPPRV_PCR_BUSY (1 << 1) ++#define ISPPRV_PCR_SOURCE (1 << 2) ++#define ISPPRV_PCR_ONESHOT (1 << 3) ++#define ISPPRV_PCR_WIDTH (1 << 4) ++#define ISPPRV_PCR_INVALAW (1 << 5) ++#define ISPPRV_PCR_DRKFEN (1 << 6) ++#define ISPPRV_PCR_DRKFCAP (1 << 7) ++#define ISPPRV_PCR_HMEDEN (1 << 8) ++#define ISPPRV_PCR_NFEN (1 << 9) ++#define ISPPRV_PCR_CFAEN (1 << 10) ++#define ISPPRV_PCR_CFAFMT_SHIFT 11 ++#define ISPPRV_PCR_CFAFMT_MASK 0x7800 ++#define ISPPRV_PCR_CFAFMT_BAYER (0 << 11) ++#define ISPPRV_PCR_CFAFMT_SONYVGA (1 << 11) ++#define ISPPRV_PCR_CFAFMT_RGBFOVEON (2 << 11) ++#define ISPPRV_PCR_CFAFMT_DNSPL (3 << 11) ++#define ISPPRV_PCR_CFAFMT_HONEYCOMB (4 << 11) ++#define ISPPRV_PCR_CFAFMT_RRGGBBFOVEON (5 << 11) ++#define ISPPRV_PCR_YNENHEN (1 << 15) ++#define ISPPRV_PCR_SUPEN (1 << 16) ++#define ISPPRV_PCR_YCPOS_SHIFT 17 ++#define ISPPRV_PCR_YCPOS_YCrYCb (0 << 17) ++#define ISPPRV_PCR_YCPOS_YCbYCr (1 << 17) ++#define ISPPRV_PCR_YCPOS_CbYCrY (2 << 17) ++#define ISPPRV_PCR_YCPOS_CrYCbY (3 << 17) ++#define ISPPRV_PCR_RSZPORT (1 << 19) ++#define ISPPRV_PCR_SDRPORT (1 << 20) ++#define ISPPRV_PCR_SCOMP_EN (1 << 21) ++#define ISPPRV_PCR_SCOMP_SFT_SHIFT (22) ++#define ISPPRV_PCR_SCOMP_SFT_MASK (7 << 22) ++#define ISPPRV_PCR_GAMMA_BYPASS (1 << 26) ++#define ISPPRV_PCR_DCOREN (1 << 27) ++#define ISPPRV_PCR_DCCOUP (1 << 28) ++#define ISPPRV_PCR_DRK_FAIL (1 << 31) ++ ++#define ISPPRV_HORZ_INFO_EPH_SHIFT 0 ++#define ISPPRV_HORZ_INFO_EPH_MASK 0x3fff ++#define ISPPRV_HORZ_INFO_SPH_SHIFT 16 ++#define ISPPRV_HORZ_INFO_SPH_MASK 0x3fff0 ++ ++#define ISPPRV_VERT_INFO_ELV_SHIFT 0 ++#define ISPPRV_VERT_INFO_ELV_MASK 0x3fff ++#define ISPPRV_VERT_INFO_SLV_SHIFT 16 ++#define ISPPRV_VERT_INFO_SLV_MASK 0x3fff0 ++ ++#define ISPPRV_AVE_EVENDIST_SHIFT 2 ++#define ISPPRV_AVE_EVENDIST_1 0x0 ++#define ISPPRV_AVE_EVENDIST_2 0x1 ++#define ISPPRV_AVE_EVENDIST_3 0x2 ++#define ISPPRV_AVE_EVENDIST_4 0x3 ++#define ISPPRV_AVE_ODDDIST_SHIFT 4 ++#define ISPPRV_AVE_ODDDIST_1 0x0 ++#define ISPPRV_AVE_ODDDIST_2 0x1 ++#define ISPPRV_AVE_ODDDIST_3 0x2 ++#define ISPPRV_AVE_ODDDIST_4 0x3 ++ ++#define ISPPRV_HMED_THRESHOLD_SHIFT 0 ++#define ISPPRV_HMED_EVENDIST (1 << 8) ++#define ISPPRV_HMED_ODDDIST (1 << 9) ++ ++#define ISPPRV_WBGAIN_COEF0_SHIFT 0 ++#define ISPPRV_WBGAIN_COEF1_SHIFT 8 ++#define ISPPRV_WBGAIN_COEF2_SHIFT 16 ++#define ISPPRV_WBGAIN_COEF3_SHIFT 24 ++ ++#define ISPPRV_WBSEL_COEF0 0x0 ++#define ISPPRV_WBSEL_COEF1 0x1 ++#define ISPPRV_WBSEL_COEF2 0x2 ++#define ISPPRV_WBSEL_COEF3 0x3 ++ ++#define ISPPRV_WBSEL_N0_0_SHIFT 0 ++#define ISPPRV_WBSEL_N0_1_SHIFT 2 ++#define ISPPRV_WBSEL_N0_2_SHIFT 4 ++#define ISPPRV_WBSEL_N0_3_SHIFT 6 ++#define ISPPRV_WBSEL_N1_0_SHIFT 8 ++#define ISPPRV_WBSEL_N1_1_SHIFT 10 ++#define ISPPRV_WBSEL_N1_2_SHIFT 12 ++#define ISPPRV_WBSEL_N1_3_SHIFT 14 ++#define ISPPRV_WBSEL_N2_0_SHIFT 16 ++#define ISPPRV_WBSEL_N2_1_SHIFT 18 ++#define ISPPRV_WBSEL_N2_2_SHIFT 20 ++#define ISPPRV_WBSEL_N2_3_SHIFT 22 ++#define ISPPRV_WBSEL_N3_0_SHIFT 24 ++#define ISPPRV_WBSEL_N3_1_SHIFT 26 ++#define ISPPRV_WBSEL_N3_2_SHIFT 28 ++#define ISPPRV_WBSEL_N3_3_SHIFT 30 ++ ++#define ISPPRV_CFA_GRADTH_HOR_SHIFT 0 ++#define ISPPRV_CFA_GRADTH_VER_SHIFT 8 ++ ++#define ISPPRV_BLKADJOFF_B_SHIFT 0 ++#define ISPPRV_BLKADJOFF_G_SHIFT 8 ++#define ISPPRV_BLKADJOFF_R_SHIFT 16 ++ ++#define ISPPRV_RGB_MAT1_MTX_RR_SHIFT 0 ++#define ISPPRV_RGB_MAT1_MTX_GR_SHIFT 16 ++ ++#define ISPPRV_RGB_MAT2_MTX_BR_SHIFT 0 ++#define ISPPRV_RGB_MAT2_MTX_RG_SHIFT 16 ++ ++#define ISPPRV_RGB_MAT3_MTX_GG_SHIFT 0 ++#define ISPPRV_RGB_MAT3_MTX_BG_SHIFT 16 ++ ++#define ISPPRV_RGB_MAT4_MTX_RB_SHIFT 0 ++#define ISPPRV_RGB_MAT4_MTX_GB_SHIFT 16 ++ ++#define ISPPRV_RGB_MAT5_MTX_BB_SHIFT 0 ++ ++#define ISPPRV_RGB_OFF1_MTX_OFFG_SHIFT 0 ++#define ISPPRV_RGB_OFF1_MTX_OFFR_SHIFT 16 ++ ++#define ISPPRV_RGB_OFF2_MTX_OFFB_SHIFT 0 ++ ++#define ISPPRV_CSC0_RY_SHIFT 0 ++#define ISPPRV_CSC0_GY_SHIFT 10 ++#define ISPPRV_CSC0_BY_SHIFT 20 ++ ++#define ISPPRV_CSC1_RCB_SHIFT 0 ++#define ISPPRV_CSC1_GCB_SHIFT 10 ++#define ISPPRV_CSC1_BCB_SHIFT 20 ++ ++#define ISPPRV_CSC2_RCR_SHIFT 0 ++#define ISPPRV_CSC2_GCR_SHIFT 10 ++#define ISPPRV_CSC2_BCR_SHIFT 20 ++ ++#define ISPPRV_CSC_OFFSET_CR_SHIFT 0 ++#define ISPPRV_CSC_OFFSET_CB_SHIFT 8 ++#define ISPPRV_CSC_OFFSET_Y_SHIFT 16 ++ ++#define ISPPRV_CNT_BRT_BRT_SHIFT 0 ++#define ISPPRV_CNT_BRT_CNT_SHIFT 8 ++ ++#define ISPPRV_CONTRAST_MAX 0x10 ++#define ISPPRV_CONTRAST_MIN 0xFF ++#define ISPPRV_BRIGHT_MIN 0x00 ++#define ISPPRV_BRIGHT_MAX 0xFF ++ ++#define ISPPRV_CSUP_CSUPG_SHIFT 0 ++#define ISPPRV_CSUP_THRES_SHIFT 8 ++#define ISPPRV_CSUP_HPYF_SHIFT 16 ++ ++#define ISPPRV_SETUP_YC_MINC_SHIFT 0 ++#define ISPPRV_SETUP_YC_MAXC_SHIFT 8 ++#define ISPPRV_SETUP_YC_MINY_SHIFT 16 ++#define ISPPRV_SETUP_YC_MAXY_SHIFT 24 ++#define ISPPRV_YC_MAX 0xFF ++#define ISPPRV_YC_MIN 0x0 ++ ++/* Define bit fields within selected registers */ ++#define ISP_REVISION_SHIFT 0 ++ ++#define ISP_SYSCONFIG_AUTOIDLE (1 << 0) ++#define ISP_SYSCONFIG_SOFTRESET (1 << 1) ++#define ISP_SYSCONFIG_MIDLEMODE_SHIFT 12 ++#define ISP_SYSCONFIG_MIDLEMODE_FORCESTANDBY 0x0 ++#define ISP_SYSCONFIG_MIDLEMODE_NOSTANBY 0x1 ++#define ISP_SYSCONFIG_MIDLEMODE_SMARTSTANDBY 0x2 ++ ++#define ISP_SYSSTATUS_RESETDONE 0 ++ ++#define IRQ0ENABLE_CSIA_IRQ (1 << 0) ++#define IRQ0ENABLE_CSIC_IRQ (1 << 1) ++#define IRQ0ENABLE_CCP2_LCM_IRQ (1 << 3) ++#define IRQ0ENABLE_CCP2_LC0_IRQ (1 << 4) ++#define IRQ0ENABLE_CCP2_LC1_IRQ (1 << 5) ++#define IRQ0ENABLE_CCP2_LC2_IRQ (1 << 6) ++#define IRQ0ENABLE_CCP2_LC3_IRQ (1 << 7) ++#define IRQ0ENABLE_CSIB_IRQ (IRQ0ENABLE_CCP2_LCM_IRQ | \ ++ IRQ0ENABLE_CCP2_LC0_IRQ | \ ++ IRQ0ENABLE_CCP2_LC1_IRQ | \ ++ IRQ0ENABLE_CCP2_LC2_IRQ | \ ++ IRQ0ENABLE_CCP2_LC3_IRQ) ++ ++#define IRQ0ENABLE_CCDC_VD0_IRQ (1 << 8) ++#define IRQ0ENABLE_CCDC_VD1_IRQ (1 << 9) ++#define IRQ0ENABLE_CCDC_VD2_IRQ (1 << 10) ++#define IRQ0ENABLE_CCDC_ERR_IRQ (1 << 11) ++#define IRQ0ENABLE_H3A_AF_DONE_IRQ (1 << 12) ++#define IRQ0ENABLE_H3A_AWB_DONE_IRQ (1 << 13) ++#define IRQ0ENABLE_HIST_DONE_IRQ (1 << 16) ++#define IRQ0ENABLE_CCDC_LSC_DONE_IRQ (1 << 17) ++#define IRQ0ENABLE_CCDC_LSC_PREF_COMP_IRQ (1 << 18) ++#define IRQ0ENABLE_CCDC_LSC_PREF_ERR_IRQ (1 << 19) ++#define IRQ0ENABLE_PRV_DONE_IRQ (1 << 20) ++#define IRQ0ENABLE_RSZ_DONE_IRQ (1 << 24) ++#define IRQ0ENABLE_OVF_IRQ (1 << 25) ++#define IRQ0ENABLE_PING_IRQ (1 << 26) ++#define IRQ0ENABLE_PONG_IRQ (1 << 27) ++#define IRQ0ENABLE_MMU_ERR_IRQ (1 << 28) ++#define IRQ0ENABLE_OCP_ERR_IRQ (1 << 29) ++#define IRQ0ENABLE_SEC_ERR_IRQ (1 << 30) ++#define IRQ0ENABLE_HS_VS_IRQ (1 << 31) ++ ++#define IRQ0STATUS_CSIA_IRQ (1 << 0) ++#define IRQ0STATUS_CSI2C_IRQ (1 << 1) ++#define IRQ0STATUS_CCP2_LCM_IRQ (1 << 3) ++#define IRQ0STATUS_CCP2_LC0_IRQ (1 << 4) ++#define IRQ0STATUS_CSIB_IRQ (IRQ0STATUS_CCP2_LCM_IRQ | \ ++ IRQ0STATUS_CCP2_LC0_IRQ) ++ ++#define IRQ0STATUS_CSIB_LC1_IRQ (1 << 5) ++#define IRQ0STATUS_CSIB_LC2_IRQ (1 << 6) ++#define IRQ0STATUS_CSIB_LC3_IRQ (1 << 7) ++#define IRQ0STATUS_CCDC_VD0_IRQ (1 << 8) ++#define IRQ0STATUS_CCDC_VD1_IRQ (1 << 9) ++#define IRQ0STATUS_CCDC_VD2_IRQ (1 << 10) ++#define IRQ0STATUS_CCDC_ERR_IRQ (1 << 11) ++#define IRQ0STATUS_H3A_AF_DONE_IRQ (1 << 12) ++#define IRQ0STATUS_H3A_AWB_DONE_IRQ (1 << 13) ++#define IRQ0STATUS_HIST_DONE_IRQ (1 << 16) ++#define IRQ0STATUS_CCDC_LSC_DONE_IRQ (1 << 17) ++#define IRQ0STATUS_CCDC_LSC_PREF_COMP_IRQ (1 << 18) ++#define IRQ0STATUS_CCDC_LSC_PREF_ERR_IRQ (1 << 19) ++#define IRQ0STATUS_PRV_DONE_IRQ (1 << 20) ++#define IRQ0STATUS_RSZ_DONE_IRQ (1 << 24) ++#define IRQ0STATUS_OVF_IRQ (1 << 25) ++#define IRQ0STATUS_PING_IRQ (1 << 26) ++#define IRQ0STATUS_PONG_IRQ (1 << 27) ++#define IRQ0STATUS_MMU_ERR_IRQ (1 << 28) ++#define IRQ0STATUS_OCP_ERR_IRQ (1 << 29) ++#define IRQ0STATUS_SEC_ERR_IRQ (1 << 30) ++#define IRQ0STATUS_HS_VS_IRQ (1 << 31) ++ ++#define TCTRL_GRESET_LEN 0 ++ ++#define TCTRL_PSTRB_REPLAY_DELAY 0 ++#define TCTRL_PSTRB_REPLAY_COUNTER_SHIFT 25 ++ ++#define ISPCTRL_PAR_SER_CLK_SEL_PARALLEL 0x0 ++#define ISPCTRL_PAR_SER_CLK_SEL_CSIA 0x1 ++#define ISPCTRL_PAR_SER_CLK_SEL_CSIB 0x2 ++#define ISPCTRL_PAR_SER_CLK_SEL_CSIC 0x3 ++#define ISPCTRL_PAR_SER_CLK_SEL_MASK 0x3 ++ ++#define ISPCTRL_PAR_BRIDGE_SHIFT 2 ++#define ISPCTRL_PAR_BRIDGE_DISABLE (0x0 << 2) ++#define ISPCTRL_PAR_BRIDGE_LENDIAN (0x2 << 2) ++#define ISPCTRL_PAR_BRIDGE_BENDIAN (0x3 << 2) ++#define ISPCTRL_PAR_BRIDGE_MASK (0x3 << 2) ++ ++#define ISPCTRL_PAR_CLK_POL_SHIFT 4 ++#define ISPCTRL_PAR_CLK_POL_INV (1 << 4) ++#define ISPCTRL_PING_PONG_EN (1 << 5) ++#define ISPCTRL_SHIFT_SHIFT 6 ++#define ISPCTRL_SHIFT_0 (0x0 << 6) ++#define ISPCTRL_SHIFT_2 (0x1 << 6) ++#define ISPCTRL_SHIFT_4 (0x2 << 6) ++#define ISPCTRL_SHIFT_MASK (0x3 << 6) ++ ++#define ISPCTRL_CCDC_CLK_EN (1 << 8) ++#define ISPCTRL_SCMP_CLK_EN (1 << 9) ++#define ISPCTRL_H3A_CLK_EN (1 << 10) ++#define ISPCTRL_HIST_CLK_EN (1 << 11) ++#define ISPCTRL_PREV_CLK_EN (1 << 12) ++#define ISPCTRL_RSZ_CLK_EN (1 << 13) ++#define ISPCTRL_SYNC_DETECT_SHIFT 14 ++#define ISPCTRL_SYNC_DETECT_HSFALL (0x0 << ISPCTRL_SYNC_DETECT_SHIFT) ++#define ISPCTRL_SYNC_DETECT_HSRISE (0x1 << ISPCTRL_SYNC_DETECT_SHIFT) ++#define ISPCTRL_SYNC_DETECT_VSFALL (0x2 << ISPCTRL_SYNC_DETECT_SHIFT) ++#define ISPCTRL_SYNC_DETECT_VSRISE (0x3 << ISPCTRL_SYNC_DETECT_SHIFT) ++#define ISPCTRL_SYNC_DETECT_MASK (0x3 << ISPCTRL_SYNC_DETECT_SHIFT) ++ ++#define ISPCTRL_CCDC_RAM_EN (1 << 16) ++#define ISPCTRL_PREV_RAM_EN (1 << 17) ++#define ISPCTRL_SBL_RD_RAM_EN (1 << 18) ++#define ISPCTRL_SBL_WR1_RAM_EN (1 << 19) ++#define ISPCTRL_SBL_WR0_RAM_EN (1 << 20) ++#define ISPCTRL_SBL_AUTOIDLE (1 << 21) ++#define ISPCTRL_SBL_SHARED_WPORTC (1 << 26) ++#define ISPCTRL_SBL_SHARED_RPORTA (1 << 27) ++#define ISPCTRL_SBL_SHARED_RPORTB (1 << 28) ++#define ISPCTRL_JPEG_FLUSH (1 << 30) ++#define ISPCTRL_CCDC_FLUSH (1 << 31) ++ ++#define ISPSECURE_SECUREMODE 0 ++ ++#define ISPTCTRL_CTRL_DIV_LOW 0x0 ++#define ISPTCTRL_CTRL_DIV_HIGH 0x1 ++#define ISPTCTRL_CTRL_DIV_BYPASS 0x1F ++ ++#define ISPTCTRL_CTRL_DIVA_SHIFT 0 ++#define ISPTCTRL_CTRL_DIVA_MASK (0x1F << ISPTCTRL_CTRL_DIVA_SHIFT) ++ ++#define ISPTCTRL_CTRL_DIVB_SHIFT 5 ++#define ISPTCTRL_CTRL_DIVB_MASK (0x1F << ISPTCTRL_CTRL_DIVB_SHIFT) ++ ++#define ISPTCTRL_CTRL_DIVC_SHIFT 10 ++#define ISPTCTRL_CTRL_DIVC_NOCLOCK (0x0 << 10) ++ ++#define ISPTCTRL_CTRL_SHUTEN (1 << 21) ++#define ISPTCTRL_CTRL_PSTRBEN (1 << 22) ++#define ISPTCTRL_CTRL_STRBEN (1 << 23) ++#define ISPTCTRL_CTRL_SHUTPOL (1 << 24) ++#define ISPTCTRL_CTRL_STRBPSTRBPOL (1 << 26) ++ ++#define ISPTCTRL_CTRL_INSEL_SHIFT 27 ++#define ISPTCTRL_CTRL_INSEL_PARALLEL (0x0 << 27) ++#define ISPTCTRL_CTRL_INSEL_CSIA (0x1 << 27) ++#define ISPTCTRL_CTRL_INSEL_CSIB (0x2 << 27) ++ ++#define ISPTCTRL_CTRL_GRESETEn (1 << 29) ++#define ISPTCTRL_CTRL_GRESETPOL (1 << 30) ++#define ISPTCTRL_CTRL_GRESETDIR (1 << 31) ++ ++#define ISPTCTRL_FRAME_SHUT_SHIFT 0 ++#define ISPTCTRL_FRAME_PSTRB_SHIFT 6 ++#define ISPTCTRL_FRAME_STRB_SHIFT 12 ++ ++#define ISPCCDC_PID_PREV_SHIFT 0 ++#define ISPCCDC_PID_CID_SHIFT 8 ++#define ISPCCDC_PID_TID_SHIFT 16 ++ ++#define ISPCCDC_PCR_EN 1 ++#define ISPCCDC_PCR_BUSY (1 << 1) ++ ++#define ISPCCDC_SYN_MODE_VDHDOUT 0x1 ++#define ISPCCDC_SYN_MODE_FLDOUT (1 << 1) ++#define ISPCCDC_SYN_MODE_VDPOL (1 << 2) ++#define ISPCCDC_SYN_MODE_HDPOL (1 << 3) ++#define ISPCCDC_SYN_MODE_FLDPOL (1 << 4) ++#define ISPCCDC_SYN_MODE_EXWEN (1 << 5) ++#define ISPCCDC_SYN_MODE_DATAPOL (1 << 6) ++#define ISPCCDC_SYN_MODE_FLDMODE (1 << 7) ++#define ISPCCDC_SYN_MODE_DATSIZ_MASK (0x7 << 8) ++#define ISPCCDC_SYN_MODE_DATSIZ_8_16 (0x0 << 8) ++#define ISPCCDC_SYN_MODE_DATSIZ_12 (0x4 << 8) ++#define ISPCCDC_SYN_MODE_DATSIZ_11 (0x5 << 8) ++#define ISPCCDC_SYN_MODE_DATSIZ_10 (0x6 << 8) ++#define ISPCCDC_SYN_MODE_DATSIZ_8 (0x7 << 8) ++#define ISPCCDC_SYN_MODE_PACK8 (1 << 11) ++#define ISPCCDC_SYN_MODE_INPMOD_MASK (3 << 12) ++#define ISPCCDC_SYN_MODE_INPMOD_RAW (0 << 12) ++#define ISPCCDC_SYN_MODE_INPMOD_YCBCR16 (1 << 12) ++#define ISPCCDC_SYN_MODE_INPMOD_YCBCR8 (2 << 12) ++#define ISPCCDC_SYN_MODE_LPF (1 << 14) ++#define ISPCCDC_SYN_MODE_FLDSTAT (1 << 15) ++#define ISPCCDC_SYN_MODE_VDHDEN (1 << 16) ++#define ISPCCDC_SYN_MODE_WEN (1 << 17) ++#define ISPCCDC_SYN_MODE_VP2SDR (1 << 18) ++#define ISPCCDC_SYN_MODE_SDR2RSZ (1 << 19) ++ ++#define ISPCCDC_HD_VD_WID_VDW_SHIFT 0 ++#define ISPCCDC_HD_VD_WID_HDW_SHIFT 16 ++ ++#define ISPCCDC_PIX_LINES_HLPRF_SHIFT 0 ++#define ISPCCDC_PIX_LINES_PPLN_SHIFT 16 ++ ++#define ISPCCDC_HORZ_INFO_NPH_SHIFT 0 ++#define ISPCCDC_HORZ_INFO_NPH_MASK 0xFFFF8000 ++#define ISPCCDC_HORZ_INFO_SPH_MASK 0x1000FFFF ++#define ISPCCDC_HORZ_INFO_SPH_SHIFT 16 ++ ++#define ISPCCDC_VERT_START_SLV0_SHIFT 16 ++#define ISPCCDC_VERT_START_SLV0_MASK 0x1000FFFF ++#define ISPCCDC_VERT_START_SLV1_SHIFT 0 ++ ++#define ISPCCDC_VERT_LINES_NLV_MASK 0xFFFF8000 ++#define ISPCCDC_VERT_LINES_NLV_SHIFT 0 ++ ++#define ISPCCDC_CULLING_CULV_SHIFT 0 ++#define ISPCCDC_CULLING_CULHODD_SHIFT 16 ++#define ISPCCDC_CULLING_CULHEVN_SHIFT 24 ++ ++#define ISPCCDC_HSIZE_OFF_SHIFT 0 ++ ++#define ISPCCDC_SDOFST_FINV (1 << 14) ++#define ISPCCDC_SDOFST_FOFST_1L 0 ++#define ISPCCDC_SDOFST_FOFST_4L (3 << 12) ++#define ISPCCDC_SDOFST_LOFST3_SHIFT 0 ++#define ISPCCDC_SDOFST_LOFST2_SHIFT 3 ++#define ISPCCDC_SDOFST_LOFST1_SHIFT 6 ++#define ISPCCDC_SDOFST_LOFST0_SHIFT 9 ++#define EVENEVEN 1 ++#define ODDEVEN 2 ++#define EVENODD 3 ++#define ODDODD 4 ++ ++#define ISPCCDC_CLAMP_OBGAIN_SHIFT 0 ++#define ISPCCDC_CLAMP_OBST_SHIFT 10 ++#define ISPCCDC_CLAMP_OBSLN_SHIFT 25 ++#define ISPCCDC_CLAMP_OBSLEN_SHIFT 28 ++#define ISPCCDC_CLAMP_CLAMPEN (1 << 31) ++ ++#define ISPCCDC_COLPTN_R_Ye 0x0 ++#define ISPCCDC_COLPTN_Gr_Cy 0x1 ++#define ISPCCDC_COLPTN_Gb_G 0x2 ++#define ISPCCDC_COLPTN_B_Mg 0x3 ++#define ISPCCDC_COLPTN_CP0PLC0_SHIFT 0 ++#define ISPCCDC_COLPTN_CP0PLC1_SHIFT 2 ++#define ISPCCDC_COLPTN_CP0PLC2_SHIFT 4 ++#define ISPCCDC_COLPTN_CP0PLC3_SHIFT 6 ++#define ISPCCDC_COLPTN_CP1PLC0_SHIFT 8 ++#define ISPCCDC_COLPTN_CP1PLC1_SHIFT 10 ++#define ISPCCDC_COLPTN_CP1PLC2_SHIFT 12 ++#define ISPCCDC_COLPTN_CP1PLC3_SHIFT 14 ++#define ISPCCDC_COLPTN_CP2PLC0_SHIFT 16 ++#define ISPCCDC_COLPTN_CP2PLC1_SHIFT 18 ++#define ISPCCDC_COLPTN_CP2PLC2_SHIFT 20 ++#define ISPCCDC_COLPTN_CP2PLC3_SHIFT 22 ++#define ISPCCDC_COLPTN_CP3PLC0_SHIFT 24 ++#define ISPCCDC_COLPTN_CP3PLC1_SHIFT 26 ++#define ISPCCDC_COLPTN_CP3PLC2_SHIFT 28 ++#define ISPCCDC_COLPTN_CP3PLC3_SHIFT 30 ++ ++#define ISPCCDC_BLKCMP_B_MG_SHIFT 0 ++#define ISPCCDC_BLKCMP_GB_G_SHIFT 8 ++#define ISPCCDC_BLKCMP_GR_CY_SHIFT 16 ++#define ISPCCDC_BLKCMP_R_YE_SHIFT 24 ++ ++#define ISPCCDC_FPC_FPNUM_SHIFT 0 ++#define ISPCCDC_FPC_FPCEN (1 << 15) ++#define ISPCCDC_FPC_FPERR (1 << 16) ++ ++#define ISPCCDC_VDINT_1_SHIFT 0 ++#define ISPCCDC_VDINT_0_SHIFT 16 ++#define ISPCCDC_VDINT_0_MASK 0x7FFF ++#define ISPCCDC_VDINT_1_MASK 0x7FFF ++ ++#define ISPCCDC_ALAW_GWDI_12_3 (0x3 << 0) ++#define ISPCCDC_ALAW_GWDI_11_2 (0x4 << 0) ++#define ISPCCDC_ALAW_GWDI_10_1 (0x5 << 0) ++#define ISPCCDC_ALAW_GWDI_9_0 (0x6 << 0) ++#define ISPCCDC_ALAW_CCDTBL (1 << 3) ++ ++#define ISPCCDC_REC656IF_R656ON 1 ++#define ISPCCDC_REC656IF_ECCFVH (1 << 1) ++ ++#define ISPCCDC_CFG_BW656 (1 << 5) ++#define ISPCCDC_CFG_FIDMD_SHIFT 6 ++#define ISPCCDC_CFG_WENLOG (1 << 8) ++#define ISPCCDC_CFG_WENLOG_AND (0 << 8) ++#define ISPCCDC_CFG_WENLOG_OR (1 << 8) ++#define ISPCCDC_CFG_Y8POS (1 << 11) ++#define ISPCCDC_CFG_BSWD (1 << 12) ++#define ISPCCDC_CFG_MSBINVI (1 << 13) ++#define ISPCCDC_CFG_VDLC (1 << 15) ++ ++#define ISPCCDC_FMTCFG_FMTEN 0x1 ++#define ISPCCDC_FMTCFG_LNALT (1 << 1) ++#define ISPCCDC_FMTCFG_LNUM_SHIFT 2 ++#define ISPCCDC_FMTCFG_PLEN_ODD_SHIFT 4 ++#define ISPCCDC_FMTCFG_PLEN_EVEN_SHIFT 8 ++#define ISPCCDC_FMTCFG_VPIN_MASK 0x00007000 ++#define ISPCCDC_FMTCFG_VPIN_12_3 (0x3 << 12) ++#define ISPCCDC_FMTCFG_VPIN_11_2 (0x4 << 12) ++#define ISPCCDC_FMTCFG_VPIN_10_1 (0x5 << 12) ++#define ISPCCDC_FMTCFG_VPIN_9_0 (0x6 << 12) ++#define ISPCCDC_FMTCFG_VPEN (1 << 15) ++ ++#define ISPCCDC_FMTCFG_VPIF_FRQ_MASK 0x003f0000 ++#define ISPCCDC_FMTCFG_VPIF_FRQ_SHIFT 16 ++#define ISPCCDC_FMTCFG_VPIF_FRQ_BY2 (0x0 << 16) ++#define ISPCCDC_FMTCFG_VPIF_FRQ_BY3 (0x1 << 16) ++#define ISPCCDC_FMTCFG_VPIF_FRQ_BY4 (0x2 << 16) ++#define ISPCCDC_FMTCFG_VPIF_FRQ_BY5 (0x3 << 16) ++#define ISPCCDC_FMTCFG_VPIF_FRQ_BY6 (0x4 << 16) ++ ++#define ISPCCDC_FMT_HORZ_FMTLNH_SHIFT 0 ++#define ISPCCDC_FMT_HORZ_FMTSPH_SHIFT 16 ++ ++#define ISPCCDC_FMT_VERT_FMTLNV_SHIFT 0 ++#define ISPCCDC_FMT_VERT_FMTSLV_SHIFT 16 ++ ++#define ISPCCDC_FMT_HORZ_FMTSPH_MASK 0x1FFF0000 ++#define ISPCCDC_FMT_HORZ_FMTLNH_MASK 0x1FFF ++ ++#define ISPCCDC_FMT_VERT_FMTSLV_MASK 0x1FFF0000 ++#define ISPCCDC_FMT_VERT_FMTLNV_MASK 0x1FFF ++ ++#define ISPCCDC_VP_OUT_HORZ_ST_SHIFT 0 ++#define ISPCCDC_VP_OUT_HORZ_NUM_SHIFT 4 ++#define ISPCCDC_VP_OUT_VERT_NUM_SHIFT 17 ++ ++#define ISPRSZ_PID_PREV_SHIFT 0 ++#define ISPRSZ_PID_CID_SHIFT 8 ++#define ISPRSZ_PID_TID_SHIFT 16 ++ ++#define ISPRSZ_PCR_ENABLE (1 << 0) ++#define ISPRSZ_PCR_BUSY (1 << 1) ++#define ISPRSZ_PCR_ONESHOT (1 << 2) ++ ++#define ISPRSZ_CNT_HRSZ_SHIFT 0 ++#define ISPRSZ_CNT_HRSZ_MASK \ ++ (0x3FF << ISPRSZ_CNT_HRSZ_SHIFT) ++#define ISPRSZ_CNT_VRSZ_SHIFT 10 ++#define ISPRSZ_CNT_VRSZ_MASK \ ++ (0x3FF << ISPRSZ_CNT_VRSZ_SHIFT) ++#define ISPRSZ_CNT_HSTPH_SHIFT 20 ++#define ISPRSZ_CNT_HSTPH_MASK (0x7 << ISPRSZ_CNT_HSTPH_SHIFT) ++#define ISPRSZ_CNT_VSTPH_SHIFT 23 ++#define ISPRSZ_CNT_VSTPH_MASK (0x7 << ISPRSZ_CNT_VSTPH_SHIFT) ++#define ISPRSZ_CNT_YCPOS (1 << 26) ++#define ISPRSZ_CNT_INPTYP (1 << 27) ++#define ISPRSZ_CNT_INPSRC (1 << 28) ++#define ISPRSZ_CNT_CBILIN (1 << 29) ++ ++#define ISPRSZ_OUT_SIZE_HORZ_SHIFT 0 ++#define ISPRSZ_OUT_SIZE_HORZ_MASK \ ++ (0xFFF << ISPRSZ_OUT_SIZE_HORZ_SHIFT) ++#define ISPRSZ_OUT_SIZE_VERT_SHIFT 16 ++#define ISPRSZ_OUT_SIZE_VERT_MASK \ ++ (0xFFF << ISPRSZ_OUT_SIZE_VERT_SHIFT) ++ ++#define ISPRSZ_IN_START_HORZ_ST_SHIFT 0 ++#define ISPRSZ_IN_START_HORZ_ST_MASK \ ++ (0x1FFF << ISPRSZ_IN_START_HORZ_ST_SHIFT) ++#define ISPRSZ_IN_START_VERT_ST_SHIFT 16 ++#define ISPRSZ_IN_START_VERT_ST_MASK \ ++ (0x1FFF << ISPRSZ_IN_START_VERT_ST_SHIFT) ++ ++#define ISPRSZ_IN_SIZE_HORZ_SHIFT 0 ++#define ISPRSZ_IN_SIZE_HORZ_MASK \ ++ (0x1FFF << ISPRSZ_IN_SIZE_HORZ_SHIFT) ++#define ISPRSZ_IN_SIZE_VERT_SHIFT 16 ++#define ISPRSZ_IN_SIZE_VERT_MASK \ ++ (0x1FFF << ISPRSZ_IN_SIZE_VERT_SHIFT) ++ ++#define ISPRSZ_SDR_INADD_ADDR_SHIFT 0 ++#define ISPRSZ_SDR_INADD_ADDR_MASK 0xFFFFFFFF ++ ++#define ISPRSZ_SDR_INOFF_OFFSET_SHIFT 0 ++#define ISPRSZ_SDR_INOFF_OFFSET_MASK \ ++ (0xFFFF << ISPRSZ_SDR_INOFF_OFFSET_SHIFT) ++ ++#define ISPRSZ_SDR_OUTADD_ADDR_SHIFT 0 ++#define ISPRSZ_SDR_OUTADD_ADDR_MASK 0xFFFFFFFF ++ ++ ++#define ISPRSZ_SDR_OUTOFF_OFFSET_SHIFT 0 ++#define ISPRSZ_SDR_OUTOFF_OFFSET_MASK \ ++ (0xFFFF << ISPRSZ_SDR_OUTOFF_OFFSET_SHIFT) ++ ++#define ISPRSZ_HFILT_COEF0_SHIFT 0 ++#define ISPRSZ_HFILT_COEF0_MASK \ ++ (0x3FF << ISPRSZ_HFILT_COEF0_SHIFT) ++#define ISPRSZ_HFILT_COEF1_SHIFT 16 ++#define ISPRSZ_HFILT_COEF1_MASK \ ++ (0x3FF << ISPRSZ_HFILT_COEF1_SHIFT) ++ ++#define ISPRSZ_HFILT32_COEF2_SHIFT 0 ++#define ISPRSZ_HFILT32_COEF2_MASK 0x3FF ++#define ISPRSZ_HFILT32_COEF3_SHIFT 16 ++#define ISPRSZ_HFILT32_COEF3_MASK 0x3FF0000 ++ ++#define ISPRSZ_HFILT54_COEF4_SHIFT 0 ++#define ISPRSZ_HFILT54_COEF4_MASK 0x3FF ++#define ISPRSZ_HFILT54_COEF5_SHIFT 16 ++#define ISPRSZ_HFILT54_COEF5_MASK 0x3FF0000 ++ ++#define ISPRSZ_HFILT76_COEFF6_SHIFT 0 ++#define ISPRSZ_HFILT76_COEFF6_MASK 0x3FF ++#define ISPRSZ_HFILT76_COEFF7_SHIFT 16 ++#define ISPRSZ_HFILT76_COEFF7_MASK 0x3FF0000 ++ ++#define ISPRSZ_HFILT98_COEFF8_SHIFT 0 ++#define ISPRSZ_HFILT98_COEFF8_MASK 0x3FF ++#define ISPRSZ_HFILT98_COEFF9_SHIFT 16 ++#define ISPRSZ_HFILT98_COEFF9_MASK 0x3FF0000 ++ ++#define ISPRSZ_HFILT1110_COEF10_SHIFT 0 ++#define ISPRSZ_HFILT1110_COEF10_MASK 0x3FF ++#define ISPRSZ_HFILT1110_COEF11_SHIFT 16 ++#define ISPRSZ_HFILT1110_COEF11_MASK 0x3FF0000 ++ ++#define ISPRSZ_HFILT1312_COEFF12_SHIFT 0 ++#define ISPRSZ_HFILT1312_COEFF12_MASK 0x3FF ++#define ISPRSZ_HFILT1312_COEFF13_SHIFT 16 ++#define ISPRSZ_HFILT1312_COEFF13_MASK 0x3FF0000 ++ ++#define ISPRSZ_HFILT1514_COEFF14_SHIFT 0 ++#define ISPRSZ_HFILT1514_COEFF14_MASK 0x3FF ++#define ISPRSZ_HFILT1514_COEFF15_SHIFT 16 ++#define ISPRSZ_HFILT1514_COEFF15_MASK 0x3FF0000 ++ ++#define ISPRSZ_HFILT1716_COEF16_SHIFT 0 ++#define ISPRSZ_HFILT1716_COEF16_MASK 0x3FF ++#define ISPRSZ_HFILT1716_COEF17_SHIFT 16 ++#define ISPRSZ_HFILT1716_COEF17_MASK 0x3FF0000 ++ ++#define ISPRSZ_HFILT1918_COEF18_SHIFT 0 ++#define ISPRSZ_HFILT1918_COEF18_MASK 0x3FF ++#define ISPRSZ_HFILT1918_COEF19_SHIFT 16 ++#define ISPRSZ_HFILT1918_COEF19_MASK 0x3FF0000 ++ ++#define ISPRSZ_HFILT2120_COEF20_SHIFT 0 ++#define ISPRSZ_HFILT2120_COEF20_MASK 0x3FF ++#define ISPRSZ_HFILT2120_COEF21_SHIFT 16 ++#define ISPRSZ_HFILT2120_COEF21_MASK 0x3FF0000 ++ ++#define ISPRSZ_HFILT2322_COEF22_SHIFT 0 ++#define ISPRSZ_HFILT2322_COEF22_MASK 0x3FF ++#define ISPRSZ_HFILT2322_COEF23_SHIFT 16 ++#define ISPRSZ_HFILT2322_COEF23_MASK 0x3FF0000 ++ ++#define ISPRSZ_HFILT2524_COEF24_SHIFT 0 ++#define ISPRSZ_HFILT2524_COEF24_MASK 0x3FF ++#define ISPRSZ_HFILT2524_COEF25_SHIFT 16 ++#define ISPRSZ_HFILT2524_COEF25_MASK 0x3FF0000 ++ ++#define ISPRSZ_HFILT2726_COEF26_SHIFT 0 ++#define ISPRSZ_HFILT2726_COEF26_MASK 0x3FF ++#define ISPRSZ_HFILT2726_COEF27_SHIFT 16 ++#define ISPRSZ_HFILT2726_COEF27_MASK 0x3FF0000 ++ ++#define ISPRSZ_HFILT2928_COEF28_SHIFT 0 ++#define ISPRSZ_HFILT2928_COEF28_MASK 0x3FF ++#define ISPRSZ_HFILT2928_COEF29_SHIFT 16 ++#define ISPRSZ_HFILT2928_COEF29_MASK 0x3FF0000 ++ ++#define ISPRSZ_HFILT3130_COEF30_SHIFT 0 ++#define ISPRSZ_HFILT3130_COEF30_MASK 0x3FF ++#define ISPRSZ_HFILT3130_COEF31_SHIFT 16 ++#define ISPRSZ_HFILT3130_COEF31_MASK 0x3FF0000 ++ ++#define ISPRSZ_VFILT_COEF0_SHIFT 0 ++#define ISPRSZ_VFILT_COEF0_MASK \ ++ (0x3FF << ISPRSZ_VFILT_COEF0_SHIFT) ++#define ISPRSZ_VFILT_COEF1_SHIFT 16 ++#define ISPRSZ_VFILT_COEF1_MASK \ ++ (0x3FF << ISPRSZ_VFILT_COEF1_SHIFT) ++ ++#define ISPRSZ_VFILT10_COEF0_SHIFT 0 ++#define ISPRSZ_VFILT10_COEF0_MASK 0x3FF ++#define ISPRSZ_VFILT10_COEF1_SHIFT 16 ++#define ISPRSZ_VFILT10_COEF1_MASK 0x3FF0000 ++ ++#define ISPRSZ_VFILT32_COEF2_SHIFT 0 ++#define ISPRSZ_VFILT32_COEF2_MASK 0x3FF ++#define ISPRSZ_VFILT32_COEF3_SHIFT 16 ++#define ISPRSZ_VFILT32_COEF3_MASK 0x3FF0000 ++ ++#define ISPRSZ_VFILT54_COEF4_SHIFT 0 ++#define ISPRSZ_VFILT54_COEF4_MASK 0x3FF ++#define ISPRSZ_VFILT54_COEF5_SHIFT 16 ++#define ISPRSZ_VFILT54_COEF5_MASK 0x3FF0000 ++ ++#define ISPRSZ_VFILT76_COEFF6_SHIFT 0 ++#define ISPRSZ_VFILT76_COEFF6_MASK 0x3FF ++#define ISPRSZ_VFILT76_COEFF7_SHIFT 16 ++#define ISPRSZ_VFILT76_COEFF7_MASK 0x3FF0000 ++ ++#define ISPRSZ_VFILT98_COEFF8_SHIFT 0 ++#define ISPRSZ_VFILT98_COEFF8_MASK 0x3FF ++#define ISPRSZ_VFILT98_COEFF9_SHIFT 16 ++#define ISPRSZ_VFILT98_COEFF9_MASK 0x3FF0000 ++ ++#define ISPRSZ_VFILT1110_COEF10_SHIFT 0 ++#define ISPRSZ_VFILT1110_COEF10_MASK 0x3FF ++#define ISPRSZ_VFILT1110_COEF11_SHIFT 16 ++#define ISPRSZ_VFILT1110_COEF11_MASK 0x3FF0000 ++ ++#define ISPRSZ_VFILT1312_COEFF12_SHIFT 0 ++#define ISPRSZ_VFILT1312_COEFF12_MASK 0x3FF ++#define ISPRSZ_VFILT1312_COEFF13_SHIFT 16 ++#define ISPRSZ_VFILT1312_COEFF13_MASK 0x3FF0000 ++ ++#define ISPRSZ_VFILT1514_COEFF14_SHIFT 0 ++#define ISPRSZ_VFILT1514_COEFF14_MASK 0x3FF ++#define ISPRSZ_VFILT1514_COEFF15_SHIFT 16 ++#define ISPRSZ_VFILT1514_COEFF15_MASK 0x3FF0000 ++ ++#define ISPRSZ_VFILT1716_COEF16_SHIFT 0 ++#define ISPRSZ_VFILT1716_COEF16_MASK 0x3FF ++#define ISPRSZ_VFILT1716_COEF17_SHIFT 16 ++#define ISPRSZ_VFILT1716_COEF17_MASK 0x3FF0000 ++ ++#define ISPRSZ_VFILT1918_COEF18_SHIFT 0 ++#define ISPRSZ_VFILT1918_COEF18_MASK 0x3FF ++#define ISPRSZ_VFILT1918_COEF19_SHIFT 16 ++#define ISPRSZ_VFILT1918_COEF19_MASK 0x3FF0000 ++ ++#define ISPRSZ_VFILT2120_COEF20_SHIFT 0 ++#define ISPRSZ_VFILT2120_COEF20_MASK 0x3FF ++#define ISPRSZ_VFILT2120_COEF21_SHIFT 16 ++#define ISPRSZ_VFILT2120_COEF21_MASK 0x3FF0000 ++ ++#define ISPRSZ_VFILT2322_COEF22_SHIFT 0 ++#define ISPRSZ_VFILT2322_COEF22_MASK 0x3FF ++#define ISPRSZ_VFILT2322_COEF23_SHIFT 16 ++#define ISPRSZ_VFILT2322_COEF23_MASK 0x3FF0000 ++ ++#define ISPRSZ_VFILT2524_COEF24_SHIFT 0 ++#define ISPRSZ_VFILT2524_COEF24_MASK 0x3FF ++#define ISPRSZ_VFILT2524_COEF25_SHIFT 16 ++#define ISPRSZ_VFILT2524_COEF25_MASK 0x3FF0000 ++ ++#define ISPRSZ_VFILT2726_COEF26_SHIFT 0 ++#define ISPRSZ_VFILT2726_COEF26_MASK 0x3FF ++#define ISPRSZ_VFILT2726_COEF27_SHIFT 16 ++#define ISPRSZ_VFILT2726_COEF27_MASK 0x3FF0000 ++ ++#define ISPRSZ_VFILT2928_COEF28_SHIFT 0 ++#define ISPRSZ_VFILT2928_COEF28_MASK 0x3FF ++#define ISPRSZ_VFILT2928_COEF29_SHIFT 16 ++#define ISPRSZ_VFILT2928_COEF29_MASK 0x3FF0000 ++ ++#define ISPRSZ_VFILT3130_COEF30_SHIFT 0 ++#define ISPRSZ_VFILT3130_COEF30_MASK 0x3FF ++#define ISPRSZ_VFILT3130_COEF31_SHIFT 16 ++#define ISPRSZ_VFILT3130_COEF31_MASK 0x3FF0000 ++ ++#define ISPRSZ_YENH_CORE_SHIFT 0 ++#define ISPRSZ_YENH_CORE_MASK \ ++ (0xFF << ISPRSZ_YENH_CORE_SHIFT) ++#define ISPRSZ_YENH_SLOP_SHIFT 8 ++#define ISPRSZ_YENH_SLOP_MASK \ ++ (0xF << ISPRSZ_YENH_SLOP_SHIFT) ++#define ISPRSZ_YENH_GAIN_SHIFT 12 ++#define ISPRSZ_YENH_GAIN_MASK \ ++ (0xF << ISPRSZ_YENH_GAIN_SHIFT) ++#define ISPRSZ_YENH_ALGO_SHIFT 16 ++#define ISPRSZ_YENH_ALGO_MASK \ ++ (0x3 << ISPRSZ_YENH_ALGO_SHIFT) ++ ++#define ISPH3A_PCR_AEW_ALAW_EN_SHIFT 1 ++#define ISPH3A_PCR_AF_MED_TH_SHIFT 3 ++#define ISPH3A_PCR_AF_RGBPOS_SHIFT 11 ++#define ISPH3A_PCR_AEW_AVE2LMT_SHIFT 22 ++#define ISPH3A_PCR_AEW_AVE2LMT_MASK 0xFFC00000 ++#define ISPH3A_PCR_BUSYAF (1 << 15) ++#define ISPH3A_PCR_BUSYAEAWB (1 << 18) ++ ++#define ISPH3A_AEWWIN1_WINHC_SHIFT 0 ++#define ISPH3A_AEWWIN1_WINHC_MASK 0x3F ++#define ISPH3A_AEWWIN1_WINVC_SHIFT 6 ++#define ISPH3A_AEWWIN1_WINVC_MASK 0x1FC0 ++#define ISPH3A_AEWWIN1_WINW_SHIFT 13 ++#define ISPH3A_AEWWIN1_WINW_MASK 0xFE000 ++#define ISPH3A_AEWWIN1_WINH_SHIFT 24 ++#define ISPH3A_AEWWIN1_WINH_MASK 0x7F000000 ++ ++#define ISPH3A_AEWINSTART_WINSH_SHIFT 0 ++#define ISPH3A_AEWINSTART_WINSH_MASK 0x0FFF ++#define ISPH3A_AEWINSTART_WINSV_SHIFT 16 ++#define ISPH3A_AEWINSTART_WINSV_MASK 0x0FFF0000 ++ ++#define ISPH3A_AEWINBLK_WINH_SHIFT 0 ++#define ISPH3A_AEWINBLK_WINH_MASK 0x7F ++#define ISPH3A_AEWINBLK_WINSV_SHIFT 16 ++#define ISPH3A_AEWINBLK_WINSV_MASK 0x0FFF0000 ++ ++#define ISPH3A_AEWSUBWIN_AEWINCH_SHIFT 0 ++#define ISPH3A_AEWSUBWIN_AEWINCH_MASK 0x0F ++#define ISPH3A_AEWSUBWIN_AEWINCV_SHIFT 8 ++#define ISPH3A_AEWSUBWIN_AEWINCV_MASK 0x0F00 ++ ++#define ISPHIST_PCR_ENABLE_SHIFT 0 ++#define ISPHIST_PCR_ENABLE_MASK 0x01 ++#define ISPHIST_PCR_ENABLE (1 << ISPHIST_PCR_ENABLE_SHIFT) ++#define ISPHIST_PCR_BUSY 0x02 ++ ++#define ISPHIST_CNT_DATASIZE_SHIFT 8 ++#define ISPHIST_CNT_DATASIZE_MASK 0x0100 ++#define ISPHIST_CNT_CLEAR_SHIFT 7 ++#define ISPHIST_CNT_CLEAR_MASK 0x080 ++#define ISPHIST_CNT_CLEAR (1 << ISPHIST_CNT_CLEAR_SHIFT) ++#define ISPHIST_CNT_CFA_SHIFT 6 ++#define ISPHIST_CNT_CFA_MASK 0x040 ++#define ISPHIST_CNT_BINS_SHIFT 4 ++#define ISPHIST_CNT_BINS_MASK 0x030 ++#define ISPHIST_CNT_SOURCE_SHIFT 3 ++#define ISPHIST_CNT_SOURCE_MASK 0x08 ++#define ISPHIST_CNT_SHIFT_SHIFT 0 ++#define ISPHIST_CNT_SHIFT_MASK 0x07 ++ ++#define ISPHIST_WB_GAIN_WG00_SHIFT 24 ++#define ISPHIST_WB_GAIN_WG00_MASK 0xFF000000 ++#define ISPHIST_WB_GAIN_WG01_SHIFT 16 ++#define ISPHIST_WB_GAIN_WG01_MASK 0xFF0000 ++#define ISPHIST_WB_GAIN_WG02_SHIFT 8 ++#define ISPHIST_WB_GAIN_WG02_MASK 0xFF00 ++#define ISPHIST_WB_GAIN_WG03_SHIFT 0 ++#define ISPHIST_WB_GAIN_WG03_MASK 0xFF ++ ++#define ISPHIST_REG_START_END_MASK 0x3FFF ++#define ISPHIST_REG_START_SHIFT 16 ++#define ISPHIST_REG_END_SHIFT 0 ++#define ISPHIST_REG_START_MASK (ISPHIST_REG_START_END_MASK << \ ++ ISPHIST_REG_START_SHIFT) ++#define ISPHIST_REG_END_MASK (ISPHIST_REG_START_END_MASK << \ ++ ISPHIST_REG_END_SHIFT) ++ ++#define ISPHIST_REG_MASK (ISPHIST_REG_START_MASK | \ ++ ISPHIST_REG_END_MASK) ++ ++#define ISPHIST_ADDR_SHIFT 0 ++#define ISPHIST_ADDR_MASK 0x3FF ++ ++#define ISPHIST_DATA_SHIFT 0 ++#define ISPHIST_DATA_MASK 0xFFFFF ++ ++#define ISPHIST_RADD_SHIFT 0 ++#define ISPHIST_RADD_MASK 0xFFFFFFFF ++ ++#define ISPHIST_RADD_OFF_SHIFT 0 ++#define ISPHIST_RADD_OFF_MASK 0xFFFF ++ ++#define ISPHIST_HV_INFO_HSIZE_SHIFT 16 ++#define ISPHIST_HV_INFO_HSIZE_MASK 0x3FFF0000 ++#define ISPHIST_HV_INFO_VSIZE_SHIFT 0 ++#define ISPHIST_HV_INFO_VSIZE_MASK 0x3FFF ++ ++#define ISPHIST_HV_INFO_MASK 0x3FFF3FFF ++ ++#define ISPCCDC_LSC_ENABLE 1 ++#define ISPCCDC_LSC_BUSY (1 << 7) ++#define ISPCCDC_LSC_GAIN_MODE_N_MASK 0x700 ++#define ISPCCDC_LSC_GAIN_MODE_N_SHIFT 8 ++#define ISPCCDC_LSC_GAIN_MODE_M_MASK 0x3800 ++#define ISPCCDC_LSC_GAIN_MODE_M_SHIFT 12 ++#define ISPCCDC_LSC_GAIN_FORMAT_MASK 0xE ++#define ISPCCDC_LSC_GAIN_FORMAT_SHIFT 1 ++#define ISPCCDC_LSC_AFTER_REFORMATTER_MASK (1<<6) ++ ++#define ISPCCDC_LSC_INITIAL_X_MASK 0x3F ++#define ISPCCDC_LSC_INITIAL_X_SHIFT 0 ++#define ISPCCDC_LSC_INITIAL_Y_MASK 0x3F0000 ++#define ISPCCDC_LSC_INITIAL_Y_SHIFT 16 ++ ++/* ----------------------------------------------------------------------------- ++ * CSI2 receiver registers (ES2.0) ++ */ ++ ++#define ISPCSI2_REVISION (0x000) ++#define ISPCSI2_SYSCONFIG (0x010) ++#define ISPCSI2_SYSCONFIG_MSTANDBY_MODE_SHIFT 12 ++#define ISPCSI2_SYSCONFIG_MSTANDBY_MODE_MASK \ ++ (0x3 << ISPCSI2_SYSCONFIG_MSTANDBY_MODE_SHIFT) ++#define ISPCSI2_SYSCONFIG_MSTANDBY_MODE_FORCE \ ++ (0x0 << ISPCSI2_SYSCONFIG_MSTANDBY_MODE_SHIFT) ++#define ISPCSI2_SYSCONFIG_MSTANDBY_MODE_NO \ ++ (0x1 << ISPCSI2_SYSCONFIG_MSTANDBY_MODE_SHIFT) ++#define ISPCSI2_SYSCONFIG_MSTANDBY_MODE_SMART \ ++ (0x2 << ISPCSI2_SYSCONFIG_MSTANDBY_MODE_SHIFT) ++#define ISPCSI2_SYSCONFIG_SOFT_RESET (1 << 1) ++#define ISPCSI2_SYSCONFIG_AUTO_IDLE (1 << 0) ++ ++#define ISPCSI2_SYSSTATUS (0x014) ++#define ISPCSI2_SYSSTATUS_RESET_DONE (1 << 0) ++ ++#define ISPCSI2_IRQSTATUS (0x018) ++#define ISPCSI2_IRQSTATUS_OCP_ERR_IRQ (1 << 14) ++#define ISPCSI2_IRQSTATUS_SHORT_PACKET_IRQ (1 << 13) ++#define ISPCSI2_IRQSTATUS_ECC_CORRECTION_IRQ (1 << 12) ++#define ISPCSI2_IRQSTATUS_ECC_NO_CORRECTION_IRQ (1 << 11) ++#define ISPCSI2_IRQSTATUS_COMPLEXIO2_ERR_IRQ (1 << 10) ++#define ISPCSI2_IRQSTATUS_COMPLEXIO1_ERR_IRQ (1 << 9) ++#define ISPCSI2_IRQSTATUS_FIFO_OVF_IRQ (1 << 8) ++#define ISPCSI2_IRQSTATUS_CONTEXT(n) (1 << (n)) ++ ++#define ISPCSI2_IRQENABLE (0x01c) ++#define ISPCSI2_CTRL (0x040) ++#define ISPCSI2_CTRL_VP_CLK_EN (1 << 15) ++#define ISPCSI2_CTRL_VP_ONLY_EN (1 << 11) ++#define ISPCSI2_CTRL_VP_OUT_CTRL_SHIFT 8 ++#define ISPCSI2_CTRL_VP_OUT_CTRL_MASK \ ++ (3 << ISPCSI2_CTRL_VP_OUT_CTRL_SHIFT) ++#define ISPCSI2_CTRL_DBG_EN (1 << 7) ++#define ISPCSI2_CTRL_BURST_SIZE_SHIFT 5 ++#define ISPCSI2_CTRL_BURST_SIZE_MASK \ ++ (3 << ISPCSI2_CTRL_BURST_SIZE_SHIFT) ++#define ISPCSI2_CTRL_FRAME (1 << 3) ++#define ISPCSI2_CTRL_ECC_EN (1 << 2) ++#define ISPCSI2_CTRL_SECURE (1 << 1) ++#define ISPCSI2_CTRL_IF_EN (1 << 0) ++ ++#define ISPCSI2_DBG_H (0x044) ++#define ISPCSI2_GNQ (0x048) ++#define ISPCSI2_PHY_CFG (0x050) ++#define ISPCSI2_PHY_CFG_RESET_CTRL (1 << 30) ++#define ISPCSI2_PHY_CFG_RESET_DONE (1 << 29) ++#define ISPCSI2_PHY_CFG_PWR_CMD_SHIFT 27 ++#define ISPCSI2_PHY_CFG_PWR_CMD_MASK \ ++ (0x3 << ISPCSI2_PHY_CFG_PWR_CMD_SHIFT) ++#define ISPCSI2_PHY_CFG_PWR_CMD_OFF \ ++ (0x0 << ISPCSI2_PHY_CFG_PWR_CMD_SHIFT) ++#define ISPCSI2_PHY_CFG_PWR_CMD_ON \ ++ (0x1 << ISPCSI2_PHY_CFG_PWR_CMD_SHIFT) ++#define ISPCSI2_PHY_CFG_PWR_CMD_ULPW \ ++ (0x2 << ISPCSI2_PHY_CFG_PWR_CMD_SHIFT) ++#define ISPCSI2_PHY_CFG_PWR_STATUS_SHIFT 25 ++#define ISPCSI2_PHY_CFG_PWR_STATUS_MASK \ ++ (0x3 << ISPCSI2_PHY_CFG_PWR_STATUS_SHIFT) ++#define ISPCSI2_PHY_CFG_PWR_STATUS_OFF \ ++ (0x0 << ISPCSI2_PHY_CFG_PWR_STATUS_SHIFT) ++#define ISPCSI2_PHY_CFG_PWR_STATUS_ON \ ++ (0x1 << ISPCSI2_PHY_CFG_PWR_STATUS_SHIFT) ++#define ISPCSI2_PHY_CFG_PWR_STATUS_ULPW \ ++ (0x2 << ISPCSI2_PHY_CFG_PWR_STATUS_SHIFT) ++#define ISPCSI2_PHY_CFG_PWR_AUTO (1 << 24) ++ ++#define ISPCSI2_PHY_CFG_DATA_POL_SHIFT(n) (3 + ((n) * 4)) ++#define ISPCSI2_PHY_CFG_DATA_POL_MASK(n) \ ++ (0x1 << ISPCSI2_PHY_CFG_DATA_POL_SHIFT(n)) ++#define ISPCSI2_PHY_CFG_DATA_POL_PN(n) \ ++ (0x0 << ISPCSI2_PHY_CFG_DATA_POL_SHIFT(n)) ++#define ISPCSI2_PHY_CFG_DATA_POL_NP(n) \ ++ (0x1 << ISPCSI2_PHY_CFG_DATA_POL_SHIFT(n)) ++ ++#define ISPCSI2_PHY_CFG_DATA_POSITION_SHIFT(n) ((n) * 4) ++#define ISPCSI2_PHY_CFG_DATA_POSITION_MASK(n) \ ++ (0x7 << ISPCSI2_PHY_CFG_DATA_POSITION_SHIFT(n)) ++#define ISPCSI2_PHY_CFG_DATA_POSITION_NC(n) \ ++ (0x0 << ISPCSI2_PHY_CFG_DATA_POSITION_SHIFT(n)) ++#define ISPCSI2_PHY_CFG_DATA_POSITION_1(n) \ ++ (0x1 << ISPCSI2_PHY_CFG_DATA_POSITION_SHIFT(n)) ++#define ISPCSI2_PHY_CFG_DATA_POSITION_2(n) \ ++ (0x2 << ISPCSI2_PHY_CFG_DATA_POSITION_SHIFT(n)) ++#define ISPCSI2_PHY_CFG_DATA_POSITION_3(n) \ ++ (0x3 << ISPCSI2_PHY_CFG_DATA_POSITION_SHIFT(n)) ++#define ISPCSI2_PHY_CFG_DATA_POSITION_4(n) \ ++ (0x4 << ISPCSI2_PHY_CFG_DATA_POSITION_SHIFT(n)) ++#define ISPCSI2_PHY_CFG_DATA_POSITION_5(n) \ ++ (0x5 << ISPCSI2_PHY_CFG_DATA_POSITION_SHIFT(n)) ++ ++#define ISPCSI2_PHY_CFG_CLOCK_POL_SHIFT 3 ++#define ISPCSI2_PHY_CFG_CLOCK_POL_MASK \ ++ (0x1 << ISPCSI2_PHY_CFG_CLOCK_POL_SHIFT) ++#define ISPCSI2_PHY_CFG_CLOCK_POL_PN \ ++ (0x0 << ISPCSI2_PHY_CFG_CLOCK_POL_SHIFT) ++#define ISPCSI2_PHY_CFG_CLOCK_POL_NP \ ++ (0x1 << ISPCSI2_PHY_CFG_CLOCK_POL_SHIFT) ++ ++#define ISPCSI2_PHY_CFG_CLOCK_POSITION_SHIFT 0 ++#define ISPCSI2_PHY_CFG_CLOCK_POSITION_MASK \ ++ (0x7 << ISPCSI2_PHY_CFG_CLOCK_POSITION_SHIFT) ++#define ISPCSI2_PHY_CFG_CLOCK_POSITION_1 \ ++ (0x1 << ISPCSI2_PHY_CFG_CLOCK_POSITION_SHIFT) ++#define ISPCSI2_PHY_CFG_CLOCK_POSITION_2 \ ++ (0x2 << ISPCSI2_PHY_CFG_CLOCK_POSITION_SHIFT) ++#define ISPCSI2_PHY_CFG_CLOCK_POSITION_3 \ ++ (0x3 << ISPCSI2_PHY_CFG_CLOCK_POSITION_SHIFT) ++#define ISPCSI2_PHY_CFG_CLOCK_POSITION_4 \ ++ (0x4 << ISPCSI2_PHY_CFG_CLOCK_POSITION_SHIFT) ++#define ISPCSI2_PHY_CFG_CLOCK_POSITION_5 \ ++ (0x5 << ISPCSI2_PHY_CFG_CLOCK_POSITION_SHIFT) ++ ++#define ISPCSI2_PHY_IRQSTATUS (0x054) ++#define ISPCSI2_PHY_IRQSTATUS_STATEALLULPMEXIT (1 << 26) ++#define ISPCSI2_PHY_IRQSTATUS_STATEALLULPMENTER (1 << 25) ++#define ISPCSI2_PHY_IRQSTATUS_STATEULPM5 (1 << 24) ++#define ISPCSI2_PHY_IRQSTATUS_STATEULPM4 (1 << 23) ++#define ISPCSI2_PHY_IRQSTATUS_STATEULPM3 (1 << 22) ++#define ISPCSI2_PHY_IRQSTATUS_STATEULPM2 (1 << 21) ++#define ISPCSI2_PHY_IRQSTATUS_STATEULPM1 (1 << 20) ++#define ISPCSI2_PHY_IRQSTATUS_ERRCONTROL5 (1 << 19) ++#define ISPCSI2_PHY_IRQSTATUS_ERRCONTROL4 (1 << 18) ++#define ISPCSI2_PHY_IRQSTATUS_ERRCONTROL3 (1 << 17) ++#define ISPCSI2_PHY_IRQSTATUS_ERRCONTROL2 (1 << 16) ++#define ISPCSI2_PHY_IRQSTATUS_ERRCONTROL1 (1 << 15) ++#define ISPCSI2_PHY_IRQSTATUS_ERRESC5 (1 << 14) ++#define ISPCSI2_PHY_IRQSTATUS_ERRESC4 (1 << 13) ++#define ISPCSI2_PHY_IRQSTATUS_ERRESC3 (1 << 12) ++#define ISPCSI2_PHY_IRQSTATUS_ERRESC2 (1 << 11) ++#define ISPCSI2_PHY_IRQSTATUS_ERRESC1 (1 << 10) ++#define ISPCSI2_PHY_IRQSTATUS_ERRSOTSYNCHS5 (1 << 9) ++#define ISPCSI2_PHY_IRQSTATUS_ERRSOTSYNCHS4 (1 << 8) ++#define ISPCSI2_PHY_IRQSTATUS_ERRSOTSYNCHS3 (1 << 7) ++#define ISPCSI2_PHY_IRQSTATUS_ERRSOTSYNCHS2 (1 << 6) ++#define ISPCSI2_PHY_IRQSTATUS_ERRSOTSYNCHS1 (1 << 5) ++#define ISPCSI2_PHY_IRQSTATUS_ERRSOTHS5 (1 << 4) ++#define ISPCSI2_PHY_IRQSTATUS_ERRSOTHS4 (1 << 3) ++#define ISPCSI2_PHY_IRQSTATUS_ERRSOTHS3 (1 << 2) ++#define ISPCSI2_PHY_IRQSTATUS_ERRSOTHS2 (1 << 1) ++#define ISPCSI2_PHY_IRQSTATUS_ERRSOTHS1 1 ++ ++#define ISPCSI2_SHORT_PACKET (0x05c) ++#define ISPCSI2_PHY_IRQENABLE (0x060) ++#define ISPCSI2_PHY_IRQENABLE_STATEALLULPMEXIT (1 << 26) ++#define ISPCSI2_PHY_IRQENABLE_STATEALLULPMENTER (1 << 25) ++#define ISPCSI2_PHY_IRQENABLE_STATEULPM5 (1 << 24) ++#define ISPCSI2_PHY_IRQENABLE_STATEULPM4 (1 << 23) ++#define ISPCSI2_PHY_IRQENABLE_STATEULPM3 (1 << 22) ++#define ISPCSI2_PHY_IRQENABLE_STATEULPM2 (1 << 21) ++#define ISPCSI2_PHY_IRQENABLE_STATEULPM1 (1 << 20) ++#define ISPCSI2_PHY_IRQENABLE_ERRCONTROL5 (1 << 19) ++#define ISPCSI2_PHY_IRQENABLE_ERRCONTROL4 (1 << 18) ++#define ISPCSI2_PHY_IRQENABLE_ERRCONTROL3 (1 << 17) ++#define ISPCSI2_PHY_IRQENABLE_ERRCONTROL2 (1 << 16) ++#define ISPCSI2_PHY_IRQENABLE_ERRCONTROL1 (1 << 15) ++#define ISPCSI2_PHY_IRQENABLE_ERRESC5 (1 << 14) ++#define ISPCSI2_PHY_IRQENABLE_ERRESC4 (1 << 13) ++#define ISPCSI2_PHY_IRQENABLE_ERRESC3 (1 << 12) ++#define ISPCSI2_PHY_IRQENABLE_ERRESC2 (1 << 11) ++#define ISPCSI2_PHY_IRQENABLE_ERRESC1 (1 << 10) ++#define ISPCSI2_PHY_IRQENABLE_ERRSOTSYNCHS5 (1 << 9) ++#define ISPCSI2_PHY_IRQENABLE_ERRSOTSYNCHS4 (1 << 8) ++#define ISPCSI2_PHY_IRQENABLE_ERRSOTSYNCHS3 (1 << 7) ++#define ISPCSI2_PHY_IRQENABLE_ERRSOTSYNCHS2 (1 << 6) ++#define ISPCSI2_PHY_IRQENABLE_ERRSOTSYNCHS1 (1 << 5) ++#define ISPCSI2_PHY_IRQENABLE_ERRSOTHS5 (1 << 4) ++#define ISPCSI2_PHY_IRQENABLE_ERRSOTHS4 (1 << 3) ++#define ISPCSI2_PHY_IRQENABLE_ERRSOTHS3 (1 << 2) ++#define ISPCSI2_PHY_IRQENABLE_ERRSOTHS2 (1 << 1) ++#define ISPCSI2_PHY_IRQENABLE_ERRSOTHS1 (1 << 0) ++ ++#define ISPCSI2_DBG_P (0x068) ++#define ISPCSI2_TIMING (0x06c) ++#define ISPCSI2_TIMING_FORCE_RX_MODE_IO(n) (1 << ((16 * ((n) - 1)) + 15)) ++#define ISPCSI2_TIMING_STOP_STATE_X16_IO(n) (1 << ((16 * ((n) - 1)) + 14)) ++#define ISPCSI2_TIMING_STOP_STATE_X4_IO(n) (1 << ((16 * ((n) - 1)) + 13)) ++#define ISPCSI2_TIMING_STOP_STATE_COUNTER_IO_SHIFT(n) (16 * ((n) - 1)) ++#define ISPCSI2_TIMING_STOP_STATE_COUNTER_IO_MASK(n) \ ++ (0x1fff << ISPCSI2_TIMING_STOP_STATE_COUNTER_IO_SHIFT(n)) ++ ++#define ISPCSI2_CTX_CTRL1(n) ((0x070) + 0x20 * (n)) ++#define ISPCSI2_CTX_CTRL1_COUNT_SHIFT 8 ++#define ISPCSI2_CTX_CTRL1_COUNT_MASK \ ++ (0xff << ISPCSI2_CTX_CTRL1_COUNT_SHIFT) ++#define ISPCSI2_CTX_CTRL1_EOF_EN (1 << 7) ++#define ISPCSI2_CTX_CTRL1_EOL_EN (1 << 6) ++#define ISPCSI2_CTX_CTRL1_CS_EN (1 << 5) ++#define ISPCSI2_CTX_CTRL1_COUNT_UNLOCK (1 << 4) ++#define ISPCSI2_CTX_CTRL1_PING_PONG (1 << 3) ++#define ISPCSI2_CTX_CTRL1_CTX_EN (1 << 0) ++ ++#define ISPCSI2_CTX_CTRL2(n) ((0x074) + 0x20 * (n)) ++#define ISPCSI2_CTX_CTRL2_USER_DEF_MAP_SHIFT 13 ++#define ISPCSI2_CTX_CTRL2_USER_DEF_MAP_MASK \ ++ (0x3 << ISPCSI2_CTX_CTRL2_USER_DEF_MAP_SHIFT) ++#define ISPCSI2_CTX_CTRL2_VIRTUAL_ID_SHIFT 11 ++#define ISPCSI2_CTX_CTRL2_VIRTUAL_ID_MASK \ ++ (0x3 << ISPCSI2_CTX_CTRL2_VIRTUAL_ID_SHIFT) ++#define ISPCSI2_CTX_CTRL2_DPCM_PRED (1 << 10) ++#define ISPCSI2_CTX_CTRL2_FORMAT_SHIFT 0 ++#define ISPCSI2_CTX_CTRL2_FORMAT_MASK \ ++ (0x3ff << ISPCSI2_CTX_CTRL2_FORMAT_SHIFT) ++#define ISPCSI2_CTX_CTRL2_FRAME_SHIFT 16 ++#define ISPCSI2_CTX_CTRL2_FRAME_MASK \ ++ (0xffff << ISPCSI2_CTX_CTRL2_FRAME_SHIFT) ++ ++#define ISPCSI2_CTX_DAT_OFST(n) ((0x078) + 0x20 * (n)) ++#define ISPCSI2_CTX_DAT_OFST_OFST_SHIFT 0 ++#define ISPCSI2_CTX_DAT_OFST_OFST_MASK \ ++ (0x1ffe0 << ISPCSI2_CTX_DAT_OFST_OFST_SHIFT) ++ ++#define ISPCSI2_CTX_DAT_PING_ADDR(n) ((0x07c) + 0x20 * (n)) ++#define ISPCSI2_CTX_DAT_PONG_ADDR(n) ((0x080) + 0x20 * (n)) ++#define ISPCSI2_CTX_IRQENABLE(n) ((0x084) + 0x20 * (n)) ++#define ISPCSI2_CTX_IRQENABLE_ECC_CORRECTION_IRQ (1 << 8) ++#define ISPCSI2_CTX_IRQENABLE_LINE_NUMBER_IRQ (1 << 7) ++#define ISPCSI2_CTX_IRQENABLE_FRAME_NUMBER_IRQ (1 << 6) ++#define ISPCSI2_CTX_IRQENABLE_CS_IRQ (1 << 5) ++#define ISPCSI2_CTX_IRQENABLE_LE_IRQ (1 << 3) ++#define ISPCSI2_CTX_IRQENABLE_LS_IRQ (1 << 2) ++#define ISPCSI2_CTX_IRQENABLE_FE_IRQ (1 << 1) ++#define ISPCSI2_CTX_IRQENABLE_FS_IRQ (1 << 0) ++ ++#define ISPCSI2_CTX_IRQSTATUS(n) ((0x088) + 0x20 * (n)) ++#define ISPCSI2_CTX_IRQSTATUS_ECC_CORRECTION_IRQ (1 << 8) ++#define ISPCSI2_CTX_IRQSTATUS_LINE_NUMBER_IRQ (1 << 7) ++#define ISPCSI2_CTX_IRQSTATUS_FRAME_NUMBER_IRQ (1 << 6) ++#define ISPCSI2_CTX_IRQSTATUS_CS_IRQ (1 << 5) ++#define ISPCSI2_CTX_IRQSTATUS_LE_IRQ (1 << 3) ++#define ISPCSI2_CTX_IRQSTATUS_LS_IRQ (1 << 2) ++#define ISPCSI2_CTX_IRQSTATUS_FE_IRQ (1 << 1) ++#define ISPCSI2_CTX_IRQSTATUS_FS_IRQ (1 << 0) ++ ++#define ISPCSI2_CTX_CTRL3(n) ((0x08c) + 0x20 * (n)) ++#define ISPCSI2_CTX_CTRL3_ALPHA_SHIFT 5 ++#define ISPCSI2_CTX_CTRL3_ALPHA_MASK \ ++ (0x3fff << ISPCSI2_CTX_CTRL3_ALPHA_SHIFT) ++ ++/* This instance is for OMAP3630 only */ ++#define ISPCSI2_CTX_TRANSCODEH(n) (0x000 + 0x8 * (n)) ++#define ISPCSI2_CTX_TRANSCODEH_HCOUNT_SHIFT 16 ++#define ISPCSI2_CTX_TRANSCODEH_HCOUNT_MASK \ ++ (0x1fff << ISPCSI2_CTX_TRANSCODEH_HCOUNT_SHIFT) ++#define ISPCSI2_CTX_TRANSCODEH_HSKIP_SHIFT 0 ++#define ISPCSI2_CTX_TRANSCODEH_HSKIP_MASK \ ++ (0x1fff << ISPCSI2_CTX_TRANSCODEH_HCOUNT_SHIFT) ++#define ISPCSI2_CTX_TRANSCODEV(n) (0x004 + 0x8 * (n)) ++#define ISPCSI2_CTX_TRANSCODEV_VCOUNT_SHIFT 16 ++#define ISPCSI2_CTX_TRANSCODEV_VCOUNT_MASK \ ++ (0x1fff << ISPCSI2_CTX_TRANSCODEV_VCOUNT_SHIFT) ++#define ISPCSI2_CTX_TRANSCODEV_VSKIP_SHIFT 0 ++#define ISPCSI2_CTX_TRANSCODEV_VSKIP_MASK \ ++ (0x1fff << ISPCSI2_CTX_TRANSCODEV_VCOUNT_SHIFT) ++ ++/* ----------------------------------------------------------------------------- ++ * CSI PHY registers ++ */ ++ ++#define ISPCSIPHY_REG0 (0x000) ++#define ISPCSIPHY_REG0_THS_TERM_SHIFT 8 ++#define ISPCSIPHY_REG0_THS_TERM_MASK \ ++ (0xff << ISPCSIPHY_REG0_THS_TERM_SHIFT) ++#define ISPCSIPHY_REG0_THS_SETTLE_SHIFT 0 ++#define ISPCSIPHY_REG0_THS_SETTLE_MASK \ ++ (0xff << ISPCSIPHY_REG0_THS_SETTLE_SHIFT) ++ ++#define ISPCSIPHY_REG1 (0x004) ++#define ISPCSIPHY_REG1_RESET_DONE_CTRLCLK (1 << 29) ++/* This field is for OMAP3630 only */ ++#define ISPCSIPHY_REG1_CLOCK_MISS_DETECTOR_STATUS (1 << 25) ++#define ISPCSIPHY_REG1_TCLK_TERM_SHIFT 18 ++#define ISPCSIPHY_REG1_TCLK_TERM_MASK \ ++ (0x7f << ISPCSIPHY_REG1_TCLK_TERM_SHIFT) ++#define ISPCSIPHY_REG1_DPHY_HS_SYNC_PATTERN_SHIFT 10 ++#define ISPCSIPHY_REG1_DPHY_HS_SYNC_PATTERN_MASK \ ++ (0xff << ISPCSIPHY_REG1_DPHY_HS_SYNC_PATTERN) ++/* This field is for OMAP3430 only */ ++#define ISPCSIPHY_REG1_TCLK_MISS_SHIFT 8 ++#define ISPCSIPHY_REG1_TCLK_MISS_MASK \ ++ (0x3 << ISPCSIPHY_REG1_TCLK_MISS_SHIFT) ++/* This field is for OMAP3630 only */ ++#define ISPCSIPHY_REG1_CTRLCLK_DIV_FACTOR_SHIFT 8 ++#define ISPCSIPHY_REG1_CTRLCLK_DIV_FACTOR_MASK \ ++ (0x3 << ISPCSIPHY_REG1_CTRLCLK_DIV_FACTOR_SHIFT) ++#define ISPCSIPHY_REG1_TCLK_SETTLE_SHIFT 0 ++#define ISPCSIPHY_REG1_TCLK_SETTLE_MASK \ ++ (0xff << ISPCSIPHY_REG1_TCLK_SETTLE_SHIFT) ++ ++/* This register is for OMAP3630 only */ ++#define ISPCSIPHY_REG2 (0x008) ++#define ISPCSIPHY_REG2_TRIGGER_CMD_RXTRIGESC0_SHIFT 30 ++#define ISPCSIPHY_REG2_TRIGGER_CMD_RXTRIGESC0_MASK \ ++ (0x3 << ISPCSIPHY_REG2_TRIGGER_CMD_RXTRIGESC0_SHIFT) ++#define ISPCSIPHY_REG2_TRIGGER_CMD_RXTRIGESC1_SHIFT 28 ++#define ISPCSIPHY_REG2_TRIGGER_CMD_RXTRIGESC1_MASK \ ++ (0x3 << ISPCSIPHY_REG2_TRIGGER_CMD_RXTRIGESC1_SHIFT) ++#define ISPCSIPHY_REG2_TRIGGER_CMD_RXTRIGESC2_SHIFT 26 ++#define ISPCSIPHY_REG2_TRIGGER_CMD_RXTRIGESC2_MASK \ ++ (0x3 << ISPCSIPHY_REG2_TRIGGER_CMD_RXTRIGESC2_SHIFT) ++#define ISPCSIPHY_REG2_TRIGGER_CMD_RXTRIGESC3_SHIFT 24 ++#define ISPCSIPHY_REG2_TRIGGER_CMD_RXTRIGESC3_MASK \ ++ (0x3 << ISPCSIPHY_REG2_TRIGGER_CMD_RXTRIGESC3_SHIFT) ++#define ISPCSIPHY_REG2_CCP2_SYNC_PATTERN_SHIFT 0 ++#define ISPCSIPHY_REG2_CCP2_SYNC_PATTERN_MASK \ ++ (0x7fffff << ISPCSIPHY_REG2_CCP2_SYNC_PATTERN_SHIFT) ++ ++#endif /* OMAP3_ISP_REG_H */ +diff --git a/drivers/media/video/isp/ispresizer.c b/drivers/media/video/isp/ispresizer.c +new file mode 100644 +index 0000000..a696450 +--- /dev/null ++++ b/drivers/media/video/isp/ispresizer.c +@@ -0,0 +1,1710 @@ ++/* ++ * ispresizer.c ++ * ++ * TI OMAP3 ISP - Resizer module ++ * ++ * Copyright (C) 2010 Nokia Corporation ++ * Copyright (C) 2009 Texas Instruments, Inc ++ * ++ * Contacts: Laurent Pinchart ++ * Sakari Ailus ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#include ++#include ++#include ++ ++#include "isp.h" ++#include "ispreg.h" ++#include "ispresizer.h" ++ ++/* ++ * Resizer Constants ++ */ ++#define MIN_RESIZE_VALUE 64 ++#define MID_RESIZE_VALUE 512 ++#define MAX_RESIZE_VALUE 1024 ++ ++#define MIN_IN_WIDTH 32 ++#define MIN_IN_HEIGHT 32 ++#define MAX_IN_WIDTH_MEMORY_MODE 4095 ++#define MAX_IN_WIDTH_ONTHEFLY_MODE_ES1 1280 ++#define MAX_IN_WIDTH_ONTHEFLY_MODE_ES2 4095 ++#define MAX_IN_HEIGHT 4095 ++ ++#define MIN_OUT_WIDTH 16 ++#define MIN_OUT_HEIGHT 2 ++#define MAX_OUT_HEIGHT 4095 ++ ++/* ++ * Resizer Use Constraints ++ * "TRM ES3.1, table 12-46" ++ */ ++#define MAX_4TAP_OUT_WIDTH_ES1 1280 ++#define MAX_7TAP_OUT_WIDTH_ES1 640 ++#define MAX_4TAP_OUT_WIDTH_ES2 3312 ++#define MAX_7TAP_OUT_WIDTH_ES2 1650 ++#define MAX_4TAP_OUT_WIDTH_3630 4096 ++#define MAX_7TAP_OUT_WIDTH_3630 2048 ++ ++/* ++ * Constants for ratio calculation ++ */ ++#define RESIZE_DIVISOR 256 ++#define DEFAULT_PHASE 1 ++ ++/* ++ * Default (and only) configuration of filter coefficients. ++ * 7-tap mode is for scale factors 0.25x to 0.5x. ++ * 4-tap mode is for scale factors 0.5x to 4.0x. ++ * There shouldn't be any reason to recalculate these, EVER. ++ */ ++static const struct isprsz_coef filter_coefs = { ++ /* For 8-phase 4-tap horizontal filter: */ ++ { ++ 0x0000, 0x0100, 0x0000, 0x0000, ++ 0x03FA, 0x00F6, 0x0010, 0x0000, ++ 0x03F9, 0x00DB, 0x002C, 0x0000, ++ 0x03FB, 0x00B3, 0x0053, 0x03FF, ++ 0x03FD, 0x0082, 0x0084, 0x03FD, ++ 0x03FF, 0x0053, 0x00B3, 0x03FB, ++ 0x0000, 0x002C, 0x00DB, 0x03F9, ++ 0x0000, 0x0010, 0x00F6, 0x03FA ++ }, ++ /* For 8-phase 4-tap vertical filter: */ ++ { ++ 0x0000, 0x0100, 0x0000, 0x0000, ++ 0x03FA, 0x00F6, 0x0010, 0x0000, ++ 0x03F9, 0x00DB, 0x002C, 0x0000, ++ 0x03FB, 0x00B3, 0x0053, 0x03FF, ++ 0x03FD, 0x0082, 0x0084, 0x03FD, ++ 0x03FF, 0x0053, 0x00B3, 0x03FB, ++ 0x0000, 0x002C, 0x00DB, 0x03F9, ++ 0x0000, 0x0010, 0x00F6, 0x03FA ++ }, ++ /* For 4-phase 7-tap horizontal filter: */ ++ #define DUMMY 0 ++ { ++ 0x0004, 0x0023, 0x005A, 0x0058, 0x0023, 0x0004, 0x0000, DUMMY, ++ 0x0002, 0x0018, 0x004d, 0x0060, 0x0031, 0x0008, 0x0000, DUMMY, ++ 0x0001, 0x000f, 0x003f, 0x0062, 0x003f, 0x000f, 0x0001, DUMMY, ++ 0x0000, 0x0008, 0x0031, 0x0060, 0x004d, 0x0018, 0x0002, DUMMY ++ }, ++ /* For 4-phase 7-tap vertical filter: */ ++ { ++ 0x0004, 0x0023, 0x005A, 0x0058, 0x0023, 0x0004, 0x0000, DUMMY, ++ 0x0002, 0x0018, 0x004d, 0x0060, 0x0031, 0x0008, 0x0000, DUMMY, ++ 0x0001, 0x000f, 0x003f, 0x0062, 0x003f, 0x000f, 0x0001, DUMMY, ++ 0x0000, 0x0008, 0x0031, 0x0060, 0x004d, 0x0018, 0x0002, DUMMY ++ } ++ /* ++ * The dummy padding is required in 7-tap mode because of how the ++ * registers are arranged physically. ++ */ ++ #undef DUMMY ++}; ++ ++/* ++ * __resizer_get_format - helper function for getting resizer format ++ * @res : pointer to resizer private structure ++ * @pad : pad number ++ * @fh : V4L2 subdev file handle ++ * @which : wanted subdev format ++ * return zero ++ */ ++static struct v4l2_mbus_framefmt * ++__resizer_get_format(struct isp_res_device *res, struct v4l2_subdev_fh *fh, ++ unsigned int pad, enum v4l2_subdev_format_whence which) ++{ ++ if (which == V4L2_SUBDEV_FORMAT_TRY) ++ return v4l2_subdev_get_try_format(fh, pad); ++ else ++ return &res->formats[pad]; ++} ++ ++/* ++ * __resizer_get_crop - helper function for getting resizer crop rectangle ++ * @res : pointer to resizer private structure ++ * @fh : V4L2 subdev file handle ++ * @which : wanted subdev crop rectangle ++ */ ++static struct v4l2_rect * ++__resizer_get_crop(struct isp_res_device *res, struct v4l2_subdev_fh *fh, ++ enum v4l2_subdev_format_whence which) ++{ ++ if (which == V4L2_SUBDEV_FORMAT_TRY) ++ return v4l2_subdev_get_try_crop(fh, RESZ_PAD_SINK); ++ else ++ return &res->crop.request; ++} ++ ++/* ++ * resizer_set_filters - Set resizer filters ++ * @res: Device context. ++ * @h_coeff: horizontal coefficient ++ * @v_coeff: vertical coefficient ++ * Return none ++ */ ++static void resizer_set_filters(struct isp_res_device *res, const u16 *h_coeff, ++ const u16 *v_coeff) ++{ ++ struct isp_device *isp = to_isp_device(res); ++ u32 startaddr_h, startaddr_v, tmp_h, tmp_v; ++ int i; ++ ++ startaddr_h = ISPRSZ_HFILT10; ++ startaddr_v = ISPRSZ_VFILT10; ++ ++ for (i = 0; i < COEFF_CNT; i += 2) { ++ tmp_h = h_coeff[i] | ++ (h_coeff[i + 1] << ISPRSZ_HFILT_COEF1_SHIFT); ++ tmp_v = v_coeff[i] | ++ (v_coeff[i + 1] << ISPRSZ_VFILT_COEF1_SHIFT); ++ isp_reg_writel(isp, tmp_h, OMAP3_ISP_IOMEM_RESZ, startaddr_h); ++ isp_reg_writel(isp, tmp_v, OMAP3_ISP_IOMEM_RESZ, startaddr_v); ++ startaddr_h += 4; ++ startaddr_v += 4; ++ } ++} ++ ++/* ++ * resizer_set_bilinear - Chrominance horizontal algorithm select ++ * @res: Device context. ++ * @type: Filtering interpolation type. ++ * ++ * Filtering that is same as luminance processing is ++ * intended only for downsampling, and bilinear interpolation ++ * is intended only for upsampling. ++ */ ++static void resizer_set_bilinear(struct isp_res_device *res, ++ enum resizer_chroma_algo type) ++{ ++ struct isp_device *isp = to_isp_device(res); ++ ++ if (type == RSZ_BILINEAR) ++ isp_reg_set(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT, ++ ISPRSZ_CNT_CBILIN); ++ else ++ isp_reg_clr(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT, ++ ISPRSZ_CNT_CBILIN); ++} ++ ++/* ++ * resizer_set_ycpos - Luminance and chrominance order ++ * @res: Device context. ++ * @order: order type. ++ */ ++static void resizer_set_ycpos(struct isp_res_device *res, ++ enum v4l2_mbus_pixelcode pixelcode) ++{ ++ struct isp_device *isp = to_isp_device(res); ++ ++ switch (pixelcode) { ++ case V4L2_MBUS_FMT_YUYV8_1X16: ++ isp_reg_set(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT, ++ ISPRSZ_CNT_YCPOS); ++ break; ++ case V4L2_MBUS_FMT_UYVY8_1X16: ++ isp_reg_clr(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT, ++ ISPRSZ_CNT_YCPOS); ++ break; ++ default: ++ return; ++ } ++} ++ ++/* ++ * resizer_set_phase - Setup horizontal and vertical starting phase ++ * @res: Device context. ++ * @h_phase: horizontal phase parameters. ++ * @v_phase: vertical phase parameters. ++ * ++ * Horizontal and vertical phase range is 0 to 7 ++ */ ++static void resizer_set_phase(struct isp_res_device *res, u32 h_phase, ++ u32 v_phase) ++{ ++ struct isp_device *isp = to_isp_device(res); ++ u32 rgval = 0; ++ ++ rgval = isp_reg_readl(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT) & ++ ~(ISPRSZ_CNT_HSTPH_MASK | ISPRSZ_CNT_VSTPH_MASK); ++ rgval |= (h_phase << ISPRSZ_CNT_HSTPH_SHIFT) & ISPRSZ_CNT_HSTPH_MASK; ++ rgval |= (v_phase << ISPRSZ_CNT_VSTPH_SHIFT) & ISPRSZ_CNT_VSTPH_MASK; ++ ++ isp_reg_writel(isp, rgval, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT); ++} ++ ++/* ++ * resizer_set_luma - Setup luminance enhancer parameters ++ * @res: Device context. ++ * @luma: Structure for luminance enhancer parameters. ++ * ++ * Algorithm select: ++ * 0x0: Disable ++ * 0x1: [-1 2 -1]/2 high-pass filter ++ * 0x2: [-1 -2 6 -2 -1]/4 high-pass filter ++ * ++ * Maximum gain: ++ * The data is coded in U4Q4 representation. ++ * ++ * Slope: ++ * The data is coded in U4Q4 representation. ++ * ++ * Coring offset: ++ * The data is coded in U8Q0 representation. ++ * ++ * The new luminance value is computed as: ++ * Y += HPF(Y) x max(GAIN, (HPF(Y) - CORE) x SLOP + 8) >> 4. ++ */ ++static void resizer_set_luma(struct isp_res_device *res, ++ struct resizer_luma_yenh *luma) ++{ ++ struct isp_device *isp = to_isp_device(res); ++ u32 rgval = 0; ++ ++ rgval = (luma->algo << ISPRSZ_YENH_ALGO_SHIFT) ++ & ISPRSZ_YENH_ALGO_MASK; ++ rgval |= (luma->gain << ISPRSZ_YENH_GAIN_SHIFT) ++ & ISPRSZ_YENH_GAIN_MASK; ++ rgval |= (luma->slope << ISPRSZ_YENH_SLOP_SHIFT) ++ & ISPRSZ_YENH_SLOP_MASK; ++ rgval |= (luma->core << ISPRSZ_YENH_CORE_SHIFT) ++ & ISPRSZ_YENH_CORE_MASK; ++ ++ isp_reg_writel(isp, rgval, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_YENH); ++} ++ ++/* ++ * resizer_set_source - Input source select ++ * @res: Device context. ++ * @source: Input source type ++ * ++ * If this field is set to RESIZER_INPUT_VP, the resizer input is fed from ++ * Preview/CCDC engine, otherwise from memory. ++ */ ++static void resizer_set_source(struct isp_res_device *res, ++ enum resizer_input_entity source) ++{ ++ struct isp_device *isp = to_isp_device(res); ++ ++ if (source == RESIZER_INPUT_MEMORY) ++ isp_reg_set(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT, ++ ISPRSZ_CNT_INPSRC); ++ else ++ isp_reg_clr(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT, ++ ISPRSZ_CNT_INPSRC); ++} ++ ++/* ++ * resizer_set_ratio - Setup horizontal and vertical resizing value ++ * @res: Device context. ++ * @ratio: Structure for ratio parameters. ++ * ++ * Resizing range from 64 to 1024 ++ */ ++static void resizer_set_ratio(struct isp_res_device *res, ++ const struct resizer_ratio *ratio) ++{ ++ struct isp_device *isp = to_isp_device(res); ++ const u16 *h_filter, *v_filter; ++ u32 rgval = 0; ++ ++ rgval = isp_reg_readl(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT) & ++ ~(ISPRSZ_CNT_HRSZ_MASK | ISPRSZ_CNT_VRSZ_MASK); ++ rgval |= ((ratio->horz - 1) << ISPRSZ_CNT_HRSZ_SHIFT) ++ & ISPRSZ_CNT_HRSZ_MASK; ++ rgval |= ((ratio->vert - 1) << ISPRSZ_CNT_VRSZ_SHIFT) ++ & ISPRSZ_CNT_VRSZ_MASK; ++ isp_reg_writel(isp, rgval, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT); ++ ++ /* prepare horizontal filter coefficients */ ++ if (ratio->horz > MID_RESIZE_VALUE) ++ h_filter = &filter_coefs.h_filter_coef_7tap[0]; ++ else ++ h_filter = &filter_coefs.h_filter_coef_4tap[0]; ++ ++ /* prepare vertical filter coefficients */ ++ if (ratio->vert > MID_RESIZE_VALUE) ++ v_filter = &filter_coefs.v_filter_coef_7tap[0]; ++ else ++ v_filter = &filter_coefs.v_filter_coef_4tap[0]; ++ ++ resizer_set_filters(res, h_filter, v_filter); ++} ++ ++/* ++ * resizer_set_dst_size - Setup the output height and width ++ * @res: Device context. ++ * @width: Output width. ++ * @height: Output height. ++ * ++ * Width : ++ * The value must be EVEN. ++ * ++ * Height: ++ * The number of bytes written to SDRAM must be ++ * a multiple of 16-bytes if the vertical resizing factor ++ * is greater than 1x (upsizing) ++ */ ++static void resizer_set_output_size(struct isp_res_device *res, ++ u32 width, u32 height) ++{ ++ struct isp_device *isp = to_isp_device(res); ++ u32 rgval = 0; ++ ++ dev_dbg(isp->dev, "Output size[w/h]: %dx%d\n", width, height); ++ rgval = (width << ISPRSZ_OUT_SIZE_HORZ_SHIFT) ++ & ISPRSZ_OUT_SIZE_HORZ_MASK; ++ rgval |= (height << ISPRSZ_OUT_SIZE_VERT_SHIFT) ++ & ISPRSZ_OUT_SIZE_VERT_MASK; ++ isp_reg_writel(isp, rgval, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_OUT_SIZE); ++} ++ ++/* ++ * resizer_set_output_offset - Setup memory offset for the output lines. ++ * @res: Device context. ++ * @offset: Memory offset. ++ * ++ * The 5 LSBs are forced to be zeros by the hardware to align on a 32-byte ++ * boundary; the 5 LSBs are read-only. For optimal use of SDRAM bandwidth, ++ * the SDRAM line offset must be set on a 256-byte boundary ++ */ ++static void resizer_set_output_offset(struct isp_res_device *res, u32 offset) ++{ ++ struct isp_device *isp = to_isp_device(res); ++ ++ isp_reg_writel(isp, offset, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_OUTOFF); ++} ++ ++/* ++ * resizer_set_start - Setup vertical and horizontal start position ++ * @res: Device context. ++ * @left: Horizontal start position. ++ * @top: Vertical start position. ++ * ++ * Vertical start line: ++ * This field makes sense only when the resizer obtains its input ++ * from the preview engine/CCDC ++ * ++ * Horizontal start pixel: ++ * Pixels are coded on 16 bits for YUV and 8 bits for color separate data. ++ * When the resizer gets its input from SDRAM, this field must be set ++ * to <= 15 for YUV 16-bit data and <= 31 for 8-bit color separate data ++ */ ++static void resizer_set_start(struct isp_res_device *res, u32 left, u32 top) ++{ ++ struct isp_device *isp = to_isp_device(res); ++ u32 rgval = 0; ++ ++ rgval = (left << ISPRSZ_IN_START_HORZ_ST_SHIFT) ++ & ISPRSZ_IN_START_HORZ_ST_MASK; ++ rgval |= (top << ISPRSZ_IN_START_VERT_ST_SHIFT) ++ & ISPRSZ_IN_START_VERT_ST_MASK; ++ ++ isp_reg_writel(isp, rgval, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_IN_START); ++} ++ ++/* ++ * resizer_set_input_size - Setup the input size ++ * @res: Device context. ++ * @width: The range is 0 to 4095 pixels ++ * @height: The range is 0 to 4095 lines ++ */ ++static void resizer_set_input_size(struct isp_res_device *res, ++ u32 width, u32 height) ++{ ++ struct isp_device *isp = to_isp_device(res); ++ u32 rgval = 0; ++ ++ dev_dbg(isp->dev, "Input size[w/h]: %dx%d\n", width, height); ++ ++ rgval = (width << ISPRSZ_IN_SIZE_HORZ_SHIFT) ++ & ISPRSZ_IN_SIZE_HORZ_MASK; ++ rgval |= (height << ISPRSZ_IN_SIZE_VERT_SHIFT) ++ & ISPRSZ_IN_SIZE_VERT_MASK; ++ ++ isp_reg_writel(isp, rgval, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_IN_SIZE); ++} ++ ++/* ++ * resizer_set_src_offs - Setup the memory offset for the input lines ++ * @res: Device context. ++ * @offset: Memory offset. ++ * ++ * The 5 LSBs are forced to be zeros by the hardware to align on a 32-byte ++ * boundary; the 5 LSBs are read-only. This field must be programmed to be ++ * 0x0 if the resizer input is from preview engine/CCDC. ++ */ ++static void resizer_set_input_offset(struct isp_res_device *res, u32 offset) ++{ ++ struct isp_device *isp = to_isp_device(res); ++ ++ isp_reg_writel(isp, offset, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_INOFF); ++} ++ ++/* ++ * resizer_set_intype - Input type select ++ * @res: Device context. ++ * @type: Pixel format type. ++ */ ++static void resizer_set_intype(struct isp_res_device *res, ++ enum resizer_colors_type type) ++{ ++ struct isp_device *isp = to_isp_device(res); ++ ++ if (type == RSZ_COLOR8) ++ isp_reg_set(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT, ++ ISPRSZ_CNT_INPTYP); ++ else ++ isp_reg_clr(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT, ++ ISPRSZ_CNT_INPTYP); ++} ++ ++/* ++ * __resizer_set_inaddr - Helper function for set input address ++ * @res : pointer to resizer private data structure ++ * @addr: input address ++ * return none ++ */ ++static void __resizer_set_inaddr(struct isp_res_device *res, u32 addr) ++{ ++ struct isp_device *isp = to_isp_device(res); ++ ++ isp_reg_writel(isp, addr, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_INADD); ++} ++ ++/* ++ * The data rate at the horizontal resizer output must not exceed half the ++ * functional clock or 100 MP/s, whichever is lower. According to the TRM ++ * there's no similar requirement for the vertical resizer output. However ++ * experience showed that vertical upscaling by 4 leads to SBL overflows (with ++ * data rates at the resizer output exceeding 300 MP/s). Limiting the resizer ++ * output data rate to the functional clock or 200 MP/s, whichever is lower, ++ * seems to get rid of SBL overflows. ++ * ++ * The maximum data rate at the output of the horizontal resizer can thus be ++ * computed with ++ * ++ * max intermediate rate <= L3 clock * input height / output height ++ * max intermediate rate <= L3 clock / 2 ++ * ++ * The maximum data rate at the resizer input is then ++ * ++ * max input rate <= max intermediate rate * input width / output width ++ * ++ * where the input width and height are the resizer input crop rectangle size. ++ * The TRM doesn't clearly explain if that's a maximum instant data rate or a ++ * maximum average data rate. ++ */ ++void omap3isp_resizer_max_rate(struct isp_res_device *res, ++ unsigned int *max_rate) ++{ ++ struct isp_pipeline *pipe = to_isp_pipeline(&res->subdev.entity); ++ const struct v4l2_mbus_framefmt *ofmt = &res->formats[RESZ_PAD_SOURCE]; ++ unsigned long limit = min(pipe->l3_ick, 200000000UL); ++ unsigned long clock; ++ ++ clock = div_u64((u64)limit * res->crop.active.height, ofmt->height); ++ clock = min(clock, limit / 2); ++ *max_rate = div_u64((u64)clock * res->crop.active.width, ofmt->width); ++} ++ ++/* ++ * When the resizer processes images from memory, the driver must slow down read ++ * requests on the input to at least comply with the internal data rate ++ * requirements. If the application real-time requirements can cope with slower ++ * processing, the resizer can be slowed down even more to put less pressure on ++ * the overall system. ++ * ++ * When the resizer processes images on the fly (either from the CCDC or the ++ * preview module), the same data rate requirements apply but they can't be ++ * enforced at the resizer level. The image input module (sensor, CCP2 or ++ * preview module) must not provide image data faster than the resizer can ++ * process. ++ * ++ * For live image pipelines, the data rate is set by the frame format, size and ++ * rate. The sensor output frame rate must not exceed the maximum resizer data ++ * rate. ++ * ++ * The resizer slows down read requests by inserting wait cycles in the SBL ++ * requests. The maximum number of 256-byte requests per second can be computed ++ * as (the data rate is multiplied by 2 to convert from pixels per second to ++ * bytes per second) ++ * ++ * request per second = data rate * 2 / 256 ++ * cycles per request = cycles per second / requests per second ++ * ++ * The number of cycles per second is controlled by the L3 clock, leading to ++ * ++ * cycles per request = L3 frequency / 2 * 256 / data rate ++ */ ++static void resizer_adjust_bandwidth(struct isp_res_device *res) ++{ ++ struct isp_pipeline *pipe = to_isp_pipeline(&res->subdev.entity); ++ struct isp_device *isp = to_isp_device(res); ++ unsigned long l3_ick = pipe->l3_ick; ++ struct v4l2_fract *timeperframe; ++ unsigned int cycles_per_frame; ++ unsigned int requests_per_frame; ++ unsigned int cycles_per_request; ++ unsigned int granularity; ++ unsigned int minimum; ++ unsigned int maximum; ++ unsigned int value; ++ ++ if (res->input != RESIZER_INPUT_MEMORY) { ++ isp_reg_clr(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_SDR_REQ_EXP, ++ ISPSBL_SDR_REQ_RSZ_EXP_MASK); ++ return; ++ } ++ ++ switch (isp->revision) { ++ case ISP_REVISION_1_0: ++ case ISP_REVISION_2_0: ++ default: ++ granularity = 1024; ++ break; ++ ++ case ISP_REVISION_15_0: ++ granularity = 32; ++ break; ++ } ++ ++ /* Compute the minimum number of cycles per request, based on the ++ * pipeline maximum data rate. This is an absolute lower bound if we ++ * don't want SBL overflows, so round the value up. ++ */ ++ cycles_per_request = div_u64((u64)l3_ick / 2 * 256 + pipe->max_rate - 1, ++ pipe->max_rate); ++ minimum = DIV_ROUND_UP(cycles_per_request, granularity); ++ ++ /* Compute the maximum number of cycles per request, based on the ++ * requested frame rate. This is a soft upper bound to achieve a frame ++ * rate equal or higher than the requested value, so round the value ++ * down. ++ */ ++ timeperframe = &pipe->max_timeperframe; ++ ++ requests_per_frame = DIV_ROUND_UP(res->crop.active.width * 2, 256) ++ * res->crop.active.height; ++ cycles_per_frame = div_u64((u64)l3_ick * timeperframe->numerator, ++ timeperframe->denominator); ++ cycles_per_request = cycles_per_frame / requests_per_frame; ++ ++ maximum = cycles_per_request / granularity; ++ ++ value = max(minimum, maximum); ++ ++ dev_dbg(isp->dev, "%s: cycles per request = %u\n", __func__, value); ++ isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_SDR_REQ_EXP, ++ ISPSBL_SDR_REQ_RSZ_EXP_MASK, ++ value << ISPSBL_SDR_REQ_RSZ_EXP_SHIFT); ++} ++ ++/* ++ * omap3isp_resizer_busy - Checks if ISP resizer is busy. ++ * ++ * Returns busy field from ISPRSZ_PCR register. ++ */ ++int omap3isp_resizer_busy(struct isp_res_device *res) ++{ ++ struct isp_device *isp = to_isp_device(res); ++ ++ return isp_reg_readl(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_PCR) & ++ ISPRSZ_PCR_BUSY; ++} ++ ++/* ++ * resizer_set_inaddr - Sets the memory address of the input frame. ++ * @addr: 32bit memory address aligned on 32byte boundary. ++ */ ++static void resizer_set_inaddr(struct isp_res_device *res, u32 addr) ++{ ++ res->addr_base = addr; ++ ++ /* This will handle crop settings in stream off state */ ++ if (res->crop_offset) ++ addr += res->crop_offset & ~0x1f; ++ ++ __resizer_set_inaddr(res, addr); ++} ++ ++/* ++ * Configures the memory address to which the output frame is written. ++ * @addr: 32bit memory address aligned on 32byte boundary. ++ * Note: For SBL efficiency reasons the address should be on a 256-byte ++ * boundary. ++ */ ++static void resizer_set_outaddr(struct isp_res_device *res, u32 addr) ++{ ++ struct isp_device *isp = to_isp_device(res); ++ ++ /* ++ * Set output address. This needs to be in its own function ++ * because it changes often. ++ */ ++ isp_reg_writel(isp, addr << ISPRSZ_SDR_OUTADD_ADDR_SHIFT, ++ OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_OUTADD); ++} ++ ++/* ++ * resizer_print_status - Prints the values of the resizer module registers. ++ */ ++#define RSZ_PRINT_REGISTER(isp, name)\ ++ dev_dbg(isp->dev, "###RSZ " #name "=0x%08x\n", \ ++ isp_reg_readl(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_##name)) ++ ++static void resizer_print_status(struct isp_res_device *res) ++{ ++ struct isp_device *isp = to_isp_device(res); ++ ++ dev_dbg(isp->dev, "-------------Resizer Register dump----------\n"); ++ ++ RSZ_PRINT_REGISTER(isp, PCR); ++ RSZ_PRINT_REGISTER(isp, CNT); ++ RSZ_PRINT_REGISTER(isp, OUT_SIZE); ++ RSZ_PRINT_REGISTER(isp, IN_START); ++ RSZ_PRINT_REGISTER(isp, IN_SIZE); ++ RSZ_PRINT_REGISTER(isp, SDR_INADD); ++ RSZ_PRINT_REGISTER(isp, SDR_INOFF); ++ RSZ_PRINT_REGISTER(isp, SDR_OUTADD); ++ RSZ_PRINT_REGISTER(isp, SDR_OUTOFF); ++ RSZ_PRINT_REGISTER(isp, YENH); ++ ++ dev_dbg(isp->dev, "--------------------------------------------\n"); ++} ++ ++/* ++ * resizer_calc_ratios - Helper function for calculate resizer ratios ++ * @res: pointer to resizer private data structure ++ * @input: input frame size ++ * @output: output frame size ++ * @ratio : return calculated ratios ++ * return none ++ * ++ * The resizer uses a polyphase sample rate converter. The upsampling filter ++ * has a fixed number of phases that depend on the resizing ratio. As the ratio ++ * computation depends on the number of phases, we need to compute a first ++ * approximation and then refine it. ++ * ++ * The input/output/ratio relationship is given by the OMAP34xx TRM: ++ * ++ * - 8-phase, 4-tap mode (RSZ = 64 ~ 512) ++ * iw = (32 * sph + (ow - 1) * hrsz + 16) >> 8 + 7 ++ * ih = (32 * spv + (oh - 1) * vrsz + 16) >> 8 + 4 ++ * - 4-phase, 7-tap mode (RSZ = 513 ~ 1024) ++ * iw = (64 * sph + (ow - 1) * hrsz + 32) >> 8 + 7 ++ * ih = (64 * spv + (oh - 1) * vrsz + 32) >> 8 + 7 ++ * ++ * iw and ih are the input width and height after cropping. Those equations need ++ * to be satisfied exactly for the resizer to work correctly. ++ * ++ * Reverting the equations, we can compute the resizing ratios with ++ * ++ * - 8-phase, 4-tap mode ++ * hrsz = ((iw - 7) * 256 - 16 - 32 * sph) / (ow - 1) ++ * vrsz = ((ih - 4) * 256 - 16 - 32 * spv) / (oh - 1) ++ * - 4-phase, 7-tap mode ++ * hrsz = ((iw - 7) * 256 - 32 - 64 * sph) / (ow - 1) ++ * vrsz = ((ih - 7) * 256 - 32 - 64 * spv) / (oh - 1) ++ * ++ * The ratios are integer values, and must be rounded down to ensure that the ++ * cropped input size is not bigger than the uncropped input size. As the ratio ++ * in 7-tap mode is always smaller than the ratio in 4-tap mode, we can use the ++ * 7-tap mode equations to compute a ratio approximation. ++ * ++ * We first clamp the output size according to the hardware capabilitie to avoid ++ * auto-cropping the input more than required to satisfy the TRM equations. The ++ * minimum output size is achieved with a scaling factor of 1024. It is thus ++ * computed using the 7-tap equations. ++ * ++ * min ow = ((iw - 7) * 256 - 32 - 64 * sph) / 1024 + 1 ++ * min oh = ((ih - 7) * 256 - 32 - 64 * spv) / 1024 + 1 ++ * ++ * Similarly, the maximum output size is achieved with a scaling factor of 64 ++ * and computed using the 4-tap equations. ++ * ++ * max ow = ((iw - 7) * 256 + 255 - 16 - 32 * sph) / 64 + 1 ++ * max oh = ((ih - 4) * 256 + 255 - 16 - 32 * spv) / 64 + 1 ++ * ++ * The additional +255 term compensates for the round down operation performed ++ * by the TRM equations when shifting the value right by 8 bits. ++ * ++ * We then compute and clamp the ratios (x1/4 ~ x4). Clamping the output size to ++ * the maximum value guarantees that the ratio value will never be smaller than ++ * the minimum, but it could still slightly exceed the maximum. Clamping the ++ * ratio will thus result in a resizing factor slightly larger than the ++ * requested value. ++ * ++ * To accomodate that, and make sure the TRM equations are satisfied exactly, we ++ * compute the input crop rectangle as the last step. ++ * ++ * As if the situation wasn't complex enough, the maximum output width depends ++ * on the vertical resizing ratio. Fortunately, the output height doesn't ++ * depend on the horizontal resizing ratio. We can then start by computing the ++ * output height and the vertical ratio, and then move to computing the output ++ * width and the horizontal ratio. ++ */ ++static void resizer_calc_ratios(struct isp_res_device *res, ++ struct v4l2_rect *input, ++ struct v4l2_mbus_framefmt *output, ++ struct resizer_ratio *ratio) ++{ ++ struct isp_device *isp = to_isp_device(res); ++ const unsigned int spv = DEFAULT_PHASE; ++ const unsigned int sph = DEFAULT_PHASE; ++ unsigned int upscaled_width; ++ unsigned int upscaled_height; ++ unsigned int min_width; ++ unsigned int min_height; ++ unsigned int max_width; ++ unsigned int max_height; ++ unsigned int width_alignment; ++ ++ /* ++ * Clamp the output height based on the hardware capabilities and ++ * compute the vertical resizing ratio. ++ */ ++ min_height = ((input->height - 7) * 256 - 32 - 64 * spv) / 1024 + 1; ++ min_height = max_t(unsigned int, min_height, MIN_OUT_HEIGHT); ++ max_height = ((input->height - 4) * 256 + 255 - 16 - 32 * spv) / 64 + 1; ++ max_height = min_t(unsigned int, max_height, MAX_OUT_HEIGHT); ++ output->height = clamp(output->height, min_height, max_height); ++ ++ ratio->vert = ((input->height - 7) * 256 - 32 - 64 * spv) ++ / (output->height - 1); ++ ratio->vert = clamp_t(unsigned int, ratio->vert, ++ MIN_RESIZE_VALUE, MAX_RESIZE_VALUE); ++ ++ if (ratio->vert <= MID_RESIZE_VALUE) { ++ upscaled_height = (output->height - 1) * ratio->vert ++ + 32 * spv + 16; ++ input->height = (upscaled_height >> 8) + 4; ++ } else { ++ upscaled_height = (output->height - 1) * ratio->vert ++ + 64 * spv + 32; ++ input->height = (upscaled_height >> 8) + 7; ++ } ++ ++ /* ++ * Compute the minimum and maximum output widths based on the hardware ++ * capabilities. The maximum depends on the vertical resizing ratio. ++ */ ++ min_width = ((input->width - 7) * 256 - 32 - 64 * sph) / 1024 + 1; ++ min_width = max_t(unsigned int, min_width, MIN_OUT_WIDTH); ++ ++ if (ratio->vert <= MID_RESIZE_VALUE) { ++ switch (isp->revision) { ++ case ISP_REVISION_1_0: ++ max_width = MAX_4TAP_OUT_WIDTH_ES1; ++ break; ++ ++ case ISP_REVISION_2_0: ++ default: ++ max_width = MAX_4TAP_OUT_WIDTH_ES2; ++ break; ++ ++ case ISP_REVISION_15_0: ++ max_width = MAX_4TAP_OUT_WIDTH_3630; ++ break; ++ } ++ } else { ++ switch (isp->revision) { ++ case ISP_REVISION_1_0: ++ max_width = MAX_7TAP_OUT_WIDTH_ES1; ++ break; ++ ++ case ISP_REVISION_2_0: ++ default: ++ max_width = MAX_7TAP_OUT_WIDTH_ES2; ++ break; ++ ++ case ISP_REVISION_15_0: ++ max_width = MAX_7TAP_OUT_WIDTH_3630; ++ break; ++ } ++ } ++ max_width = min(((input->width - 7) * 256 + 255 - 16 - 32 * sph) / 64 ++ + 1, max_width); ++ ++ /* ++ * The output width must be even, and must be a multiple of 16 bytes ++ * when upscaling vertically. Clamp the output width to the valid range. ++ * Take the alignment into account (the maximum width in 7-tap mode on ++ * ES2 isn't a multiple of 8) and align the result up to make sure it ++ * won't be smaller than the minimum. ++ */ ++ width_alignment = ratio->vert < 256 ? 8 : 2; ++ output->width = clamp(output->width, min_width, ++ max_width & ~(width_alignment - 1)); ++ output->width = ALIGN(output->width, width_alignment); ++ ++ ratio->horz = ((input->width - 7) * 256 - 32 - 64 * sph) ++ / (output->width - 1); ++ ratio->horz = clamp_t(unsigned int, ratio->horz, ++ MIN_RESIZE_VALUE, MAX_RESIZE_VALUE); ++ ++ if (ratio->horz <= MID_RESIZE_VALUE) { ++ upscaled_width = (output->width - 1) * ratio->horz ++ + 32 * sph + 16; ++ input->width = (upscaled_width >> 8) + 7; ++ } else { ++ upscaled_width = (output->width - 1) * ratio->horz ++ + 64 * sph + 32; ++ input->width = (upscaled_width >> 8) + 7; ++ } ++} ++ ++/* ++ * resizer_set_crop_params - Setup hardware with cropping parameters ++ * @res : resizer private structure ++ * @crop_rect : current crop rectangle ++ * @ratio : resizer ratios ++ * return none ++ */ ++static void resizer_set_crop_params(struct isp_res_device *res, ++ const struct v4l2_mbus_framefmt *input, ++ const struct v4l2_mbus_framefmt *output) ++{ ++ resizer_set_ratio(res, &res->ratio); ++ ++ /* Set chrominance horizontal algorithm */ ++ if (res->ratio.horz >= RESIZE_DIVISOR) ++ resizer_set_bilinear(res, RSZ_THE_SAME); ++ else ++ resizer_set_bilinear(res, RSZ_BILINEAR); ++ ++ resizer_adjust_bandwidth(res); ++ ++ if (res->input == RESIZER_INPUT_MEMORY) { ++ /* Calculate additional offset for crop */ ++ res->crop_offset = (res->crop.active.top * input->width + ++ res->crop.active.left) * 2; ++ /* ++ * Write lowest 4 bits of horizontal pixel offset (in pixels), ++ * vertical start must be 0. ++ */ ++ resizer_set_start(res, (res->crop_offset / 2) & 0xf, 0); ++ ++ /* ++ * Set start (read) address for cropping, in bytes. ++ * Lowest 5 bits must be zero. ++ */ ++ __resizer_set_inaddr(res, ++ res->addr_base + (res->crop_offset & ~0x1f)); ++ } else { ++ /* ++ * Set vertical start line and horizontal starting pixel. ++ * If the input is from CCDC/PREV, horizontal start field is ++ * in bytes (twice number of pixels). ++ */ ++ resizer_set_start(res, res->crop.active.left * 2, ++ res->crop.active.top); ++ /* Input address and offset must be 0 for preview/ccdc input */ ++ __resizer_set_inaddr(res, 0); ++ resizer_set_input_offset(res, 0); ++ } ++ ++ /* Set the input size */ ++ resizer_set_input_size(res, res->crop.active.width, ++ res->crop.active.height); ++} ++ ++static void resizer_configure(struct isp_res_device *res) ++{ ++ struct v4l2_mbus_framefmt *informat, *outformat; ++ struct resizer_luma_yenh luma = {0, 0, 0, 0}; ++ ++ resizer_set_source(res, res->input); ++ ++ informat = &res->formats[RESZ_PAD_SINK]; ++ outformat = &res->formats[RESZ_PAD_SOURCE]; ++ ++ /* RESZ_PAD_SINK */ ++ if (res->input == RESIZER_INPUT_VP) ++ resizer_set_input_offset(res, 0); ++ else ++ resizer_set_input_offset(res, ALIGN(informat->width, 0x10) * 2); ++ ++ /* YUV422 interleaved, default phase, no luma enhancement */ ++ resizer_set_intype(res, RSZ_YUV422); ++ resizer_set_ycpos(res, informat->code); ++ resizer_set_phase(res, DEFAULT_PHASE, DEFAULT_PHASE); ++ resizer_set_luma(res, &luma); ++ ++ /* RESZ_PAD_SOURCE */ ++ resizer_set_output_offset(res, ALIGN(outformat->width * 2, 32)); ++ resizer_set_output_size(res, outformat->width, outformat->height); ++ ++ resizer_set_crop_params(res, informat, outformat); ++} ++ ++/* ----------------------------------------------------------------------------- ++ * Interrupt handling ++ */ ++ ++static void resizer_enable_oneshot(struct isp_res_device *res) ++{ ++ struct isp_device *isp = to_isp_device(res); ++ ++ isp_reg_set(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_PCR, ++ ISPRSZ_PCR_ENABLE | ISPRSZ_PCR_ONESHOT); ++} ++ ++void omap3isp_resizer_isr_frame_sync(struct isp_res_device *res) ++{ ++ /* ++ * If ISP_VIDEO_DMAQUEUE_QUEUED is set, DMA queue had an underrun ++ * condition, the module was paused and now we have a buffer queued ++ * on the output again. Restart the pipeline if running in continuous ++ * mode. ++ */ ++ if (res->state == ISP_PIPELINE_STREAM_CONTINUOUS && ++ res->video_out.dmaqueue_flags & ISP_VIDEO_DMAQUEUE_QUEUED) { ++ resizer_enable_oneshot(res); ++ isp_video_dmaqueue_flags_clr(&res->video_out); ++ } ++} ++ ++static void resizer_isr_buffer(struct isp_res_device *res) ++{ ++ struct isp_pipeline *pipe = to_isp_pipeline(&res->subdev.entity); ++ struct isp_buffer *buffer; ++ int restart = 0; ++ ++ if (res->state == ISP_PIPELINE_STREAM_STOPPED) ++ return; ++ ++ /* Complete the output buffer and, if reading from memory, the input ++ * buffer. ++ */ ++ buffer = omap3isp_video_buffer_next(&res->video_out, res->error); ++ if (buffer != NULL) { ++ resizer_set_outaddr(res, buffer->isp_addr); ++ restart = 1; ++ } ++ ++ pipe->state |= ISP_PIPELINE_IDLE_OUTPUT; ++ ++ if (res->input == RESIZER_INPUT_MEMORY) { ++ buffer = omap3isp_video_buffer_next(&res->video_in, 0); ++ if (buffer != NULL) ++ resizer_set_inaddr(res, buffer->isp_addr); ++ pipe->state |= ISP_PIPELINE_IDLE_INPUT; ++ } ++ ++ if (res->state == ISP_PIPELINE_STREAM_SINGLESHOT) { ++ if (isp_pipeline_ready(pipe)) ++ omap3isp_pipeline_set_stream(pipe, ++ ISP_PIPELINE_STREAM_SINGLESHOT); ++ } else { ++ /* If an underrun occurs, the video queue operation handler will ++ * restart the resizer. Otherwise restart it immediately. ++ */ ++ if (restart) ++ resizer_enable_oneshot(res); ++ } ++ ++ res->error = 0; ++} ++ ++/* ++ * omap3isp_resizer_isr - ISP resizer interrupt handler ++ * ++ * Manage the resizer video buffers and configure shadowed and busy-locked ++ * registers. ++ */ ++void omap3isp_resizer_isr(struct isp_res_device *res) ++{ ++ struct v4l2_mbus_framefmt *informat, *outformat; ++ ++ if (omap3isp_module_sync_is_stopping(&res->wait, &res->stopping)) ++ return; ++ ++ if (res->applycrop) { ++ outformat = __resizer_get_format(res, NULL, RESZ_PAD_SOURCE, ++ V4L2_SUBDEV_FORMAT_ACTIVE); ++ informat = __resizer_get_format(res, NULL, RESZ_PAD_SINK, ++ V4L2_SUBDEV_FORMAT_ACTIVE); ++ resizer_set_crop_params(res, informat, outformat); ++ res->applycrop = 0; ++ } ++ ++ resizer_isr_buffer(res); ++} ++ ++/* ----------------------------------------------------------------------------- ++ * ISP video operations ++ */ ++ ++static int resizer_video_queue(struct isp_video *video, ++ struct isp_buffer *buffer) ++{ ++ struct isp_res_device *res = &video->isp->isp_res; ++ ++ if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) ++ resizer_set_inaddr(res, buffer->isp_addr); ++ ++ /* ++ * We now have a buffer queued on the output. Despite what the ++ * TRM says, the resizer can't be restarted immediately. ++ * Enabling it in one shot mode in the middle of a frame (or at ++ * least asynchronously to the frame) results in the output ++ * being shifted randomly left/right and up/down, as if the ++ * hardware didn't synchronize itself to the beginning of the ++ * frame correctly. ++ * ++ * Restart the resizer on the next sync interrupt if running in ++ * continuous mode or when starting the stream. ++ */ ++ if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) ++ resizer_set_outaddr(res, buffer->isp_addr); ++ ++ return 0; ++} ++ ++static const struct isp_video_operations resizer_video_ops = { ++ .queue = resizer_video_queue, ++}; ++ ++/* ----------------------------------------------------------------------------- ++ * V4L2 subdev operations ++ */ ++ ++/* ++ * resizer_set_stream - Enable/Disable streaming on resizer subdev ++ * @sd: ISP resizer V4L2 subdev ++ * @enable: 1 == Enable, 0 == Disable ++ * ++ * The resizer hardware can't be enabled without a memory buffer to write to. ++ * As the s_stream operation is called in response to a STREAMON call without ++ * any buffer queued yet, just update the state field and return immediately. ++ * The resizer will be enabled in resizer_video_queue(). ++ */ ++static int resizer_set_stream(struct v4l2_subdev *sd, int enable) ++{ ++ struct isp_res_device *res = v4l2_get_subdevdata(sd); ++ struct isp_video *video_out = &res->video_out; ++ struct isp_device *isp = to_isp_device(res); ++ struct device *dev = to_device(res); ++ ++ if (res->state == ISP_PIPELINE_STREAM_STOPPED) { ++ if (enable == ISP_PIPELINE_STREAM_STOPPED) ++ return 0; ++ ++ omap3isp_subclk_enable(isp, OMAP3_ISP_SUBCLK_RESIZER); ++ resizer_configure(res); ++ res->error = 0; ++ resizer_print_status(res); ++ } ++ ++ switch (enable) { ++ case ISP_PIPELINE_STREAM_CONTINUOUS: ++ omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_RESIZER_WRITE); ++ if (video_out->dmaqueue_flags & ISP_VIDEO_DMAQUEUE_QUEUED) { ++ resizer_enable_oneshot(res); ++ isp_video_dmaqueue_flags_clr(video_out); ++ } ++ break; ++ ++ case ISP_PIPELINE_STREAM_SINGLESHOT: ++ if (res->input == RESIZER_INPUT_MEMORY) ++ omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_RESIZER_READ); ++ omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_RESIZER_WRITE); ++ ++ resizer_enable_oneshot(res); ++ break; ++ ++ case ISP_PIPELINE_STREAM_STOPPED: ++ if (omap3isp_module_sync_idle(&sd->entity, &res->wait, ++ &res->stopping)) ++ dev_dbg(dev, "%s: module stop timeout.\n", sd->name); ++ omap3isp_sbl_disable(isp, OMAP3_ISP_SBL_RESIZER_READ | ++ OMAP3_ISP_SBL_RESIZER_WRITE); ++ omap3isp_subclk_disable(isp, OMAP3_ISP_SUBCLK_RESIZER); ++ isp_video_dmaqueue_flags_clr(video_out); ++ break; ++ } ++ ++ res->state = enable; ++ return 0; ++} ++ ++/* ++ * resizer_g_crop - handle get crop subdev operation ++ * @sd : pointer to v4l2 subdev structure ++ * @pad : subdev pad ++ * @crop : pointer to crop structure ++ * @which : active or try format ++ * return zero ++ */ ++static int resizer_g_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_crop *crop) ++{ ++ struct isp_res_device *res = v4l2_get_subdevdata(sd); ++ struct v4l2_mbus_framefmt *format; ++ struct resizer_ratio ratio; ++ ++ /* Only sink pad has crop capability */ ++ if (crop->pad != RESZ_PAD_SINK) ++ return -EINVAL; ++ ++ format = __resizer_get_format(res, fh, RESZ_PAD_SOURCE, crop->which); ++ crop->rect = *__resizer_get_crop(res, fh, crop->which); ++ resizer_calc_ratios(res, &crop->rect, format, &ratio); ++ ++ return 0; ++} ++ ++/* ++ * resizer_try_crop - mangles crop parameters. ++ */ ++static void resizer_try_crop(const struct v4l2_mbus_framefmt *sink, ++ const struct v4l2_mbus_framefmt *source, ++ struct v4l2_rect *crop) ++{ ++ const unsigned int spv = DEFAULT_PHASE; ++ const unsigned int sph = DEFAULT_PHASE; ++ ++ /* Crop rectangle is constrained to the output size so that zoom ratio ++ * cannot exceed +/-4.0. ++ */ ++ unsigned int min_width = ++ ((32 * sph + (source->width - 1) * 64 + 16) >> 8) + 7; ++ unsigned int min_height = ++ ((32 * spv + (source->height - 1) * 64 + 16) >> 8) + 4; ++ unsigned int max_width = ++ ((64 * sph + (source->width - 1) * 1024 + 32) >> 8) + 7; ++ unsigned int max_height = ++ ((64 * spv + (source->height - 1) * 1024 + 32) >> 8) + 7; ++ ++ crop->width = clamp_t(u32, crop->width, min_width, max_width); ++ crop->height = clamp_t(u32, crop->height, min_height, max_height); ++ ++ /* Crop can not go beyond of the input rectangle */ ++ crop->left = clamp_t(u32, crop->left, 0, sink->width - MIN_IN_WIDTH); ++ crop->width = clamp_t(u32, crop->width, MIN_IN_WIDTH, ++ sink->width - crop->left); ++ crop->top = clamp_t(u32, crop->top, 0, sink->height - MIN_IN_HEIGHT); ++ crop->height = clamp_t(u32, crop->height, MIN_IN_HEIGHT, ++ sink->height - crop->top); ++} ++ ++/* ++ * resizer_s_crop - handle set crop subdev operation ++ * @sd : pointer to v4l2 subdev structure ++ * @pad : subdev pad ++ * @crop : pointer to crop structure ++ * @which : active or try format ++ * return -EINVAL or zero when succeed ++ */ ++static int resizer_s_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_crop *crop) ++{ ++ struct isp_res_device *res = v4l2_get_subdevdata(sd); ++ struct isp_device *isp = to_isp_device(res); ++ struct v4l2_mbus_framefmt *format_sink, *format_source; ++ struct resizer_ratio ratio; ++ ++ /* Only sink pad has crop capability */ ++ if (crop->pad != RESZ_PAD_SINK) ++ return -EINVAL; ++ ++ format_sink = __resizer_get_format(res, fh, RESZ_PAD_SINK, ++ crop->which); ++ format_source = __resizer_get_format(res, fh, RESZ_PAD_SOURCE, ++ crop->which); ++ ++ dev_dbg(isp->dev, "%s: L=%d,T=%d,W=%d,H=%d,which=%d\n", __func__, ++ crop->rect.left, crop->rect.top, crop->rect.width, ++ crop->rect.height, crop->which); ++ ++ dev_dbg(isp->dev, "%s: input=%dx%d, output=%dx%d\n", __func__, ++ format_sink->width, format_sink->height, ++ format_source->width, format_source->height); ++ ++ resizer_try_crop(format_sink, format_source, &crop->rect); ++ *__resizer_get_crop(res, fh, crop->which) = crop->rect; ++ resizer_calc_ratios(res, &crop->rect, format_source, &ratio); ++ ++ if (crop->which == V4L2_SUBDEV_FORMAT_TRY) ++ return 0; ++ ++ res->ratio = ratio; ++ res->crop.active = crop->rect; ++ ++ /* ++ * s_crop can be called while streaming is on. In this case ++ * the crop values will be set in the next IRQ. ++ */ ++ if (res->state != ISP_PIPELINE_STREAM_STOPPED) ++ res->applycrop = 1; ++ ++ return 0; ++} ++ ++/* resizer pixel formats */ ++static const unsigned int resizer_formats[] = { ++ V4L2_MBUS_FMT_UYVY8_1X16, ++ V4L2_MBUS_FMT_YUYV8_1X16, ++}; ++ ++static unsigned int resizer_max_in_width(struct isp_res_device *res) ++{ ++ struct isp_device *isp = to_isp_device(res); ++ ++ if (res->input == RESIZER_INPUT_MEMORY) { ++ return MAX_IN_WIDTH_MEMORY_MODE; ++ } else { ++ if (isp->revision == ISP_REVISION_1_0) ++ return MAX_IN_WIDTH_ONTHEFLY_MODE_ES1; ++ else ++ return MAX_IN_WIDTH_ONTHEFLY_MODE_ES2; ++ } ++} ++ ++/* ++ * resizer_try_format - Handle try format by pad subdev method ++ * @res : ISP resizer device ++ * @fh : V4L2 subdev file handle ++ * @pad : pad num ++ * @fmt : pointer to v4l2 format structure ++ * @which : wanted subdev format ++ */ ++static void resizer_try_format(struct isp_res_device *res, ++ struct v4l2_subdev_fh *fh, unsigned int pad, ++ struct v4l2_mbus_framefmt *fmt, ++ enum v4l2_subdev_format_whence which) ++{ ++ struct v4l2_mbus_framefmt *format; ++ struct resizer_ratio ratio; ++ struct v4l2_rect crop; ++ ++ switch (pad) { ++ case RESZ_PAD_SINK: ++ if (fmt->code != V4L2_MBUS_FMT_YUYV8_1X16 && ++ fmt->code != V4L2_MBUS_FMT_UYVY8_1X16) ++ fmt->code = V4L2_MBUS_FMT_YUYV8_1X16; ++ ++ fmt->width = clamp_t(u32, fmt->width, MIN_IN_WIDTH, ++ resizer_max_in_width(res)); ++ fmt->height = clamp_t(u32, fmt->height, MIN_IN_HEIGHT, ++ MAX_IN_HEIGHT); ++ break; ++ ++ case RESZ_PAD_SOURCE: ++ format = __resizer_get_format(res, fh, RESZ_PAD_SINK, which); ++ fmt->code = format->code; ++ ++ crop = *__resizer_get_crop(res, fh, which); ++ resizer_calc_ratios(res, &crop, fmt, &ratio); ++ break; ++ } ++ ++ fmt->colorspace = V4L2_COLORSPACE_JPEG; ++ fmt->field = V4L2_FIELD_NONE; ++} ++ ++/* ++ * resizer_enum_mbus_code - Handle pixel format enumeration ++ * @sd : pointer to v4l2 subdev structure ++ * @fh : V4L2 subdev file handle ++ * @code : pointer to v4l2_subdev_mbus_code_enum structure ++ * return -EINVAL or zero on success ++ */ ++static int resizer_enum_mbus_code(struct v4l2_subdev *sd, ++ struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_mbus_code_enum *code) ++{ ++ struct isp_res_device *res = v4l2_get_subdevdata(sd); ++ struct v4l2_mbus_framefmt *format; ++ ++ if (code->pad == RESZ_PAD_SINK) { ++ if (code->index >= ARRAY_SIZE(resizer_formats)) ++ return -EINVAL; ++ ++ code->code = resizer_formats[code->index]; ++ } else { ++ if (code->index != 0) ++ return -EINVAL; ++ ++ format = __resizer_get_format(res, fh, RESZ_PAD_SINK, ++ V4L2_SUBDEV_FORMAT_TRY); ++ code->code = format->code; ++ } ++ ++ return 0; ++} ++ ++static int resizer_enum_frame_size(struct v4l2_subdev *sd, ++ struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_frame_size_enum *fse) ++{ ++ struct isp_res_device *res = v4l2_get_subdevdata(sd); ++ struct v4l2_mbus_framefmt format; ++ ++ if (fse->index != 0) ++ return -EINVAL; ++ ++ format.code = fse->code; ++ format.width = 1; ++ format.height = 1; ++ resizer_try_format(res, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); ++ fse->min_width = format.width; ++ fse->min_height = format.height; ++ ++ if (format.code != fse->code) ++ return -EINVAL; ++ ++ format.code = fse->code; ++ format.width = -1; ++ format.height = -1; ++ resizer_try_format(res, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); ++ fse->max_width = format.width; ++ fse->max_height = format.height; ++ ++ return 0; ++} ++ ++/* ++ * resizer_get_format - Handle get format by pads subdev method ++ * @sd : pointer to v4l2 subdev structure ++ * @fh : V4L2 subdev file handle ++ * @fmt : pointer to v4l2 subdev format structure ++ * return -EINVAL or zero on sucess ++ */ ++static int resizer_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_format *fmt) ++{ ++ struct isp_res_device *res = v4l2_get_subdevdata(sd); ++ struct v4l2_mbus_framefmt *format; ++ ++ format = __resizer_get_format(res, fh, fmt->pad, fmt->which); ++ if (format == NULL) ++ return -EINVAL; ++ ++ fmt->format = *format; ++ return 0; ++} ++ ++/* ++ * resizer_set_format - Handle set format by pads subdev method ++ * @sd : pointer to v4l2 subdev structure ++ * @fh : V4L2 subdev file handle ++ * @fmt : pointer to v4l2 subdev format structure ++ * return -EINVAL or zero on success ++ */ ++static int resizer_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_format *fmt) ++{ ++ struct isp_res_device *res = v4l2_get_subdevdata(sd); ++ struct v4l2_mbus_framefmt *format; ++ struct v4l2_rect *crop; ++ ++ format = __resizer_get_format(res, fh, fmt->pad, fmt->which); ++ if (format == NULL) ++ return -EINVAL; ++ ++ resizer_try_format(res, fh, fmt->pad, &fmt->format, fmt->which); ++ *format = fmt->format; ++ ++ if (fmt->pad == RESZ_PAD_SINK) { ++ /* reset crop rectangle */ ++ crop = __resizer_get_crop(res, fh, fmt->which); ++ crop->left = 0; ++ crop->top = 0; ++ crop->width = fmt->format.width; ++ crop->height = fmt->format.height; ++ ++ /* Propagate the format from sink to source */ ++ format = __resizer_get_format(res, fh, RESZ_PAD_SOURCE, ++ fmt->which); ++ *format = fmt->format; ++ resizer_try_format(res, fh, RESZ_PAD_SOURCE, format, ++ fmt->which); ++ } ++ ++ if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { ++ /* Compute and store the active crop rectangle and resizer ++ * ratios. format already points to the source pad active ++ * format. ++ */ ++ res->crop.active = res->crop.request; ++ resizer_calc_ratios(res, &res->crop.active, format, ++ &res->ratio); ++ } ++ ++ return 0; ++} ++ ++/* ++ * resizer_init_formats - Initialize formats on all pads ++ * @sd: ISP resizer V4L2 subdevice ++ * @fh: V4L2 subdev file handle ++ * ++ * Initialize all pad formats with default values. If fh is not NULL, try ++ * formats are initialized on the file handle. Otherwise active formats are ++ * initialized on the device. ++ */ ++static int resizer_init_formats(struct v4l2_subdev *sd, ++ struct v4l2_subdev_fh *fh) ++{ ++ struct v4l2_subdev_format format; ++ ++ memset(&format, 0, sizeof(format)); ++ format.pad = RESZ_PAD_SINK; ++ format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; ++ format.format.code = V4L2_MBUS_FMT_YUYV8_1X16; ++ format.format.width = 4096; ++ format.format.height = 4096; ++ resizer_set_format(sd, fh, &format); ++ ++ return 0; ++} ++ ++/* subdev core operations */ ++static const struct v4l2_subdev_core_ops resizer_v4l2_core_ops = { ++ .queryctrl = v4l2_subdev_queryctrl, ++ .querymenu = v4l2_subdev_querymenu, ++ .g_ctrl = v4l2_subdev_g_ctrl, ++ .s_ctrl = v4l2_subdev_s_ctrl, ++ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, ++ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, ++ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, ++}; ++ ++/* subdev file operations */ ++static const struct v4l2_subdev_file_ops resizer_v4l2_file_ops = { ++ .open = resizer_init_formats, ++}; ++ ++/* subdev video operations */ ++static const struct v4l2_subdev_video_ops resizer_v4l2_video_ops = { ++ .s_stream = resizer_set_stream, ++}; ++ ++/* subdev pad operations */ ++static const struct v4l2_subdev_pad_ops resizer_v4l2_pad_ops = { ++ .enum_mbus_code = resizer_enum_mbus_code, ++ .enum_frame_size = resizer_enum_frame_size, ++ .get_fmt = resizer_get_format, ++ .set_fmt = resizer_set_format, ++ .get_crop = resizer_g_crop, ++ .set_crop = resizer_s_crop, ++}; ++ ++/* subdev operations */ ++static const struct v4l2_subdev_ops resizer_v4l2_ops = { ++ .core = &resizer_v4l2_core_ops, ++ .file = &resizer_v4l2_file_ops, ++ .video = &resizer_v4l2_video_ops, ++ .pad = &resizer_v4l2_pad_ops, ++}; ++ ++ ++/* ----------------------------------------------------------------------------- ++ * Media entity operations ++ */ ++ ++/* ++ * resizer_link_setup - Setup resizer connections. ++ * @entity : Pointer to media entity structure ++ * @local : Pointer to local pad array ++ * @remote : Pointer to remote pad array ++ * @flags : Link flags ++ * return -EINVAL or zero on success ++ */ ++static int resizer_link_setup(struct media_entity *entity, ++ const struct media_pad *local, ++ const struct media_pad *remote, u32 flags) ++{ ++ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); ++ struct isp_res_device *res = v4l2_get_subdevdata(sd); ++ ++ switch (local->index | media_entity_type(remote->entity)) { ++ case RESZ_PAD_SINK | MEDIA_ENT_T_DEVNODE: ++ /* read from memory */ ++ if (flags & MEDIA_LNK_FL_ENABLED) { ++ if (res->input == RESIZER_INPUT_VP) ++ return -EBUSY; ++ res->input = RESIZER_INPUT_MEMORY; ++ } else { ++ if (res->input == RESIZER_INPUT_MEMORY) ++ res->input = RESIZER_INPUT_NONE; ++ } ++ break; ++ ++ case RESZ_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: ++ /* read from ccdc or previewer */ ++ if (flags & MEDIA_LNK_FL_ENABLED) { ++ if (res->input == RESIZER_INPUT_MEMORY) ++ return -EBUSY; ++ res->input = RESIZER_INPUT_VP; ++ } else { ++ if (res->input == RESIZER_INPUT_VP) ++ res->input = RESIZER_INPUT_NONE; ++ } ++ break; ++ ++ case RESZ_PAD_SOURCE | MEDIA_ENT_T_DEVNODE: ++ /* resizer always write to memory */ ++ break; ++ ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++/* media operations */ ++static const struct media_entity_operations resizer_media_ops = { ++ .link_setup = resizer_link_setup, ++}; ++ ++/* ++ * resizer_init_entities - Initialize resizer subdev and media entity. ++ * @res : Pointer to resizer device structure ++ * return -ENOMEM or zero on success ++ */ ++static int resizer_init_entities(struct isp_res_device *res) ++{ ++ struct v4l2_subdev *sd = &res->subdev; ++ struct media_pad *pads = res->pads; ++ struct media_entity *me = &sd->entity; ++ int ret; ++ ++ res->input = RESIZER_INPUT_NONE; ++ ++ v4l2_subdev_init(sd, &resizer_v4l2_ops); ++ strlcpy(sd->name, "OMAP3 ISP resizer", sizeof(sd->name)); ++ sd->grp_id = 1 << 16; /* group ID for isp subdevs */ ++ v4l2_set_subdevdata(sd, res); ++ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; ++ ++ v4l2_ctrl_handler_init(&res->ctrls, 1); ++ sd->ctrl_handler = &res->ctrls; ++ ++ pads[RESZ_PAD_SINK].flags = MEDIA_PAD_FL_INPUT; ++ pads[RESZ_PAD_SOURCE].flags = MEDIA_PAD_FL_OUTPUT; ++ ++ me->ops = &resizer_media_ops; ++ ret = media_entity_init(me, RESZ_PADS_NUM, pads, 0); ++ if (ret < 0) ++ return ret; ++ ++ resizer_init_formats(sd, NULL); ++ ++ res->video_in.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; ++ res->video_in.ops = &resizer_video_ops; ++ res->video_in.isp = to_isp_device(res); ++ res->video_in.capture_mem = PAGE_ALIGN(4096 * 4096) * 2 * 3; ++ res->video_in.bpl_alignment = 32; ++ res->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ res->video_out.ops = &resizer_video_ops; ++ res->video_out.isp = to_isp_device(res); ++ res->video_out.capture_mem = PAGE_ALIGN(4096 * 4096) * 2 * 3; ++ res->video_out.bpl_alignment = 32; ++ ++ ret = omap3isp_video_init(&res->video_in, "resizer"); ++ if (ret < 0) ++ return ret; ++ ++ ret = omap3isp_video_init(&res->video_out, "resizer"); ++ if (ret < 0) ++ return ret; ++ ++ /* Connect the video nodes to the resizer subdev. */ ++ ret = media_entity_create_link(&res->video_in.video.entity, 0, ++ &res->subdev.entity, RESZ_PAD_SINK, 0); ++ if (ret < 0) ++ return ret; ++ ++ ret = media_entity_create_link(&res->subdev.entity, RESZ_PAD_SOURCE, ++ &res->video_out.video.entity, 0, 0); ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++void omap3isp_resizer_unregister_entities(struct isp_res_device *res) ++{ ++ media_entity_cleanup(&res->subdev.entity); ++ ++ v4l2_device_unregister_subdev(&res->subdev); ++ v4l2_ctrl_handler_free(&res->ctrls); ++ omap3isp_video_unregister(&res->video_in); ++ omap3isp_video_unregister(&res->video_out); ++} ++ ++int omap3isp_resizer_register_entities(struct isp_res_device *res, ++ struct v4l2_device *vdev) ++{ ++ int ret; ++ ++ /* Register the subdev and video nodes. */ ++ ret = v4l2_device_register_subdev(vdev, &res->subdev); ++ if (ret < 0) ++ goto error; ++ ++ ret = omap3isp_video_register(&res->video_in, vdev); ++ if (ret < 0) ++ goto error; ++ ++ ret = omap3isp_video_register(&res->video_out, vdev); ++ if (ret < 0) ++ goto error; ++ ++ return 0; ++ ++error: ++ omap3isp_resizer_unregister_entities(res); ++ return ret; ++} ++ ++/* ----------------------------------------------------------------------------- ++ * ISP resizer initialization and cleanup ++ */ ++ ++void omap3isp_resizer_cleanup(struct isp_device *isp) ++{ ++} ++ ++/* ++ * isp_resizer_init - Resizer initialization. ++ * @isp : Pointer to ISP device ++ * return -ENOMEM or zero on success ++ */ ++int omap3isp_resizer_init(struct isp_device *isp) ++{ ++ struct isp_res_device *res = &isp->isp_res; ++ int ret; ++ ++ init_waitqueue_head(&res->wait); ++ atomic_set(&res->stopping, 0); ++ ret = resizer_init_entities(res); ++ if (ret < 0) ++ goto out; ++ ++out: ++ if (ret) ++ omap3isp_resizer_cleanup(isp); ++ ++ return ret; ++} +diff --git a/drivers/media/video/isp/ispresizer.h b/drivers/media/video/isp/ispresizer.h +new file mode 100644 +index 0000000..39d188f +--- /dev/null ++++ b/drivers/media/video/isp/ispresizer.h +@@ -0,0 +1,150 @@ ++/* ++ * ispresizer.h ++ * ++ * TI OMAP3 ISP - Resizer module ++ * ++ * Copyright (C) 2010 Nokia Corporation ++ * Copyright (C) 2009 Texas Instruments, Inc ++ * ++ * Contacts: Laurent Pinchart ++ * Sakari Ailus ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#ifndef OMAP3_ISP_RESIZER_H ++#define OMAP3_ISP_RESIZER_H ++ ++#include ++#include ++ ++/* ++ * Constants for filter coefficents count ++ */ ++#define COEFF_CNT 32 ++ ++/* ++ * struct isprsz_coef - Structure for resizer filter coeffcients. ++ * @h_filter_coef_4tap: Horizontal filter coefficients for 8-phase/4-tap ++ * mode (.5x-4x) ++ * @v_filter_coef_4tap: Vertical filter coefficients for 8-phase/4-tap ++ * mode (.5x-4x) ++ * @h_filter_coef_7tap: Horizontal filter coefficients for 4-phase/7-tap ++ * mode (.25x-.5x) ++ * @v_filter_coef_7tap: Vertical filter coefficients for 4-phase/7-tap ++ * mode (.25x-.5x) ++ */ ++struct isprsz_coef { ++ u16 h_filter_coef_4tap[32]; ++ u16 v_filter_coef_4tap[32]; ++ /* Every 8th value is a dummy value in the following arrays: */ ++ u16 h_filter_coef_7tap[32]; ++ u16 v_filter_coef_7tap[32]; ++}; ++ ++/* Chrominance horizontal algorithm */ ++enum resizer_chroma_algo { ++ RSZ_THE_SAME = 0, /* Chrominance the same as Luminance */ ++ RSZ_BILINEAR = 1, /* Chrominance uses bilinear interpolation */ ++}; ++ ++/* Resizer input type select */ ++enum resizer_colors_type { ++ RSZ_YUV422 = 0, /* YUV422 color is interleaved */ ++ RSZ_COLOR8 = 1, /* Color separate data on 8 bits */ ++}; ++ ++/* ++ * Structure for horizontal and vertical resizing value ++ */ ++struct resizer_ratio { ++ u32 horz; ++ u32 vert; ++}; ++ ++/* ++ * Structure for luminance enhancer parameters. ++ */ ++struct resizer_luma_yenh { ++ u8 algo; /* algorithm select. */ ++ u8 gain; /* maximum gain. */ ++ u8 slope; /* slope. */ ++ u8 core; /* core offset. */ ++}; ++ ++enum resizer_input_entity { ++ RESIZER_INPUT_NONE, ++ RESIZER_INPUT_VP, /* input video port - prev or ccdc */ ++ RESIZER_INPUT_MEMORY, ++}; ++ ++/* Sink and source resizer pads */ ++#define RESZ_PAD_SINK 0 ++#define RESZ_PAD_SOURCE 1 ++#define RESZ_PADS_NUM 2 ++ ++/* ++ * struct isp_res_device - OMAP3 ISP resizer module ++ * @crop.request: Crop rectangle requested by the user ++ * @crop.active: Active crop rectangle (based on hardware requirements) ++ */ ++struct isp_res_device { ++ struct v4l2_subdev subdev; ++ struct media_pad pads[RESZ_PADS_NUM]; ++ struct v4l2_mbus_framefmt formats[RESZ_PADS_NUM]; ++ ++ struct v4l2_ctrl_handler ctrls; ++ ++ enum resizer_input_entity input; ++ struct isp_video video_in; ++ struct isp_video video_out; ++ unsigned int error; ++ ++ u32 addr_base; /* stored source buffer address in memory mode */ ++ u32 crop_offset; /* additional offset for crop in memory mode */ ++ struct resizer_ratio ratio; ++ int pm_state; ++ unsigned int applycrop:1; ++ enum isp_pipeline_stream_state state; ++ wait_queue_head_t wait; ++ atomic_t stopping; ++ ++ struct { ++ struct v4l2_rect request; ++ struct v4l2_rect active; ++ } crop; ++}; ++ ++struct isp_device; ++ ++int omap3isp_resizer_init(struct isp_device *isp); ++void omap3isp_resizer_cleanup(struct isp_device *isp); ++ ++int omap3isp_resizer_register_entities(struct isp_res_device *res, ++ struct v4l2_device *vdev); ++void omap3isp_resizer_unregister_entities(struct isp_res_device *res); ++void omap3isp_resizer_isr_frame_sync(struct isp_res_device *res); ++void omap3isp_resizer_isr(struct isp_res_device *isp_res); ++ ++void omap3isp_resizer_max_rate(struct isp_res_device *res, ++ unsigned int *max_rate); ++ ++void omap3isp_resizer_suspend(struct isp_res_device *isp_res); ++ ++void omap3isp_resizer_resume(struct isp_res_device *isp_res); ++ ++int omap3isp_resizer_busy(struct isp_res_device *isp_res); ++ ++#endif /* OMAP3_ISP_RESIZER_H */ +diff --git a/drivers/media/video/isp/ispstat.c b/drivers/media/video/isp/ispstat.c +new file mode 100644 +index 0000000..3406572 +--- /dev/null ++++ b/drivers/media/video/isp/ispstat.c +@@ -0,0 +1,1100 @@ ++/* ++ * ispstat.c ++ * ++ * TI OMAP3 ISP - Statistics core ++ * ++ * Copyright (C) 2010 Nokia Corporation ++ * Copyright (C) 2009 Texas Instruments, Inc ++ * ++ * Contacts: David Cohen ++ * Laurent Pinchart ++ * Sakari Ailus ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#include ++#include ++#include ++ ++#include "isp.h" ++ ++#define IS_COHERENT_BUF(stat) ((stat)->dma_ch >= 0) ++ ++/* ++ * MAGIC_SIZE must always be the greatest common divisor of ++ * AEWB_PACKET_SIZE and AF_PAXEL_SIZE. ++ */ ++#define MAGIC_SIZE 16 ++#define MAGIC_NUM 0x55 ++ ++/* HACK: AF module seems to be writing one more paxel data than it should. */ ++#define AF_EXTRA_DATA OMAP3ISP_AF_PAXEL_SIZE ++ ++/* ++ * HACK: H3A modules go to an invalid state after have a SBL overflow. It makes ++ * the next buffer to start to be written in the same point where the overflow ++ * occurred instead of the configured address. The only known way to make it to ++ * go back to a valid state is having a valid buffer processing. Of course it ++ * requires at least a doubled buffer size to avoid an access to invalid memory ++ * region. But it does not fix everything. It may happen more than one ++ * consecutive SBL overflows. In that case, it might be unpredictable how many ++ * buffers the allocated memory should fit. For that case, a recover ++ * configuration was created. It produces the minimum buffer size for each H3A ++ * module and decrease the change for more SBL overflows. This recover state ++ * will be enabled every time a SBL overflow occur. As the output buffer size ++ * isn't big, it's possible to have an extra size able to fit many recover ++ * buffers making it extreamily unlikely to have an access to invalid memory ++ * region. ++ */ ++#define NUM_H3A_RECOVER_BUFS 10 ++ ++/* ++ * HACK: Because of HW issues the generic layer sometimes need to have ++ * different behaviour for different statistic modules. ++ */ ++#define IS_H3A_AF(stat) ((stat) == &(stat)->isp->isp_af) ++#define IS_H3A_AEWB(stat) ((stat) == &(stat)->isp->isp_aewb) ++#define IS_H3A(stat) (IS_H3A_AF(stat) || IS_H3A_AEWB(stat)) ++ ++static void __isp_stat_buf_sync_magic(struct ispstat *stat, ++ struct ispstat_buffer *buf, ++ u32 buf_size, enum dma_data_direction dir, ++ void (*dma_sync)(struct device *, ++ dma_addr_t, unsigned long, size_t, ++ enum dma_data_direction)) ++{ ++ struct device *dev = stat->isp->dev; ++ struct page *pg; ++ dma_addr_t dma_addr; ++ u32 offset; ++ ++ /* Initial magic words */ ++ pg = vmalloc_to_page(buf->virt_addr); ++ dma_addr = page_to_dma(dev, pg); ++ dma_sync(dev, dma_addr, 0, MAGIC_SIZE, dir); ++ ++ /* Final magic words */ ++ pg = vmalloc_to_page(buf->virt_addr + buf_size); ++ dma_addr = page_to_dma(dev, pg); ++ offset = ((u32)buf->virt_addr + buf_size) & ~PAGE_MASK; ++ dma_sync(dev, dma_addr, offset, MAGIC_SIZE, dir); ++} ++ ++static void isp_stat_buf_sync_magic_for_device(struct ispstat *stat, ++ struct ispstat_buffer *buf, ++ u32 buf_size, ++ enum dma_data_direction dir) ++{ ++ if (IS_COHERENT_BUF(stat)) ++ return; ++ ++ __isp_stat_buf_sync_magic(stat, buf, buf_size, dir, ++ dma_sync_single_range_for_device); ++} ++ ++static void isp_stat_buf_sync_magic_for_cpu(struct ispstat *stat, ++ struct ispstat_buffer *buf, ++ u32 buf_size, ++ enum dma_data_direction dir) ++{ ++ if (IS_COHERENT_BUF(stat)) ++ return; ++ ++ __isp_stat_buf_sync_magic(stat, buf, buf_size, dir, ++ dma_sync_single_range_for_cpu); ++} ++ ++static int isp_stat_buf_check_magic(struct ispstat *stat, ++ struct ispstat_buffer *buf) ++{ ++ const u32 buf_size = IS_H3A_AF(stat) ? ++ buf->buf_size + AF_EXTRA_DATA : buf->buf_size; ++ u8 *w; ++ u8 *end; ++ int ret = -EINVAL; ++ ++ isp_stat_buf_sync_magic_for_cpu(stat, buf, buf_size, DMA_FROM_DEVICE); ++ ++ /* Checking initial magic numbers. They shouldn't be here anymore. */ ++ for (w = buf->virt_addr, end = w + MAGIC_SIZE; w < end; w++) ++ if (likely(*w != MAGIC_NUM)) ++ ret = 0; ++ ++ if (ret) { ++ dev_dbg(stat->isp->dev, "%s: beginning magic check does not " ++ "match.\n", stat->subdev.name); ++ return ret; ++ } ++ ++ /* Checking magic numbers at the end. They must be still here. */ ++ for (w = buf->virt_addr + buf_size, end = w + MAGIC_SIZE; ++ w < end; w++) { ++ if (unlikely(*w != MAGIC_NUM)) { ++ dev_dbg(stat->isp->dev, "%s: endding magic check does " ++ "not match.\n", stat->subdev.name); ++ return -EINVAL; ++ } ++ } ++ ++ isp_stat_buf_sync_magic_for_device(stat, buf, buf_size, ++ DMA_FROM_DEVICE); ++ ++ return 0; ++} ++ ++static void isp_stat_buf_insert_magic(struct ispstat *stat, ++ struct ispstat_buffer *buf) ++{ ++ const u32 buf_size = IS_H3A_AF(stat) ? ++ stat->buf_size + AF_EXTRA_DATA : stat->buf_size; ++ ++ isp_stat_buf_sync_magic_for_cpu(stat, buf, buf_size, DMA_FROM_DEVICE); ++ ++ /* ++ * Inserting MAGIC_NUM at the beginning and end of the buffer. ++ * buf->buf_size is set only after the buffer is queued. For now the ++ * right buf_size for the current configuration is pointed by ++ * stat->buf_size. ++ */ ++ memset(buf->virt_addr, MAGIC_NUM, MAGIC_SIZE); ++ memset(buf->virt_addr + buf_size, MAGIC_NUM, MAGIC_SIZE); ++ ++ isp_stat_buf_sync_magic_for_device(stat, buf, buf_size, ++ DMA_BIDIRECTIONAL); ++} ++ ++static void isp_stat_buf_sync_for_device(struct ispstat *stat, ++ struct ispstat_buffer *buf) ++{ ++ if (IS_COHERENT_BUF(stat)) ++ return; ++ ++ dma_sync_sg_for_device(stat->isp->dev, buf->iovm->sgt->sgl, ++ buf->iovm->sgt->nents, DMA_FROM_DEVICE); ++} ++ ++static void isp_stat_buf_sync_for_cpu(struct ispstat *stat, ++ struct ispstat_buffer *buf) ++{ ++ if (IS_COHERENT_BUF(stat)) ++ return; ++ ++ dma_sync_sg_for_cpu(stat->isp->dev, buf->iovm->sgt->sgl, ++ buf->iovm->sgt->nents, DMA_FROM_DEVICE); ++} ++ ++static void isp_stat_buf_clear(struct ispstat *stat) ++{ ++ int i; ++ ++ for (i = 0; i < STAT_MAX_BUFS; i++) ++ stat->buf[i].empty = 1; ++} ++ ++static struct ispstat_buffer * ++__isp_stat_buf_find(struct ispstat *stat, int look_empty) ++{ ++ struct ispstat_buffer *found = NULL; ++ int i; ++ ++ for (i = 0; i < STAT_MAX_BUFS; i++) { ++ struct ispstat_buffer *curr = &stat->buf[i]; ++ ++ /* ++ * Don't select the buffer which is being copied to ++ * userspace or used by the module. ++ */ ++ if (curr == stat->locked_buf || curr == stat->active_buf) ++ continue; ++ ++ /* Don't select uninitialised buffers if it's not required */ ++ if (!look_empty && curr->empty) ++ continue; ++ ++ /* Pick uninitialised buffer over anything else if look_empty */ ++ if (curr->empty) { ++ found = curr; ++ break; ++ } ++ ++ /* Choose the oldest buffer */ ++ if (!found || ++ (s32)curr->frame_number - (s32)found->frame_number < 0) ++ found = curr; ++ } ++ ++ return found; ++} ++ ++static inline struct ispstat_buffer * ++isp_stat_buf_find_oldest(struct ispstat *stat) ++{ ++ return __isp_stat_buf_find(stat, 0); ++} ++ ++static inline struct ispstat_buffer * ++isp_stat_buf_find_oldest_or_empty(struct ispstat *stat) ++{ ++ return __isp_stat_buf_find(stat, 1); ++} ++ ++static int isp_stat_buf_queue(struct ispstat *stat) ++{ ++ if (!stat->active_buf) ++ return STAT_NO_BUF; ++ ++ do_gettimeofday(&stat->active_buf->ts); ++ ++ stat->active_buf->buf_size = stat->buf_size; ++ if (isp_stat_buf_check_magic(stat, stat->active_buf)) { ++ dev_dbg(stat->isp->dev, "%s: data wasn't properly written.\n", ++ stat->subdev.name); ++ return STAT_NO_BUF; ++ } ++ stat->active_buf->config_counter = stat->config_counter; ++ stat->active_buf->frame_number = stat->frame_number; ++ stat->active_buf->empty = 0; ++ stat->active_buf = NULL; ++ ++ return STAT_BUF_DONE; ++} ++ ++/* Get next free buffer to write the statistics to and mark it active. */ ++static void isp_stat_buf_next(struct ispstat *stat) ++{ ++ if (unlikely(stat->active_buf)) ++ /* Overwriting unused active buffer */ ++ dev_dbg(stat->isp->dev, "%s: new buffer requested without " ++ "queuing active one.\n", ++ stat->subdev.name); ++ else ++ stat->active_buf = isp_stat_buf_find_oldest_or_empty(stat); ++} ++ ++static void isp_stat_buf_release(struct ispstat *stat) ++{ ++ unsigned long flags; ++ ++ isp_stat_buf_sync_for_device(stat, stat->locked_buf); ++ spin_lock_irqsave(&stat->isp->stat_lock, flags); ++ stat->locked_buf = NULL; ++ spin_unlock_irqrestore(&stat->isp->stat_lock, flags); ++} ++ ++/* Get buffer to userspace. */ ++static struct ispstat_buffer *isp_stat_buf_get(struct ispstat *stat, ++ struct omap3isp_stat_data *data) ++{ ++ int rval = 0; ++ unsigned long flags; ++ struct ispstat_buffer *buf; ++ ++ spin_lock_irqsave(&stat->isp->stat_lock, flags); ++ ++ while (1) { ++ buf = isp_stat_buf_find_oldest(stat); ++ if (!buf) { ++ spin_unlock_irqrestore(&stat->isp->stat_lock, flags); ++ dev_dbg(stat->isp->dev, "%s: cannot find a buffer.\n", ++ stat->subdev.name); ++ return ERR_PTR(-EBUSY); ++ } ++ if (isp_stat_buf_check_magic(stat, buf)) { ++ dev_dbg(stat->isp->dev, "%s: current buffer has " ++ "corrupted data\n.", stat->subdev.name); ++ /* Mark empty because it doesn't have valid data. */ ++ buf->empty = 1; ++ } else { ++ /* Buffer isn't corrupted. */ ++ break; ++ } ++ } ++ ++ stat->locked_buf = buf; ++ ++ spin_unlock_irqrestore(&stat->isp->stat_lock, flags); ++ ++ if (buf->buf_size > data->buf_size) { ++ dev_warn(stat->isp->dev, "%s: userspace's buffer size is " ++ "not enough.\n", stat->subdev.name); ++ isp_stat_buf_release(stat); ++ return ERR_PTR(-EINVAL); ++ } ++ ++ isp_stat_buf_sync_for_cpu(stat, buf); ++ ++ rval = copy_to_user(data->buf, ++ buf->virt_addr, ++ buf->buf_size); ++ ++ if (rval) { ++ dev_info(stat->isp->dev, ++ "%s: failed copying %d bytes of stat data\n", ++ stat->subdev.name, rval); ++ buf = ERR_PTR(-EFAULT); ++ isp_stat_buf_release(stat); ++ } ++ ++ return buf; ++} ++ ++static void isp_stat_bufs_free(struct ispstat *stat) ++{ ++ struct isp_device *isp = stat->isp; ++ int i; ++ ++ for (i = 0; i < STAT_MAX_BUFS; i++) { ++ struct ispstat_buffer *buf = &stat->buf[i]; ++ ++ if (!IS_COHERENT_BUF(stat)) { ++ if (IS_ERR_OR_NULL((void *)buf->iommu_addr)) ++ continue; ++ if (buf->iovm) ++ dma_unmap_sg(isp->dev, buf->iovm->sgt->sgl, ++ buf->iovm->sgt->nents, ++ DMA_FROM_DEVICE); ++ iommu_vfree(isp->iommu, buf->iommu_addr); ++ } else { ++ if (!buf->virt_addr) ++ continue; ++ dma_free_coherent(stat->isp->dev, stat->buf_alloc_size, ++ buf->virt_addr, buf->dma_addr); ++ } ++ buf->iommu_addr = 0; ++ buf->iovm = NULL; ++ buf->dma_addr = 0; ++ buf->virt_addr = NULL; ++ buf->empty = 1; ++ } ++ ++ dev_dbg(stat->isp->dev, "%s: all buffers were freed.\n", ++ stat->subdev.name); ++ ++ stat->buf_alloc_size = 0; ++ stat->active_buf = NULL; ++} ++ ++static int isp_stat_bufs_alloc_iommu(struct ispstat *stat, unsigned int size) ++{ ++ struct isp_device *isp = stat->isp; ++ int i; ++ ++ stat->buf_alloc_size = size; ++ ++ for (i = 0; i < STAT_MAX_BUFS; i++) { ++ struct ispstat_buffer *buf = &stat->buf[i]; ++ struct iovm_struct *iovm; ++ ++ WARN_ON(buf->dma_addr); ++ buf->iommu_addr = iommu_vmalloc(isp->iommu, 0, size, ++ IOMMU_FLAG); ++ if (IS_ERR((void *)buf->iommu_addr)) { ++ dev_err(stat->isp->dev, ++ "%s: Can't acquire memory for " ++ "buffer %d\n", stat->subdev.name, i); ++ isp_stat_bufs_free(stat); ++ return -ENOMEM; ++ } ++ ++ iovm = find_iovm_area(isp->iommu, buf->iommu_addr); ++ if (!iovm || ++ !dma_map_sg(isp->dev, iovm->sgt->sgl, iovm->sgt->nents, ++ DMA_FROM_DEVICE)) { ++ isp_stat_bufs_free(stat); ++ return -ENOMEM; ++ } ++ buf->iovm = iovm; ++ ++ buf->virt_addr = da_to_va(stat->isp->iommu, ++ (u32)buf->iommu_addr); ++ buf->empty = 1; ++ dev_dbg(stat->isp->dev, "%s: buffer[%d] allocated." ++ "iommu_addr=0x%08lx virt_addr=0x%08lx", ++ stat->subdev.name, i, buf->iommu_addr, ++ (unsigned long)buf->virt_addr); ++ } ++ ++ return 0; ++} ++ ++static int isp_stat_bufs_alloc_dma(struct ispstat *stat, unsigned int size) ++{ ++ int i; ++ ++ stat->buf_alloc_size = size; ++ ++ for (i = 0; i < STAT_MAX_BUFS; i++) { ++ struct ispstat_buffer *buf = &stat->buf[i]; ++ ++ WARN_ON(buf->iommu_addr); ++ buf->virt_addr = dma_alloc_coherent(stat->isp->dev, size, ++ &buf->dma_addr, GFP_KERNEL | GFP_DMA); ++ ++ if (!buf->virt_addr || !buf->dma_addr) { ++ dev_info(stat->isp->dev, ++ "%s: Can't acquire memory for " ++ "DMA buffer %d\n", stat->subdev.name, i); ++ isp_stat_bufs_free(stat); ++ return -ENOMEM; ++ } ++ buf->empty = 1; ++ ++ dev_dbg(stat->isp->dev, "%s: buffer[%d] allocated." ++ "dma_addr=0x%08lx virt_addr=0x%08lx\n", ++ stat->subdev.name, i, (unsigned long)buf->dma_addr, ++ (unsigned long)buf->virt_addr); ++ } ++ ++ return 0; ++} ++ ++static int isp_stat_bufs_alloc(struct ispstat *stat, u32 size) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&stat->isp->stat_lock, flags); ++ ++ BUG_ON(stat->locked_buf != NULL); ++ ++ /* Are the old buffers big enough? */ ++ if (stat->buf_alloc_size >= size) { ++ spin_unlock_irqrestore(&stat->isp->stat_lock, flags); ++ return 0; ++ } ++ ++ if (stat->state != ISPSTAT_DISABLED || stat->buf_processing) { ++ dev_info(stat->isp->dev, ++ "%s: trying to allocate memory when busy\n", ++ stat->subdev.name); ++ spin_unlock_irqrestore(&stat->isp->stat_lock, flags); ++ return -EBUSY; ++ } ++ ++ spin_unlock_irqrestore(&stat->isp->stat_lock, flags); ++ ++ isp_stat_bufs_free(stat); ++ ++ if (IS_COHERENT_BUF(stat)) ++ return isp_stat_bufs_alloc_dma(stat, size); ++ else ++ return isp_stat_bufs_alloc_iommu(stat, size); ++} ++ ++static void isp_stat_queue_event(struct ispstat *stat, int err) ++{ ++ struct video_device *vdev = &stat->subdev.devnode; ++ struct v4l2_event event; ++ struct omap3isp_stat_event_status *status = (void *)event.u.data; ++ ++ memset(&event, 0, sizeof(event)); ++ if (!err) { ++ status->frame_number = stat->frame_number; ++ status->config_counter = stat->config_counter; ++ } else { ++ status->buf_err = 1; ++ } ++ event.type = stat->event_type; ++ v4l2_event_queue(vdev, &event); ++} ++ ++ ++/* ++ * omap3isp_stat_request_statistics - Request statistics. ++ * @data: Pointer to return statistics data. ++ * ++ * Returns 0 if successful. ++ */ ++int omap3isp_stat_request_statistics(struct ispstat *stat, ++ struct omap3isp_stat_data *data) ++{ ++ struct ispstat_buffer *buf; ++ ++ if (stat->state != ISPSTAT_ENABLED) { ++ dev_dbg(stat->isp->dev, "%s: engine not enabled.\n", ++ stat->subdev.name); ++ return -EINVAL; ++ } ++ ++ mutex_lock(&stat->ioctl_lock); ++ buf = isp_stat_buf_get(stat, data); ++ if (IS_ERR(buf)) { ++ mutex_unlock(&stat->ioctl_lock); ++ return PTR_ERR(buf); ++ } ++ ++ data->ts = buf->ts; ++ data->config_counter = buf->config_counter; ++ data->frame_number = buf->frame_number; ++ data->buf_size = buf->buf_size; ++ ++ /* ++ * Deprecated. Number of new buffers is always equal to number of ++ * queued events without error flag. By setting it to 0, userspace ++ * won't try to request new buffer without receiving new event. ++ * This field must go away in future. ++ */ ++ data->new_bufs = 0; ++ ++ buf->empty = 1; ++ isp_stat_buf_release(stat); ++ mutex_unlock(&stat->ioctl_lock); ++ ++ return 0; ++} ++ ++/* ++ * omap3isp_stat_config - Receives new statistic engine configuration. ++ * @new_conf: Pointer to config structure. ++ * ++ * Returns 0 if successful, -EINVAL if new_conf pointer is NULL, -ENOMEM if ++ * was unable to allocate memory for the buffer, or other errors if parameters ++ * are invalid. ++ */ ++int omap3isp_stat_config(struct ispstat *stat, void *new_conf) ++{ ++ int ret; ++ unsigned long irqflags; ++ struct ispstat_generic_config *user_cfg = new_conf; ++ u32 buf_size = user_cfg->buf_size; ++ ++ if (!new_conf) { ++ dev_dbg(stat->isp->dev, "%s: configuration is NULL\n", ++ stat->subdev.name); ++ return -EINVAL; ++ } ++ ++ mutex_lock(&stat->ioctl_lock); ++ ++ dev_dbg(stat->isp->dev, "%s: configuring module with buffer " ++ "size=0x%08lx\n", stat->subdev.name, (unsigned long)buf_size); ++ ++ ret = stat->ops->validate_params(stat, new_conf); ++ if (ret) { ++ mutex_unlock(&stat->ioctl_lock); ++ dev_dbg(stat->isp->dev, "%s: configuration values are " ++ "invalid.\n", stat->subdev.name); ++ return ret; ++ } ++ ++ if (buf_size != user_cfg->buf_size) ++ dev_dbg(stat->isp->dev, "%s: driver has corrected buffer size " ++ "request to 0x%08lx\n", stat->subdev.name, ++ (unsigned long)user_cfg->buf_size); ++ ++ /* ++ * Hack: H3A modules may need a doubled buffer size to avoid access ++ * to a invalid memory address after a SBL overflow. ++ * The buffer size is always PAGE_ALIGNED. ++ * Hack 2: MAGIC_SIZE is added to buf_size so a magic word can be ++ * inserted at the end to data integrity check purpose. ++ * Hack 3: AF module writes one paxel data more than it should, so ++ * the buffer allocation must consider it to avoid invalid memory ++ * access. ++ * Hack 4: H3A need to allocate extra space for the recover state. ++ */ ++ if (IS_H3A(stat)) { ++ buf_size = user_cfg->buf_size * 2 + MAGIC_SIZE; ++ if (IS_H3A_AF(stat)) ++ /* ++ * Adding one extra paxel data size for each recover ++ * buffer + 2 regular ones. ++ */ ++ buf_size += AF_EXTRA_DATA * (NUM_H3A_RECOVER_BUFS + 2); ++ if (stat->recover_priv) { ++ struct ispstat_generic_config *recover_cfg = ++ stat->recover_priv; ++ buf_size += recover_cfg->buf_size * ++ NUM_H3A_RECOVER_BUFS; ++ } ++ buf_size = PAGE_ALIGN(buf_size); ++ } else { /* Histogram */ ++ buf_size = PAGE_ALIGN(user_cfg->buf_size + MAGIC_SIZE); ++ } ++ ++ ret = isp_stat_bufs_alloc(stat, buf_size); ++ if (ret) { ++ mutex_unlock(&stat->ioctl_lock); ++ return ret; ++ } ++ ++ spin_lock_irqsave(&stat->isp->stat_lock, irqflags); ++ stat->ops->set_params(stat, new_conf); ++ spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags); ++ ++ /* ++ * Returning the right future config_counter for this setup, so ++ * userspace can *know* when it has been applied. ++ */ ++ user_cfg->config_counter = stat->config_counter + stat->inc_config; ++ ++ /* Module has a valid configuration. */ ++ stat->configured = 1; ++ dev_dbg(stat->isp->dev, "%s: module has been successfully " ++ "configured.\n", stat->subdev.name); ++ ++ mutex_unlock(&stat->ioctl_lock); ++ ++ return 0; ++} ++ ++/* ++ * isp_stat_buf_process - Process statistic buffers. ++ * @buf_state: points out if buffer is ready to be processed. It's necessary ++ * because histogram needs to copy the data from internal memory ++ * before be able to process the buffer. ++ */ ++static int isp_stat_buf_process(struct ispstat *stat, int buf_state) ++{ ++ int ret = STAT_NO_BUF; ++ ++ if (!atomic_add_unless(&stat->buf_err, -1, 0) && ++ buf_state == STAT_BUF_DONE && stat->state == ISPSTAT_ENABLED) { ++ ret = isp_stat_buf_queue(stat); ++ isp_stat_buf_next(stat); ++ } ++ ++ return ret; ++} ++ ++int omap3isp_stat_pcr_busy(struct ispstat *stat) ++{ ++ return stat->ops->busy(stat); ++} ++ ++int omap3isp_stat_busy(struct ispstat *stat) ++{ ++ return omap3isp_stat_pcr_busy(stat) | stat->buf_processing | ++ (stat->state != ISPSTAT_DISABLED); ++} ++ ++/* ++ * isp_stat_pcr_enable - Disables/Enables statistic engines. ++ * @pcr_enable: 0/1 - Disables/Enables the engine. ++ * ++ * Must be called from ISP driver when the module is idle and synchronized ++ * with CCDC. ++ */ ++static void isp_stat_pcr_enable(struct ispstat *stat, u8 pcr_enable) ++{ ++ if ((stat->state != ISPSTAT_ENABLING && ++ stat->state != ISPSTAT_ENABLED) && pcr_enable) ++ /* Userspace has disabled the module. Aborting. */ ++ return; ++ ++ stat->ops->enable(stat, pcr_enable); ++ if (stat->state == ISPSTAT_DISABLING && !pcr_enable) ++ stat->state = ISPSTAT_DISABLED; ++ else if (stat->state == ISPSTAT_ENABLING && pcr_enable) ++ stat->state = ISPSTAT_ENABLED; ++} ++ ++void omap3isp_stat_suspend(struct ispstat *stat) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&stat->isp->stat_lock, flags); ++ ++ if (stat->state != ISPSTAT_DISABLED) ++ stat->ops->enable(stat, 0); ++ if (stat->state == ISPSTAT_ENABLED) ++ stat->state = ISPSTAT_SUSPENDED; ++ ++ spin_unlock_irqrestore(&stat->isp->stat_lock, flags); ++} ++ ++void omap3isp_stat_resume(struct ispstat *stat) ++{ ++ /* Module will be re-enabled with its pipeline */ ++ if (stat->state == ISPSTAT_SUSPENDED) ++ stat->state = ISPSTAT_ENABLING; ++} ++ ++static void isp_stat_try_enable(struct ispstat *stat) ++{ ++ unsigned long irqflags; ++ ++ if (stat->priv == NULL) ++ /* driver wasn't initialised */ ++ return; ++ ++ spin_lock_irqsave(&stat->isp->stat_lock, irqflags); ++ if (stat->state == ISPSTAT_ENABLING && !stat->buf_processing && ++ stat->buf_alloc_size) { ++ /* ++ * Userspace's requested to enable the engine but it wasn't yet. ++ * Let's do that now. ++ */ ++ stat->update = 1; ++ isp_stat_buf_next(stat); ++ stat->ops->setup_regs(stat, stat->priv); ++ isp_stat_buf_insert_magic(stat, stat->active_buf); ++ ++ /* ++ * H3A module has some hw issues which forces the driver to ++ * ignore next buffers even if it was disabled in the meantime. ++ * On the other hand, Histogram shouldn't ignore buffers anymore ++ * if it's being enabled. ++ */ ++ if (!IS_H3A(stat)) ++ atomic_set(&stat->buf_err, 0); ++ ++ isp_stat_pcr_enable(stat, 1); ++ spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags); ++ dev_dbg(stat->isp->dev, "%s: module is enabled.\n", ++ stat->subdev.name); ++ } else { ++ spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags); ++ } ++} ++ ++void omap3isp_stat_isr_frame_sync(struct ispstat *stat) ++{ ++ isp_stat_try_enable(stat); ++} ++ ++void omap3isp_stat_sbl_overflow(struct ispstat *stat) ++{ ++ unsigned long irqflags; ++ ++ spin_lock_irqsave(&stat->isp->stat_lock, irqflags); ++ /* ++ * Due to a H3A hw issue which prevents the next buffer to start from ++ * the correct memory address, 2 buffers must be ignored. ++ */ ++ atomic_set(&stat->buf_err, 2); ++ ++ /* ++ * If more than one SBL overflow happen in a row, H3A module may access ++ * invalid memory region. ++ * stat->sbl_ovl_recover is set to tell to the driver to temporarily use ++ * a soft configuration which helps to avoid consecutive overflows. ++ */ ++ if (stat->recover_priv) ++ stat->sbl_ovl_recover = 1; ++ spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags); ++} ++ ++/* ++ * omap3isp_stat_enable - Disable/Enable statistic engine as soon as possible ++ * @enable: 0/1 - Disables/Enables the engine. ++ * ++ * Client should configure all the module registers before this. ++ * This function can be called from a userspace request. ++ */ ++int omap3isp_stat_enable(struct ispstat *stat, u8 enable) ++{ ++ unsigned long irqflags; ++ ++ dev_dbg(stat->isp->dev, "%s: user wants to %s module.\n", ++ stat->subdev.name, enable ? "enable" : "disable"); ++ ++ /* Prevent enabling while configuring */ ++ mutex_lock(&stat->ioctl_lock); ++ ++ spin_lock_irqsave(&stat->isp->stat_lock, irqflags); ++ ++ if (!stat->configured && enable) { ++ spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags); ++ mutex_unlock(&stat->ioctl_lock); ++ dev_dbg(stat->isp->dev, "%s: cannot enable module as it's " ++ "never been successfully configured so far.\n", ++ stat->subdev.name); ++ return -EINVAL; ++ } ++ ++ if (enable) { ++ if (stat->state == ISPSTAT_DISABLING) ++ /* Previous disabling request wasn't done yet */ ++ stat->state = ISPSTAT_ENABLED; ++ else if (stat->state == ISPSTAT_DISABLED) ++ /* Module is now being enabled */ ++ stat->state = ISPSTAT_ENABLING; ++ } else { ++ if (stat->state == ISPSTAT_ENABLING) { ++ /* Previous enabling request wasn't done yet */ ++ stat->state = ISPSTAT_DISABLED; ++ } else if (stat->state == ISPSTAT_ENABLED) { ++ /* Module is now being disabled */ ++ stat->state = ISPSTAT_DISABLING; ++ isp_stat_buf_clear(stat); ++ } ++ } ++ ++ spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags); ++ mutex_unlock(&stat->ioctl_lock); ++ ++ return 0; ++} ++ ++int omap3isp_stat_s_stream(struct v4l2_subdev *subdev, int enable) ++{ ++ struct ispstat *stat = v4l2_get_subdevdata(subdev); ++ ++ if (enable) { ++ /* ++ * Only set enable PCR bit if the module was previously ++ * enabled through ioct. ++ */ ++ isp_stat_try_enable(stat); ++ } else { ++ unsigned long flags; ++ /* Disable PCR bit and config enable field */ ++ omap3isp_stat_enable(stat, 0); ++ spin_lock_irqsave(&stat->isp->stat_lock, flags); ++ stat->ops->enable(stat, 0); ++ spin_unlock_irqrestore(&stat->isp->stat_lock, flags); ++ ++ /* ++ * If module isn't busy, a new interrupt may come or not to ++ * set the state to DISABLED. As Histogram needs to read its ++ * internal memory to clear it, let interrupt handler ++ * responsible of changing state to DISABLED. If the last ++ * interrupt is coming, it's still safe as the handler will ++ * ignore the second time when state is already set to DISABLED. ++ * It's necessary to synchronize Histogram with streamoff, once ++ * the module may be considered idle before last SDMA transfer ++ * starts if we return here. ++ */ ++ if (!omap3isp_stat_pcr_busy(stat)) ++ omap3isp_stat_isr(stat); ++ ++ dev_dbg(stat->isp->dev, "%s: module is being disabled\n", ++ stat->subdev.name); ++ } ++ ++ return 0; ++} ++ ++/* ++ * __stat_isr - Interrupt handler for statistic drivers ++ */ ++static void __stat_isr(struct ispstat *stat, int from_dma) ++{ ++ int ret = STAT_BUF_DONE; ++ int buf_processing; ++ unsigned long irqflags; ++ struct isp_pipeline *pipe; ++ ++ /* ++ * stat->buf_processing must be set before disable module. It's ++ * necessary to not inform too early the buffers aren't busy in case ++ * of SDMA is going to be used. ++ */ ++ spin_lock_irqsave(&stat->isp->stat_lock, irqflags); ++ if (stat->state == ISPSTAT_DISABLED) { ++ spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags); ++ return; ++ } ++ buf_processing = stat->buf_processing; ++ stat->buf_processing = 1; ++ stat->ops->enable(stat, 0); ++ ++ if (buf_processing && !from_dma) { ++ if (stat->state == ISPSTAT_ENABLED) { ++ spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags); ++ dev_err(stat->isp->dev, ++ "%s: interrupt occurred when module was still " ++ "processing a buffer.\n", stat->subdev.name); ++ ret = STAT_NO_BUF; ++ goto out; ++ } else { ++ /* ++ * Interrupt handler was called from streamoff when ++ * the module wasn't busy anymore to ensure it is being ++ * disabled after process last buffer. If such buffer ++ * processing has already started, no need to do ++ * anything else. ++ */ ++ spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags); ++ return; ++ } ++ } ++ spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags); ++ ++ /* If it's busy we can't process this buffer anymore */ ++ if (!omap3isp_stat_pcr_busy(stat)) { ++ if (!from_dma && stat->ops->buf_process) ++ /* Module still need to copy data to buffer. */ ++ ret = stat->ops->buf_process(stat); ++ if (ret == STAT_BUF_WAITING_DMA) ++ /* Buffer is not ready yet */ ++ return; ++ ++ spin_lock_irqsave(&stat->isp->stat_lock, irqflags); ++ ++ /* ++ * Histogram needs to read its internal memory to clear it ++ * before be disabled. For that reason, common statistic layer ++ * can return only after call stat's buf_process() operator. ++ */ ++ if (stat->state == ISPSTAT_DISABLING) { ++ stat->state = ISPSTAT_DISABLED; ++ spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags); ++ stat->buf_processing = 0; ++ return; ++ } ++ pipe = to_isp_pipeline(&stat->subdev.entity); ++ stat->frame_number = atomic_read(&pipe->frame_number); ++ ++ /* ++ * Before this point, 'ret' stores the buffer's status if it's ++ * ready to be processed. Afterwards, it holds the status if ++ * it was processed successfully. ++ */ ++ ret = isp_stat_buf_process(stat, ret); ++ ++ if (likely(!stat->sbl_ovl_recover)) { ++ stat->ops->setup_regs(stat, stat->priv); ++ } else { ++ /* ++ * Using recover config to increase the chance to have ++ * a good buffer processing and make the H3A module to ++ * go back to a valid state. ++ */ ++ stat->update = 1; ++ stat->ops->setup_regs(stat, stat->recover_priv); ++ stat->sbl_ovl_recover = 0; ++ ++ /* ++ * Set 'update' in case of the module needs to use ++ * regular configuration after next buffer. ++ */ ++ stat->update = 1; ++ } ++ ++ isp_stat_buf_insert_magic(stat, stat->active_buf); ++ ++ /* ++ * Hack: H3A modules may access invalid memory address or send ++ * corrupted data to userspace if more than 1 SBL overflow ++ * happens in a row without re-writing its buffer's start memory ++ * address in the meantime. Such situation is avoided if the ++ * module is not immediately re-enabled when the ISR misses the ++ * timing to process the buffer and to setup the registers. ++ * Because of that, pcr_enable(1) was moved to inside this 'if' ++ * block. But the next interruption will still happen as during ++ * pcr_enable(0) the module was busy. ++ */ ++ isp_stat_pcr_enable(stat, 1); ++ spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags); ++ } else { ++ /* ++ * If a SBL overflow occurs and the H3A driver misses the timing ++ * to process the buffer, stat->buf_err is set and won't be ++ * cleared now. So the next buffer will be correctly ignored. ++ * It's necessary due to a hw issue which makes the next H3A ++ * buffer to start from the memory address where the previous ++ * one stopped, instead of start where it was configured to. ++ * Do not "stat->buf_err = 0" here. ++ */ ++ ++ if (stat->ops->buf_process) ++ /* ++ * Driver may need to erase current data prior to ++ * process a new buffer. If it misses the timing, the ++ * next buffer might be wrong. So should be ignored. ++ * It happens only for Histogram. ++ */ ++ atomic_set(&stat->buf_err, 1); ++ ++ ret = STAT_NO_BUF; ++ dev_dbg(stat->isp->dev, "%s: cannot process buffer, " ++ "device is busy.\n", stat->subdev.name); ++ } ++ ++out: ++ stat->buf_processing = 0; ++ isp_stat_queue_event(stat, ret != STAT_BUF_DONE); ++} ++ ++void omap3isp_stat_isr(struct ispstat *stat) ++{ ++ __stat_isr(stat, 0); ++} ++ ++void omap3isp_stat_dma_isr(struct ispstat *stat) ++{ ++ __stat_isr(stat, 1); ++} ++ ++static int isp_stat_init_entities(struct ispstat *stat, const char *name, ++ const struct v4l2_subdev_ops *sd_ops) ++{ ++ struct v4l2_subdev *subdev = &stat->subdev; ++ struct media_entity *me = &subdev->entity; ++ ++ v4l2_subdev_init(subdev, sd_ops); ++ snprintf(subdev->name, V4L2_SUBDEV_NAME_SIZE, "OMAP3 ISP %s", name); ++ subdev->grp_id = 1 << 16; /* group ID for isp subdevs */ ++ subdev->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE; ++ subdev->nevents = STAT_NEVENTS; ++ v4l2_set_subdevdata(subdev, stat); ++ ++ stat->pad.flags = MEDIA_PAD_FL_INPUT; ++ me->ops = NULL; ++ ++ return media_entity_init(me, 1, &stat->pad, 0); ++} ++ ++int omap3isp_stat_subscribe_event(struct v4l2_subdev *subdev, ++ struct v4l2_fh *fh, ++ struct v4l2_event_subscription *sub) ++{ ++ struct ispstat *stat = v4l2_get_subdevdata(subdev); ++ ++ if (sub->type != stat->event_type) ++ return -EINVAL; ++ ++ return v4l2_event_subscribe(fh, sub); ++} ++ ++int omap3isp_stat_unsubscribe_event(struct v4l2_subdev *subdev, ++ struct v4l2_fh *fh, ++ struct v4l2_event_subscription *sub) ++{ ++ return v4l2_event_unsubscribe(fh, sub); ++} ++ ++void omap3isp_stat_unregister_entities(struct ispstat *stat) ++{ ++ media_entity_cleanup(&stat->subdev.entity); ++ v4l2_device_unregister_subdev(&stat->subdev); ++} ++ ++int omap3isp_stat_register_entities(struct ispstat *stat, ++ struct v4l2_device *vdev) ++{ ++ return v4l2_device_register_subdev(vdev, &stat->subdev); ++} ++ ++int omap3isp_stat_init(struct ispstat *stat, const char *name, ++ const struct v4l2_subdev_ops *sd_ops) ++{ ++ stat->buf = kcalloc(STAT_MAX_BUFS, sizeof(*stat->buf), GFP_KERNEL); ++ if (!stat->buf) ++ return -ENOMEM; ++ isp_stat_buf_clear(stat); ++ mutex_init(&stat->ioctl_lock); ++ atomic_set(&stat->buf_err, 0); ++ ++ return isp_stat_init_entities(stat, name, sd_ops); ++} ++ ++void omap3isp_stat_free(struct ispstat *stat) ++{ ++ isp_stat_bufs_free(stat); ++ kfree(stat->buf); ++} +diff --git a/drivers/media/video/isp/ispstat.h b/drivers/media/video/isp/ispstat.h +new file mode 100644 +index 0000000..5298d33 +--- /dev/null ++++ b/drivers/media/video/isp/ispstat.h +@@ -0,0 +1,169 @@ ++/* ++ * ispstat.h ++ * ++ * TI OMAP3 ISP - Statistics core ++ * ++ * Copyright (C) 2010 Nokia Corporation ++ * Copyright (C) 2009 Texas Instruments, Inc ++ * ++ * Contacts: David Cohen ++ * Laurent Pinchart ++ * Sakari Ailus ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#ifndef OMAP3_ISP_STAT_H ++#define OMAP3_ISP_STAT_H ++ ++#include ++#include ++#include ++#include ++ ++#include "isp.h" ++#include "ispvideo.h" ++ ++#define STAT_MAX_BUFS 5 ++#define STAT_NEVENTS 8 ++ ++#define STAT_BUF_DONE 0 /* Buffer is ready */ ++#define STAT_NO_BUF 1 /* An error has occurred */ ++#define STAT_BUF_WAITING_DMA 2 /* Histogram only: DMA is running */ ++ ++struct ispstat; ++ ++struct ispstat_buffer { ++ unsigned long iommu_addr; ++ struct iovm_struct *iovm; ++ void *virt_addr; ++ dma_addr_t dma_addr; ++ struct timeval ts; ++ u32 buf_size; ++ u32 frame_number; ++ u16 config_counter; ++ u8 empty; ++}; ++ ++struct ispstat_ops { ++ /* ++ * Validate new params configuration. ++ * new_conf->buf_size value must be changed to the exact buffer size ++ * necessary for the new configuration if it's smaller. ++ */ ++ int (*validate_params)(struct ispstat *stat, void *new_conf); ++ ++ /* ++ * Save new params configuration. ++ * stat->priv->buf_size value must be set to the exact buffer size for ++ * the new configuration. ++ * stat->update is set to 1 if new configuration is different than ++ * current one. ++ */ ++ void (*set_params)(struct ispstat *stat, void *new_conf); ++ ++ /* Apply stored configuration. */ ++ void (*setup_regs)(struct ispstat *stat, void *priv); ++ ++ /* Enable/Disable module. */ ++ void (*enable)(struct ispstat *stat, int enable); ++ ++ /* Verify is module is busy. */ ++ int (*busy)(struct ispstat *stat); ++ ++ /* Used for specific operations during generic buf process task. */ ++ int (*buf_process)(struct ispstat *stat); ++}; ++ ++enum ispstat_state_t { ++ ISPSTAT_DISABLED = 0, ++ ISPSTAT_DISABLING, ++ ISPSTAT_ENABLED, ++ ISPSTAT_ENABLING, ++ ISPSTAT_SUSPENDED, ++}; ++ ++struct ispstat { ++ struct v4l2_subdev subdev; ++ struct media_pad pad; /* sink pad */ ++ ++ /* Control */ ++ unsigned configured:1; ++ unsigned update:1; ++ unsigned buf_processing:1; ++ unsigned sbl_ovl_recover:1; ++ u8 inc_config; ++ atomic_t buf_err; ++ enum ispstat_state_t state; /* enabling/disabling state */ ++ struct omap_dma_channel_params dma_config; ++ struct isp_device *isp; ++ void *priv; /* pointer to priv config struct */ ++ void *recover_priv; /* pointer to recover priv configuration */ ++ struct mutex ioctl_lock; /* serialize private ioctl */ ++ ++ const struct ispstat_ops *ops; ++ ++ /* Buffer */ ++ u8 wait_acc_frames; ++ u16 config_counter; ++ u32 frame_number; ++ u32 buf_size; ++ u32 buf_alloc_size; ++ int dma_ch; ++ unsigned long event_type; ++ struct ispstat_buffer *buf; ++ struct ispstat_buffer *active_buf; ++ struct ispstat_buffer *locked_buf; ++}; ++ ++struct ispstat_generic_config { ++ /* ++ * Fields must be in the same order as in: ++ * - isph3a_aewb_config ++ * - isph3a_af_config ++ * - isphist_config ++ */ ++ u32 buf_size; ++ u16 config_counter; ++}; ++ ++int omap3isp_stat_config(struct ispstat *stat, void *new_conf); ++int omap3isp_stat_request_statistics(struct ispstat *stat, ++ struct omap3isp_stat_data *data); ++int omap3isp_stat_init(struct ispstat *stat, const char *name, ++ const struct v4l2_subdev_ops *sd_ops); ++void omap3isp_stat_free(struct ispstat *stat); ++int omap3isp_stat_subscribe_event(struct v4l2_subdev *subdev, ++ struct v4l2_fh *fh, ++ struct v4l2_event_subscription *sub); ++int omap3isp_stat_unsubscribe_event(struct v4l2_subdev *subdev, ++ struct v4l2_fh *fh, ++ struct v4l2_event_subscription *sub); ++int omap3isp_stat_s_stream(struct v4l2_subdev *subdev, int enable); ++ ++int omap3isp_stat_busy(struct ispstat *stat); ++int omap3isp_stat_pcr_busy(struct ispstat *stat); ++void omap3isp_stat_suspend(struct ispstat *stat); ++void omap3isp_stat_resume(struct ispstat *stat); ++int omap3isp_stat_enable(struct ispstat *stat, u8 enable); ++void omap3isp_stat_sbl_overflow(struct ispstat *stat); ++void omap3isp_stat_isr(struct ispstat *stat); ++void omap3isp_stat_isr_frame_sync(struct ispstat *stat); ++void omap3isp_stat_dma_isr(struct ispstat *stat); ++int omap3isp_stat_register_entities(struct ispstat *stat, ++ struct v4l2_device *vdev); ++void omap3isp_stat_unregister_entities(struct ispstat *stat); ++ ++#endif /* OMAP3_ISP_STAT_H */ +diff --git a/drivers/media/video/isp/ispvideo.c b/drivers/media/video/isp/ispvideo.c +new file mode 100644 +index 0000000..ef0adb0 +--- /dev/null ++++ b/drivers/media/video/isp/ispvideo.c +@@ -0,0 +1,1264 @@ ++/* ++ * ispvideo.c ++ * ++ * TI OMAP3 ISP - Generic video node ++ * ++ * Copyright (C) 2009-2010 Nokia Corporation ++ * ++ * Contacts: Laurent Pinchart ++ * Sakari Ailus ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "ispvideo.h" ++#include "isp.h" ++ ++ ++/* ----------------------------------------------------------------------------- ++ * Helper functions ++ */ ++ ++static struct isp_format_info formats[] = { ++ { V4L2_MBUS_FMT_Y8_1X8, V4L2_MBUS_FMT_Y8_1X8, ++ V4L2_MBUS_FMT_Y8_1X8, V4L2_PIX_FMT_GREY, 8, }, ++ { V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, ++ V4L2_MBUS_FMT_SGRBG10_1X10, V4L2_PIX_FMT_SGRBG10DPCM8, 8, }, ++ { V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_MBUS_FMT_SBGGR10_1X10, ++ V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_PIX_FMT_SBGGR10, 10, }, ++ { V4L2_MBUS_FMT_SGBRG10_1X10, V4L2_MBUS_FMT_SGBRG10_1X10, ++ V4L2_MBUS_FMT_SGBRG10_1X10, V4L2_PIX_FMT_SGBRG10, 10, }, ++ { V4L2_MBUS_FMT_SGRBG10_1X10, V4L2_MBUS_FMT_SGRBG10_1X10, ++ V4L2_MBUS_FMT_SGRBG10_1X10, V4L2_PIX_FMT_SGRBG10, 10, }, ++ { V4L2_MBUS_FMT_SRGGB10_1X10, V4L2_MBUS_FMT_SRGGB10_1X10, ++ V4L2_MBUS_FMT_SRGGB10_1X10, V4L2_PIX_FMT_SRGGB10, 10, }, ++ { V4L2_MBUS_FMT_SBGGR12_1X12, V4L2_MBUS_FMT_SBGGR10_1X10, ++ V4L2_MBUS_FMT_SBGGR12_1X12, V4L2_PIX_FMT_SBGGR12, 12, }, ++ { V4L2_MBUS_FMT_SGBRG12_1X12, V4L2_MBUS_FMT_SGBRG10_1X10, ++ V4L2_MBUS_FMT_SGBRG12_1X12, V4L2_PIX_FMT_SGBRG12, 12, }, ++ { V4L2_MBUS_FMT_SGRBG12_1X12, V4L2_MBUS_FMT_SGRBG10_1X10, ++ V4L2_MBUS_FMT_SGRBG12_1X12, V4L2_PIX_FMT_SGRBG12, 12, }, ++ { V4L2_MBUS_FMT_SRGGB12_1X12, V4L2_MBUS_FMT_SRGGB10_1X10, ++ V4L2_MBUS_FMT_SRGGB12_1X12, V4L2_PIX_FMT_SRGGB12, 12, }, ++ { V4L2_MBUS_FMT_UYVY8_1X16, V4L2_MBUS_FMT_UYVY8_1X16, ++ V4L2_MBUS_FMT_UYVY8_1X16, V4L2_PIX_FMT_UYVY, 16, }, ++ { V4L2_MBUS_FMT_YUYV8_1X16, V4L2_MBUS_FMT_YUYV8_1X16, ++ V4L2_MBUS_FMT_YUYV8_1X16, V4L2_PIX_FMT_YUYV, 16, }, ++}; ++ ++const struct isp_format_info * ++omap3isp_video_format_info(enum v4l2_mbus_pixelcode code) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(formats); ++i) { ++ if (formats[i].code == code) ++ return &formats[i]; ++ } ++ ++ return NULL; ++} ++ ++/* ++ * isp_video_mbus_to_pix - Convert v4l2_mbus_framefmt to v4l2_pix_format ++ * @video: ISP video instance ++ * @mbus: v4l2_mbus_framefmt format (input) ++ * @pix: v4l2_pix_format format (output) ++ * ++ * Fill the output pix structure with information from the input mbus format. ++ * The bytesperline and sizeimage fields are computed from the requested bytes ++ * per line value in the pix format and information from the video instance. ++ * ++ * Return the number of padding bytes at end of line. ++ */ ++static unsigned int isp_video_mbus_to_pix(const struct isp_video *video, ++ const struct v4l2_mbus_framefmt *mbus, ++ struct v4l2_pix_format *pix) ++{ ++ unsigned int bpl = pix->bytesperline; ++ unsigned int min_bpl; ++ unsigned int i; ++ ++ memset(pix, 0, sizeof(*pix)); ++ pix->width = mbus->width; ++ pix->height = mbus->height; ++ ++ for (i = 0; i < ARRAY_SIZE(formats); ++i) { ++ if (formats[i].code == mbus->code) ++ break; ++ } ++ ++ if (WARN_ON(i == ARRAY_SIZE(formats))) ++ return 0; ++ ++ min_bpl = pix->width * ALIGN(formats[i].bpp, 8) / 8; ++ ++ /* Clamp the requested bytes per line value. If the maximum bytes per ++ * line value is zero, the module doesn't support user configurable line ++ * sizes. Override the requested value with the minimum in that case. ++ */ ++ if (video->bpl_max) ++ bpl = clamp(bpl, min_bpl, video->bpl_max); ++ else ++ bpl = min_bpl; ++ ++ if (!video->bpl_zero_padding || bpl != min_bpl) ++ bpl = ALIGN(bpl, video->bpl_alignment); ++ ++ pix->pixelformat = formats[i].pixelformat; ++ pix->bytesperline = bpl; ++ pix->sizeimage = pix->bytesperline * pix->height; ++ pix->colorspace = mbus->colorspace; ++ pix->field = mbus->field; ++ ++ return bpl - min_bpl; ++} ++ ++static void isp_video_pix_to_mbus(const struct v4l2_pix_format *pix, ++ struct v4l2_mbus_framefmt *mbus) ++{ ++ unsigned int i; ++ ++ memset(mbus, 0, sizeof(*mbus)); ++ mbus->width = pix->width; ++ mbus->height = pix->height; ++ ++ for (i = 0; i < ARRAY_SIZE(formats); ++i) { ++ if (formats[i].pixelformat == pix->pixelformat) ++ break; ++ } ++ ++ if (WARN_ON(i == ARRAY_SIZE(formats))) ++ return; ++ ++ mbus->code = formats[i].code; ++ mbus->colorspace = pix->colorspace; ++ mbus->field = pix->field; ++} ++ ++static struct v4l2_subdev * ++isp_video_remote_subdev(struct isp_video *video, u32 *pad) ++{ ++ struct media_pad *remote; ++ ++ remote = media_entity_remote_source(&video->pad); ++ ++ if (remote == NULL || ++ media_entity_type(remote->entity) != MEDIA_ENT_T_V4L2_SUBDEV) ++ return NULL; ++ ++ if (pad) ++ *pad = remote->index; ++ ++ return media_entity_to_v4l2_subdev(remote->entity); ++} ++ ++/* Return a pointer to the ISP video instance at the far end of the pipeline. */ ++static struct isp_video * ++isp_video_far_end(struct isp_video *video) ++{ ++ struct media_entity_graph graph; ++ struct media_entity *entity = &video->video.entity; ++ struct media_device *mdev = entity->parent; ++ struct isp_video *far_end = NULL; ++ ++ mutex_lock(&mdev->graph_mutex); ++ media_entity_graph_walk_start(&graph, entity); ++ ++ while ((entity = media_entity_graph_walk_next(&graph))) { ++ if (entity == &video->video.entity) ++ continue; ++ ++ if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE) ++ continue; ++ ++ far_end = to_isp_video(media_entity_to_video_device(entity)); ++ if (far_end->type != video->type) ++ break; ++ ++ far_end = NULL; ++ } ++ ++ mutex_unlock(&mdev->graph_mutex); ++ return far_end; ++} ++ ++/* ++ * Validate a pipeline by checking both ends of all links for format ++ * discrepancies. ++ * ++ * Compute the minimum time per frame value as the maximum of time per frame ++ * limits reported by every block in the pipeline. ++ * ++ * Return 0 if all formats match, or -EPIPE if at least one link is found with ++ * different formats on its two ends. ++ */ ++static int isp_video_validate_pipeline(struct isp_pipeline *pipe) ++{ ++ struct isp_device *isp = pipe->output->isp; ++ struct v4l2_subdev_format fmt_source; ++ struct v4l2_subdev_format fmt_sink; ++ struct media_pad *pad; ++ struct v4l2_subdev *subdev; ++ int ret; ++ ++ pipe->max_rate = pipe->l3_ick; ++ ++ subdev = isp_video_remote_subdev(pipe->output, NULL); ++ if (subdev == NULL) ++ return -EPIPE; ++ ++ while (1) { ++ /* Retrieve the sink format */ ++ pad = &subdev->entity.pads[0]; ++ if (!(pad->flags & MEDIA_PAD_FL_INPUT)) ++ break; ++ ++ fmt_sink.pad = pad->index; ++ fmt_sink.which = V4L2_SUBDEV_FORMAT_ACTIVE; ++ ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt_sink); ++ if (ret < 0 && ret != -ENOIOCTLCMD) ++ return -EPIPE; ++ ++ /* Update the maximum frame rate */ ++ if (subdev == &isp->isp_res.subdev) ++ omap3isp_resizer_max_rate(&isp->isp_res, ++ &pipe->max_rate); ++ ++ /* Check ccdc maximum data rate when data comes from sensor ++ * TODO: Include ccdc rate in pipe->max_rate and compare the ++ * total pipe rate with the input data rate from sensor. ++ */ ++ if (subdev == &isp->isp_ccdc.subdev && pipe->input == NULL) { ++ unsigned int rate = UINT_MAX; ++ ++ omap3isp_ccdc_max_rate(&isp->isp_ccdc, &rate); ++ if (isp->isp_ccdc.vpcfg.pixelclk > rate) ++ return -ENOSPC; ++ } ++ ++ /* Retrieve the source format */ ++ pad = media_entity_remote_source(pad); ++ if (pad == NULL || ++ media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) ++ break; ++ ++ subdev = media_entity_to_v4l2_subdev(pad->entity); ++ ++ fmt_source.pad = pad->index; ++ fmt_source.which = V4L2_SUBDEV_FORMAT_ACTIVE; ++ ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt_source); ++ if (ret < 0 && ret != -ENOIOCTLCMD) ++ return -EPIPE; ++ ++ /* Check if the two ends match */ ++ if (fmt_source.format.code != fmt_sink.format.code || ++ fmt_source.format.width != fmt_sink.format.width || ++ fmt_source.format.height != fmt_sink.format.height) ++ return -EPIPE; ++ } ++ ++ return 0; ++} ++ ++static int ++__isp_video_get_format(struct isp_video *video, struct v4l2_format *format) ++{ ++ struct v4l2_subdev_format fmt; ++ struct v4l2_subdev *subdev; ++ u32 pad; ++ int ret; ++ ++ subdev = isp_video_remote_subdev(video, &pad); ++ if (subdev == NULL) ++ return -EINVAL; ++ ++ mutex_lock(&video->mutex); ++ ++ fmt.pad = pad; ++ fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; ++ ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt); ++ if (ret == -ENOIOCTLCMD) ++ ret = -EINVAL; ++ ++ mutex_unlock(&video->mutex); ++ ++ if (ret) ++ return ret; ++ ++ format->type = video->type; ++ return isp_video_mbus_to_pix(video, &fmt.format, &format->fmt.pix); ++} ++ ++static int ++isp_video_check_format(struct isp_video *video, struct isp_video_fh *vfh) ++{ ++ struct v4l2_format format; ++ int ret; ++ ++ memcpy(&format, &vfh->format, sizeof(format)); ++ ret = __isp_video_get_format(video, &format); ++ if (ret < 0) ++ return ret; ++ ++ if (vfh->format.fmt.pix.pixelformat != format.fmt.pix.pixelformat || ++ vfh->format.fmt.pix.height != format.fmt.pix.height || ++ vfh->format.fmt.pix.width != format.fmt.pix.width || ++ vfh->format.fmt.pix.bytesperline != format.fmt.pix.bytesperline || ++ vfh->format.fmt.pix.sizeimage != format.fmt.pix.sizeimage) ++ return -EINVAL; ++ ++ return ret; ++} ++ ++/* ----------------------------------------------------------------------------- ++ * IOMMU management ++ */ ++ ++#define IOMMU_FLAG (IOVMF_ENDIAN_LITTLE | IOVMF_ELSZ_8) ++ ++/* ++ * ispmmu_vmap - Wrapper for Virtual memory mapping of a scatter gather list ++ * @dev: Device pointer specific to the OMAP3 ISP. ++ * @sglist: Pointer to source Scatter gather list to allocate. ++ * @sglen: Number of elements of the scatter-gatter list. ++ * ++ * Returns a resulting mapped device address by the ISP MMU, or -ENOMEM if ++ * we ran out of memory. ++ */ ++static dma_addr_t ++ispmmu_vmap(struct isp_device *isp, const struct scatterlist *sglist, int sglen) ++{ ++ struct sg_table *sgt; ++ u32 da; ++ ++ sgt = kmalloc(sizeof(*sgt), GFP_KERNEL); ++ if (sgt == NULL) ++ return -ENOMEM; ++ ++ sgt->sgl = (struct scatterlist *)sglist; ++ sgt->nents = sglen; ++ sgt->orig_nents = sglen; ++ ++ da = iommu_vmap(isp->iommu, 0, sgt, IOMMU_FLAG); ++ if (IS_ERR_VALUE(da)) ++ kfree(sgt); ++ ++ return da; ++} ++ ++/* ++ * ispmmu_vunmap - Unmap a device address from the ISP MMU ++ * @dev: Device pointer specific to the OMAP3 ISP. ++ * @da: Device address generated from a ispmmu_vmap call. ++ */ ++static void ispmmu_vunmap(struct isp_device *isp, dma_addr_t da) ++{ ++ struct sg_table *sgt; ++ ++ sgt = iommu_vunmap(isp->iommu, (u32)da); ++ kfree(sgt); ++} ++ ++/* ----------------------------------------------------------------------------- ++ * Video queue operations ++ */ ++ ++static void isp_video_queue_prepare(struct isp_video_queue *queue, ++ unsigned int *nbuffers, unsigned int *size) ++{ ++ struct isp_video_fh *vfh = ++ container_of(queue, struct isp_video_fh, queue); ++ struct isp_video *video = vfh->video; ++ ++ *size = vfh->format.fmt.pix.sizeimage; ++ if (*size == 0) ++ return; ++ ++ *nbuffers = min(*nbuffers, video->capture_mem / PAGE_ALIGN(*size)); ++} ++ ++static void isp_video_buffer_cleanup(struct isp_video_buffer *buf) ++{ ++ struct isp_video_fh *vfh = isp_video_queue_to_isp_video_fh(buf->queue); ++ struct isp_buffer *buffer = to_isp_buffer(buf); ++ struct isp_video *video = vfh->video; ++ ++ if (buffer->isp_addr) { ++ ispmmu_vunmap(video->isp, buffer->isp_addr); ++ buffer->isp_addr = 0; ++ } ++} ++ ++static int isp_video_buffer_prepare(struct isp_video_buffer *buf) ++{ ++ struct isp_video_fh *vfh = isp_video_queue_to_isp_video_fh(buf->queue); ++ struct isp_buffer *buffer = to_isp_buffer(buf); ++ struct isp_video *video = vfh->video; ++ unsigned long addr; ++ ++ addr = ispmmu_vmap(video->isp, buf->sglist, buf->sglen); ++ if (IS_ERR_VALUE(addr)) ++ return -EIO; ++ ++ if (!IS_ALIGNED(addr, 32)) { ++ dev_dbg(video->isp->dev, "Buffer address must be " ++ "aligned to 32 bytes boundary.\n"); ++ ispmmu_vunmap(video->isp, buffer->isp_addr); ++ return -EINVAL; ++ } ++ ++ buf->vbuf.bytesused = vfh->format.fmt.pix.sizeimage; ++ buffer->isp_addr = addr; ++ return 0; ++} ++ ++/* ++ * isp_video_buffer_queue - Add buffer to streaming queue ++ * @buf: Video buffer ++ * ++ * In memory-to-memory mode, start streaming on the pipeline if buffers are ++ * queued on both the input and the output, if the pipeline isn't already busy. ++ * If the pipeline is busy, it will be restarted in the output module interrupt ++ * handler. ++ */ ++static void isp_video_buffer_queue(struct isp_video_buffer *buf) ++{ ++ struct isp_video_fh *vfh = isp_video_queue_to_isp_video_fh(buf->queue); ++ struct isp_buffer *buffer = to_isp_buffer(buf); ++ struct isp_video *video = vfh->video; ++ struct isp_pipeline *pipe = to_isp_pipeline(&video->video.entity); ++ enum isp_pipeline_state state; ++ unsigned long flags; ++ unsigned int empty; ++ unsigned int start; ++ ++ empty = list_empty(&video->dmaqueue); ++ list_add_tail(&buffer->buffer.irqlist, &video->dmaqueue); ++ ++ if (empty) { ++ if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) ++ state = ISP_PIPELINE_QUEUE_OUTPUT; ++ else ++ state = ISP_PIPELINE_QUEUE_INPUT; ++ ++ spin_lock_irqsave(&pipe->lock, flags); ++ pipe->state |= state; ++ video->ops->queue(video, buffer); ++ video->dmaqueue_flags |= ISP_VIDEO_DMAQUEUE_QUEUED; ++ ++ start = isp_pipeline_ready(pipe); ++ if (start) ++ pipe->state |= ISP_PIPELINE_STREAM; ++ spin_unlock_irqrestore(&pipe->lock, flags); ++ ++ if (start) ++ omap3isp_pipeline_set_stream(pipe, ++ ISP_PIPELINE_STREAM_SINGLESHOT); ++ } ++} ++ ++static const struct isp_video_queue_operations isp_video_queue_ops = { ++ .queue_prepare = &isp_video_queue_prepare, ++ .buffer_prepare = &isp_video_buffer_prepare, ++ .buffer_queue = &isp_video_buffer_queue, ++ .buffer_cleanup = &isp_video_buffer_cleanup, ++}; ++ ++/* ++ * omap3isp_video_buffer_next - Complete the current buffer and return the next ++ * @video: ISP video object ++ * @error: Whether an error occured during capture ++ * ++ * Remove the current video buffer from the DMA queue and fill its timestamp, ++ * field count and state fields before waking up its completion handler. ++ * ++ * The buffer state is set to VIDEOBUF_DONE if no error occured (@error is 0) ++ * or VIDEOBUF_ERROR otherwise (@error is non-zero). ++ * ++ * The DMA queue is expected to contain at least one buffer. ++ * ++ * Return a pointer to the next buffer in the DMA queue, or NULL if the queue is ++ * empty. ++ */ ++struct isp_buffer *omap3isp_video_buffer_next(struct isp_video *video, ++ unsigned int error) ++{ ++ struct isp_pipeline *pipe = to_isp_pipeline(&video->video.entity); ++ struct isp_video_queue *queue = video->queue; ++ enum isp_pipeline_state state; ++ struct isp_video_buffer *buf; ++ unsigned long flags; ++ struct timespec ts; ++ ++ spin_lock_irqsave(&queue->irqlock, flags); ++ if (WARN_ON(list_empty(&video->dmaqueue))) { ++ spin_unlock_irqrestore(&queue->irqlock, flags); ++ return NULL; ++ } ++ ++ buf = list_first_entry(&video->dmaqueue, struct isp_video_buffer, ++ irqlist); ++ list_del(&buf->irqlist); ++ spin_unlock_irqrestore(&queue->irqlock, flags); ++ ++ ktime_get_ts(&ts); ++ buf->vbuf.timestamp.tv_sec = ts.tv_sec; ++ buf->vbuf.timestamp.tv_usec = ts.tv_nsec / NSEC_PER_USEC; ++ ++ /* Do frame number propagation only if this is the output video node. ++ * Frame number either comes from the CSI receivers or it gets ++ * incremented here if H3A is not active. ++ * Note: There is no guarantee that the output buffer will finish ++ * first, so the input number might lag behind by 1 in some cases. ++ */ ++ if (video == pipe->output && !pipe->do_propagation) ++ buf->vbuf.sequence = atomic_inc_return(&pipe->frame_number); ++ else ++ buf->vbuf.sequence = atomic_read(&pipe->frame_number); ++ ++ buf->state = error ? ISP_BUF_STATE_ERROR : ISP_BUF_STATE_DONE; ++ ++ wake_up(&buf->wait); ++ ++ if (list_empty(&video->dmaqueue)) { ++ if (queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) ++ state = ISP_PIPELINE_QUEUE_OUTPUT ++ | ISP_PIPELINE_STREAM; ++ else ++ state = ISP_PIPELINE_QUEUE_INPUT ++ | ISP_PIPELINE_STREAM; ++ ++ spin_lock_irqsave(&pipe->lock, flags); ++ pipe->state &= ~state; ++ if (video->pipe.stream_state == ISP_PIPELINE_STREAM_CONTINUOUS) ++ video->dmaqueue_flags |= ISP_VIDEO_DMAQUEUE_UNDERRUN; ++ spin_unlock_irqrestore(&pipe->lock, flags); ++ return NULL; ++ } ++ ++ if (queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && pipe->input != NULL) { ++ spin_lock_irqsave(&pipe->lock, flags); ++ pipe->state &= ~ISP_PIPELINE_STREAM; ++ spin_unlock_irqrestore(&pipe->lock, flags); ++ } ++ ++ buf = list_first_entry(&video->dmaqueue, struct isp_video_buffer, ++ irqlist); ++ buf->state = ISP_BUF_STATE_ACTIVE; ++ return to_isp_buffer(buf); ++} ++ ++/* ++ * omap3isp_video_resume - Perform resume operation on the buffers ++ * @video: ISP video object ++ * @continuous: Pipeline is in single shot mode if 0 or continous mode otherwise ++ * ++ * This function is intended to be used on suspend/resume scenario. It ++ * requests video queue layer to discard buffers marked as DONE if it's in ++ * continuous mode and requests ISP modules to queue again the ACTIVE buffer ++ * if there's any. ++ */ ++void omap3isp_video_resume(struct isp_video *video, int continuous) ++{ ++ struct isp_buffer *buf = NULL; ++ ++ if (continuous && video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) ++ omap3isp_video_queue_discard_done(video->queue); ++ ++ if (!list_empty(&video->dmaqueue)) { ++ buf = list_first_entry(&video->dmaqueue, ++ struct isp_buffer, buffer.irqlist); ++ video->ops->queue(video, buf); ++ video->dmaqueue_flags |= ISP_VIDEO_DMAQUEUE_QUEUED; ++ } else { ++ if (continuous) ++ video->dmaqueue_flags |= ISP_VIDEO_DMAQUEUE_UNDERRUN; ++ } ++} ++ ++/* ----------------------------------------------------------------------------- ++ * V4L2 ioctls ++ */ ++ ++static int ++isp_video_querycap(struct file *file, void *fh, struct v4l2_capability *cap) ++{ ++ struct isp_video *video = video_drvdata(file); ++ ++ strlcpy(cap->driver, ISP_VIDEO_DRIVER_NAME, sizeof(cap->driver)); ++ strlcpy(cap->card, video->video.name, sizeof(cap->card)); ++ strlcpy(cap->bus_info, "media", sizeof(cap->bus_info)); ++ cap->version = ISP_VIDEO_DRIVER_VERSION; ++ ++ if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) ++ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; ++ else ++ cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; ++ ++ return 0; ++} ++ ++static int ++isp_video_get_format(struct file *file, void *fh, struct v4l2_format *format) ++{ ++ struct isp_video_fh *vfh = to_isp_video_fh(fh); ++ struct isp_video *video = video_drvdata(file); ++ ++ if (format->type != video->type) ++ return -EINVAL; ++ ++ mutex_lock(&video->mutex); ++ *format = vfh->format; ++ mutex_unlock(&video->mutex); ++ ++ return 0; ++} ++ ++static int ++isp_video_set_format(struct file *file, void *fh, struct v4l2_format *format) ++{ ++ struct isp_video_fh *vfh = to_isp_video_fh(fh); ++ struct isp_video *video = video_drvdata(file); ++ struct v4l2_mbus_framefmt fmt; ++ ++ if (format->type != video->type) ++ return -EINVAL; ++ ++ mutex_lock(&video->mutex); ++ ++ /* Fill the bytesperline and sizeimage fields by converting to media bus ++ * format and back to pixel format. ++ */ ++ isp_video_pix_to_mbus(&format->fmt.pix, &fmt); ++ isp_video_mbus_to_pix(video, &fmt, &format->fmt.pix); ++ ++ vfh->format = *format; ++ ++ mutex_unlock(&video->mutex); ++ return 0; ++} ++ ++static int ++isp_video_try_format(struct file *file, void *fh, struct v4l2_format *format) ++{ ++ struct isp_video *video = video_drvdata(file); ++ struct v4l2_subdev_format fmt; ++ struct v4l2_subdev *subdev; ++ u32 pad; ++ int ret; ++ ++ if (format->type != video->type) ++ return -EINVAL; ++ ++ subdev = isp_video_remote_subdev(video, &pad); ++ if (subdev == NULL) ++ return -EINVAL; ++ ++ isp_video_pix_to_mbus(&format->fmt.pix, &fmt.format); ++ ++ fmt.pad = pad; ++ fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; ++ ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt); ++ if (ret) ++ return ret == -ENOIOCTLCMD ? -EINVAL : ret; ++ ++ isp_video_mbus_to_pix(video, &fmt.format, &format->fmt.pix); ++ return 0; ++} ++ ++static int ++isp_video_cropcap(struct file *file, void *fh, struct v4l2_cropcap *cropcap) ++{ ++ struct isp_video *video = video_drvdata(file); ++ struct v4l2_subdev *subdev; ++ int ret; ++ ++ subdev = isp_video_remote_subdev(video, NULL); ++ if (subdev == NULL) ++ return -EINVAL; ++ ++ mutex_lock(&video->mutex); ++ ret = v4l2_subdev_call(subdev, video, cropcap, cropcap); ++ mutex_unlock(&video->mutex); ++ ++ return ret == -ENOIOCTLCMD ? -EINVAL : ret; ++} ++ ++static int ++isp_video_get_crop(struct file *file, void *fh, struct v4l2_crop *crop) ++{ ++ struct isp_video *video = video_drvdata(file); ++ struct v4l2_subdev_format format; ++ struct v4l2_subdev *subdev; ++ u32 pad; ++ int ret; ++ ++ subdev = isp_video_remote_subdev(video, &pad); ++ if (subdev == NULL) ++ return -EINVAL; ++ ++ /* Try the get crop operation first and fallback to get format if not ++ * implemented. ++ */ ++ ret = v4l2_subdev_call(subdev, video, g_crop, crop); ++ if (ret != -ENOIOCTLCMD) ++ return ret; ++ ++ format.pad = pad; ++ format.which = V4L2_SUBDEV_FORMAT_ACTIVE; ++ ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &format); ++ if (ret < 0) ++ return ret == -ENOIOCTLCMD ? -EINVAL : ret; ++ ++ crop->c.left = 0; ++ crop->c.top = 0; ++ crop->c.width = format.format.width; ++ crop->c.height = format.format.height; ++ ++ return 0; ++} ++ ++static int ++isp_video_set_crop(struct file *file, void *fh, struct v4l2_crop *crop) ++{ ++ struct isp_video *video = video_drvdata(file); ++ struct v4l2_subdev *subdev; ++ int ret; ++ ++ subdev = isp_video_remote_subdev(video, NULL); ++ if (subdev == NULL) ++ return -EINVAL; ++ ++ mutex_lock(&video->mutex); ++ ret = v4l2_subdev_call(subdev, video, s_crop, crop); ++ mutex_unlock(&video->mutex); ++ ++ return ret == -ENOIOCTLCMD ? -EINVAL : ret; ++} ++ ++static int ++isp_video_get_param(struct file *file, void *fh, struct v4l2_streamparm *a) ++{ ++ struct isp_video_fh *vfh = to_isp_video_fh(fh); ++ struct isp_video *video = video_drvdata(file); ++ ++ if (video->type != V4L2_BUF_TYPE_VIDEO_OUTPUT || ++ video->type != a->type) ++ return -EINVAL; ++ ++ memset(a, 0, sizeof(*a)); ++ a->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; ++ a->parm.output.capability = V4L2_CAP_TIMEPERFRAME; ++ a->parm.output.timeperframe = vfh->timeperframe; ++ ++ return 0; ++} ++ ++static int ++isp_video_set_param(struct file *file, void *fh, struct v4l2_streamparm *a) ++{ ++ struct isp_video_fh *vfh = to_isp_video_fh(fh); ++ struct isp_video *video = video_drvdata(file); ++ ++ if (video->type != V4L2_BUF_TYPE_VIDEO_OUTPUT || ++ video->type != a->type) ++ return -EINVAL; ++ ++ if (a->parm.output.timeperframe.denominator == 0) ++ a->parm.output.timeperframe.denominator = 1; ++ ++ vfh->timeperframe = a->parm.output.timeperframe; ++ ++ return 0; ++} ++ ++static int ++isp_video_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *rb) ++{ ++ struct isp_video_fh *vfh = to_isp_video_fh(fh); ++ ++ return omap3isp_video_queue_reqbufs(&vfh->queue, rb); ++} ++ ++static int ++isp_video_querybuf(struct file *file, void *fh, struct v4l2_buffer *b) ++{ ++ struct isp_video_fh *vfh = to_isp_video_fh(fh); ++ ++ return omap3isp_video_queue_querybuf(&vfh->queue, b); ++} ++ ++static int ++isp_video_qbuf(struct file *file, void *fh, struct v4l2_buffer *b) ++{ ++ struct isp_video_fh *vfh = to_isp_video_fh(fh); ++ ++ return omap3isp_video_queue_qbuf(&vfh->queue, b); ++} ++ ++static int ++isp_video_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b) ++{ ++ struct isp_video_fh *vfh = to_isp_video_fh(fh); ++ ++ return omap3isp_video_queue_dqbuf(&vfh->queue, b, ++ file->f_flags & O_NONBLOCK); ++} ++ ++/* ++ * Stream management ++ * ++ * Every ISP pipeline has a single input and a single output. The input can be ++ * either a sensor or a video node. The output is always a video node. ++ * ++ * As every pipeline has an output video node, the ISP video objects at the ++ * pipeline output stores the pipeline state. It tracks the streaming state of ++ * both the input and output, as well as the availability of buffers. ++ * ++ * In sensor-to-memory mode, frames are always available at the pipeline input. ++ * Starting the sensor usually requires I2C transfers and must be done in ++ * interruptible context. The pipeline is started and stopped synchronously ++ * to the stream on/off commands. All modules in the pipeline will get their ++ * subdev set stream handler called. The module at the end of the pipeline must ++ * delay starting the hardware until buffers are available at its output. ++ * ++ * In memory-to-memory mode, starting/stopping the stream requires ++ * synchronization between the input and output. ISP modules can't be stopped ++ * in the middle of a frame, and at least some of the modules seem to become ++ * busy as soon as they're started, even if they don't receive a frame start ++ * event. For that reason frames need to be processed in single-shot mode. The ++ * driver needs to wait until a frame is completely processed and written to ++ * memory before restarting the pipeline for the next frame. Pipelined ++ * processing might be possible but requires more testing. ++ * ++ * Stream start must be delayed until buffers are available at both the input ++ * and output. The pipeline must be started in the videobuf queue callback with ++ * the buffers queue spinlock held. The modules subdev set stream operation must ++ * not sleep. ++ */ ++static int ++isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) ++{ ++ struct isp_video_fh *vfh = to_isp_video_fh(fh); ++ struct isp_video *video = video_drvdata(file); ++ enum isp_pipeline_state state; ++ struct isp_pipeline *pipe; ++ struct isp_video *far_end; ++ unsigned long flags; ++ int ret; ++ ++ if (type != video->type) ++ return -EINVAL; ++ ++ mutex_lock(&video->stream_lock); ++ ++ if (video->streaming) { ++ mutex_unlock(&video->stream_lock); ++ return -EBUSY; ++ } ++ ++ /* Start streaming on the pipeline. No link touching an entity in the ++ * pipeline can be activated or deactivated once streaming is started. ++ */ ++ pipe = video->video.entity.pipe ++ ? to_isp_pipeline(&video->video.entity) : &video->pipe; ++ media_entity_pipeline_start(&video->video.entity, &pipe->pipe); ++ ++ /* Verify that the currently configured format matches the output of ++ * the connected subdev. ++ */ ++ ret = isp_video_check_format(video, vfh); ++ if (ret < 0) ++ goto error; ++ ++ video->bpl_padding = ret; ++ video->bpl_value = vfh->format.fmt.pix.bytesperline; ++ ++ /* Find the ISP video node connected at the far end of the pipeline and ++ * update the pipeline. ++ */ ++ far_end = isp_video_far_end(video); ++ ++ if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { ++ state = ISP_PIPELINE_STREAM_OUTPUT | ISP_PIPELINE_IDLE_OUTPUT; ++ pipe->input = far_end; ++ pipe->output = video; ++ } else { ++ if (far_end == NULL) { ++ ret = -EPIPE; ++ goto error; ++ } ++ ++ state = ISP_PIPELINE_STREAM_INPUT | ISP_PIPELINE_IDLE_INPUT; ++ pipe->input = video; ++ pipe->output = far_end; ++ } ++ ++ /* Make sure the interconnect clock runs fast enough. ++ * ++ * Formula from: resource34xx.c set_opp() ++ * If MPU freq is above 500MHz, make sure the interconnect ++ * is at 100Mhz or above. ++ * throughput in KiB/s for 100 Mhz = 100 * 1000 * 4. ++ * ++ * We want to be fast enough then set OCP clock to be max as ++ * possible, in that case 185Mhz then: ++ * throughput in KiB/s for 185Mhz = 185 * 1000 * 4 = 740000 KiB/s ++ */ ++ omap_pm_set_min_bus_tput(video->isp->dev, OCP_INITIATOR_AGENT, 740000); ++ pipe->l3_ick = clk_get_rate(video->isp->clock[ISP_CLK_L3_ICK]); ++ ++ /* Validate the pipeline and update its state. */ ++ ret = isp_video_validate_pipeline(pipe); ++ if (ret < 0) ++ goto error; ++ ++ spin_lock_irqsave(&pipe->lock, flags); ++ pipe->state &= ~ISP_PIPELINE_STREAM; ++ pipe->state |= state; ++ spin_unlock_irqrestore(&pipe->lock, flags); ++ ++ /* Set the maximum time per frame as the value requested by userspace. ++ * This is a soft limit that can be overridden if the hardware doesn't ++ * support the request limit. ++ */ ++ if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) ++ pipe->max_timeperframe = vfh->timeperframe; ++ ++ video->queue = &vfh->queue; ++ INIT_LIST_HEAD(&video->dmaqueue); ++ atomic_set(&pipe->frame_number, -1); ++ ++ ret = omap3isp_video_queue_streamon(&vfh->queue); ++ if (ret < 0) ++ goto error; ++ ++ /* In sensor-to-memory mode, the stream can be started synchronously ++ * to the stream on command. In memory-to-memory mode, it will be ++ * started when buffers are queued on both the input and output. ++ */ ++ if (pipe->input == NULL) { ++ ret = omap3isp_pipeline_set_stream(pipe, ++ ISP_PIPELINE_STREAM_CONTINUOUS); ++ if (ret < 0) ++ goto error; ++ spin_lock_irqsave(&video->queue->irqlock, flags); ++ if (list_empty(&video->dmaqueue)) ++ video->dmaqueue_flags |= ISP_VIDEO_DMAQUEUE_UNDERRUN; ++ spin_unlock_irqrestore(&video->queue->irqlock, flags); ++ } ++ ++error: ++ if (ret < 0) { ++ omap3isp_video_queue_streamoff(&vfh->queue); ++ omap_pm_set_min_bus_tput(video->isp->dev, ++ OCP_INITIATOR_AGENT, 0); ++ media_entity_pipeline_stop(&video->video.entity); ++ video->queue = NULL; ++ } ++ ++ if (!ret) ++ video->streaming = 1; ++ ++ mutex_unlock(&video->stream_lock); ++ return ret; ++} ++ ++static int ++isp_video_streamoff(struct file *file, void *fh, enum v4l2_buf_type type) ++{ ++ struct isp_video_fh *vfh = to_isp_video_fh(fh); ++ struct isp_video *video = video_drvdata(file); ++ struct isp_pipeline *pipe = to_isp_pipeline(&video->video.entity); ++ enum isp_pipeline_state state; ++ unsigned int streaming; ++ unsigned long flags; ++ ++ if (type != video->type) ++ return -EINVAL; ++ ++ mutex_lock(&video->stream_lock); ++ ++ /* Make sure we're not streaming yet. */ ++ mutex_lock(&vfh->queue.lock); ++ streaming = vfh->queue.streaming; ++ mutex_unlock(&vfh->queue.lock); ++ ++ if (!streaming) ++ goto done; ++ ++ /* Update the pipeline state. */ ++ if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) ++ state = ISP_PIPELINE_STREAM_OUTPUT ++ | ISP_PIPELINE_QUEUE_OUTPUT; ++ else ++ state = ISP_PIPELINE_STREAM_INPUT ++ | ISP_PIPELINE_QUEUE_INPUT; ++ ++ spin_lock_irqsave(&pipe->lock, flags); ++ pipe->state &= ~state; ++ spin_unlock_irqrestore(&pipe->lock, flags); ++ ++ /* Stop the stream. */ ++ omap3isp_pipeline_set_stream(pipe, ISP_PIPELINE_STREAM_STOPPED); ++ omap3isp_video_queue_streamoff(&vfh->queue); ++ video->queue = NULL; ++ video->streaming = 0; ++ ++ omap_pm_set_min_bus_tput(video->isp->dev, OCP_INITIATOR_AGENT, 0); ++ media_entity_pipeline_stop(&video->video.entity); ++ ++done: ++ mutex_unlock(&video->stream_lock); ++ return 0; ++} ++ ++static int ++isp_video_enum_input(struct file *file, void *fh, struct v4l2_input *input) ++{ ++ if (input->index > 0) ++ return -EINVAL; ++ ++ strlcpy(input->name, "camera", sizeof(input->name)); ++ input->type = V4L2_INPUT_TYPE_CAMERA; ++ ++ return 0; ++} ++ ++static int ++isp_video_g_input(struct file *file, void *fh, unsigned int *input) ++{ ++ *input = 0; ++ ++ return 0; ++} ++ ++static int ++isp_video_s_input(struct file *file, void *fh, unsigned int input) ++{ ++ return input == 0 ? 0 : -EINVAL; ++} ++ ++static const struct v4l2_ioctl_ops isp_video_ioctl_ops = { ++ .vidioc_querycap = isp_video_querycap, ++ .vidioc_g_fmt_vid_cap = isp_video_get_format, ++ .vidioc_s_fmt_vid_cap = isp_video_set_format, ++ .vidioc_try_fmt_vid_cap = isp_video_try_format, ++ .vidioc_g_fmt_vid_out = isp_video_get_format, ++ .vidioc_s_fmt_vid_out = isp_video_set_format, ++ .vidioc_try_fmt_vid_out = isp_video_try_format, ++ .vidioc_cropcap = isp_video_cropcap, ++ .vidioc_g_crop = isp_video_get_crop, ++ .vidioc_s_crop = isp_video_set_crop, ++ .vidioc_g_parm = isp_video_get_param, ++ .vidioc_s_parm = isp_video_set_param, ++ .vidioc_reqbufs = isp_video_reqbufs, ++ .vidioc_querybuf = isp_video_querybuf, ++ .vidioc_qbuf = isp_video_qbuf, ++ .vidioc_dqbuf = isp_video_dqbuf, ++ .vidioc_streamon = isp_video_streamon, ++ .vidioc_streamoff = isp_video_streamoff, ++ .vidioc_enum_input = isp_video_enum_input, ++ .vidioc_g_input = isp_video_g_input, ++ .vidioc_s_input = isp_video_s_input, ++}; ++ ++/* ----------------------------------------------------------------------------- ++ * V4L2 file operations ++ */ ++ ++static int isp_video_open(struct file *file) ++{ ++ struct isp_video *video = video_drvdata(file); ++ struct isp_video_fh *handle; ++ int ret = 0; ++ ++ handle = kzalloc(sizeof(*handle), GFP_KERNEL); ++ if (handle == NULL) ++ return -ENOMEM; ++ ++ v4l2_fh_init(&handle->vfh, &video->video); ++ v4l2_fh_add(&handle->vfh); ++ ++ /* If this is the first user, initialise the pipeline. */ ++ if (omap3isp_get(video->isp) == NULL) { ++ ret = -EBUSY; ++ goto done; ++ } ++ ++ ret = omap3isp_pipeline_pm_use(&video->video.entity, 1); ++ if (ret < 0) { ++ omap3isp_put(video->isp); ++ goto done; ++ } ++ ++ omap3isp_video_queue_init(&handle->queue, video->type, ++ &isp_video_queue_ops, video->isp->dev, ++ sizeof(struct isp_buffer)); ++ ++ memset(&handle->format, 0, sizeof(handle->format)); ++ handle->format.type = video->type; ++ handle->timeperframe.denominator = 1; ++ ++ handle->video = video; ++ file->private_data = &handle->vfh; ++ ++done: ++ if (ret < 0) { ++ v4l2_fh_del(&handle->vfh); ++ kfree(handle); ++ } ++ ++ return ret; ++} ++ ++static int isp_video_release(struct file *file) ++{ ++ struct isp_video *video = video_drvdata(file); ++ struct v4l2_fh *vfh = file->private_data; ++ struct isp_video_fh *handle = to_isp_video_fh(vfh); ++ ++ /* Disable streaming and free the buffers queue resources. */ ++ isp_video_streamoff(file, vfh, video->type); ++ ++ mutex_lock(&handle->queue.lock); ++ omap3isp_video_queue_cleanup(&handle->queue); ++ mutex_unlock(&handle->queue.lock); ++ ++ omap3isp_pipeline_pm_use(&video->video.entity, 0); ++ ++ /* Release the file handle. */ ++ v4l2_fh_del(vfh); ++ kfree(handle); ++ file->private_data = NULL; ++ ++ omap3isp_put(video->isp); ++ ++ return 0; ++} ++ ++static unsigned int isp_video_poll(struct file *file, poll_table *wait) ++{ ++ struct isp_video_fh *vfh = to_isp_video_fh(file->private_data); ++ struct isp_video_queue *queue = &vfh->queue; ++ ++ return omap3isp_video_queue_poll(queue, file, wait); ++} ++ ++static int isp_video_mmap(struct file *file, struct vm_area_struct *vma) ++{ ++ struct isp_video_fh *vfh = to_isp_video_fh(file->private_data); ++ ++ return omap3isp_video_queue_mmap(&vfh->queue, vma); ++} ++ ++static struct v4l2_file_operations isp_video_fops = { ++ .owner = THIS_MODULE, ++ .unlocked_ioctl = video_ioctl2, ++ .open = isp_video_open, ++ .release = isp_video_release, ++ .poll = isp_video_poll, ++ .mmap = isp_video_mmap, ++}; ++ ++/* ----------------------------------------------------------------------------- ++ * ISP video core ++ */ ++ ++static const struct isp_video_operations isp_video_dummy_ops = { ++}; ++ ++int omap3isp_video_init(struct isp_video *video, const char *name) ++{ ++ const char *direction; ++ int ret; ++ ++ switch (video->type) { ++ case V4L2_BUF_TYPE_VIDEO_CAPTURE: ++ direction = "output"; ++ video->pad.flags = MEDIA_PAD_FL_INPUT; ++ break; ++ case V4L2_BUF_TYPE_VIDEO_OUTPUT: ++ direction = "input"; ++ video->pad.flags = MEDIA_PAD_FL_OUTPUT; ++ break; ++ ++ default: ++ return -EINVAL; ++ } ++ ++ ret = media_entity_init(&video->video.entity, 1, &video->pad, 0); ++ if (ret < 0) ++ return ret; ++ ++ mutex_init(&video->mutex); ++ atomic_set(&video->active, 0); ++ ++ spin_lock_init(&video->pipe.lock); ++ mutex_init(&video->stream_lock); ++ ++ /* Initialize the video device. */ ++ if (video->ops == NULL) ++ video->ops = &isp_video_dummy_ops; ++ ++ video->video.fops = &isp_video_fops; ++ snprintf(video->video.name, sizeof(video->video.name), ++ "OMAP3 ISP %s %s", name, direction); ++ video->video.vfl_type = VFL_TYPE_GRABBER; ++ video->video.release = video_device_release_empty; ++ video->video.ioctl_ops = &isp_video_ioctl_ops; ++ video->pipe.stream_state = ISP_PIPELINE_STREAM_STOPPED; ++ ++ video_set_drvdata(&video->video, video); ++ ++ return 0; ++} ++ ++int omap3isp_video_register(struct isp_video *video, struct v4l2_device *vdev) ++{ ++ int ret; ++ ++ video->video.v4l2_dev = vdev; ++ ++ ret = video_register_device(&video->video, VFL_TYPE_GRABBER, -1); ++ if (ret < 0) ++ printk(KERN_ERR "%s: could not register video device (%d)\n", ++ __func__, ret); ++ ++ return ret; ++} ++ ++void omap3isp_video_unregister(struct isp_video *video) ++{ ++ if (video_is_registered(&video->video)) { ++ media_entity_cleanup(&video->video.entity); ++ video_unregister_device(&video->video); ++ } ++} +diff --git a/drivers/media/video/isp/ispvideo.h b/drivers/media/video/isp/ispvideo.h +new file mode 100644 +index 0000000..41c8fb9 +--- /dev/null ++++ b/drivers/media/video/isp/ispvideo.h +@@ -0,0 +1,202 @@ ++/* ++ * ispvideo.h ++ * ++ * TI OMAP3 ISP - Generic video node ++ * ++ * Copyright (C) 2009-2010 Nokia Corporation ++ * ++ * Contacts: Laurent Pinchart ++ * Sakari Ailus ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#ifndef OMAP3_ISP_VIDEO_H ++#define OMAP3_ISP_VIDEO_H ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "ispqueue.h" ++ ++#define ISP_VIDEO_DRIVER_NAME "ispvideo" ++#define ISP_VIDEO_DRIVER_VERSION KERNEL_VERSION(0, 0, 1) ++ ++struct isp_device; ++struct isp_video; ++struct v4l2_mbus_framefmt; ++struct v4l2_pix_format; ++ ++/* ++ * struct isp_format_info - ISP media bus format information ++ * @code: V4L2 media bus format code ++ * @truncated: V4L2 media bus format code for the same format truncated to 10 ++ * bits. Identical to @code if the format is 10 bits wide or less. ++ * @uncompressed: V4L2 media bus format code for the corresponding uncompressed ++ * format. Identical to @code if the format is not DPCM compressed. ++ * @pixelformat: V4L2 pixel format FCC identifier ++ * @bpp: Bits per pixel ++ */ ++struct isp_format_info { ++ enum v4l2_mbus_pixelcode code; ++ enum v4l2_mbus_pixelcode truncated; ++ enum v4l2_mbus_pixelcode uncompressed; ++ u32 pixelformat; ++ unsigned int bpp; ++}; ++ ++enum isp_pipeline_stream_state { ++ ISP_PIPELINE_STREAM_STOPPED = 0, ++ ISP_PIPELINE_STREAM_CONTINUOUS = 1, ++ ISP_PIPELINE_STREAM_SINGLESHOT = 2, ++}; ++ ++enum isp_pipeline_state { ++ /* The stream has been started on the input video node. */ ++ ISP_PIPELINE_STREAM_INPUT = 1, ++ /* The stream has been started on the output video node. */ ++ ISP_PIPELINE_STREAM_OUTPUT = 2, ++ /* At least one buffer is queued on the input video node. */ ++ ISP_PIPELINE_QUEUE_INPUT = 4, ++ /* At least one buffer is queued on the output video node. */ ++ ISP_PIPELINE_QUEUE_OUTPUT = 8, ++ /* The input entity is idle, ready to be started. */ ++ ISP_PIPELINE_IDLE_INPUT = 16, ++ /* The output entity is idle, ready to be started. */ ++ ISP_PIPELINE_IDLE_OUTPUT = 32, ++ /* The pipeline is currently streaming. */ ++ ISP_PIPELINE_STREAM = 64, ++}; ++ ++struct isp_pipeline { ++ struct media_pipeline pipe; ++ spinlock_t lock; ++ unsigned int state; ++ enum isp_pipeline_stream_state stream_state; ++ struct isp_video *input; ++ struct isp_video *output; ++ unsigned long l3_ick; ++ unsigned int max_rate; ++ atomic_t frame_number; ++ bool do_propagation; /* of frame number */ ++ struct v4l2_fract max_timeperframe; ++}; ++ ++#define to_isp_pipeline(__e) \ ++ container_of((__e)->pipe, struct isp_pipeline, pipe) ++ ++static inline int isp_pipeline_ready(struct isp_pipeline *pipe) ++{ ++ return pipe->state == (ISP_PIPELINE_STREAM_INPUT | ++ ISP_PIPELINE_STREAM_OUTPUT | ++ ISP_PIPELINE_QUEUE_INPUT | ++ ISP_PIPELINE_QUEUE_OUTPUT | ++ ISP_PIPELINE_IDLE_INPUT | ++ ISP_PIPELINE_IDLE_OUTPUT); ++} ++ ++/* ++ * struct isp_buffer - ISP buffer ++ * @buffer: ISP video buffer ++ * @isp_addr: MMU mapped address (a.k.a. device address) of the buffer. ++ */ ++struct isp_buffer { ++ struct isp_video_buffer buffer; ++ dma_addr_t isp_addr; ++}; ++ ++#define to_isp_buffer(buf) container_of(buf, struct isp_buffer, buffer) ++ ++enum isp_video_dmaqueue_flags { ++ /* Set if DMA queue becomes empty when ISP_PIPELINE_STREAM_CONTINUOUS */ ++ ISP_VIDEO_DMAQUEUE_UNDERRUN = (1 << 0), ++ /* Set when queuing buffer to an empty DMA queue */ ++ ISP_VIDEO_DMAQUEUE_QUEUED = (1 << 1), ++}; ++ ++#define isp_video_dmaqueue_flags_clr(video) \ ++ ({ (video)->dmaqueue_flags = 0; }) ++ ++/* ++ * struct isp_video_operations - ISP video operations ++ * @queue: Resume streaming when a buffer is queued. Called on VIDIOC_QBUF ++ * if there was no buffer previously queued. ++ */ ++struct isp_video_operations { ++ int(*queue)(struct isp_video *video, struct isp_buffer *buffer); ++}; ++ ++struct isp_video { ++ struct video_device video; ++ enum v4l2_buf_type type; ++ struct media_pad pad; ++ ++ struct mutex mutex; ++ atomic_t active; ++ ++ struct isp_device *isp; ++ ++ unsigned int capture_mem; ++ unsigned int bpl_alignment; /* alignment value */ ++ unsigned int bpl_zero_padding; /* whether the alignment is optional */ ++ unsigned int bpl_max; /* maximum bytes per line value */ ++ unsigned int bpl_value; /* bytes per line value */ ++ unsigned int bpl_padding; /* padding at end of line */ ++ ++ /* Entity video node streaming */ ++ unsigned int streaming:1; ++ ++ /* Pipeline state */ ++ struct isp_pipeline pipe; ++ struct mutex stream_lock; ++ ++ /* Video buffers queue */ ++ struct isp_video_queue *queue; ++ struct list_head dmaqueue; ++ enum isp_video_dmaqueue_flags dmaqueue_flags; ++ ++ const struct isp_video_operations *ops; ++}; ++ ++#define to_isp_video(vdev) container_of(vdev, struct isp_video, video) ++ ++struct isp_video_fh { ++ struct v4l2_fh vfh; ++ struct isp_video *video; ++ struct isp_video_queue queue; ++ struct v4l2_format format; ++ struct v4l2_fract timeperframe; ++}; ++ ++#define to_isp_video_fh(fh) container_of(fh, struct isp_video_fh, vfh) ++#define isp_video_queue_to_isp_video_fh(q) \ ++ container_of(q, struct isp_video_fh, queue) ++ ++int omap3isp_video_init(struct isp_video *video, const char *name); ++int omap3isp_video_register(struct isp_video *video, ++ struct v4l2_device *vdev); ++void omap3isp_video_unregister(struct isp_video *video); ++struct isp_buffer *omap3isp_video_buffer_next(struct isp_video *video, ++ unsigned int error); ++void omap3isp_video_resume(struct isp_video *video, int continuous); ++struct media_pad *omap3isp_video_remote_pad(struct isp_video *video); ++ ++const struct isp_format_info * ++omap3isp_video_format_info(enum v4l2_mbus_pixelcode code); ++ ++#endif /* OMAP3_ISP_VIDEO_H */ +diff --git a/drivers/media/video/isp/luma_enhance_table.h b/drivers/media/video/isp/luma_enhance_table.h +new file mode 100644 +index 0000000..56d93c2 +--- /dev/null ++++ b/drivers/media/video/isp/luma_enhance_table.h +@@ -0,0 +1,154 @@ ++/* ++ * luma_enhance_table.h ++ * ++ * TI OMAP3 ISP - Luminance enhancement table ++ * ++ * Copyright (C) 2010 Nokia Corporation ++ * Copyright (C) 2009 Texas Instruments, Inc. ++ * ++ * Contacts: Laurent Pinchart ++ * Sakari Ailus ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++1047552, ++1047552, ++1047552, ++1047552, ++1047552, ++1047552, ++1047552, ++1047552, ++1047552, ++1047552, ++1047552, ++1047552, ++1047552, ++1047552, ++1047552, ++1047552, ++1047552, ++1047552, ++1047552, ++1047552, ++1047552, ++1047552, ++1047552, ++1047552, ++1047552, ++1047552, ++1047552, ++1047552, ++1048575, ++1047551, ++1046527, ++1045503, ++1044479, ++1043455, ++1042431, ++1041407, ++1040383, ++1039359, ++1038335, ++1037311, ++1036287, ++1035263, ++1034239, ++1033215, ++1032191, ++1031167, ++1030143, ++1028096, ++1028096, ++1028096, ++1028096, ++1028096, ++1028096, ++1028096, ++1028096, ++1028096, ++1028096, ++1028100, ++1032196, ++1036292, ++1040388, ++1044484, ++0, ++0, ++0, ++5, ++5125, ++10245, ++15365, ++20485, ++25605, ++30720, ++30720, ++30720, ++30720, ++30720, ++30720, ++30720, ++30720, ++30720, ++30720, ++30720, ++31743, ++30719, ++29695, ++28671, ++27647, ++26623, ++25599, ++24575, ++23551, ++22527, ++21503, ++20479, ++19455, ++18431, ++17407, ++16383, ++15359, ++14335, ++13311, ++12287, ++11263, ++10239, ++9215, ++8191, ++7167, ++6143, ++5119, ++4095, ++3071, ++1024, ++1024, ++1024, ++1024, ++1024, ++1024, ++1024, ++1024, ++1024, ++1024, ++1024, ++1024, ++1024, ++1024, ++1024, ++1024, ++1024 +diff --git a/drivers/media/video/isp/noise_filter_table.h b/drivers/media/video/isp/noise_filter_table.h +new file mode 100644 +index 0000000..4b4c085 +--- /dev/null ++++ b/drivers/media/video/isp/noise_filter_table.h +@@ -0,0 +1,90 @@ ++/* ++ * noise_filter_table.h ++ * ++ * TI OMAP3 ISP - Noise filter table ++ * ++ * Copyright (C) 2010 Nokia Corporation ++ * Copyright (C) 2009 Texas Instruments, Inc. ++ * ++ * Contacts: Laurent Pinchart ++ * Sakari Ailus ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++16, ++16, ++16, ++16, ++16, ++16, ++16, ++16, ++16, ++16, ++16, ++16, ++16, ++16, ++16, ++16, ++16, ++16, ++16, ++16, ++16, ++16, ++16, ++16, ++16, ++16, ++16, ++16, ++16, ++16, ++16, ++16, ++31, ++31, ++31, ++31, ++31, ++31, ++31, ++31, ++31, ++31, ++31, ++31, ++31, ++31, ++31, ++31, ++31, ++31, ++31, ++31, ++31, ++31, ++31, ++31, ++31, ++31, ++31, ++31, ++31, ++31, ++31, ++31 +diff --git a/include/linux/Kbuild b/include/linux/Kbuild +index c0db7f4..f65f612 100644 +--- a/include/linux/Kbuild ++++ b/include/linux/Kbuild +@@ -272,6 +272,7 @@ header-y += nfsacl.h + header-y += nl80211.h + header-y += nubus.h + header-y += nvram.h ++header-y += omap3isp.h + header-y += omapfb.h + header-y += oom.h + header-y += param.h +diff --git a/include/linux/omap3isp.h b/include/linux/omap3isp.h +new file mode 100644 +index 0000000..ab249b2 +--- /dev/null ++++ b/include/linux/omap3isp.h +@@ -0,0 +1,631 @@ ++/* ++ * omap3isp.h ++ * ++ * TI OMAP3 ISP - User-space API ++ * ++ * Copyright (C) 2010 Nokia Corporation ++ * Copyright (C) 2009 Texas Instruments, Inc. ++ * ++ * Contacts: Laurent Pinchart ++ * Sakari Ailus ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#ifndef OMAP3_ISP_USER_H ++#define OMAP3_ISP_USER_H ++ ++#include ++ ++/* Private IOCTLs */ ++ ++#define VIDIOC_OMAP3ISP_CCDC_CFG \ ++ _IOWR('V', BASE_VIDIOC_PRIVATE + 1, struct omap3isp_ccdc_update_config) ++#define VIDIOC_OMAP3ISP_PRV_CFG \ ++ _IOWR('V', BASE_VIDIOC_PRIVATE + 2, struct omap3isp_prev_update_config) ++#define VIDIOC_OMAP3ISP_AEWB_CFG \ ++ _IOWR('V', BASE_VIDIOC_PRIVATE + 3, struct omap3isp_h3a_aewb_config) ++#define VIDIOC_OMAP3ISP_HIST_CFG \ ++ _IOWR('V', BASE_VIDIOC_PRIVATE + 4, struct omap3isp_hist_config) ++#define VIDIOC_OMAP3ISP_AF_CFG \ ++ _IOWR('V', BASE_VIDIOC_PRIVATE + 5, struct omap3isp_h3a_af_config) ++#define VIDIOC_OMAP3ISP_STAT_REQ \ ++ _IOWR('V', BASE_VIDIOC_PRIVATE + 6, struct omap3isp_stat_data) ++#define VIDIOC_OMAP3ISP_STAT_EN \ ++ _IOWR('V', BASE_VIDIOC_PRIVATE + 7, unsigned long) ++ ++/* Events */ ++ ++#define V4L2_EVENT_OMAP3ISP_CLASS (V4L2_EVENT_PRIVATE_START | 0x100) ++#define V4L2_EVENT_OMAP3ISP_AEWB (V4L2_EVENT_OMAP3ISP_CLASS | 0x1) ++#define V4L2_EVENT_OMAP3ISP_AF (V4L2_EVENT_OMAP3ISP_CLASS | 0x2) ++#define V4L2_EVENT_OMAP3ISP_HIST (V4L2_EVENT_OMAP3ISP_CLASS | 0x3) ++#define V4L2_EVENT_OMAP3ISP_HS_VS (V4L2_EVENT_OMAP3ISP_CLASS | 0x4) ++ ++struct omap3isp_stat_event_status { ++ __u32 frame_number; ++ __u16 config_counter; ++ __u8 buf_err; ++}; ++ ++/* AE/AWB related structures and flags*/ ++ ++/* H3A Range Constants */ ++#define OMAP3ISP_AEWB_MAX_SATURATION_LIM 1023 ++#define OMAP3ISP_AEWB_MIN_WIN_H 2 ++#define OMAP3ISP_AEWB_MAX_WIN_H 256 ++#define OMAP3ISP_AEWB_MIN_WIN_W 6 ++#define OMAP3ISP_AEWB_MAX_WIN_W 256 ++#define OMAP3ISP_AEWB_MIN_WINVC 1 ++#define OMAP3ISP_AEWB_MIN_WINHC 1 ++#define OMAP3ISP_AEWB_MAX_WINVC 128 ++#define OMAP3ISP_AEWB_MAX_WINHC 36 ++#define OMAP3ISP_AEWB_MAX_WINSTART 4095 ++#define OMAP3ISP_AEWB_MIN_SUB_INC 2 ++#define OMAP3ISP_AEWB_MAX_SUB_INC 32 ++#define OMAP3ISP_AEWB_MAX_BUF_SIZE 83600 ++ ++#define OMAP3ISP_AF_IIRSH_MIN 0 ++#define OMAP3ISP_AF_IIRSH_MAX 4095 ++#define OMAP3ISP_AF_PAXEL_HORIZONTAL_COUNT_MIN 1 ++#define OMAP3ISP_AF_PAXEL_HORIZONTAL_COUNT_MAX 36 ++#define OMAP3ISP_AF_PAXEL_VERTICAL_COUNT_MIN 1 ++#define OMAP3ISP_AF_PAXEL_VERTICAL_COUNT_MAX 128 ++#define OMAP3ISP_AF_PAXEL_INCREMENT_MIN 2 ++#define OMAP3ISP_AF_PAXEL_INCREMENT_MAX 32 ++#define OMAP3ISP_AF_PAXEL_HEIGHT_MIN 2 ++#define OMAP3ISP_AF_PAXEL_HEIGHT_MAX 256 ++#define OMAP3ISP_AF_PAXEL_WIDTH_MIN 16 ++#define OMAP3ISP_AF_PAXEL_WIDTH_MAX 256 ++#define OMAP3ISP_AF_PAXEL_HZSTART_MIN 1 ++#define OMAP3ISP_AF_PAXEL_HZSTART_MAX 4095 ++#define OMAP3ISP_AF_PAXEL_VTSTART_MIN 0 ++#define OMAP3ISP_AF_PAXEL_VTSTART_MAX 4095 ++#define OMAP3ISP_AF_THRESHOLD_MAX 255 ++#define OMAP3ISP_AF_COEF_MAX 4095 ++#define OMAP3ISP_AF_PAXEL_SIZE 48 ++#define OMAP3ISP_AF_MAX_BUF_SIZE 221184 ++ ++/** ++ * struct omap3isp_h3a_aewb_config - AE AWB configuration reset values ++ * saturation_limit: Saturation limit. ++ * @win_height: Window Height. Range 2 - 256, even values only. ++ * @win_width: Window Width. Range 6 - 256, even values only. ++ * @ver_win_count: Vertical Window Count. Range 1 - 128. ++ * @hor_win_count: Horizontal Window Count. Range 1 - 36. ++ * @ver_win_start: Vertical Window Start. Range 0 - 4095. ++ * @hor_win_start: Horizontal Window Start. Range 0 - 4095. ++ * @blk_ver_win_start: Black Vertical Windows Start. Range 0 - 4095. ++ * @blk_win_height: Black Window Height. Range 2 - 256, even values only. ++ * @subsample_ver_inc: Subsample Vertical points increment Range 2 - 32, even ++ * values only. ++ * @subsample_hor_inc: Subsample Horizontal points increment Range 2 - 32, even ++ * values only. ++ * @alaw_enable: AEW ALAW EN flag. ++ * @aewb_enable: AE AWB stats generation EN flag. ++ */ ++struct omap3isp_h3a_aewb_config { ++ /* ++ * Common fields. ++ * They should be the first ones and must be in the same order as in ++ * ispstat_generic_config struct. ++ */ ++ __u32 buf_size; ++ __u16 config_counter; ++ ++ /* Private fields */ ++ __u16 saturation_limit; ++ __u16 win_height; ++ __u16 win_width; ++ __u16 ver_win_count; ++ __u16 hor_win_count; ++ __u16 ver_win_start; ++ __u16 hor_win_start; ++ __u16 blk_ver_win_start; ++ __u16 blk_win_height; ++ __u16 subsample_ver_inc; ++ __u16 subsample_hor_inc; ++ __u8 alaw_enable; ++}; ++ ++/** ++ * struct omap3isp_stat_data - Statistic data sent to or received from user ++ * @buf: Pointer to pass to user. ++ * @frame_number: Frame number of requested stats. ++ * @cur_frame: Current frame number being processed. ++ * @buf_size: Buffer size requested and returned. ++ * @ts: Timestamp of returned framestats. ++ */ ++struct omap3isp_stat_data { ++ struct timeval ts; ++ void __user *buf; ++ __u32 buf_size; ++ __u16 frame_number; ++ __u16 cur_frame; ++ __u16 config_counter; ++ __u16 new_bufs; /* Deprecated */ ++}; ++ ++ ++/* Histogram related structs */ ++ ++/* Flags for number of bins */ ++#define OMAP3ISP_HIST_BINS_32 0 ++#define OMAP3ISP_HIST_BINS_64 1 ++#define OMAP3ISP_HIST_BINS_128 2 ++#define OMAP3ISP_HIST_BINS_256 3 ++ ++/* Number of bins * 4 colors * 4-bytes word */ ++#define OMAP3ISP_HIST_MEM_SIZE_BINS(n) ((1 << ((n)+5))*4*4) ++ ++#define OMAP3ISP_HIST_MEM_SIZE 1024 ++#define OMAP3ISP_HIST_MIN_REGIONS 1 ++#define OMAP3ISP_HIST_MAX_REGIONS 4 ++#define OMAP3ISP_HIST_MAX_WB_GAIN 255 ++#define OMAP3ISP_HIST_MIN_WB_GAIN 0 ++#define OMAP3ISP_HIST_MAX_BIT_WIDTH 14 ++#define OMAP3ISP_HIST_MIN_BIT_WIDTH 8 ++#define OMAP3ISP_HIST_MAX_WG 4 ++#define OMAP3ISP_HIST_MAX_BUF_SIZE 4096 ++ ++/* Source */ ++#define OMAP3ISP_HIST_SOURCE_CCDC 0 ++#define OMAP3ISP_HIST_SOURCE_MEM 1 ++ ++/* CFA pattern */ ++#define OMAP3ISP_HIST_CFA_BAYER 0 ++#define OMAP3ISP_HIST_CFA_FOVEONX3 1 ++ ++struct omap3isp_hist_region { ++ __u16 h_start; ++ __u16 h_end; ++ __u16 v_start; ++ __u16 v_end; ++}; ++ ++struct omap3isp_hist_config { ++ /* ++ * Common fields. ++ * They should be the first ones and must be in the same order as in ++ * ispstat_generic_config struct. ++ */ ++ __u32 buf_size; ++ __u16 config_counter; ++ ++ __u8 num_acc_frames; /* Num of image frames to be processed and ++ accumulated for each histogram frame */ ++ __u16 hist_bins; /* number of bins: 32, 64, 128, or 256 */ ++ __u8 cfa; /* BAYER or FOVEON X3 */ ++ __u8 wg[OMAP3ISP_HIST_MAX_WG]; /* White Balance Gain */ ++ __u8 num_regions; /* number of regions to be configured */ ++ struct omap3isp_hist_region region[OMAP3ISP_HIST_MAX_REGIONS]; ++}; ++ ++/* Auto Focus related structs */ ++ ++#define OMAP3ISP_AF_NUM_COEF 11 ++ ++enum omap3isp_h3a_af_fvmode { ++ OMAP3ISP_AF_MODE_SUMMED = 0, ++ OMAP3ISP_AF_MODE_PEAK = 1 ++}; ++ ++/* Red, Green, and blue pixel location in the AF windows */ ++enum omap3isp_h3a_af_rgbpos { ++ OMAP3ISP_AF_GR_GB_BAYER = 0, /* GR and GB as Bayer pattern */ ++ OMAP3ISP_AF_RG_GB_BAYER = 1, /* RG and GB as Bayer pattern */ ++ OMAP3ISP_AF_GR_BG_BAYER = 2, /* GR and BG as Bayer pattern */ ++ OMAP3ISP_AF_RG_BG_BAYER = 3, /* RG and BG as Bayer pattern */ ++ OMAP3ISP_AF_GG_RB_CUSTOM = 4, /* GG and RB as custom pattern */ ++ OMAP3ISP_AF_RB_GG_CUSTOM = 5 /* RB and GG as custom pattern */ ++}; ++ ++/* Contains the information regarding the Horizontal Median Filter */ ++struct omap3isp_h3a_af_hmf { ++ __u8 enable; /* Status of Horizontal Median Filter */ ++ __u8 threshold; /* Threshhold Value for Horizontal Median Filter */ ++}; ++ ++/* Contains the information regarding the IIR Filters */ ++struct omap3isp_h3a_af_iir { ++ __u16 h_start; /* IIR horizontal start */ ++ __u16 coeff_set0[OMAP3ISP_AF_NUM_COEF]; /* Filter coefficient, set 0 */ ++ __u16 coeff_set1[OMAP3ISP_AF_NUM_COEF]; /* Filter coefficient, set 1 */ ++}; ++ ++/* Contains the information regarding the Paxels Structure in AF Engine */ ++struct omap3isp_h3a_af_paxel { ++ __u16 h_start; /* Horizontal Start Position */ ++ __u16 v_start; /* Vertical Start Position */ ++ __u8 width; /* Width of the Paxel */ ++ __u8 height; /* Height of the Paxel */ ++ __u8 h_cnt; /* Horizontal Count */ ++ __u8 v_cnt; /* vertical Count */ ++ __u8 line_inc; /* Line Increment */ ++}; ++ ++/* Contains the parameters required for hardware set up of AF Engine */ ++struct omap3isp_h3a_af_config { ++ /* ++ * Common fields. ++ * They should be the first ones and must be in the same order as in ++ * ispstat_generic_config struct. ++ */ ++ __u32 buf_size; ++ __u16 config_counter; ++ ++ struct omap3isp_h3a_af_hmf hmf; /* HMF configurations */ ++ struct omap3isp_h3a_af_iir iir; /* IIR filter configurations */ ++ struct omap3isp_h3a_af_paxel paxel; /* Paxel parameters */ ++ enum omap3isp_h3a_af_rgbpos rgb_pos; /* RGB Positions */ ++ enum omap3isp_h3a_af_fvmode fvmode; /* Accumulator mode */ ++ __u8 alaw_enable; /* AF ALAW status */ ++}; ++ ++/* ISP CCDC structs */ ++ ++/* Abstraction layer CCDC configurations */ ++#define OMAP3ISP_CCDC_ALAW (1 << 0) ++#define OMAP3ISP_CCDC_LPF (1 << 1) ++#define OMAP3ISP_CCDC_BLCLAMP (1 << 2) ++#define OMAP3ISP_CCDC_BCOMP (1 << 3) ++#define OMAP3ISP_CCDC_FPC (1 << 4) ++#define OMAP3ISP_CCDC_CULL (1 << 5) ++#define OMAP3ISP_CCDC_CONFIG_LSC (1 << 7) ++#define OMAP3ISP_CCDC_TBL_LSC (1 << 8) ++ ++#define OMAP3ISP_RGB_MAX 3 ++ ++/* Enumeration constants for Alaw input width */ ++enum omap3isp_alaw_ipwidth { ++ OMAP3ISP_ALAW_BIT12_3 = 0x3, ++ OMAP3ISP_ALAW_BIT11_2 = 0x4, ++ OMAP3ISP_ALAW_BIT10_1 = 0x5, ++ OMAP3ISP_ALAW_BIT9_0 = 0x6 ++}; ++ ++/** ++ * struct omap3isp_ccdc_lsc_config - LSC configuration ++ * @offset: Table Offset of the gain table. ++ * @gain_mode_n: Vertical dimension of a paxel in LSC configuration. ++ * @gain_mode_m: Horizontal dimension of a paxel in LSC configuration. ++ * @gain_format: Gain table format. ++ * @fmtsph: Start pixel horizontal from start of the HS sync pulse. ++ * @fmtlnh: Number of pixels in horizontal direction to use for the data ++ * reformatter. ++ * @fmtslv: Start line from start of VS sync pulse for the data reformatter. ++ * @fmtlnv: Number of lines in vertical direction for the data reformatter. ++ * @initial_x: X position, in pixels, of the first active pixel in reference ++ * to the first active paxel. Must be an even number. ++ * @initial_y: Y position, in pixels, of the first active pixel in reference ++ * to the first active paxel. Must be an even number. ++ * @size: Size of LSC gain table. Filled when loaded from userspace. ++ */ ++struct omap3isp_ccdc_lsc_config { ++ __u16 offset; ++ __u8 gain_mode_n; ++ __u8 gain_mode_m; ++ __u8 gain_format; ++ __u16 fmtsph; ++ __u16 fmtlnh; ++ __u16 fmtslv; ++ __u16 fmtlnv; ++ __u8 initial_x; ++ __u8 initial_y; ++ __u32 size; ++}; ++ ++/** ++ * struct omap3isp_ccdc_bclamp - Optical & Digital black clamp subtract ++ * @obgain: Optical black average gain. ++ * @obstpixel: Start Pixel w.r.t. HS pulse in Optical black sample. ++ * @oblines: Optical Black Sample lines. ++ * @oblen: Optical Black Sample Length. ++ * @dcsubval: Digital Black Clamp subtract value. ++ */ ++struct omap3isp_ccdc_bclamp { ++ __u8 obgain; ++ __u8 obstpixel; ++ __u8 oblines; ++ __u8 oblen; ++ __u16 dcsubval; ++}; ++ ++/** ++ * struct omap3isp_ccdc_fpc - Faulty Pixels Correction ++ * @fpnum: Number of faulty pixels to be corrected in the frame. ++ * @fpcaddr: Memory address of the FPC Table ++ */ ++struct omap3isp_ccdc_fpc { ++ __u16 fpnum; ++ __u32 fpcaddr; ++}; ++ ++/** ++ * struct omap3isp_ccdc_blcomp - Black Level Compensation parameters ++ * @b_mg: B/Mg pixels. 2's complement. -128 to +127. ++ * @gb_g: Gb/G pixels. 2's complement. -128 to +127. ++ * @gr_cy: Gr/Cy pixels. 2's complement. -128 to +127. ++ * @r_ye: R/Ye pixels. 2's complement. -128 to +127. ++ */ ++struct omap3isp_ccdc_blcomp { ++ __u8 b_mg; ++ __u8 gb_g; ++ __u8 gr_cy; ++ __u8 r_ye; ++}; ++ ++/** ++ * omap3isp_ccdc_culling - Culling parameters ++ * @v_pattern: Vertical culling pattern. ++ * @h_odd: Horizontal Culling pattern for odd lines. ++ * @h_even: Horizontal Culling pattern for even lines. ++ */ ++struct omap3isp_ccdc_culling { ++ __u8 v_pattern; ++ __u16 h_odd; ++ __u16 h_even; ++}; ++ ++/** ++ * omap3isp_ccdc_update_config - CCDC configuration ++ * @update: Specifies which CCDC registers should be updated. ++ * @flag: Specifies which CCDC functions should be enabled. ++ * @alawip: Enable/Disable A-Law compression. ++ * @bclamp: Black clamp control register. ++ * @blcomp: Black level compensation value for RGrGbB Pixels. 2's complement. ++ * @fpc: Number of faulty pixels corrected in the frame, address of FPC table. ++ * @cull: Cull control register. ++ * @lsc: Pointer to LSC gain table. ++ */ ++struct omap3isp_ccdc_update_config { ++ __u16 update; ++ __u16 flag; ++ enum omap3isp_alaw_ipwidth alawip; ++ struct omap3isp_ccdc_bclamp __user *bclamp; ++ struct omap3isp_ccdc_blcomp __user *blcomp; ++ struct omap3isp_ccdc_fpc __user *fpc; ++ struct omap3isp_ccdc_lsc_config __user *lsc_cfg; ++ struct omap3isp_ccdc_culling __user *cull; ++ __u8 __user *lsc; ++}; ++ ++/* Preview configurations */ ++#define OMAP3ISP_PREV_LUMAENH (1 << 0) ++#define OMAP3ISP_PREV_INVALAW (1 << 1) ++#define OMAP3ISP_PREV_HRZ_MED (1 << 2) ++#define OMAP3ISP_PREV_CFA (1 << 3) ++#define OMAP3ISP_PREV_CHROMA_SUPP (1 << 4) ++#define OMAP3ISP_PREV_WB (1 << 5) ++#define OMAP3ISP_PREV_BLKADJ (1 << 6) ++#define OMAP3ISP_PREV_RGB2RGB (1 << 7) ++#define OMAP3ISP_PREV_COLOR_CONV (1 << 8) ++#define OMAP3ISP_PREV_YC_LIMIT (1 << 9) ++#define OMAP3ISP_PREV_DEFECT_COR (1 << 10) ++#define OMAP3ISP_PREV_GAMMABYPASS (1 << 11) ++#define OMAP3ISP_PREV_DRK_FRM_CAPTURE (1 << 12) ++#define OMAP3ISP_PREV_DRK_FRM_SUBTRACT (1 << 13) ++#define OMAP3ISP_PREV_LENS_SHADING (1 << 14) ++#define OMAP3ISP_PREV_NF (1 << 15) ++#define OMAP3ISP_PREV_GAMMA (1 << 16) ++ ++#define OMAP3ISP_PREV_NF_TBL_SIZE 64 ++#define OMAP3ISP_PREV_CFA_TBL_SIZE 576 ++#define OMAP3ISP_PREV_GAMMA_TBL_SIZE 1024 ++#define OMAP3ISP_PREV_YENH_TBL_SIZE 128 ++ ++#define OMAP3ISP_PREV_DETECT_CORRECT_CHANNELS 4 ++ ++/** ++ * struct omap3isp_prev_hmed - Horizontal Median Filter ++ * @odddist: Distance between consecutive pixels of same color in the odd line. ++ * @evendist: Distance between consecutive pixels of same color in the even ++ * line. ++ * @thres: Horizontal median filter threshold. ++ */ ++struct omap3isp_prev_hmed { ++ __u8 odddist; ++ __u8 evendist; ++ __u8 thres; ++}; ++ ++/* ++ * Enumeration for CFA Formats supported by preview ++ */ ++enum omap3isp_cfa_fmt { ++ OMAP3ISP_CFAFMT_BAYER, ++ OMAP3ISP_CFAFMT_SONYVGA, ++ OMAP3ISP_CFAFMT_RGBFOVEON, ++ OMAP3ISP_CFAFMT_DNSPL, ++ OMAP3ISP_CFAFMT_HONEYCOMB, ++ OMAP3ISP_CFAFMT_RRGGBBFOVEON ++}; ++ ++/** ++ * struct omap3isp_prev_cfa - CFA Interpolation ++ * @format: CFA Format Enum value supported by preview. ++ * @gradthrs_vert: CFA Gradient Threshold - Vertical. ++ * @gradthrs_horz: CFA Gradient Threshold - Horizontal. ++ * @table: Pointer to the CFA table. ++ */ ++struct omap3isp_prev_cfa { ++ enum omap3isp_cfa_fmt format; ++ __u8 gradthrs_vert; ++ __u8 gradthrs_horz; ++ __u32 table[OMAP3ISP_PREV_CFA_TBL_SIZE]; ++}; ++ ++/** ++ * struct omap3isp_prev_csup - Chrominance Suppression ++ * @gain: Gain. ++ * @thres: Threshold. ++ * @hypf_en: Flag to enable/disable the High Pass Filter. ++ */ ++struct omap3isp_prev_csup { ++ __u8 gain; ++ __u8 thres; ++ __u8 hypf_en; ++}; ++ ++/** ++ * struct omap3isp_prev_wbal - White Balance ++ * @dgain: Digital gain (U10Q8). ++ * @coef3: White balance gain - COEF 3 (U8Q5). ++ * @coef2: White balance gain - COEF 2 (U8Q5). ++ * @coef1: White balance gain - COEF 1 (U8Q5). ++ * @coef0: White balance gain - COEF 0 (U8Q5). ++ */ ++struct omap3isp_prev_wbal { ++ __u16 dgain; ++ __u8 coef3; ++ __u8 coef2; ++ __u8 coef1; ++ __u8 coef0; ++}; ++ ++/** ++ * struct omap3isp_prev_blkadj - Black Level Adjustment ++ * @red: Black level offset adjustment for Red in 2's complement format ++ * @green: Black level offset adjustment for Green in 2's complement format ++ * @blue: Black level offset adjustment for Blue in 2's complement format ++ */ ++struct omap3isp_prev_blkadj { ++ /*Black level offset adjustment for Red in 2's complement format */ ++ __u8 red; ++ /*Black level offset adjustment for Green in 2's complement format */ ++ __u8 green; ++ /* Black level offset adjustment for Blue in 2's complement format */ ++ __u8 blue; ++}; ++ ++/** ++ * struct omap3isp_prev_rgbtorgb - RGB to RGB Blending ++ * @matrix: Blending values(S12Q8 format) ++ * [RR] [GR] [BR] ++ * [RG] [GG] [BG] ++ * [RB] [GB] [BB] ++ * @offset: Blending offset value for R,G,B in 2's complement integer format. ++ */ ++struct omap3isp_prev_rgbtorgb { ++ __u16 matrix[OMAP3ISP_RGB_MAX][OMAP3ISP_RGB_MAX]; ++ __u16 offset[OMAP3ISP_RGB_MAX]; ++}; ++ ++/** ++ * struct omap3isp_prev_csc - Color Space Conversion from RGB-YCbYCr ++ * @matrix: Color space conversion coefficients(S10Q8) ++ * [CSCRY] [CSCGY] [CSCBY] ++ * [CSCRCB] [CSCGCB] [CSCBCB] ++ * [CSCRCR] [CSCGCR] [CSCBCR] ++ * @offset: CSC offset values for Y offset, CB offset and CR offset respectively ++ */ ++struct omap3isp_prev_csc { ++ __u16 matrix[OMAP3ISP_RGB_MAX][OMAP3ISP_RGB_MAX]; ++ __s16 offset[OMAP3ISP_RGB_MAX]; ++}; ++ ++/** ++ * struct omap3isp_prev_yclimit - Y, C Value Limit ++ * @minC: Minimum C value ++ * @maxC: Maximum C value ++ * @minY: Minimum Y value ++ * @maxY: Maximum Y value ++ */ ++struct omap3isp_prev_yclimit { ++ __u8 minC; ++ __u8 maxC; ++ __u8 minY; ++ __u8 maxY; ++}; ++ ++/** ++ * struct omap3isp_prev_dcor - Defect correction ++ * @couplet_mode_en: Flag to enable or disable the couplet dc Correction in NF ++ * @detect_correct: Thresholds for correction bit 0:10 detect 16:25 correct ++ */ ++struct omap3isp_prev_dcor { ++ __u8 couplet_mode_en; ++ __u32 detect_correct[OMAP3ISP_PREV_DETECT_CORRECT_CHANNELS]; ++}; ++ ++/** ++ * struct omap3isp_prev_nf - Noise Filter ++ * @spread: Spread value to be used in Noise Filter ++ * @table: Pointer to the Noise Filter table ++ */ ++struct omap3isp_prev_nf { ++ __u8 spread; ++ __u32 table[OMAP3ISP_PREV_NF_TBL_SIZE]; ++}; ++ ++/** ++ * struct omap3isp_prev_gtables - Gamma correction tables ++ * @red: Array for red gamma table. ++ * @green: Array for green gamma table. ++ * @blue: Array for blue gamma table. ++ */ ++struct omap3isp_prev_gtables { ++ __u32 red[OMAP3ISP_PREV_GAMMA_TBL_SIZE]; ++ __u32 green[OMAP3ISP_PREV_GAMMA_TBL_SIZE]; ++ __u32 blue[OMAP3ISP_PREV_GAMMA_TBL_SIZE]; ++}; ++ ++/** ++ * struct omap3isp_prev_luma - Luma enhancement ++ * @table: Array for luma enhancement table. ++ */ ++struct omap3isp_prev_luma { ++ __u32 table[OMAP3ISP_PREV_YENH_TBL_SIZE]; ++}; ++ ++/** ++ * struct omap3isp_prev_update_config - Preview engine configuration (user) ++ * @update: Specifies which ISP Preview registers should be updated. ++ * @flag: Specifies which ISP Preview functions should be enabled. ++ * @shading_shift: 3bit value of shift used in shading compensation. ++ * @luma: Pointer to luma enhancement structure. ++ * @hmed: Pointer to structure containing the odd and even distance. ++ * between the pixels in the image along with the filter threshold. ++ * @cfa: Pointer to structure containing the CFA interpolation table, CFA. ++ * format in the image, vertical and horizontal gradient threshold. ++ * @csup: Pointer to Structure for Chrominance Suppression coefficients. ++ * @wbal: Pointer to structure for White Balance. ++ * @blkadj: Pointer to structure for Black Adjustment. ++ * @rgb2rgb: Pointer to structure for RGB to RGB Blending. ++ * @csc: Pointer to structure for Color Space Conversion from RGB-YCbYCr. ++ * @yclimit: Pointer to structure for Y, C Value Limit. ++ * @dcor: Pointer to structure for defect correction. ++ * @nf: Pointer to structure for Noise Filter ++ * @gamma: Pointer to gamma structure. ++ */ ++struct omap3isp_prev_update_config { ++ __u32 update; ++ __u32 flag; ++ __u32 shading_shift; ++ struct omap3isp_prev_luma __user *luma; ++ struct omap3isp_prev_hmed __user *hmed; ++ struct omap3isp_prev_cfa __user *cfa; ++ struct omap3isp_prev_csup __user *csup; ++ struct omap3isp_prev_wbal __user *wbal; ++ struct omap3isp_prev_blkadj __user *blkadj; ++ struct omap3isp_prev_rgbtorgb __user *rgb2rgb; ++ struct omap3isp_prev_csc __user *csc; ++ struct omap3isp_prev_yclimit __user *yclimit; ++ struct omap3isp_prev_dcor __user *dcor; ++ struct omap3isp_prev_nf __user *nf; ++ struct omap3isp_prev_gtables __user *gamma; ++}; ++ ++#endif /* OMAP3_ISP_USER_H */ +-- +1.6.6.1 + diff --git a/recipes/linux/linux-omap_2.6.37.bb b/recipes/linux/linux-omap_2.6.37.bb index 7855ebe1f0..dba3fbbc2e 100644 --- a/recipes/linux/linux-omap_2.6.37.bb +++ b/recipes/linux/linux-omap_2.6.37.bb @@ -149,6 +149,49 @@ SRC_URI_append = " \ file://wl1271/0013-drivers-media-radio-Update-Kconfig-and-Makefile-for-.patch \ file://wl1271/0014-drivers-misc-ti-st-change-protocol-parse-logic.patch \ file://wl1271/0015-Bluetooth-btwilink-driver.patch \ + \ + file://media/0001-v4l-Share-code-between-video_usercopy-and-video_ioct.patch \ + file://media/0002-v4l-subdev-Don-t-require-core-operations.patch \ + file://media/0003-v4l-subdev-Merge-v4l2_i2c_new_subdev_cfg-and-v4l2_i2.patch \ + file://media/0004-v4l-subdev-Add-device-node-support.patch \ + file://media/0005-v4l-subdev-Uninline-the-v4l2_subdev_init-function.patch \ + file://media/0006-v4l-subdev-Control-ioctls-support.patch \ + file://media/0007-v4l-subdev-Events-support.patch \ + file://media/0008-media-Media-device-node-support.patch \ + file://media/0009-media-Media-device.patch \ + file://media/0010-media-Entities-pads-and-links.patch \ + file://media/0011-media-Entity-graph-traversal.patch \ + file://media/0012-media-Entity-use-count.patch \ + file://media/0013-media-Media-device-information-query.patch \ + file://media/0014-media-Entities-pads-and-links-enumeration.patch \ + file://media/0015-media-Links-setup.patch \ + file://media/0016-media-Pipelines-and-media-streams.patch \ + file://media/0017-v4l-Add-a-media_device-pointer-to-the-v4l2_device-st.patch \ + file://media/0018-v4l-Make-video_device-inherit-from-media_entity.patch \ + file://media/0019-v4l-Make-v4l2_subdev-inherit-from-media_entity.patch \ + file://media/0020-v4l-Move-the-media-v4l2-mediabus.h-header-to-include.patch \ + file://media/0021-v4l-Replace-enums-with-fixed-sized-fields-in-public-.patch \ + file://media/0022-v4l-Rename-V4L2_MBUS_FMT_GREY8_1X8-to-V4L2_MBUS_FMT_.patch \ + file://media/0023-v4l-Group-media-bus-pixel-codes-by-types-and-sort-th.patch \ + file://media/0024-v4l-Create-v4l2-subdev-file-handle-structure.patch \ + file://media/0025-v4l-subdev-Add-a-new-file-operations-class.patch \ + file://media/0026-v4l-v4l2_subdev-pad-level-operations.patch \ + file://media/0028-v4l-v4l2_subdev-userspace-format-API.patch \ + file://media/0029-v4l-v4l2_subdev-userspace-frame-interval-API.patch \ + file://media/0030-v4l-v4l2_subdev-userspace-crop-API.patch \ + file://media/0031-v4l-subdev-Generic-ioctl-support.patch \ + file://media/0032-v4l-Add-subdev-sensor-g_skip_frames-operation.patch \ + file://media/0033-v4l-Include-linux-videodev2.h-in-media-v4l2-ctrls.h.patch \ + file://media/0034-v4l-Fix-a-use-before-set-in-the-control-framework.patch \ + file://media/0035-v4l-Add-8-bit-YUYV-on-16-bit-bus-and-SGRBG10-media-b.patch \ + file://media/0036-v4l-Add-remaining-RAW10-patterns-w-DPCM-pixel-code-v.patch \ + file://media/0037-v4l-Add-missing-12-bits-bayer-media-bus-formats.patch \ + file://media/0038-v4l-Add-12-bits-bayer-pixel-formats.patch \ + file://media/0039-ARM-OMAP3-Update-Camera-ISP-definitions-for-OMAP3630.patch \ + file://media/0040-omap3-Remove-unusued-ISP-CBUFF-resource.patch \ + file://media/0041-omap3-Add-function-to-register-omap3isp-platform-dev.patch \ + file://media/0042-omap2-Fix-camera-resources-for-multiomap.patch \ + file://media/0043-OMAP3-ISP-driver.patch \ " SRC_URI_append_usrp-e1xx = "\ -- cgit 1.2.3-korg