From 5e8c7c54a9b297dae0081dd19a7bb94e23040a3d Mon Sep 17 00:00:00 2001 From: Joshua Lock Date: Tue, 18 May 2010 14:51:13 +0100 Subject: linux-moblin: add 2.6.33.2 kernel from MeeGo 1.0 Signed-off-by: Joshua Lock --- ...-2.6.35-moorestown-camera-driver-10.0-2-3.patch | 9779 ++++++++++++++++++++ 1 file changed, 9779 insertions(+) create mode 100644 meta-moblin/packages/linux/linux-moblin-2.6.33.2/linux-2.6.35-moorestown-camera-driver-10.0-2-3.patch (limited to 'meta-moblin/packages/linux/linux-moblin-2.6.33.2/linux-2.6.35-moorestown-camera-driver-10.0-2-3.patch') diff --git a/meta-moblin/packages/linux/linux-moblin-2.6.33.2/linux-2.6.35-moorestown-camera-driver-10.0-2-3.patch b/meta-moblin/packages/linux/linux-moblin-2.6.33.2/linux-2.6.35-moorestown-camera-driver-10.0-2-3.patch new file mode 100644 index 000000000..f3e8159eb --- /dev/null +++ b/meta-moblin/packages/linux/linux-moblin-2.6.33.2/linux-2.6.35-moorestown-camera-driver-10.0-2-3.patch @@ -0,0 +1,9779 @@ +From b5dc72f2a0bc509a241e03eea51e739204315f93 Mon Sep 17 00:00:00 2001 +From: Zheng Ba +Date: Thu, 1 Apr 2010 16:24:20 +0800 +Subject: [PATCH 2/3] Moorestown Camera Imaging driver Beta 10.0 + +Patch-mainline: 2.6.35? + +Changes from Beta 9.0: +1. Fixed hsd sighting + 3469638 3469639 3469710 3469822 (high) + 3469697 (medium) + +Changes from Beta 8.0: +1. Fixed hsd sighting + 3469056 3469058 (critical) + 3469705 3469696 3469709 3469510 (medium) + +Changes from Beta 7.0: +1. Fixed hsd sighting 3469681,3469682,3469683 (high) + +Changes from Beta 6.0: +1. Fixed hsd sighting 3469668 (high) +2. Fixed ov5630 v4l2 view-finding dark issue +3. Enabled support for popular v4l2 applications (cheese, skype, ffmpeg) + +Changes from Beta 5.1: +1. Fixed CRITICAL sighting 3469558 -- ciapp fails to launch with segment fault +2. Fixed HIGH sighting 3479513 -- ov5630 AWB unstable +3. Improved KMOT sensor 720p fps from 30 to 40 + +Changes from Beta 5.0: +Fixed a critical issue of camera driver not loading -- hsd 3469557 + +Main changes from Beta 4.0: +Fixed 4 HSD sightings: 3469392,3469099,3469470,3469500 + +Main changes from Beta 3.0: +Fixed 7 HSD sightings: 3469264,3469112,3469395,3469103,3469105,3469471,3469484 + +Main changes from Beta 2.0: +Fixed 6 HSD sightings: 3469047,3469315,3469317,3469101,3468409,3469391 + +Main changes from Beta 1.1: +1. Added interrupt mode for jpeg capture and KMOT viewfinding +2. Fixed HSD sighting 3469228 and 3469147 + +Main changes from Alpha2: +Enabled MIPI interface in ISP driver and KMOT sensor s5k4e1. +Enabled FIFO in ISP driver, which doubled the fps in view-finding mode. +Enabled Subdev Framework in CI kernel driver. +Enabled AF Continuous Mode. +Enabled AE scene evaluation. + +Enabled the camera drivers in kernel: +Device Drivers --> Multimedia support --> Video For Linux +Device Drivers --> Mulitmedia support --> Video capture adapters --> +--> Moorestown Langwell Camera Imaging Subsystem support. + +Kernel configs: +1. camera driver depends on GPIO library and I2C driver. +CONFIG_GENERIC_GPIO=y +CONFIG_I2C=y +CONFIG_GPIOLIB=y +2. camera driver depends on videobuf-core and videobuf-dma-contig. +VIDEOBUF_GEN=y +VIDEOBUF_DMA_CONTIG=y +3. enable multimedia support and video capture. +CONFIG_MEDIA_SUPPORT=y +CONFIG_VIDEO_DEV=y +CONFIG_VIDEO_V4L2_COMMON=y +CONFIG_VIDEO_MEDIA=y +CONFIG_VIDEO_V4L2=y +4. camera drivers incluing ISP, 5630, 5630-motor, s5k4e1, s5k4e1-motor, 2650, +9665, flash. +CONFIG_VIDEO_MRSTCI=y +CONFIG_VIDEO_MRST_ISP=y +CONFIG_VIDEO_MRST_OV5630=y +CONFIG_VIDEO_MRST_OV5630_MOTOR=y +CONFIG_VIDEO_MRST_S5K4E1=y +CONFIG_VIDEO_MRST_S5K4E1_MOTOR=y +CONFIG_VIDEO_MRST_FLASH=y +CONFIG_VIDEO_MRST_OV2650=y +CONFIG_VIDEO_MRST_OV9665=y + +Signed-off-by: Zheng Ba +--- + drivers/media/video/mrstci/Kconfig | 26 + + drivers/media/video/mrstci/Makefile | 8 + + drivers/media/video/mrstci/mrstisp/Kconfig | 10 + + drivers/media/video/mrstci/mrstisp/Makefile | 7 + + .../video/mrstci/mrstisp/__mrstisp_private_ioctl.c | 324 +++ + drivers/media/video/mrstci/mrstisp/mrstisp_dp.c | 1301 +++++++++ + drivers/media/video/mrstci/mrstisp/mrstisp_hw.c | 1622 +++++++++++ + drivers/media/video/mrstci/mrstisp/mrstisp_isp.c | 1993 +++++++++++++ + drivers/media/video/mrstci/mrstisp/mrstisp_jpe.c | 569 ++++ + drivers/media/video/mrstci/mrstisp/mrstisp_main.c | 2977 ++++++++++++++++++++ + drivers/media/video/mrstci/mrstisp/mrstisp_mif.c | 763 +++++ + 11 files changed, 9600 insertions(+), 0 deletions(-) + create mode 100644 drivers/media/video/mrstci/Kconfig + create mode 100644 drivers/media/video/mrstci/Makefile + create mode 100644 drivers/media/video/mrstci/mrstisp/Kconfig + create mode 100644 drivers/media/video/mrstci/mrstisp/Makefile + create mode 100644 drivers/media/video/mrstci/mrstisp/__mrstisp_private_ioctl.c + create mode 100644 drivers/media/video/mrstci/mrstisp/mrstisp_dp.c + create mode 100644 drivers/media/video/mrstci/mrstisp/mrstisp_hw.c + create mode 100644 drivers/media/video/mrstci/mrstisp/mrstisp_isp.c + create mode 100644 drivers/media/video/mrstci/mrstisp/mrstisp_jpe.c + create mode 100644 drivers/media/video/mrstci/mrstisp/mrstisp_main.c + create mode 100644 drivers/media/video/mrstci/mrstisp/mrstisp_mif.c + +diff --git a/drivers/media/video/mrstci/Kconfig b/drivers/media/video/mrstci/Kconfig +new file mode 100644 +index 0000000..9ac7065 +--- /dev/null ++++ b/drivers/media/video/mrstci/Kconfig +@@ -0,0 +1,26 @@ ++menuconfig VIDEO_MRSTCI ++ bool "Moorestown Langwell Camera Imaging Subsystem support" ++ depends on VIDEO_V4L2 ++ default y ++ ++ ---help--- ++ Say Y here to enable selecting the Intel Moorestown Langwell Camera Imaging Subsystem for webcams. ++ ++if VIDEO_MRSTCI && VIDEO_V4L2 ++ ++source "drivers/media/video/mrstci/mrstisp/Kconfig" ++ ++source "drivers/media/video/mrstci/mrstov5630/Kconfig" ++source "drivers/media/video/mrstci/mrstov5630_motor/Kconfig" ++ ++source "drivers/media/video/mrstci/mrsts5k4e1/Kconfig" ++source "drivers/media/video/mrstci/mrsts5k4e1_motor/Kconfig" ++ ++source "drivers/media/video/mrstci/mrstflash/Kconfig" ++ ++source "drivers/media/video/mrstci/mrstov2650/Kconfig" ++ ++source "drivers/media/video/mrstci/mrstov9665/Kconfig" ++ ++endif # VIDEO_MRSTCI ++ +diff --git a/drivers/media/video/mrstci/Makefile b/drivers/media/video/mrstci/Makefile +new file mode 100644 +index 0000000..9d3449e +--- /dev/null ++++ b/drivers/media/video/mrstci/Makefile +@@ -0,0 +1,8 @@ ++obj-$(CONFIG_VIDEO_MRST_OV2650) += mrstov2650/ ++obj-$(CONFIG_VIDEO_MRST_OV9665) += mrstov9665/ ++obj-$(CONFIG_VIDEO_MRST_OV5630) += mrstov5630/ ++obj-$(CONFIG_VIDEO_MRST_OV5630_MOTOR) += mrstov5630_motor/ ++obj-$(CONFIG_VIDEO_MRST_S5K4E1) += mrsts5k4e1/ ++obj-$(CONFIG_VIDEO_MRST_S5K4E1_MOTOR) += mrsts5k4e1_motor/ ++obj-$(CONFIG_VIDEO_MRST_FLASH) += mrstflash/ ++obj-$(CONFIG_VIDEO_MRST_ISP) += mrstisp/ +diff --git a/drivers/media/video/mrstci/mrstisp/Kconfig b/drivers/media/video/mrstci/mrstisp/Kconfig +new file mode 100644 +index 0000000..8e58a87 +--- /dev/null ++++ b/drivers/media/video/mrstci/mrstisp/Kconfig +@@ -0,0 +1,10 @@ ++config VIDEO_MRST_ISP ++ tristate "Moorstown Marvin - ISP Driver" ++ depends on VIDEO_V4L2 ++ select VIDEOBUF_DMA_CONTIG ++ default y ++ ---help--- ++ Say Y here if you want support for cameras based on the Intel Moorestown platform. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called mrstisp.ko. +diff --git a/drivers/media/video/mrstci/mrstisp/Makefile b/drivers/media/video/mrstci/mrstisp/Makefile +new file mode 100644 +index 0000000..30f4e62 +--- /dev/null ++++ b/drivers/media/video/mrstci/mrstisp/Makefile +@@ -0,0 +1,7 @@ ++mrstisp-objs := mrstisp_main.o mrstisp_hw.o mrstisp_isp.o \ ++ mrstisp_dp.o mrstisp_mif.o mrstisp_jpe.o \ ++ __mrstisp_private_ioctl.o ++ ++obj-$(CONFIG_VIDEO_MRST_ISP) += mrstisp.o ++ ++EXTRA_CFLAGS += -I$(src)/../include -I$(src)/include +diff --git a/drivers/media/video/mrstci/mrstisp/__mrstisp_private_ioctl.c b/drivers/media/video/mrstci/mrstisp/__mrstisp_private_ioctl.c +new file mode 100644 +index 0000000..85cc482 +--- /dev/null ++++ b/drivers/media/video/mrstci/mrstisp/__mrstisp_private_ioctl.c +@@ -0,0 +1,324 @@ ++/* ++ * Support for Moorestown Langwell Camera Imaging ISP subsystem. ++ * ++ * Copyright (c) 2009 Intel Corporation. All Rights Reserved. ++ * ++ * 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 Street, Fifth Floor, Boston, MA ++ * 02110-1301, USA. ++ * ++ * ++ * Xiaolin Zhang ++ */ ++ ++#include "mrstisp_stdinc.h" ++ ++/* ++static u32 copy_sensor_config_from_user(struct ci_sensor_config *des, ++ struct ci_sensor_config *src) ++{ ++ u32 ret = 0; ++ ret = copy_from_user((void *)des, (const void *)src, ++ sizeof(struct ci_sensor_config)); ++ if (ret) ++ return -EFAULT; ++ return ret; ++} ++ ++static u32 copy_sensor_caps_from_user(struct ci_sensor_caps *des, ++ struct ci_sensor_caps *src) ++{ ++ u32 ret = 0; ++ ret = copy_from_user((void *)des, (const void *)src, ++ sizeof(struct ci_sensor_caps)); ++ if (ret) ++ return -EFAULT; ++ return ret; ++} ++ ++static u32 copy_isp_config_from_user(struct ci_isp_config *des, ++ struct ci_isp_config *src) ++{ ++ int ret = 0; ++ ret = copy_from_user((void *)des, (const void *)src, ++ sizeof(struct ci_isp_config)); ++ if (ret) { ++ eprintk("returning %d", ret); ++ return ret; ++ } ++ return 0; ++} ++*/ ++ ++static void print_bls_cfg(struct ci_isp_config *isp_cfg) ++{ ++ struct ci_isp_bls_config *bls_cfg = &isp_cfg->bls_cfg; ++ ++ dprintk(4, "print_bls_cfg:"); ++ dprintk(4, "enable_automatic:%d", (bls_cfg->enable_automatic ? 1 : 0)); ++ dprintk(4, "disable_h:%d", (bls_cfg->disable_h ? 1 : 0)); ++ dprintk(4, "disable_v:%d", (bls_cfg->disable_v ? 1 : 0)); ++ dprintk(4, "enable_window1:%d", ++ (bls_cfg->isp_bls_window1.enable_window ? 1 : 0)); ++ dprintk(4, "start_h:%d", (int)bls_cfg->isp_bls_window1.start_h); ++ dprintk(4, "stop_h:%d", (int)bls_cfg->isp_bls_window1.stop_h); ++ dprintk(4, "start_v:%d", (int)bls_cfg->isp_bls_window1.start_v); ++ dprintk(4, "stop_v:%d", (int)bls_cfg->isp_bls_window1.stop_v); ++ dprintk(4, "enable_window2: %d", ++ (bls_cfg->isp_bls_window2.enable_window ? 1 : 0)); ++ dprintk(4, "start_h%d", (int)bls_cfg->isp_bls_window2.start_h); ++ dprintk(4, "stop_h%d", (int)bls_cfg->isp_bls_window2.stop_h); ++ dprintk(4, "start_v%d", (int)bls_cfg->isp_bls_window2.start_v); ++ dprintk(4, "stop_v%d", (int)bls_cfg->isp_bls_window2.stop_v); ++ dprintk(4, "bls_samples%d", (int)bls_cfg->bls_samples); ++ dprintk(4, "fixed_a0x%02x", (int)bls_cfg->bls_subtraction.fixed_a); ++ dprintk(4, "fixed_b0x%02x", (int)bls_cfg->bls_subtraction.fixed_b); ++ dprintk(4, "fixed_c0x%02x", (int)bls_cfg->bls_subtraction.fixed_c); ++ dprintk(4, "fixed_d0x%02x", (int)bls_cfg->bls_subtraction.fixed_d); ++ dprintk(4, "\n"); ++} ++ ++static int mrst_isp_set_cfg(struct file *file, void *priv, ++ struct ci_pl_system_config *arg) ++{ ++ struct video_device *dev = video_devdata(file); ++ struct mrst_isp_device *isp = video_get_drvdata(dev); ++ ++ WARN_ON(priv != file->private_data); ++ ++ DBG_entering; ++ ++ if (arg == NULL) { ++ eprintk("NULL pointer of arg"); ++ return 0; ++ } ++ mutex_lock(&isp->mutex); ++ ++ /* ++ if (arg->isi_config != NULL) { ++ dprintk(2, "sync isi cfg"); ++ copy_sensor_config_from_user(isp->sys_conf.isi_config, ++ arg->isi_config); ++ } else { ++ eprintk("NULL arg->isi_config"); ++ ret = CI_STATUS_NULL_POINTER; ++ goto exit_unlock; ++ } ++ ++ if (arg->isi_caps != NULL) { ++ dprintk(2, "sync isi caps"); ++ copy_sensor_caps_from_user(isp->sys_conf.isi_caps, ++ arg->isi_caps); ++ } else { ++ eprintk("NULL arg->isi_caps"); ++ ret = CI_STATUS_NULL_POINTER; ++ goto exit_unlock; ++ } ++ */ ++ ++ memcpy(&isp->sys_conf.isp_cfg, &arg->isp_cfg, ++ sizeof(struct ci_isp_config)); ++ ++ print_bls_cfg(&isp->sys_conf.isp_cfg); ++ ++ dprintk(2, "gammagamma2 = %d", arg->isp_cfg.flags.gamma2); ++ dprintk(2, "gammagamma2 now = %d", isp->sys_conf.isp_cfg.flags.gamma2); ++ mutex_unlock(&isp->mutex); ++ ++ isp->sys_conf.isp_hal_enable = 1; ++ ++ DBG_leaving; ++ return 0; ++} ++ ++/* for buffer sharing between CI and VA */ ++static int mrst_isp_get_frame_info(struct file *file, void *priv, ++ struct ci_frame_info *arg) ++{ ++ struct video_device *dev = video_devdata(file); ++ struct mrst_isp_device *isp = video_get_drvdata(dev); ++ ++ WARN_ON(priv != file->private_data); ++ ++ DBG_entering; ++ ++ mutex_lock(&isp->mutex); ++ ++ arg->width = isp->bufwidth; ++ arg->height = isp->bufheight; ++ arg->fourcc = isp->pixelformat; ++ arg->stride = isp->bufwidth; /* should be 64 bit alignment*/ ++ arg->offset = arg->frame_id * PAGE_ALIGN(isp->frame_size); ++#if 0 ++ if (isp->bufwidth == 640 && isp->bufheight == 480) ++ arg->offset = arg->frame_id * 0x71000; ++ else if (isp->bufwidth == 1280 && isp->bufheight == 720) ++ arg->offset = arg->frame_id * 0x152000; ++#endif ++ ++ ++ dprintk(2, "w=%d, h=%d, 4cc =%x, stride=%d, offset=%d,fsize=%d", ++ arg->width, arg->height, arg->fourcc, arg->stride, ++ arg->offset, isp->frame_size); ++ ++ mutex_unlock(&isp->mutex); ++ ++ DBG_leaving; ++ return 0; ++} ++ ++static int mrst_isp_set_jpg_enc_ratio(struct file *file, void *priv, int *arg) ++{ ++ struct video_device *dev = video_devdata(file); ++ struct mrst_isp_device *isp = video_get_drvdata(dev); ++ ++ WARN_ON(priv != file->private_data); ++ ++ DBG_entering; ++ ++ dprintk(2, "set jpg compression ratio is %d", *arg); ++ ++ mutex_lock(&isp->mutex); ++ isp->sys_conf.isp_cfg.jpeg_enc_ratio = *arg; ++ mutex_unlock(&isp->mutex); ++ ++ DBG_leaving; ++ return 0; ++} ++ ++int mrst_isp_get_isp_mem_info(struct file *file, void *priv, ++ struct ci_isp_mem_info *arg) ++{ ++ u32 ret = 0; ++ struct video_device *dev = video_devdata(file); ++ struct mrst_isp_device *isp = video_get_drvdata(dev); ++ ++ WARN_ON(priv != file->private_data); ++ ++ DBG_entering; ++ ++ mutex_lock(&isp->mutex); ++ arg->isp_bar0_pa = isp->mb0; ++ arg->isp_bar0_size = isp->mb0_size; ++ arg->isp_bar1_pa = isp->mb1; ++ arg->isp_bar1_size = isp->mb1_size; ++ mutex_unlock(&isp->mutex); ++ ++ DBG_leaving; ++ return ret; ++} ++ ++int mrst_isp_create_jpg_review_frame(struct file *file, void *priv, ++ struct v4l2_jpg_review_buffer *arg) ++{ ++ struct video_device *dev = video_devdata(file); ++ struct mrst_isp_device *isp = video_get_drvdata(dev); ++ ++ u32 width = arg->width; ++ u32 height = arg->height; ++ u32 pix_fmt = arg->pix_fmt; ++ u32 jpg_frame = arg->jpg_frame; ++ ++ static struct v4l2_jpg_review_buffer *jpg_review; ++ ++ jpg_review = &isp->sys_conf.jpg_review; ++ ++ WARN_ON(priv != file->private_data); ++ ++ DBG_entering; ++ ++ if (width > 640 || height > 480 || width < 32 || height < 16) { ++ eprintk("unsupported resolution: %d * %d", width, height); ++ return -EINVAL; ++ } ++ ++ if (jpg_frame >= isp->num_frames) { ++ eprintk("error jpeg frame id"); ++ return -1; ++ } ++ ++ jpg_review->width = width; ++ jpg_review->height = height; ++ jpg_review->pix_fmt = pix_fmt; ++ jpg_review->jpg_frame = jpg_frame; ++ ++ switch (arg->pix_fmt) { ++ case V4L2_PIX_FMT_YUV422P: ++ jpg_review->bytesperline = width * 2; ++ jpg_review->frame_size = width * height * 2; ++ break; ++ case V4L2_PIX_FMT_YUV420: ++ case V4L2_PIX_FMT_YVU420: ++ case V4L2_PIX_FMT_NV12: ++ jpg_review->bytesperline = width * 3/2; ++ jpg_review->frame_size = width * height * 3/2; ++ break; ++ default: ++ eprintk("unsupported pix_fmt: %d", arg->pix_fmt); ++ return -EINVAL; ++ } ++ ++ jpg_review->offset = isp->mb1_size - 640*480*2; ++ ++ isp->sys_conf.jpg_review_enable = 1; /* enable jpg review flag */ ++ ++ /* set user space data */ ++ arg->bytesperline = jpg_review->bytesperline; ++ arg->frame_size = jpg_review->frame_size; ++ arg->offset = jpg_review->offset; ++ ++ dprintk(1, "create jpg review frame successfully: " ++ "bytesperline = %d, frame_size = %d," ++ " offset = %d\n", arg->bytesperline, ++ arg->frame_size, arg->offset); ++ ++ DBG_leaving; ++ return 0; ++} ++ ++/* isp private ioctl for libci */ ++long mrst_isp_vidioc_default(struct file *file, void *fh, ++ int cmd, void *arg) ++{ ++ void *priv = file->private_data; ++ ++ DBG_entering; ++ ++ switch (cmd) { ++ case VIDIOC_GET_ISP_MEM_INFO: ++ return mrst_isp_get_isp_mem_info(file, priv, ++ (struct ci_isp_mem_info *)arg); ++ ++ case VIDIOC_SET_SYS_CFG: ++ return mrst_isp_set_cfg(file, priv, ++ (struct ci_pl_system_config *)arg); ++ ++ case VIDIOC_SET_JPG_ENC_RATIO: ++ return mrst_isp_set_jpg_enc_ratio(file, priv, (int *)arg); ++ ++ case ISP_IOCTL_GET_FRAME_INFO: ++ return mrst_isp_get_frame_info(file, priv, ++ (struct ci_frame_info *)arg); ++ ++ case VIDIOC_CREATE_JPG_REVIEW_BUF: ++ return mrst_isp_create_jpg_review_frame(file, priv, ++ (struct v4l2_jpg_review_buffer *)arg); ++ default: ++ v4l_print_ioctl("lnw_isp", cmd); ++ dprintk(2, "VIDIOC_SET_SYS_CFG = %x", VIDIOC_SET_SYS_CFG); ++ return -EINVAL; ++ } ++ ++ DBG_leaving; ++ return 0; ++} +diff --git a/drivers/media/video/mrstci/mrstisp/mrstisp_dp.c b/drivers/media/video/mrstci/mrstisp/mrstisp_dp.c +new file mode 100644 +index 0000000..dd892fb +--- /dev/null ++++ b/drivers/media/video/mrstci/mrstisp/mrstisp_dp.c +@@ -0,0 +1,1301 @@ ++/* ++ * Support for Moorestown Langwell Camera Imaging ISP subsystem. ++ * ++ * Copyright (c) 2009 Intel Corporation. All Rights Reserved. ++ * ++ * Copyright (c) Silicon Image 2008 www.siliconimage.com ++ * ++ * 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 Street, Fifth Floor, Boston, MA ++ * 02110-1301, USA. ++ * ++ * ++ * Xiaolin Zhang ++ */ ++ ++#include "mrstisp_stdinc.h" ++ ++/* mask for all chroma subsampling settings */ ++#define CI_ISP_DPD_CSS_MASK (CI_ISP_DPD_CSS_H_MASK | CI_ISP_DPD_CSS_V_MASK) ++ ++#define SCALER_COFFS_COSITED 0x400 ++#define FIXEDPOINT_ONE 0x1000 ++ ++/* limitations of main and self scaler */ ++#define MAIN_SCALER_WIDTH_MAX 2600 ++ ++#define SELF_SCALER_WIDTH_MAX 640 ++#define SCALER_MIN 16 ++ ++#define SELF_UPSCALE_FACTOR_MAX 5 ++ ++#define MAIN_UPSCALE_FACTOR_MAX 5 ++ ++/* ++ * upscale lookup table for smooth edges ++ * (linear interpolation between pixels) ++ */ ++ ++/* smooth edges */ ++static const struct ci_isp_rsz_lut isp_rsz_lut_smooth_lin = { ++ { ++ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, ++ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, ++ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, ++ 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, ++ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, ++ 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, ++ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, ++ 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F ++ } ++}; ++ ++/* ++ * upscale lookup table for sharp edges ++ * (no interpolation, just duplicate pixels) ++ */ ++ ++/* sharp edges */ ++static const struct ci_isp_rsz_lut isp_rsz_lut_sharp = { ++ { ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, ++ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, ++ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, ++ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F ++ } ++}; ++ ++/* structure combining virtual ISP windows settings */ ++struct ci_isp_virtual_isp_wnds { ++ struct ci_isp_window wnd_blacklines; ++ struct ci_isp_window wnd_zoom_crop; ++}; ++ ++/* static storage to remember last applied virtual ISP window settings */ ++static struct ci_isp_virtual_isp_wnds last_isp_wnds; ++ ++/* ++ * Calculates the value to program into the struct ci_isp_scale or ++ * tsMrvSScale structures to scale from in pixels to out pixels. ++ * ++ * The formulas are taken from the MARVIN / MARVIN3PLUS user ++ * manuals (fixed-point calculation using 32 bit during ++ * processing, will overflow at an output size of 1048575 pixels). ++ */ ++static u32 ci_get_scale_reg(u16 in, u16 out) ++{ ++ if (in > out) { ++ /* downscaling */ ++ return (u32) (((((u32) out - 1) * RSZ_SCALER_BYPASS) / ++ (u32) (in - 1)) + 1); ++ } else if (in < out) { ++ /* upscaling */ ++ return (u32) (((((u32) in - 1) * RSZ_SCALER_BYPASS) / ++ (u32) (out - 1)) | (u32) RSZ_UPSCALE_ENABLE); ++ } ++ ++ /* no scaling */ ++ return RSZ_SCALER_BYPASS; ++} ++ ++/* ++ * Calculates the values of the ci_isp_scale structure for the ++ * given input size and data path descriptor. ++ */ ++static u32 ci_calc_scale_factors(const struct ci_isp_datapath_desc *source, ++ const struct ci_isp_datapath_desc *path, ++ struct ci_isp_scale *scale, int implementation) ++{ ++ u32 scaler_output_format; ++ u32 cssflags; ++ u32 scaler_input_format; ++ ++ u16 chroma_in_w; ++ u16 chroma_in_h; ++ u16 chroma_out_wcr; ++ u16 chroma_out_wcb; ++ u16 chroma_out_h; ++ ++ memset(scale, 0, sizeof(struct ci_isp_scale)); ++ dprintk(1, "srcw %d, srch %d;", source->out_w, source->out_h); ++ dprintk(1, "dstw %d, dsth %d", path->out_w, path->out_h); ++ ++ /* calculate Y scale factors */ ++ scale->scale_hy = ci_get_scale_reg(source->out_w, path->out_w); ++ scale->scale_vy = ci_get_scale_reg(source->out_h, path->out_h); ++ ++ /* figure out the color input format of the scaler */ ++ switch (path->flags & CI_ISP_DPD_MODE_MASK) { ++ case CI_ISP_DPD_MODE_DMAYC_DIRECT: ++ case CI_ISP_DPD_MODE_DMAYC_ISP: ++ case CI_ISP_DPD_MODE_DMAJPEG_DIRECT: ++ case CI_ISP_DPD_MODE_DMAJPEG_ISP: ++ /* DMA-read originated data */ ++ scaler_input_format = path->flags & CI_ISP_DPD_DMA_IN_MASK; ++ break; ++ default: ++ /* ISP originated data */ ++ scaler_input_format = CI_ISP_DPD_DMA_IN_422; ++ break; ++ } ++ ++ dprintk(1, "scaler_input_format is 0x%x", scaler_input_format); ++ ++ switch (scaler_input_format) { ++ case CI_ISP_DPD_DMA_IN_422: ++ chroma_in_w = source->out_w / 2; ++ chroma_in_h = source->out_h; ++ chroma_out_wcr = path->out_w / 2; ++ chroma_out_wcb = (path->out_w + 1) / 2; ++ chroma_out_h = path->out_h; ++ break; ++ case CI_ISP_DPD_DMA_IN_420: ++ chroma_in_w = source->out_w / 2; ++ chroma_in_h = source->out_h / 2; ++ chroma_out_wcr = path->out_w / 2; ++ chroma_out_wcb = (path->out_w + 1) / 2; ++ chroma_out_h = path->out_h / 2; ++ break; ++ case CI_ISP_DPD_DMA_IN_411: ++ chroma_in_w = source->out_w / 4; ++ chroma_in_h = source->out_h; ++ chroma_out_wcr = path->out_w / 4; ++ chroma_out_wcb = (path->out_w + 2) / 4; ++ chroma_out_h = path->out_h; ++ break; ++ case CI_ISP_DPD_DMA_IN_444: ++ default: ++ chroma_in_w = source->out_w; ++ chroma_in_h = source->out_h; ++ chroma_out_wcb = chroma_out_wcr = path->out_w; ++ chroma_out_h = path->out_h; ++ break; ++ } ++ ++ /* calculate chrominance scale factors */ ++ switch (path->flags & CI_ISP_DPD_CSS_H_MASK) { ++ case CI_ISP_DPD_CSS_H2: ++ chroma_out_wcb /= 2; ++ chroma_out_wcr /= 2; ++ break; ++ case CI_ISP_DPD_CSS_H4: ++ chroma_out_wcb /= 4; ++ chroma_out_wcr /= 4; ++ break; ++ case CI_ISP_DPD_CSS_HUP2: ++ chroma_out_wcb *= 2; ++ chroma_out_wcr *= 2; ++ break; ++ case CI_ISP_DPD_CSS_HUP4: ++ chroma_out_wcb *= 4; ++ chroma_out_wcr *= 4; ++ break; ++ default: ++ /*leave chroma_out_w untouched*/ ++ break; ++ } ++ ++ scale->scale_hcr = ci_get_scale_reg(chroma_in_w, chroma_out_wcr); ++ scale->scale_hcb = ci_get_scale_reg(chroma_in_w, chroma_out_wcb); ++ scale->scale_hcb = scale->scale_hcr; ++ ++ switch (path->flags & CI_ISP_DPD_CSS_V_MASK) { ++ case CI_ISP_DPD_CSS_V2: ++ chroma_out_h /= 2; ++ break; ++ case CI_ISP_DPD_CSS_V4: ++ chroma_out_h /= 4; ++ break; ++ case CI_ISP_DPD_CSS_VUP2: ++ chroma_out_h *= 2; ++ break; ++ case CI_ISP_DPD_CSS_VUP4: ++ chroma_out_h *= 4; ++ break; ++ default: ++ /* leave chroma_out_h untouched */ ++ break; ++ } ++ ++ scale->scale_vc = ci_get_scale_reg(chroma_in_h, chroma_out_h); ++ ++ /* additional chrominance phase shifts */ ++ if (path->flags & CI_ISP_DPD_CSS_HSHIFT) ++ scale->phase_hc = SCALER_COFFS_COSITED; ++ if (path->flags & CI_ISP_DPD_CSS_VSHIFT) ++ scale->phase_vc = SCALER_COFFS_COSITED; ++ ++ /* additional luminance phase shifts */ ++ if (path->flags & CI_ISP_DPD_LUMA_HSHIFT) ++ scale->phase_hy = SCALER_COFFS_COSITED; ++ if (path->flags & CI_ISP_DPD_LUMA_VSHIFT) ++ scale->phase_vy = SCALER_COFFS_COSITED; ++ ++ /* try to figure out the outcoming YCbCr format */ ++ cssflags = path->flags & CI_ISP_DPD_CSS_MASK; ++ if (cssflags == (CI_ISP_DPD_CSS_H_OFF | CI_ISP_DPD_CSS_V_OFF)) { ++ /* trivial case: the output format is not changed */ ++ scaler_output_format = scaler_input_format; ++ } else { ++ /* output format gets changed by the scaler setting */ ++ /* assume invalid format by default */ ++ scaler_output_format = (u32) (-1); ++ switch (scaler_input_format) { ++ case CI_ISP_DPD_DMA_IN_444: ++ if (cssflags == (CI_ISP_DPD_CSS_H2 ++ | CI_ISP_DPD_CSS_V_OFF)) { ++ /* conversion 444 -> 422 */ ++ scaler_output_format = CI_ISP_DPD_DMA_IN_422; ++ } else if (cssflags == (CI_ISP_DPD_CSS_H4 ++ | CI_ISP_DPD_CSS_V_OFF)) { ++ /* conversion 444 -> 411 */ ++ scaler_output_format = CI_ISP_DPD_DMA_IN_411; ++ } else if (cssflags == (CI_ISP_DPD_CSS_H2 ++ | CI_ISP_DPD_CSS_V2)) { ++ /* conversion 444 -> 420 */ ++ scaler_output_format = CI_ISP_DPD_DMA_IN_420; ++ } ++ break; ++ ++ case CI_ISP_DPD_DMA_IN_422: ++ if (cssflags == (CI_ISP_DPD_CSS_HUP2 ++ | CI_ISP_DPD_CSS_V_OFF)) { ++ /* conversion 422 -> 444 */ ++ scaler_output_format = CI_ISP_DPD_DMA_IN_444; ++ } else if (cssflags == (CI_ISP_DPD_CSS_H2 ++ | CI_ISP_DPD_CSS_V_OFF)) { ++ /* conversion 422 -> 411 */ ++ scaler_output_format = CI_ISP_DPD_DMA_IN_411; ++ } else if (cssflags == (CI_ISP_DPD_CSS_H_OFF ++ | CI_ISP_DPD_CSS_V2)) { ++ /* conversion 422 -> 420 */ ++ scaler_output_format = CI_ISP_DPD_DMA_IN_420; ++ } ++ break; ++ ++ case CI_ISP_DPD_DMA_IN_420: ++ if (cssflags == (CI_ISP_DPD_CSS_HUP2 ++ | CI_ISP_DPD_CSS_VUP2)) { ++ /* conversion 420 -> 444 */ ++ scaler_output_format = CI_ISP_DPD_DMA_IN_444; ++ } else if (cssflags == (CI_ISP_DPD_CSS_H2 ++ | CI_ISP_DPD_CSS_VUP2)) { ++ /* conversion 420 -> 411 */ ++ scaler_output_format = CI_ISP_DPD_DMA_IN_411; ++ } else if (cssflags == (CI_ISP_DPD_CSS_H_OFF ++ | CI_ISP_DPD_CSS_VUP2)) { ++ /* conversion 420 -> 422 */ ++ scaler_output_format = CI_ISP_DPD_DMA_IN_422; ++ } ++ break; ++ ++ case CI_ISP_DPD_DMA_IN_411: ++ if (cssflags == (CI_ISP_DPD_CSS_HUP4 ++ | CI_ISP_DPD_CSS_V_OFF)) { ++ /* conversion 411 -> 444 */ ++ scaler_output_format = CI_ISP_DPD_DMA_IN_444; ++ } else if (cssflags == (CI_ISP_DPD_CSS_HUP2 ++ | CI_ISP_DPD_CSS_V_OFF)) { ++ /* conversion 411 -> 422 */ ++ scaler_output_format = CI_ISP_DPD_DMA_IN_422; ++ } else if (cssflags == (CI_ISP_DPD_CSS_HUP2 ++ | CI_ISP_DPD_CSS_V2)) { ++ /* conversion 411 -> 420 */ ++ scaler_output_format = CI_ISP_DPD_DMA_IN_420; ++ } ++ break; ++ ++ default: ++ /* DMA input format not supported */ ++ break; ++ } ++ } ++ ++ return scaler_output_format; ++} ++ ++/* ++ * Returns the address of up-scaling lookup table to use for ++ * the given data path flags. ++ */ ++static const struct ci_isp_rsz_lut *ci_get_rsz_lut(u32 flags) ++{ ++ const struct ci_isp_rsz_lut *ret_val; ++ switch (flags & CI_ISP_DPD_UPSCALE_MASK) { ++ case CI_ISP_DPD_UPSCALE_SHARP: ++ ret_val = &isp_rsz_lut_sharp; ++ break; ++ default: ++ ret_val = &isp_rsz_lut_smooth_lin; ++ break; ++ } ++ return ret_val; ++} ++ ++/* ++ * Fills in scale factors and MI configuration for the main path. ++ * Note that only self path related settings will be written into ++ * the MI configuration struct, so this routine can be used for ++ * both ISP and DMA originated data path setups. ++ * ++ * Following fields are being filled in: ++ * scale_main: [all fields] ++ * mrv_mi_ctrl: mrv_mif_mp_pic_form main_path ++ */ ++static int ci_calc_main_path_settings(const struct ci_isp_datapath_desc *source, ++ const struct ci_isp_datapath_desc *main, ++ struct ci_isp_scale *scale_main, ++ struct ci_isp_mi_ctrl *mrv_mi_ctrl) ++{ ++ u32 main_flag; ++ ++ WARN_ON(!(source != NULL)); ++ WARN_ON(!(scale_main != NULL)); ++ WARN_ON(!(mrv_mi_ctrl != NULL)); ++ ++ /* assume datapath deactivation if no selfpath pointer is given) */ ++ if (main) ++ main_flag = main->flags; ++ else ++ main_flag = 0; ++ ++ /* initialize the given parameters */ ++ memset(scale_main, 0, sizeof(struct ci_isp_scale)); ++ scale_main->scale_hy = RSZ_SCALER_BYPASS; ++ scale_main->scale_hcb = RSZ_SCALER_BYPASS; ++ scale_main->scale_hcr = RSZ_SCALER_BYPASS; ++ scale_main->scale_vy = RSZ_SCALER_BYPASS; ++ scale_main->scale_vc = RSZ_SCALER_BYPASS; ++ ++ if (main_flag & CI_ISP_DPD_ENABLE) { ++ switch (main_flag & CI_ISP_DPD_MODE_MASK) { ++ case CI_ISP_DPD_MODE_ISPYC: ++ case CI_ISP_DPD_MODE_DMAYC_ISP: ++ mrv_mi_ctrl->main_path = CI_ISP_PATH_ON; ++ break; ++ case CI_ISP_DPD_MODE_ISPJPEG: ++ case CI_ISP_DPD_MODE_DMAJPEG_DIRECT: ++ case CI_ISP_DPD_MODE_DMAJPEG_ISP: ++ mrv_mi_ctrl->main_path = CI_ISP_PATH_JPE; ++ break; ++ case CI_ISP_DPD_MODE_ISPRAW: ++ mrv_mi_ctrl->main_path = CI_ISP_PATH_RAW8; ++ break; ++ case CI_ISP_DPD_MODE_ISPRAW_16B: ++ mrv_mi_ctrl->main_path = CI_ISP_PATH_RAW816; ++ break; ++ default: ++ eprintk("unsupported mode for main path"); ++ return CI_STATUS_NOTSUPP; ++ } ++ if (main_flag & (CI_ISP_DPD_H_FLIP | CI_ISP_DPD_V_FLIP | ++ CI_ISP_DPD_90DEG_CCW)) { ++ eprintk("not supported for main path"); ++ return CI_STATUS_NOTSUPP; ++ } ++ if (main_flag & CI_ISP_DPD_NORESIZE) { ++ if (main_flag & CI_ISP_DPD_CSS_MASK) { ++ eprintk("main path needs rezizer"); ++ return CI_STATUS_NOTSUPP; ++ } ++ if (main_flag & ++ (CI_ISP_DPD_LUMA_HSHIFT | CI_ISP_DPD_LUMA_VSHIFT)) { ++ eprintk("main path needs rezizer"); ++ return CI_STATUS_NOTSUPP; ++ } ++ } else { ++ if ((mrv_mi_ctrl->main_path == CI_ISP_PATH_RAW8) ++ || (mrv_mi_ctrl->main_path == CI_ISP_PATH_RAW8)) { ++ eprintk("scaler not in RAW mode"); ++ return CI_STATUS_NOTSUPP; ++ } ++ /* changed to avoid LINT warnings (Warning 613) */ ++ if (main != NULL) { ++ if ((((u32) (source->out_w) * ++ MAIN_UPSCALE_FACTOR_MAX) < main->out_w) ++ || ++ (((u32) (source->out_h) * ++ MAIN_UPSCALE_FACTOR_MAX) < ++ main->out_h)) { ++ eprintk("main upscaling exceeded"); ++ return CI_STATUS_NOTSUPP; ++ } ++ if ((main->out_w > ++ MAIN_SCALER_WIDTH_MAX) ++ || (main->out_w < SCALER_MIN) ++ || (main->out_h < SCALER_MIN)) { ++ eprintk("main scaler ange exceeded"); ++ return CI_STATUS_NOTSUPP; ++ } ++ } else { ++ WARN_ON(main == NULL); ++ } ++ ++ if (source->out_w & 0x01) { ++ eprintk("input width must be even!"); ++ return CI_STATUS_NOTSUPP; ++ } ++ ++ /* calculate scale factors. */ ++ (void)ci_calc_scale_factors(source, main, scale_main, ++ MARVIN_FEATURE_MSCALE_FACTORCALC); ++ } ++ } else { ++ mrv_mi_ctrl->main_path = CI_ISP_PATH_OFF; ++ } ++ ++ /* hardcoded MI settings */ ++ dprintk(1, "main_flag is 0x%x", main_flag); ++ if (main_flag & CI_ISP_DPD_HWRGB_MASK) { ++ switch (main_flag & CI_ISP_DPD_HWRGB_MASK) { ++ case CI_ISP_DPD_YUV_420: ++ case CI_ISP_DPD_YUV_422: ++ mrv_mi_ctrl->mrv_mif_mp_pic_form = ++ CI_ISP_MIF_PIC_FORM_PLANAR; ++ break; ++ case CI_ISP_DPD_YUV_NV12: ++ mrv_mi_ctrl->mrv_mif_mp_pic_form = ++ CI_ISP_MIF_PIC_FORM_SEMI_PLANAR; ++ break; ++ case CI_ISP_DPD_YUV_YUYV: ++ mrv_mi_ctrl->mrv_mif_mp_pic_form = ++ CI_ISP_MIF_PIC_FORM_INTERLEAVED; ++ break; ++ default: ++ mrv_mi_ctrl->mrv_mif_mp_pic_form = ++ CI_ISP_MIF_PIC_FORM_PLANAR; ++ } ++ } ++ ++ return CI_STATUS_SUCCESS; ++} ++ ++/* ++ * Fills in scale factors and MI configuration for the self ++ * path. Note that only self path related settings will be written into ++ * the MI config struct, so this routine can be used for both ISP and DMA ++ * originated datapath setups. ++ * ++ * Following fields are being filled in: ++ * scale_flag : ++ * [all fields] ++ * mrv_mi_ctrl : ++ * mrv_mif_sp_out_form ++ * mrv_mif_sp_in_form ++ * mrv_mif_sp_pic_form ++ * mrv_mif_sp_mode ++ * self_path ++ */ ++static int ci_calc_self_path_settings(const struct ci_isp_datapath_desc *source, ++ const struct ci_isp_datapath_desc *self, ++ struct ci_isp_scale *scale_flag, ++ struct ci_isp_mi_ctrl *mrv_mi_ctrl) ++{ ++ u32 scaler_out_col_format; ++ u32 self_flag; ++ ++ WARN_ON(!(source != NULL)); ++ WARN_ON(!(scale_flag != NULL)); ++ WARN_ON(!(mrv_mi_ctrl != NULL)); ++ ++ /* assume datapath deactivation if no selfpath pointer is given) */ ++ if (self) ++ self_flag = self->flags; ++ else ++ self_flag = 0; ++ ++ /* initialize the given parameters */ ++ memset(scale_flag, 0, sizeof(struct ci_isp_scale)); ++ scale_flag->scale_hy = RSZ_SCALER_BYPASS; ++ scale_flag->scale_hcb = RSZ_SCALER_BYPASS; ++ scale_flag->scale_hcr = RSZ_SCALER_BYPASS; ++ scale_flag->scale_vy = RSZ_SCALER_BYPASS; ++ scale_flag->scale_vc = RSZ_SCALER_BYPASS; ++ ++ if (self_flag & CI_ISP_DPD_ENABLE) { ++ ++ switch (self_flag & CI_ISP_DPD_MODE_MASK) { ++ case CI_ISP_DPD_MODE_ISPYC: ++ mrv_mi_ctrl->self_path = CI_ISP_PATH_ON; ++ scaler_out_col_format = CI_ISP_DPD_DMA_IN_422; ++ break; ++ case CI_ISP_DPD_MODE_DMAYC_ISP: ++ case CI_ISP_DPD_MODE_DMAYC_DIRECT: ++ mrv_mi_ctrl->self_path = CI_ISP_PATH_ON; ++ scaler_out_col_format = ++ self_flag & CI_ISP_DPD_DMA_IN_MASK; ++ break; ++ default: ++ eprintk("unsupported mode for self path"); ++ return CI_STATUS_NOTSUPP; ++ } ++ ++ if (self_flag & CI_ISP_DPD_NORESIZE) { ++ if (self_flag & CI_ISP_DPD_CSS_MASK) { ++ eprintk("in self path needs rezizer"); ++ return CI_STATUS_NOTSUPP; ++ } ++ if (self_flag & ++ (CI_ISP_DPD_LUMA_HSHIFT | CI_ISP_DPD_LUMA_VSHIFT)) { ++ eprintk("n self path needs rezizer"); ++ return CI_STATUS_NOTSUPP; ++ } ++ /* changed to avoid LINT warnings (Warning 613) */ ++ if (self != NULL) { ++ if ((source->out_w != self->out_w) || ++ (source->out_h != self->out_h)) { ++ eprintk("sizes needs resizer"); ++ return CI_STATUS_NOTSUPP; ++ } ++ } else { ++ WARN_ON(self == NULL); ++ } ++ } else { ++ /* changed to avoid LINT warnings (Warning 613) */ ++ if (self != NULL) { ++ /* upscaling only to factor ++ * SELF_UPSCALE_FACTOR_MAX possible ++ */ ++ if ((((u32) (source->out_w) * ++ SELF_UPSCALE_FACTOR_MAX) < ++ self->out_w) ++ || ++ (((u32) (source->out_h) * ++ SELF_UPSCALE_FACTOR_MAX) < ++ self->out_h)) { ++ eprintk("apability exceeded"); ++ return CI_STATUS_NOTSUPP; ++ } ++ if ((self->out_w > ++ SELF_SCALER_WIDTH_MAX) ++ || (self->out_w < SCALER_MIN) ++ || (self->out_h < SCALER_MIN)) { ++ eprintk("out range exceeded"); ++ return CI_STATUS_NOTSUPP; ++ } ++ } else { ++ WARN_ON(self == NULL); ++ } ++ /* Remember that the input picture width should be ++ * even if the scaler is used */ ++ ++ /* (otherwise the scaler may show unexpected ++ * behaviour in some rare cases) */ ++ if (source->out_w & 0x01) { ++ eprintk("width must be even!"); ++ return CI_STATUS_NOTSUPP; ++ } ++ ++ /* calculate scale factors. */ ++ scaler_out_col_format = ++ ci_calc_scale_factors(source, self, scale_flag, ++ MARVIN_FEATURE_SSCALE_FACTORCALC); ++ } ++ ++ dprintk(2, "step1"); ++ /* figure out the input format setting */ ++ switch (scaler_out_col_format) { ++ case CI_ISP_DPD_DMA_IN_444: ++ mrv_mi_ctrl->mrv_mif_sp_in_form = ++ CI_ISP_MIF_COL_FORMAT_YCBCR_444; ++ break; ++ case CI_ISP_DPD_DMA_IN_422: ++ mrv_mi_ctrl->mrv_mif_sp_in_form = ++ CI_ISP_MIF_COL_FORMAT_YCBCR_422; ++ break; ++ case CI_ISP_DPD_DMA_IN_420: ++ mrv_mi_ctrl->mrv_mif_sp_in_form = ++ CI_ISP_MIF_COL_FORMAT_YCBCR_420; ++ break; ++ /* no break, does not seem to be supported by HW */ ++ case CI_ISP_DPD_DMA_IN_411: ++ default: ++ eprintk("input color format not supported"); ++ return CI_STATUS_NOTSUPP; ++ } ++ ++ /* figure out the output format setting */ ++ dprintk(2, "step2, self_flag is 0x%x", self_flag); ++ ++ switch (self_flag & CI_ISP_DPD_HWRGB_MASK) { ++ case CI_ISP_DPD_HWRGB_565: ++ mrv_mi_ctrl->mrv_mif_sp_out_form = ++ CI_ISP_MIF_COL_FORMAT_RGB_565; ++ mrv_mi_ctrl->mrv_mif_sp_pic_form = ++ CI_ISP_MIF_PIC_FORM_PLANAR; ++ break; ++ case CI_ISP_DPD_HWRGB_666: ++ mrv_mi_ctrl->mrv_mif_sp_out_form = ++ CI_ISP_MIF_COL_FORMAT_RGB_666; ++ mrv_mi_ctrl->mrv_mif_sp_pic_form = ++ CI_ISP_MIF_PIC_FORM_PLANAR; ++ break; ++ case CI_ISP_DPD_HWRGB_888: ++ mrv_mi_ctrl->mrv_mif_sp_out_form = ++ CI_ISP_MIF_COL_FORMAT_RGB_888; ++ mrv_mi_ctrl->mrv_mif_sp_pic_form = ++ CI_ISP_MIF_PIC_FORM_PLANAR; ++ break; ++ case CI_ISP_DPD_YUV_420: ++ mrv_mi_ctrl->mrv_mif_sp_pic_form = ++ CI_ISP_MIF_PIC_FORM_PLANAR; ++ mrv_mi_ctrl->mrv_mif_sp_out_form = ++ CI_ISP_MIF_COL_FORMAT_YCBCR_420; ++ break; ++ case CI_ISP_DPD_YUV_422: ++ mrv_mi_ctrl->mrv_mif_sp_pic_form = ++ CI_ISP_MIF_PIC_FORM_PLANAR; ++ mrv_mi_ctrl->mrv_mif_sp_out_form = ++ CI_ISP_MIF_COL_FORMAT_YCBCR_422; ++ break; ++ case CI_ISP_DPD_YUV_NV12: ++ mrv_mi_ctrl->mrv_mif_sp_pic_form = ++ CI_ISP_MIF_PIC_FORM_SEMI_PLANAR; ++ mrv_mi_ctrl->mrv_mif_sp_out_form = ++ CI_ISP_MIF_COL_FORMAT_YCBCR_420; ++ break; ++ case CI_ISP_DPD_YUV_YUYV: ++ mrv_mi_ctrl->mrv_mif_sp_pic_form = ++ CI_ISP_MIF_PIC_FORM_INTERLEAVED; ++ mrv_mi_ctrl->mrv_mif_sp_out_form = ++ CI_ISP_MIF_COL_FORMAT_YCBCR_422; ++ break; ++ ++ case CI_ISP_DPD_HWRGB_OFF: ++ mrv_mi_ctrl->mrv_mif_sp_out_form = ++ mrv_mi_ctrl->mrv_mif_sp_in_form; ++ mrv_mi_ctrl->mrv_mif_sp_pic_form = ++ CI_ISP_MIF_PIC_FORM_PLANAR; ++ break; ++ default: ++ eprintk("output color format not supported"); ++ return CI_STATUS_NOTSUPP; ++ } ++ ++ /* picture flipping / rotation */ ++ dprintk(2, "step3"); ++ ++ switch (self_flag & ++ (CI_ISP_DPD_90DEG_CCW | CI_ISP_DPD_V_FLIP | ++ CI_ISP_DPD_H_FLIP)) { ++ case (CI_ISP_DPD_H_FLIP): ++ mrv_mi_ctrl->mrv_mif_sp_mode = ++ CI_ISP_MIF_SP_HORIZONTAL_FLIP; ++ break; ++ case (CI_ISP_DPD_V_FLIP): ++ mrv_mi_ctrl->mrv_mif_sp_mode = ++ CI_ISP_MIF_SP_VERTICAL_FLIP; ++ break; ++ case (CI_ISP_DPD_V_FLIP | CI_ISP_DPD_H_FLIP): ++ mrv_mi_ctrl->mrv_mif_sp_mode = ++ CI_ISP_MIF_SP_ROTATION_180_DEG; ++ break; ++ case (CI_ISP_DPD_90DEG_CCW): ++ mrv_mi_ctrl->mrv_mif_sp_mode = ++ CI_ISP_MIF_SP_ROTATION_090_DEG; ++ break; ++ case (CI_ISP_DPD_90DEG_CCW | CI_ISP_DPD_H_FLIP): ++ mrv_mi_ctrl->mrv_mif_sp_mode = ++ CI_ISP_MIF_SP_ROT_270_V_FLIP; ++ break; ++ case (CI_ISP_DPD_90DEG_CCW | CI_ISP_DPD_V_FLIP): ++ mrv_mi_ctrl->mrv_mif_sp_mode = ++ CI_ISP_MIF_SP_ROT_090_V_FLIP; ++ break; ++ case (CI_ISP_DPD_90DEG_CCW | CI_ISP_DPD_V_FLIP | ++ CI_ISP_DPD_H_FLIP): ++ mrv_mi_ctrl->mrv_mif_sp_mode = ++ CI_ISP_MIF_SP_ROTATION_270_DEG; ++ break; ++ default: ++ mrv_mi_ctrl->mrv_mif_sp_mode = CI_ISP_MIF_SP_ORIGINAL; ++ break; ++ } ++ ++ } else { ++ mrv_mi_ctrl->self_path = CI_ISP_PATH_OFF; ++ } ++ ++ dprintk(2, "step4"); ++ /*mrv_mi_ctrl->mrv_mif_sp_pic_form = CI_ISP_MIF_PIC_FORM_PLANAR;*/ ++ ++ return CI_STATUS_SUCCESS; ++} ++ ++/* ++ * Translates the given memory interface configuration struct ++ * into appropriate values to program the data path multiplexers. ++ */ ++static int ci_calc_dp_mux_settings(const struct ci_isp_mi_ctrl *mi_ctrl, ++ enum ci_isp_ycs_chn_mode *peYcsChnMode, ++ enum ci_isp_dp_switch *peDpSwitch) ++{ ++ switch (mi_ctrl->main_path) { ++ case CI_ISP_PATH_RAW8: ++ case CI_ISP_PATH_RAW816: ++ *peDpSwitch = CI_ISP_DP_RAW; ++ *peYcsChnMode = CI_ISP_YCS_MVRaw; ++ if (mi_ctrl->self_path != CI_ISP_PATH_OFF) { ++ eprintk("ombined with RAW mode of main path"); ++ return CI_STATUS_NOTSUPP; ++ } ++ break; ++ ++ case CI_ISP_PATH_JPE: ++ *peDpSwitch = CI_ISP_DP_JPEG; ++ if (mi_ctrl->self_path != CI_ISP_PATH_OFF) ++ *peYcsChnMode = CI_ISP_YCS_MV_SP; ++ else ++ *peYcsChnMode = CI_ISP_YCS_MV; ++ break; ++ ++ case CI_ISP_PATH_ON: ++ *peDpSwitch = CI_ISP_DP_MV; ++ if (mi_ctrl->self_path != CI_ISP_PATH_OFF) ++ *peYcsChnMode = CI_ISP_YCS_MV_SP; ++ else ++ *peYcsChnMode = CI_ISP_YCS_MV; ++ break; ++ ++ case CI_ISP_PATH_OFF: ++ *peDpSwitch = CI_ISP_DP_MV; ++ if (mi_ctrl->self_path != CI_ISP_PATH_OFF) ++ *peYcsChnMode = CI_ISP_YCS_SP; ++ else ++ *peYcsChnMode = CI_ISP_YCS_OFF; ++ break; ++ ++ default: ++ return CI_STATUS_NOTSUPP; ++ } ++ ++ return CI_STATUS_SUCCESS; ++} ++ ++/* the windows to cut away black pixels and to zoom/crop the */ ++#define ISPWND_COMBINE_WNDS 0x00000001 ++/* image must be combined before they are applyed to the marvin registers */ ++/* call of the ci_isp_set_output_formatter() routine necessary */ ++#define ISPWND_APPLY_OUTFORM 0x00000002 ++/* call of the ci_isp_is_set_config() routine necessary */ ++#define ISPWND_APPLY_ISCONF 0x00000004 ++/* no cropping supported at all */ ++#define ISPWND_NO_CROPPING 0x00000008 ++ ++/* ++ * Returns information about how to combine black pixel and ++ * zoom/crop windows for programming the ISP output formatter and the image ++ * stabilization unit for the given marvin derivative and ISP path. ++ */ ++static u32 ci_get_isp_wnd_style(enum ci_isp_path isp_path) ++{ ++ u32 res = 0; ++ ++ /* output formatter exists at ISP input */ ++ /* image stabilization in both bayer and YCbCr paths */ ++ if ((isp_path == CI_ISP_PATH_BAYER) || ++ (isp_path == CI_ISP_PATH_YCBCR)) ++ /*we need to program the output formatter with the blackline ++ * window and */ ++ res = ISPWND_APPLY_OUTFORM | ISPWND_APPLY_ISCONF; ++ else ++ res = ISPWND_COMBINE_WNDS | ISPWND_APPLY_OUTFORM; ++ ++ return res; ++} ++ ++/* ++ * the given windows for cutting away blacklines coming from ++ * the image sensor and further cropping of the image for other ++ * purposes like e.g. digital zoom to the output formatter and/or ++ * image stabilisation modules of Marvins ISP. ++ */ ++static int ci_set_isp_windows(const struct ci_sensor_config *isi_sensor_config, ++ const struct ci_isp_window *wnd_blackline, ++ const struct ci_isp_window *wnd_zoom_crop) ++{ ++ struct ci_isp_window wnd_out_form; ++ struct ci_isp_is_config is_conf; ++ enum ci_isp_path isp_path; ++ u32 wnd_style; ++ ++ memset(&wnd_out_form, 0, sizeof(wnd_out_form)); ++ memset(&is_conf, 0, sizeof(is_conf)); ++ ++ /* ++ * figure out the path through the ISP to process the data from the ++ * image sensor ++ */ ++ isp_path = ci_isp_select_path(isi_sensor_config, NULL); ++ if (isp_path == CI_ISP_PATH_UNKNOWN) { ++ eprintk("detect marvin ISP path to use"); ++ return CI_STATUS_NOTSUPP; ++ } ++ ++ /* ++ * get the recommended way to configure output formatter and/or ++ * image stabilization ++ */ ++ wnd_style = ci_get_isp_wnd_style(isp_path); ++ if (wnd_style & ISPWND_NO_CROPPING) { ++ /* ++ * cropping not possible -> make sure that it is *not* ++ * supposed to be used ++ */ ++ u16 isiX; ++ u16 isiY; ++ /* changed to avoid LINT warnings (Warning 534) */ ++ (void)ci_sensor_res2size(isi_sensor_config->res, &isiX, &isiY); ++ if ((wnd_zoom_crop->hsize != isiX) ++ || (wnd_zoom_crop->vsize != isiY) ++ || (wnd_zoom_crop->hoffs != 0) ++ || (wnd_zoom_crop->voffs != 0)) { ++ eprintk("in selected ISP data path"); ++ return CI_STATUS_NOTSUPP; ++ } ++ if ((wnd_blackline->hsize != isiX) || ++ (wnd_blackline->vsize != isiY) || ++ (wnd_blackline->hoffs != 0) || ++ (wnd_blackline->voffs != 0)) { ++ eprintk("supported in selected ISP data path"); ++ return CI_STATUS_NOTSUPP; ++ } ++ } ++ ++ /* ++ * The image stabilization is allowed to move the window in both ++ * directions by the same amount of pixels we have calculated for ++ * the offsets. The initial image stabilization window is equal to ++ * the zoom/crop window ++ */ ++ is_conf.max_dx = wnd_zoom_crop->hoffs; ++ is_conf.max_dy = wnd_zoom_crop->voffs; ++ is_conf.mrv_is_window = *wnd_zoom_crop; ++ ++ /* combine both blackline and zoom/crop window */ ++ if (wnd_style & ISPWND_COMBINE_WNDS) { ++ /* combine both blackline and zoom/crop window */ ++ wnd_out_form = *wnd_zoom_crop; ++ wnd_out_form.voffs += wnd_blackline->voffs; ++ wnd_out_form.hoffs += wnd_blackline->hoffs; ++ is_conf.mrv_is_window = wnd_out_form; ++ if (wnd_style & ISPWND_APPLY_OUTFORM) { ++ /* ++ * if the output formatter is to be used, offsets ++ * are cut away there, so ++ * we don't need additional ones in the imags ++ * stabilization unit ++ */ ++ is_conf.mrv_is_window.hoffs = 0; ++ is_conf.mrv_is_window.voffs = 0; ++ } ++ } else { ++ /* ++ * do not combine windows --> blacklines done with output ++ * formatter, zoom/cropping done with image stabilization ++ */ ++ wnd_out_form = *wnd_blackline; ++ is_conf.mrv_is_window = *wnd_zoom_crop; ++ } ++ ++ /* finally, apply the settings to marvin */ ++ if (wnd_style & ISPWND_APPLY_OUTFORM) { ++ ci_isp_set_output_formatter(&wnd_out_form, ++ CI_ISP_CFG_UPDATE_IMMEDIATE); ++ } ++ if (wnd_style & ISPWND_APPLY_ISCONF) { ++ int res = ci_isp_is_set_config(&is_conf); ++ if (res != CI_STATUS_SUCCESS) { ++ eprintk("set image stabilization config"); ++ return res; ++ } ++ } ++ ++ /* success - remember our virtual settings */ ++ last_isp_wnds.wnd_blacklines = *wnd_blackline; ++ last_isp_wnds.wnd_zoom_crop = *wnd_zoom_crop; ++ ++ return CI_STATUS_SUCCESS; ++} ++ ++/* sets extended YCbCr mode */ ++static int ci_ext_ycb_cr_mode(const struct ci_isp_datapath_desc *path) ++{ ++ u32 main_flag; ++ ++ WARN_ON(!(path != NULL)); ++ ++ /* assume datapath deactivation if no selfpath pointer is given) */ ++ if (path) ++ main_flag = path->flags; ++ else ++ main_flag = 0; ++ ++ /* if flag CI_ISP_DPD_YCBCREXT is set set extended YCbCr mode */ ++ if (main_flag & CI_ISP_DPD_ENABLE) { ++ if (main_flag & CI_ISP_DPD_YCBCREXT) ++ ci_isp_set_ext_ycmode(); ++ } ++ ++ return CI_STATUS_SUCCESS; ++} ++ ++/* ++ * Configures main and self data pathes and scaler for data coming from the ISP. ++ * ++ * Following MARVIN subsystems are programmed: ++ * - ISP output formatter ++ * - Image stabilization module ++ * - YC-Splitter ++ * - Self path DMA-read multiplexer ++ * - Main path multiplexer ++ * - Main & Self path resizer ++ * - Small output unit ++ * - Memory Interface (MI) input source, en/disable and data format ++ * ++ * Following MARVIN subsystems are *NOT* programmed: ++ * - All ISP functionality but the output formatter & image stabilization module ++ * - color Processing block ++ * - JPEG encode subsystem (quantisation tables etc.) ++ * - Memory Interface (MI) output buffer addresses and sizes ++ */ ++int ci_datapath_isp(const struct ci_pl_system_config *sys_conf, ++ const struct ci_sensor_config *isi_config, ++ const struct ci_isp_datapath_desc *main, ++ const struct ci_isp_datapath_desc *self, int zoom) ++{ ++ int res; ++ /* ++ * copy of flags for main and self path to simplify access (no ++ * pointer de-reference) ++ */ ++ u32 main_flag; ++ u32 self_flag; ++ /* resolution from sensor configuration */ ++ u16 isiX; ++ u16 isiY; ++ /* things to apply to MARVIN */ ++ struct ci_isp_scale scale_main; ++ struct ci_isp_scale scale_flag; ++ enum ci_isp_ycs_chn_mode chn_mode = 0; ++ enum ci_isp_dp_switch dp_switch = 0; ++ struct ci_isp_mi_ctrl mrv_mi_ctrl; ++ struct ci_isp_datapath_desc source; ++ /* ISP windowing because of cutting away blacklines from the sensor */ ++ struct ci_isp_window wnd_blackline; ++ /* ISP windowing because of aspect ratio change and/or zoom */ ++ struct ci_isp_window wnd_zoom_crop; ++ ++ const struct ci_isp_datapath_desc *target = NULL; ++ ++ /* assume dapapath deactivation for not provided descriptors */ ++ main_flag = 0; ++ self_flag = 0; ++ if (main) ++ main_flag = main->flags; /* 0x012 */ ++ ++ if (self) ++ self_flag = self->flags; /* 0x10015 */ ++ ++ /* initialize variables on the stack */ ++ res = CI_STATUS_SUCCESS; ++ /* changed to avoid LINT warnings (Warning 534) */ ++ (void)ci_sensor_res2size(isi_config->res, &isiX, &isiY); ++ memset(&mrv_mi_ctrl, 0, sizeof(struct ci_isp_mi_ctrl)); ++ memset(&wnd_blackline, 0, sizeof(wnd_blackline)); ++ memset(&wnd_zoom_crop, 0, sizeof(wnd_zoom_crop)); ++ ++ /* ++ * ISP Windowing - fill in wnd_out_form, apply_out_form, is_conf and ++ * apply_is_conf ++ */ ++ ++ /* ++ * by default, size of both blackline and zoom/crop window ++ * is what the camera delivers. ++ */ ++ ++ /* (no cropping, no offset) */ ++ wnd_blackline.hsize = isiX; ++ wnd_blackline.vsize = isiY; ++ wnd_zoom_crop = wnd_blackline; ++ ++ /* ++ * check if we have to crop because of aspect ratio ++ * preservement of an ++ */ ++ ++ /* output channel */ ++ if ((main_flag & CI_ISP_DPD_ENABLE) && ++ (main_flag & CI_ISP_DPD_KEEPRATIO)) { ++ target = main; ++ } ++ if ((self_flag & CI_ISP_DPD_ENABLE) && ++ (self_flag & CI_ISP_DPD_KEEPRATIO)) { ++ if (target) { ++ eprintk("only allowed for one path"); ++ return CI_STATUS_NOTSUPP; ++ } ++ target = self; ++ } ++ ++ /* if so, calculate the cropping */ ++ if (target) { ++ u32 aspect_cam = (0x1000 * ((u32) isiX)) / isiY; ++ u32 aspect_target = (0x1000 * ((u32) (target->out_w))) / ++ target->out_h; ++ if (aspect_cam < aspect_target) { ++ /* ++ * camera aspect is more 'portrait-like' as ++ * target aspect. We have to crop the ++ * camera picture by cutting off a bit of ++ * the top & bottom changed to avoid LINT ++ * warnings (Info 734) ++ */ ++ wnd_zoom_crop.vsize = (u16) (((u32) isiX * ++ (u32) (target->out_h)) / target->out_w); ++ } else { ++ /* camera aspect is more 'landscape-like' ++ * as target aspect. We have to crop the ++ * camera picture by cutting off a bit of ++ * the left and right changed to avoid LINT ++ * warnings (Info 734) */ ++ wnd_zoom_crop.hsize = (u16) (((u32) isiY * ++ (u32) (target->out_w)) / target->out_h); ++ } ++ } ++ ++ /* ++ * now, we may also want to do digital zoom. If so, we need ++ * to shrink the ISP window by the desired zoom factor. ++ */ ++ if (zoom > 0) { ++ /* changed to avoid LINT warnings (Warning 573) */ ++ wnd_zoom_crop.vsize = (u16) (((u32) (wnd_zoom_crop.vsize) * ++ 1024) / (1024 + (u32) zoom)); ++ /* changed to avoid LINT warnings (Warning 573) */ ++ wnd_zoom_crop.hsize = (u16) (((u32) (wnd_zoom_crop.hsize) * ++ 1024) / (1024 + (u32) zoom)); ++ } ++ /* ++ * Remember that the output formatter h_size should be ++ * even if the scaler is used ++ * (otherwise the scaler may show unexpected behaviour in ++ * some rare cases) ++ */ ++ wnd_zoom_crop.hsize &= ~0x01; ++ /* ++ * At last, we care about the offset of the ISP window. We ++ * want it centered on the image data delivered by the ++ * sensor (not counting possible black lines) ++ */ ++ wnd_zoom_crop.hoffs = (isiX - wnd_zoom_crop.hsize) / 2; ++ wnd_zoom_crop.voffs = (isiY - wnd_zoom_crop.vsize) / 2; ++ /* ++ * If the image sensor delivers blacklines, we cut them ++ * away with moving wnd_blackline window by the given ++ * amount of lines ++ */ ++ switch (isi_config->bls) { ++ /* no black lines */ ++ case SENSOR_BLS_OFF: ++ break; ++ /* two black lines at frame start */ ++ case SENSOR_BLS_TWO_LINES: ++ wnd_blackline.voffs += 2; ++ break; ++ /* two black lines at frame start and two at the end */ ++ case SENSOR_BLS_FOUR_LINES: ++ wnd_blackline.voffs += 2; ++ break; ++ default: ++ eprintk("config"); ++ return CI_STATUS_NOTSUPP; ++ } ++ /* ++ * if we are instructed to show the blacklines and the ++ * sensor generates them, ++ * we have to move the ISP windows to the upper border of ++ * the whole sensor, and deny the image stabilization to ++ * move around the window in vertical direction. ++ */ ++ if (isi_config->bls != SENSOR_BLS_OFF) { ++ if (((main_flag & CI_ISP_DPD_ENABLE) ++ && (main_flag & CI_ISP_DPD_BLACKLINES_TOP)) ++ || ((self_flag & CI_ISP_DPD_ENABLE) ++ && (self_flag & CI_ISP_DPD_BLACKLINES_TOP))) { ++ if ((main_flag & CI_ISP_DPD_ENABLE) ++ && (self_flag & CI_ISP_DPD_ENABLE) ++ && ((main_flag & CI_ISP_DPD_BLACKLINES_TOP) ++ != (self_flag & CI_ISP_DPD_BLACKLINES_TOP))) { ++ eprintk("and self path"); ++ return CI_STATUS_NOTSUPP; ++ } ++ wnd_blackline.voffs = 0; ++ wnd_zoom_crop.voffs = 0; ++ } ++ } ++ ++ source.out_w = wnd_zoom_crop.hsize; ++ source.out_h = wnd_zoom_crop.vsize; ++ source.flags = CI_ISP_DPD_DMA_IN_422; ++ ++ /*to use crop set crop_flag first*/ ++ if (crop_flag) { ++ wnd_zoom_crop.hsize = main->out_w; ++ wnd_zoom_crop.vsize = main->out_h; ++ } ++ ++ dprintk(1, "source.out_w %d, source.out_h %d", ++ source.out_w, source.out_h); ++ if (main) ++ dprintk(1, "main.out_w %d, main.out_h %d", ++ main->out_w, main->out_h); ++ if (self) ++ dprintk(1, "self.out_w %d, self.out_h %d", ++ self->out_w, self->out_h); ++ ++ /* ++ * At this point, wnd_zoom_crop and wnd_blackline contain ++ * the window sizes that reflect the users request. We have ++ * to configure the ISP output formatter and the image ++ * stabilization formatter in order to achieve this, but ++ * how they interact is highly dependant of the curr ++ * marvin derivative and which datapath of the ISP is ++ * activated. Therefore, translating wnd_zoom_crop and ++ * wnd_blackline into marvin register settings is a bit ++ * complicated and will be done by the ++ * ci_set_isp_windows() routine. ++ */ ++ ++ /* ISP Window */ ++ /* MAIN path - fill in main_path, scale_main and main_rsz_lut */ ++ /* basic selfpath settings */ ++ res = ci_calc_main_path_settings(&source, main, &scale_main, ++ &mrv_mi_ctrl); ++ if (res != CI_STATUS_SUCCESS) ++ return res; ++ ++ /* additional settings specific for main path fed from ISP */ ++ if (main_flag & CI_ISP_DPD_ENABLE) { ++ switch (main_flag & CI_ISP_DPD_MODE_MASK) { ++ case CI_ISP_DPD_MODE_ISPYC: ++ case CI_ISP_DPD_MODE_ISPRAW: ++ case CI_ISP_DPD_MODE_ISPRAW_16B: ++ case CI_ISP_DPD_MODE_ISPJPEG: ++ /* allowed cases, just proceed */ ++ break; ++ default: ++ eprintk("data coming from the ISP"); ++ return CI_STATUS_NOTSUPP; ++ } ++ } ++ ++ /* SELF path - fill in self_path & scale_flag */ ++ /* basic selfpath settings */ ++ res = ci_calc_self_path_settings(&source, self, &scale_flag, ++ &mrv_mi_ctrl); ++ if (res != CI_STATUS_SUCCESS) ++ return res; ++ ++ if (sys_conf->isp_cfg.flags.ycbcr_non_cosited) ++ mrv_mi_ctrl.mrv_mif_sp_in_phase = mrv_mif_col_phase_non_cosited; ++ else ++ mrv_mi_ctrl.mrv_mif_sp_in_phase = mrv_mif_col_phase_cosited; ++ if (sys_conf->isp_cfg.flags.ycbcr_full_range) ++ mrv_mi_ctrl.mrv_mif_sp_in_range = mrv_mif_col_range_full; ++ else ++ mrv_mi_ctrl.mrv_mif_sp_in_range = mrv_mif_col_range_std; ++ if (self_flag & CI_ISP_DPD_ENABLE) { ++ switch (self_flag & CI_ISP_DPD_MODE_MASK) { ++ case CI_ISP_DPD_MODE_ISPYC: ++ /* only allowed case, just proceed */ ++ break; ++ default: ++ eprintk("data coming from the ISP"); ++ return CI_STATUS_NOTSUPP; ++ } ++ } ++ ++ /* Datapath multiplexers */ ++ res = ci_calc_dp_mux_settings(&mrv_mi_ctrl, &chn_mode, &dp_switch); ++ if (res != CI_STATUS_SUCCESS) ++ return res; ++ ++ /* hardcoded global settings of the memory interface */ ++ mrv_mi_ctrl.byte_swap_enable = false; ++ ++ mrv_mi_ctrl.init_vals = CI_ISP_MIF_INIT_OFFSAndBase; ++ ++ /* ++ * If we reach this point, we have collected all values to program ++ * the MARVIN for the requested datapath setup. Now all we've left ++ * to do is apply these to MARVINs register set. For this, we ++ * mostly use the low level MARVIN driver routines. ++ */ ++ /*to use crop set crop_flag first*/ ++ if (crop_flag) { ++ wnd_blackline.hsize = main->out_w; ++ wnd_blackline.vsize = main->out_h; ++ } ++ ++ res = ci_set_isp_windows(isi_config, &wnd_blackline, ++ &wnd_zoom_crop); ++ if (res != CI_STATUS_SUCCESS) { ++ eprintk("failed to set ISP window configuration"); ++ return res; ++ } ++ res = ci_isp_set_data_path(chn_mode, dp_switch); ++ if (res != CI_STATUS_SUCCESS) ++ return res; ++ ++ res = ci_isp_set_mipi_smia(isi_config->mode); ++ if (res != CI_STATUS_SUCCESS) ++ return res; ++ ++ if (mrv_mi_ctrl.self_path != CI_ISP_PATH_OFF) ++ ci_isp_res_set_self_resize(&scale_flag, ++ CI_ISP_CFG_UPDATE_IMMEDIATE, ++ ci_get_rsz_lut(self_flag)); ++ ++ if (mrv_mi_ctrl.main_path != CI_ISP_PATH_OFF) ++ ci_isp_res_set_main_resize(&scale_main, ++ CI_ISP_CFG_UPDATE_IMMEDIATE, ++ ci_get_rsz_lut(main_flag)); ++ ++ ci_isp_set_dma_read_mode(CI_ISP_DMA_RD_OFF, ++ CI_ISP_CFG_UPDATE_IMMEDIATE); ++ ++ res = ci_isp_mif_set_path_and_orientation(&mrv_mi_ctrl); ++ if (res != CI_STATUS_SUCCESS) { ++ eprintk("failed to set MI path and orientation"); ++ return res; ++ } ++ ++ /* here the extended YCbCr mode is configured */ ++ if (sys_conf->isp_cfg.flags.ycbcr_full_range) ++ res = ci_ext_ycb_cr_mode(main); ++ else ++ (void)ci_isp_set_yc_mode(); ++ ++ if (res != CI_STATUS_SUCCESS) { ++ eprintk("failed to set ISP YCbCr extended mode"); ++ return res; ++ } ++ ++ return CI_STATUS_SUCCESS; ++} +diff --git a/drivers/media/video/mrstci/mrstisp/mrstisp_hw.c b/drivers/media/video/mrstci/mrstisp/mrstisp_hw.c +new file mode 100644 +index 0000000..56891c1 +--- /dev/null ++++ b/drivers/media/video/mrstci/mrstisp/mrstisp_hw.c +@@ -0,0 +1,1622 @@ ++/* ++ * Support for Moorestown Langwell Camera Imaging ISP subsystem. ++ * ++ * Copyright (c) 2009 Intel Corporation. All Rights Reserved. ++ * ++ * Copyright (c) Silicon Image 2008 www.siliconimage.com ++ * ++ * 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 Street, Fifth Floor, Boston, MA ++ * 02110-1301, USA. ++ * ++ * ++ * Xiaolin Zhang ++ */ ++ ++#include "mrstisp_stdinc.h" ++ ++static unsigned long jiffies_start; ++ ++void mrst_timer_start(void) ++{ ++ jiffies_start = jiffies; ++} ++ ++void mrst_timer_stop(void) ++{ ++ jiffies_start = 0; ++} ++ ++unsigned long mrst_get_micro_sec(void) ++{ ++ unsigned long time_diff = 0; ++ ++ time_diff = jiffies - jiffies_start; ++ ++ return jiffies_to_msecs(time_diff); ++} ++ ++/* ++ * Returns the ISP hardware ID. ++ */ ++static u32 ci_isp_get_ci_isp_id(void) ++{ ++ struct isp_register *mrv_reg = (struct isp_register *) MEM_MRV_REG_BASE; ++ u32 result = 0; ++ ++ result = REG_GET_SLICE(mrv_reg->vi_id, MRV_REV_ID); ++ ++ return result; ++} ++ ++/* ++ * Gets the hardware ID and compares it with the expected one. ++ */ ++static int ci_isp_verify_chip_id(void) ++{ ++ u32 mrv_id = ci_isp_get_ci_isp_id(); ++ dprintk(1, "HW-Id: 0x%08X", mrv_id); ++ ++ if (mrv_id != MARVIN_FEATURE_CHIP_ID) { ++ eprintk("HW-Id does not match! read:0x%08X, expected:0x%08X", ++ mrv_id, MARVIN_FEATURE_CHIP_ID); ++ return CI_STATUS_FAILURE; ++ } ++ return CI_STATUS_SUCCESS; ++} ++ ++/* ++ * Triggers an entire reset of MARVIN (equaling an asynchronous ++ * hardware reset). ++ * Checks the hardware ID. A debug warning is issued if the ++ * module ID does not match the expected ID. ++ * Enables all clocks of all sub-modules. ++ * MARVIN is in idle state afterwards. ++ */ ++void ci_isp_init(void) ++{ ++ struct isp_register *mrv_reg = (struct isp_register *) MEM_MRV_REG_BASE; ++ ++ /* verify ID, but no consequences if it doesn't match */ ++ (void)ci_isp_verify_chip_id(); ++ ++ /* enable main clock */ ++ REG_SET_SLICE(mrv_reg->vi_ccl, MRV_VI_CCLFDIS, MRV_VI_CCLFDIS_ENABLE); ++ ++ /* ++ * enable all clocks to make sure that all submodules will be able to ++ * perform the reset correctly ++ */ ++ REG_SET_SLICE(mrv_reg->vi_iccl, MRV_VI_ALL_CLK_ENABLE, ENABLE); ++ ++ /* ++ * Reset of the entire MARVIN triggered by software. The minimum time ++ * permitted by mdelay ensures enough delay. ++ */ ++ ++ /* The reset bit will be cleared by the reset itself. */ ++ ++ /* ++ * The default value of the clock registers is all clocks on. So we ++ * don't have to enable the clocks again afterwards. ++ */ ++ ++ REG_SET_SLICE(mrv_reg->vi_ircl, MRV_VI_MARVIN_RST, ON); ++ /*mdelay(CI_ISP_DELAY_AFTER_RESET);*/ ++ msleep(CI_ISP_DELAY_AFTER_RESET); ++} ++ ++void ci_isp_off(void) ++{ ++ struct isp_register *mrv_reg = (struct isp_register *) MEM_MRV_REG_BASE; ++ ++ /* enable main clock */ ++ REG_SET_SLICE(mrv_reg->vi_ccl, MRV_VI_CCLFDIS, ++ MRV_VI_CCLFDIS_DISABLE); ++ ++ /* ++ * enable all clocks to make sure that all submodules will be able to ++ * perform the reset correctly ++ */ ++ REG_SET_SLICE(mrv_reg->vi_iccl, MRV_VI_ALL_CLK_ENABLE, DISABLE); ++} ++ ++/* ++ * Returns the mask for the frame end interrupts, which are ++ * used for Isp. ++ */ ++u32 ci_isp_get_frame_end_irq_mask_isp(void) ++{ ++ struct isp_register *mrv_reg = (struct isp_register *) MEM_MRV_REG_BASE; ++ ++ switch (REG_GET_SLICE(mrv_reg->vi_dpcl, MRV_VI_DMA_SWITCH)) { ++ /* ++ * 2: path to image effects block (i.e. replacement for data coming ++ * from the ISP) ++ */ ++ case MRV_VI_DMA_SWITCH_IE: ++ /* datapath is used by DMA */ ++ return 0; ++ /* ++ * 0: direct path to self path mux ++ */ ++ case MRV_VI_DMA_SWITCH_SELF: ++ /* ++ * 1: path to superimpose block ++ */ ++ case MRV_VI_DMA_SWITCH_SI: ++ /* ++ * 3: direct path to JPEG encoder (R2B-buffer-less encodein mode) ++ */ ++ case MRV_VI_DMA_SWITCH_JPG: ++ default: ++ /* main and/or self path depends on the YC-splitter setting */ ++ { ++ switch (REG_GET_SLICE ++ (mrv_reg->vi_dpcl, MRV_VI_CHAN_MODE)) { ++ case MRV_VI_CHAN_MODE_MP: ++ return MRV_MI_MP_FRAME_END_MASK; ++ case MRV_VI_CHAN_MODE_SP: ++ return MRV_MI_SP_FRAME_END_MASK; ++ case MRV_VI_CHAN_MODE_MP_SP: ++ return MRV_MI_MP_FRAME_END_MASK | ++ MRV_MI_SP_FRAME_END_MASK; ++ default: ++ return 0; ++ } ++ } ++ } ++ ++} ++ ++/* ++ * Programs the number of frames to capture. Clears frame end ++ * interrupt to allow waiting in ci_isp_wait_for_frame_end(). ++ * Enables the ISP input acquisition and output formatter. ++ * If immediate=false, the hardware assures that enabling is ++ * done frame synchronously. ++ */ ++void ci_isp_start(u16 number_of_frames, ++ enum ci_isp_conf_update_time update_time) ++{ ++ struct isp_register *mrv_reg = (struct isp_register *) MEM_MRV_REG_BASE; ++ u32 isp_ctrl = REG_READ(mrv_reg->isp_ctrl); ++ u32 eof_irq_mask = ci_isp_get_frame_end_irq_mask_isp(); ++ ++ /* max. 10 bits allowed */ ++ WARN_ON(!(number_of_frames <= MRV_ISP_ACQ_NR_FRAMES_MAX)); ++ ++ REG_SET_SLICE(mrv_reg->isp_acq_nr_frames, MRV_ISP_ACQ_NR_FRAMES, ++ number_of_frames); ++ ++ /* clear frame end interrupt */ ++ REG_WRITE(mrv_reg->mi_icr, eof_irq_mask); ++ ++ /* Enable ISP input Acquisition and output formatter. */ ++ ++ /* ++ * Input Acquisition is always enabled synchronous to the image sensor ++ * (no configuration update required). As soon as the input ++ * acquisition is started bit in_enable_shd in the register ++ * isp_flags_shd is set by hardware. In the following a frame end ++ * recognized by the input acquisition unit leads to ++ * ris_in_frame_end=1 in isp_ris. However a recognized frame end and ++ * no signaled errors are no guarantee for a valid configuration. ++ */ ++ ++ /* ++ * The output formatter is enabled frame synchronously according to ++ * the internal sync signals. Bit MRV_GEN_CFG_UPD has to be set. Bit ++ * isp_on_shd in isp_flags_shd is set when the output formatter is ++ * started. A recognized frame end is signaled with ris_out_frame_end ++ * in isp_ris. ++ */ ++ ++ /* ++ * The configuration of the input acquisition and the output ++ * formatter has to be correct to generate proper internal sync ++ * signals and thus a proper frame-synchronous update signal. ++ */ ++ ++ /* If the output formatter does not start check the following: ++ * sync polarities ++ * sample edge ++ * mode in register isp_ctrl ++ * sampling window of input acquisition <= picture size of image ++ * sensor ++ * output formatter window <= sampling window of input ++ * acquisition ++ */ ++ ++ /* ++ * If problems with the window sizes are suspected preferably add some ++ * offsets and reduce the window sizes, so that the above relations ++ * are true by all means. ++ */ ++ ++ switch (update_time) { ++ case CI_ISP_CFG_UPDATE_FRAME_SYNC: ++ REG_SET_SLICE(isp_ctrl, MRV_ISP_ISP_GEN_CFG_UPD, ENABLE); ++ break; ++ case CI_ISP_CFG_UPDATE_IMMEDIATE: ++ /* ++ * MRV_ISP_ISP_CFG_UPD is used instead of ++ * MRV_ISP_ISP_GEN_CFG_UPD. This updates the configuration ++ * right away and MARVIN is ready to aquire the next incoming ++ * frame. ++ */ ++ REG_SET_SLICE(isp_ctrl, MRV_ISP_ISP_CFG_UPD, ENABLE); ++ break; ++ case CI_ISP_CFG_UPDATE_LATER: ++ /* no update from within this function ++ * but enable ISP and Input */ ++ break; ++ default: ++ break; ++ } ++ ++ REG_SET_SLICE(isp_ctrl, MRV_ISP_ISP_INFORM_ENABLE, ENABLE); ++ REG_SET_SLICE(isp_ctrl, MRV_ISP_ISP_ENABLE, ENABLE); ++ REG_WRITE(mrv_reg->isp_ctrl, isp_ctrl); ++ ++ dprintk(3, "ISP_CTRL = 0x%08X", mrv_reg->isp_ctrl); ++} ++ ++/* ++ * Clear frame end interrupt to allow waiting in ++ * ci_isp_wait_for_frame_end(). Disable output formatter (frame ++ * synchronously). ++ */ ++void ci_isp_stop(enum ci_isp_conf_update_time update_time) ++{ ++ struct isp_register *mrv_reg = (struct isp_register *) MEM_MRV_REG_BASE; ++ u32 isp_ctrl = REG_READ(mrv_reg->isp_ctrl); ++ u32 eof_irq_mask = ci_isp_get_frame_end_irq_mask_isp(); ++ ++ /* clear frame end interrupt */ ++ REG_WRITE(mrv_reg->mi_icr, eof_irq_mask); ++ /* disable output formatter */ ++ REG_SET_SLICE(isp_ctrl, MRV_ISP_ISP_ENABLE, DISABLE); ++ ++ switch (update_time) { ++ case CI_ISP_CFG_UPDATE_FRAME_SYNC: ++ REG_SET_SLICE(isp_ctrl, MRV_ISP_ISP_GEN_CFG_UPD, ENABLE); ++ break; ++ case CI_ISP_CFG_UPDATE_IMMEDIATE: ++ REG_SET_SLICE(isp_ctrl, MRV_ISP_ISP_CFG_UPD, ENABLE); ++ break; ++ case CI_ISP_CFG_UPDATE_LATER: ++ break; ++ default: ++ break; ++ } ++ ++ REG_WRITE(mrv_reg->isp_ctrl, isp_ctrl); ++} ++ ++/* ++ * Changes the data path settings. ++ */ ++int ci_isp_set_data_path(enum ci_isp_ycs_chn_mode ycs_chn_mode, ++ enum ci_isp_dp_switch dp_switch) ++{ ++ struct isp_register *mrv_reg = (struct isp_register *) MEM_MRV_REG_BASE; ++ u32 vi_dpcl = REG_READ(mrv_reg->vi_dpcl); ++ u32 vi_chan_mode; ++ u32 vi_mp_mux; ++ ++ /* get desired setting for ycs_chan_mode (or vi_chan_mode) bits */ ++ switch (ycs_chn_mode) { ++ case CI_ISP_YCS_OFF: ++ vi_chan_mode = MRV_VI_CHAN_MODE_OFF; ++ break; ++ case CI_ISP_YCS_Y: ++ vi_chan_mode = MRV_VI_CHAN_MODE_Y; ++ break; ++ case CI_ISP_YCS_MVRaw: ++ vi_chan_mode = MRV_VI_CHAN_MODE_MP_RAW; ++ break; ++ case CI_ISP_YCS_MV: ++ vi_chan_mode = MRV_VI_CHAN_MODE_MP; ++ break; ++ case CI_ISP_YCS_SP: ++ vi_chan_mode = MRV_VI_CHAN_MODE_SP; ++ break; ++ case CI_ISP_YCS_MV_SP: ++ vi_chan_mode = MRV_VI_CHAN_MODE_MP_SP; ++ break; ++ default: ++ eprintk("unknown value for ycs_chn_mode"); ++ return CI_STATUS_NOTSUPP; ++ } ++ ++ if (vi_chan_mode & ~(MRV_VI_CHAN_MODE_MASK >> MRV_VI_CHAN_MODE_SHIFT)) { ++ eprintk("enum ci_isp_ycs_chn_mode not supported"); ++ return CI_STATUS_NOTSUPP; ++ } ++ ++ /* get desired setting for vi_dp_switch (or vi_dp_mux) bits */ ++ switch (dp_switch) { ++ case CI_ISP_DP_RAW: ++ vi_mp_mux = MRV_VI_MP_MUX_RAW; ++ break; ++ case CI_ISP_DP_JPEG: ++ vi_mp_mux = MRV_VI_MP_MUX_JPEG; ++ break; ++ case CI_ISP_DP_MV: ++ vi_mp_mux = MRV_VI_MP_MUX_MP; ++ break; ++ default: ++ eprintk("unknown value for dp_switch"); ++ return CI_STATUS_NOTSUPP; ++ } ++ ++ if (vi_mp_mux & ~MRV_VI_MP_MUX_MASK) { ++ eprintk("dp_switch value not supported"); ++ return CI_STATUS_NOTSUPP; ++ } ++ ++ /* program settings into MARVIN vi_dpcl register */ ++ REG_SET_SLICE(vi_dpcl, MRV_VI_CHAN_MODE, vi_chan_mode); ++ REG_SET_SLICE(vi_dpcl, MRV_VI_MP_MUX, vi_mp_mux); ++ REG_WRITE(mrv_reg->vi_dpcl, vi_dpcl); ++ ++ return CI_STATUS_SUCCESS; ++} ++ ++/* ++ * Changes the data path settings to SMIA or MIPI. ++ */ ++int ci_isp_set_mipi_smia(u32 mode) ++{ ++ struct isp_register *mrv_reg = (struct isp_register *) MEM_MRV_REG_BASE; ++ u32 if_select; ++ ++ /* get desired setting for if_select bits */ ++ switch (mode) { ++ case SENSOR_MODE_SMIA: ++ if_select = MRV_IF_SELECT_SMIA; ++ break; ++ case SENSOR_MODE_MIPI: ++ if_select = MRV_IF_SELECT_MIPI; ++ break; ++ case SENSOR_MODE_BAYER: ++ case SENSOR_MODE_BT601: ++ case SENSOR_MODE_BT656: ++ case SENSOR_MODE_PICT: ++ case SENSOR_MODE_DATA: ++ case SENSOR_MODE_BAY_BT656: ++ case SENSOR_MODE_RAW_BT656: ++ if_select = MRV_IF_SELECT_PAR; ++ break; ++ default: ++ eprintk("unknown value for mode"); ++ return CI_STATUS_NOTSUPP; ++ } ++ ++ /* program settings into MARVIN vi_dpcl register */ ++ REG_SET_SLICE(mrv_reg->vi_dpcl, MRV_IF_SELECT, if_select); ++ ++ if (if_select == MRV_IF_SELECT_MIPI) { ++ REG_WRITE(mrv_reg->mipi_ctrl, 0x1001); /*XXX FLUSH_FIFO? */ ++ /* REG_WRITE(mrv_reg->mipi_ctrl, 0x0001); FLUSH_FIFO? */ ++ } ++ ++ return CI_STATUS_SUCCESS; ++} ++ ++/* ++ * Waits until the specified bits becomes signaled in the mi_ris ++ * register. ++ */ ++static int ci_isp_wait_for_mi(struct mrst_isp_device *intel, u32 bit_mask) ++{ ++ struct isp_register *mrv_reg = (struct isp_register *) MEM_MRV_REG_BASE; ++#if 0 ++ int ret = 0; ++ INIT_COMPLETION(intel->mi_complete); ++ ret = wait_for_completion_interruptible_timeout(&intel->mi_complete, ++ 10*HZ); ++ if (ret == 0) { ++ eprintk("time out in wait for mi"); ++ /* ++ * Try to recover. Softreset of submodules (but not ++ * entire marvin) resets processing and status ++ * information, but not configuration register ++ * content. Bits are sticky. So we have to clear them. ++ * Reset affects the MARVIN 1..2 clock cycles after ++ * the bits are set to high. So we don't have to wait ++ * in software before clearing them. ++ */ ++ ++ /* ++ * Note that only modules with clock enabled will be ++ * affected. ++ */ ++ REG_SET_SLICE(mrv_reg->vi_ircl, MRV_VI_ALL_SOFT_RST, ON); ++ REG_SET_SLICE(mrv_reg->vi_ircl, MRV_VI_ALL_SOFT_RST, OFF); ++ mdelay(CI_ISP_DELAY_AFTER_RESET); ++ /* ++ * isp config update, neccessary to update v/h_size ++ * into shadow registers ++ */ ++ REG_SET_SLICE(mrv_reg->isp_ctrl, MRV_ISP_ISP_CFG_UPD, ON); ++ return CI_STATUS_FAILURE; ++ } ++ return CI_STATUS_SUCCESS; ++#endif ++ u32 irq; ++ static int err_frame_cnt; ++ mrst_timer_start(); ++ /* ++ * Wait for the curr BitMask. If the BitMask is zero, then it's no ++ * waiting. ++ */ ++ while ((mrv_reg->mi_ris & bit_mask) != bit_mask) { ++ ++ irq = REG_READ(mrv_reg->isp_ris); ++ if (irq & (MRV_ISP_RIS_DATA_LOSS_MASK ++ | MRV_ISP_RIS_PIC_SIZE_ERR_MASK)){ ++ err_frame_cnt++; ++ dprintk(1, "irq = 0x%x, err rumber = %d", irq, ++ err_frame_cnt); ++ } ++ if (mrst_get_micro_sec() > 1000) { ++ /* ++ * Note: Don't use REG_READ because content of ++ * registers would be already printed here. ++ */ ++ dprintk(1, "time out"); ++ mrst_timer_stop(); ++ /* ++ * Try to recover. Softreset of submodules (but not ++ * entire marvin) resets processing and status ++ * information, but not configuration register ++ * content. Bits are sticky. So we have to clear them. ++ * Reset affects the MARVIN 1..2 clock cycles after ++ * the bits are set to high. So we don't have to wait ++ * in software before clearing them. ++ */ ++ ++ /* ++ * Note that only modules with clock enabled will be ++ * affected. ++ */ ++ REG_SET_SLICE(mrv_reg->vi_ircl, ++ MRV_VI_ALL_SOFT_RST, ON); ++ REG_SET_SLICE(mrv_reg->vi_ircl, ++ MRV_VI_ALL_SOFT_RST, OFF); ++ /*mdelay(CI_ISP_DELAY_AFTER_RESET);*/ ++ msleep(CI_ISP_DELAY_AFTER_RESET); ++ /* ++ * isp config update, neccessary to update v/h_size ++ * into shadow registers ++ */ ++ REG_SET_SLICE(mrv_reg->isp_ctrl, MRV_ISP_ISP_CFG_UPD, ++ ON); ++ return CI_STATUS_FAILURE; ++ } ++ } ++ ++ mrst_timer_stop(); ++ if (REG_GET_SLICE(mrv_reg->isp_ris, MRV_ISP_RIS_DATA_LOSS)) ++ dprintk(1, "no failure, but MRV_ISPINT_DATA_LOSS"); ++ ++ return CI_STATUS_SUCCESS; ++} ++ ++/* ++ * Waits until a frame is written to memory (frame end ++ * interrupt occurs). ++ * Waits for the frame end interrupt of the memory ++ * interface. ++ */ ++int ci_isp_wait_for_frame_end(struct mrst_isp_device *intel) ++{ ++ return ci_isp_wait_for_mi(intel, ci_isp_get_frame_end_irq_mask_isp()); ++} ++ ++/* ++ * Writes '0xFFFFFFFF' into all *_icr registers to clear all ++ * interrupts. ++ */ ++void ci_isp_reset_interrupt_status(void) ++{ ++ struct isp_register *mrv_reg = (struct isp_register *) MEM_MRV_REG_BASE; ++ ++ /* ISP interrupt clear register */ ++ REG_SET_SLICE(mrv_reg->isp_icr, MRV_ISP_ICR_ALL, ON); ++ REG_SET_SLICE(mrv_reg->isp_err_clr, MRV_ISP_ALL_ERR, ON); ++ REG_SET_SLICE(mrv_reg->mi_icr, MRV_MI_ALLIRQS, ON); ++ /* JPEG error interrupt clear register */ ++ REG_SET_SLICE(mrv_reg->jpe_error_icr, MRV_JPE_ALL_ERR, ON); ++ /* JPEG status interrupt clear register */ ++ REG_SET_SLICE(mrv_reg->jpe_status_icr, MRV_JPE_ALL_STAT, ON); ++ ++ REG_WRITE(mrv_reg->mipi_icr, 0xffffffff); /*XXX replace by a macro */ ++} ++ ++void mrst_isp_disable_interrupt(struct mrst_isp_device *isp) ++{ ++ struct isp_register *mrv_reg = (struct isp_register *)MEM_MRV_REG_BASE; ++ REG_SET_SLICE(mrv_reg->isp_imsc, MRV_ISP_IMSC_ALL, OFF); ++ REG_SET_SLICE(mrv_reg->mi_imsc, MRV_MI_ALLIRQS, OFF); ++ REG_SET_SLICE(mrv_reg->jpe_error_imr, MRV_JPE_ALL_ERR, OFF); ++ REG_SET_SLICE(mrv_reg->jpe_status_imr, MRV_JPE_ALL_STAT, OFF); ++ REG_WRITE(mrv_reg->mipi_imsc, 0x00000000); ++} ++ ++void mrst_isp_enable_interrupt(struct mrst_isp_device *isp) ++{ ++ struct isp_register *mrv_reg = (struct isp_register *)MEM_MRV_REG_BASE; ++ ++ REG_SET_SLICE(mrv_reg->isp_imsc, MRV_ISP_IMSC_DATA_LOSS, ON); ++ REG_SET_SLICE(mrv_reg->isp_imsc, MRV_ISP_IMSC_PIC_SIZE_ERR, ON); ++ ++ REG_WRITE(mrv_reg->mi_imsc, MRV_MI_MP_FRAME_END_MASK); ++ ++ REG_SET_SLICE(mrv_reg->mi_imsc, MRV_MI_MBLK_LINE, ON); ++ ++ REG_SET_SLICE(mrv_reg->jpe_error_imr, MRV_JPE_ALL_ERR, ON); ++ REG_SET_SLICE(mrv_reg->jpe_status_imr, MRV_JPE_ALL_STAT, ON); ++ ++ REG_WRITE(mrv_reg->mipi_imsc, 0x00f00000); ++ ++ ci_isp_reset_interrupt_status(); ++} ++ ++/* ++ * Selects DMA read mode (i.e. sink of the data read from system ++ * memory by the DMA-read block). ++ * update_time is only used on Marvin3plus, ++ * on all other Marvin derivates immediate update is made ++ */ ++void ci_isp_set_dma_read_mode(enum ci_isp_dma_read_mode mode, ++ enum ci_isp_conf_update_time update_time) ++{ ++ struct isp_register *mrv_reg = (struct isp_register *) MEM_MRV_REG_BASE; ++ /* added to avoid LINT warnings (Info 530) */ ++ u32 vi_dma_switch = 0; ++ /* added to avoid LINT warnings (Info 530) */ ++ u32 vi_dma_spmux = 0; ++ /* added to avoid LINT warnings (Info 530) */ ++ u32 vi_dma_iemux = 0; ++ /* added to avoid LINT warnings (Info 530) */ ++ int dma_jpeg_select = false; ++ ++ u32 vi_dpcl = REG_READ(mrv_reg->vi_dpcl); ++ ++ /* ++ * DMA-read feature connected through a dedicated DMA-read ++ * multiplexer. ++ */ ++ ++ /* Programming is done via vi_dpcl register only */ ++#define DMA_READ_MODE_PROGRAMMING_VI_SPMCL 0 ++#define DMA_READ_MODE_PROGRAMMING_VI_DPCL 1 ++ WARN_ON(!((mode == CI_ISP_DMA_RD_OFF) || ++ (mode == CI_ISP_DMA_RD_SELF_PATH) || ++ (mode == CI_ISP_DMA_RD_IE_PATH) || ++ (mode == CI_ISP_DMA_RD_SUPERIMPOSE))); ++ ++ switch (mode) { ++ case CI_ISP_DMA_RD_OFF: ++ vi_dma_switch = MRV_VI_DMA_SWITCH_SELF; ++ vi_dma_spmux = MRV_VI_DMA_SPMUX_CAM; ++ vi_dma_iemux = MRV_VI_DMA_IEMUX_CAM; ++ dma_jpeg_select = false; ++ break; ++ case CI_ISP_DMA_RD_SELF_PATH: ++ vi_dma_switch = MRV_VI_DMA_SWITCH_SELF; ++ vi_dma_spmux = MRV_VI_DMA_SPMUX_DMA; ++ vi_dma_iemux = MRV_VI_DMA_IEMUX_CAM; ++ dma_jpeg_select = false; ++ break; ++ case CI_ISP_DMA_RD_IE_PATH: ++ vi_dma_switch = MRV_VI_DMA_SWITCH_IE; ++ vi_dma_spmux = MRV_VI_DMA_SPMUX_CAM; ++ vi_dma_iemux = MRV_VI_DMA_IEMUX_DMA; ++ dma_jpeg_select = false; ++ break; ++ case CI_ISP_DMA_RD_JPG_ENC: ++ vi_dma_switch = MRV_VI_DMA_SWITCH_JPG; ++ vi_dma_spmux = MRV_VI_DMA_SPMUX_CAM; ++ vi_dma_iemux = MRV_VI_DMA_IEMUX_CAM; ++ dma_jpeg_select = true; ++ break; ++ case CI_ISP_DMA_RD_SUPERIMPOSE: ++ vi_dma_switch = MRV_VI_DMA_SWITCH_SI; ++ vi_dma_spmux = MRV_VI_DMA_SPMUX_CAM; ++ vi_dma_iemux = MRV_VI_DMA_IEMUX_CAM; ++ dma_jpeg_select = false; ++ break; ++ default: ++ /* unknown DMA-read mode */ ++ WARN_ON(1); ++ } ++ ++ REG_SET_SLICE(vi_dpcl, MRV_VI_DMA_SWITCH, vi_dma_switch); ++ REG_SET_SLICE(vi_dpcl, MRV_VI_DMA_SPMUX, vi_dma_spmux); ++ REG_SET_SLICE(vi_dpcl, MRV_VI_DMA_IEMUX, vi_dma_iemux); ++#if ((MRV_VI_MP_MUX_JPGDIRECT & \ ++~(MRV_VI_MP_MUX_MASK >> MRV_VI_MP_MUX_SHIFT)) == 0) ++ if (dma_jpeg_select) { ++ REG_SET_SLICE(vi_dpcl, MRV_VI_MP_MUX, ++ MRV_VI_MP_MUX_JPGDIRECT); ++ } ++#else ++ /* direct DMA to JPEG not supported */ ++ UNUSED_PARAM(dma_jpeg_select); ++#endif ++ REG_WRITE(mrv_reg->vi_dpcl, vi_dpcl); ++} ++ ++/* ++ * Set extended mode with unrestricted values for YCbCr ++ * Y (0-255) CbCr (0-255) ++ */ ++void ci_isp_set_ext_ycmode(void) ++{ ++ struct isp_register *mrv_reg = (struct isp_register *) MEM_MRV_REG_BASE; ++ u32 isp_ctrl = REG_READ(mrv_reg->isp_ctrl); ++ ++ /* modify isp_ctrl register */ ++ REG_SET_SLICE(isp_ctrl, MRV_ISP_ISP_CSM_C_RANGE, ++ MRV_ISP_ISP_CSM_C_RANGE_FULL); ++ REG_SET_SLICE(isp_ctrl, MRV_ISP_ISP_CSM_Y_RANGE, ++ MRV_ISP_ISP_CSM_Y_RANGE_FULL); ++ REG_WRITE(mrv_reg->isp_ctrl, isp_ctrl); ++ ++ /* program RGB to YUV color conversion with extended range */ ++ REG_SET_SLICE(mrv_reg->isp_cc_coeff_0, MRV_ISP_CC_COEFF_0, 0x0026); ++ REG_SET_SLICE(mrv_reg->isp_cc_coeff_1, MRV_ISP_CC_COEFF_1, 0x004B); ++ REG_SET_SLICE(mrv_reg->isp_cc_coeff_2, MRV_ISP_CC_COEFF_2, 0x000F); ++ REG_SET_SLICE(mrv_reg->isp_cc_coeff_3, MRV_ISP_CC_COEFF_3, 0x01EA); ++ REG_SET_SLICE(mrv_reg->isp_cc_coeff_4, MRV_ISP_CC_COEFF_4, 0x01D6); ++ REG_SET_SLICE(mrv_reg->isp_cc_coeff_5, MRV_ISP_CC_COEFF_5, 0x0040); ++ REG_SET_SLICE(mrv_reg->isp_cc_coeff_6, MRV_ISP_CC_COEFF_6, 0x0040); ++ REG_SET_SLICE(mrv_reg->isp_cc_coeff_7, MRV_ISP_CC_COEFF_7, 0x01CA); ++ REG_SET_SLICE(mrv_reg->isp_cc_coeff_8, MRV_ISP_CC_COEFF_8, 0x01F6); ++} ++ ++void ci_isp_set_yc_mode(void) ++{ ++ struct isp_register *mrv_reg = (struct isp_register *)MEM_MRV_REG_BASE; ++ u32 isp_ctrl = REG_READ(mrv_reg->isp_ctrl); ++ ++ /* modify isp_ctrl register */ ++ REG_SET_SLICE(isp_ctrl, MRV_ISP_ISP_CSM_C_RANGE, ++ MRV_ISP_ISP_CSM_Y_RANGE_BT601); ++ REG_SET_SLICE(isp_ctrl, MRV_ISP_ISP_CSM_Y_RANGE, ++ MRV_ISP_ISP_CSM_Y_RANGE_BT601); ++ REG_WRITE(mrv_reg->isp_ctrl, isp_ctrl); ++ ++ /* program RGB to YUV color conversion with extended range */ ++ REG_SET_SLICE(mrv_reg->isp_cc_coeff_0, MRV_ISP_CC_COEFF_0, 0x0021); ++ REG_SET_SLICE(mrv_reg->isp_cc_coeff_1, MRV_ISP_CC_COEFF_1, 0x0040); ++ REG_SET_SLICE(mrv_reg->isp_cc_coeff_2, MRV_ISP_CC_COEFF_2, 0x000D); ++ REG_SET_SLICE(mrv_reg->isp_cc_coeff_3, MRV_ISP_CC_COEFF_3, 0x01ED); ++ REG_SET_SLICE(mrv_reg->isp_cc_coeff_4, MRV_ISP_CC_COEFF_4, 0x01DB); ++ REG_SET_SLICE(mrv_reg->isp_cc_coeff_5, MRV_ISP_CC_COEFF_5, 0x0038); ++ REG_SET_SLICE(mrv_reg->isp_cc_coeff_6, MRV_ISP_CC_COEFF_6, 0x0038); ++ REG_SET_SLICE(mrv_reg->isp_cc_coeff_7, MRV_ISP_CC_COEFF_7, 0x01D1); ++ REG_SET_SLICE(mrv_reg->isp_cc_coeff_8, MRV_ISP_CC_COEFF_8, 0x01F7); ++} ++ ++/* ++ * writes the color values for contrast, brightness, ++ * saturation and hue into the appropriate Marvin ++ * registers ++ */ ++void ci_isp_col_set_color_processing( ++ const struct ci_isp_color_settings *col) ++{ ++ struct isp_register *mrv_reg = ++ (struct isp_register *) MEM_MRV_REG_BASE; ++ ++ if (col == NULL) { ++ /* disable color processing (bypass) */ ++ mrv_reg->c_proc_ctrl = 0; ++ } else { ++ mrv_reg->c_proc_contrast = col->contrast; ++ mrv_reg->c_proc_brightness = col->brightness; ++ mrv_reg->c_proc_saturation = col->saturation; ++ mrv_reg->c_proc_hue = col->hue; ++ ++ /* modify color processing registers */ ++ ++ if (col->flags & CI_ISP_CPROC_C_OUT_RANGE) { ++ mrv_reg->c_proc_ctrl = ++ mrv_reg->c_proc_ctrl | CI_ISP_CPROC_C_OUT_RANGE; ++ } ++ ++ if (col->flags & CI_ISP_CPROC_Y_IN_RANGE) { ++ mrv_reg->c_proc_ctrl = ++ mrv_reg->c_proc_ctrl | CI_ISP_CPROC_Y_IN_RANGE; ++ } ++ ++ if (col->flags & CI_ISP_CPROC_Y_OUT_RANGE) { ++ mrv_reg->c_proc_ctrl = ++ mrv_reg->c_proc_ctrl | CI_ISP_CPROC_Y_OUT_RANGE; ++ } ++ ++ if (col->flags & CI_ISP_CPROC_ENABLE) { ++ mrv_reg->c_proc_ctrl = ++ mrv_reg->c_proc_ctrl | CI_ISP_CPROC_ENABLE; ++ } ++ } ++} ++ ++/* ++ * Translates a chrominance component value from usual ++ * representation (range 16..240, 128=neutral grey) ++ * to the one used by the ie_tint register ++ * The value is returned as 32 bit unsigned to support shift ++ * operation without explicit cast. ++ * The translation formular implemented here is taken from ++ * the image effects functional specification document, ++ * Doc-ID 30-001-481.130, revision 1.1 from november, 21st. 2005 ++ */ ++static u32 ci_isp_ie_tint_cx2_reg_val(u8 cx) ++{ ++ s32 temp; ++ u32 reg_val; ++ ++ /* ++ * apply scaling as specified in the image effects functional ++ * specification ++ */ ++ temp = 128 - (s32) cx; ++ temp = ((temp * 64) / 110); ++ ++ /* convert from two's complement to sign/value */ ++ if (temp < 0) { ++ reg_val = 0x80; ++ temp *= (-1); ++ } else ++ reg_val = 0; ++ ++ /* saturate at 7 bits */ ++ if (temp > 0x7F) ++ temp = 0x7F; ++ ++ /* combine sign and value to build the regiter value */ ++ reg_val |= (u32) temp; ++ ++ return reg_val; ++} ++ ++/* ++ * Translates usual (decimal) matrix coefficient into the ++ * 4 bit register representation (used in the ie_mat_X registers). ++ * for unsupported decimal numbers, a supported replacement is ++ * selected automatically. ++ * The value is returned as 32 bit unsigned to support shift ++ * operation without explicit cast. ++ * The translation formular implemented here is taken from ++ * the image effects functional specification document, ++ * Doc-ID 30-001-481.130, revision 1.1 from november, 21st. 2005 ++ */ ++static u32 ci_isp_ie_mx_dec2_reg_val(s8 dec) ++{ ++ if (dec <= (-6)) { ++ /* equivlent to -8 */ ++ return 0x0f; ++ } else if (dec <= (-3)) { ++ /* equivlent to -4 */ ++ return 0x0e; ++ } else if (dec == (-2)) { ++ /* equivlent to -2 */ ++ return 0x0d; ++ } else if (dec == (-1)) { ++ /* equivlent to -1 */ ++ return 0x0c; ++ } else if (dec == 0) { ++ /* equivlent to 0 (entry not used) */ ++ return 0x00; ++ } else if (dec == 1) { ++ /* equivlent to 1 */ ++ return 0x08; ++ } else if (dec == 2) { ++ /* equivlent to 2 */ ++ return 0x09; ++ } else if (dec < 6) { ++ /* equivlent to 4 */ ++ return 0x0a; ++ } else { ++ /* equivlent to 8 */ ++ return 0x0b; ++ } ++} ++ ++/* ++ * translates the values of the given configuration ++ * structure into register settings for the image effects ++ * submodule and loads the registers. ++ */ ++int ci_isp_ie_set_config(const struct ci_isp_ie_config *ie_config) ++{ ++ struct isp_register *mrv_reg = (struct isp_register *) MEM_MRV_REG_BASE; ++ ++ if (!ie_config) { ++ /* just disable the module, i.e. put it in bypass mode */ ++ REG_SET_SLICE(mrv_reg->img_eff_ctrl, MRV_IMGEFF_BYPASS_MODE, ++ MRV_IMGEFF_BYPASS_MODE_BYPASS); ++ } else { ++ /* apply the given settings */ ++ u32 ul_ie_ctrl = REG_READ(mrv_reg->img_eff_ctrl); ++ u32 ul_ie_csel = REG_READ(mrv_reg->img_eff_color_sel); ++ u32 ul_ie_tint = REG_READ(mrv_reg->img_eff_tint); ++ u32 ul_ie_mat1 = REG_READ(mrv_reg->img_eff_mat_1); ++ u32 ul_ie_mat2 = REG_READ(mrv_reg->img_eff_mat_2); ++ u32 ul_ie_mat3 = REG_READ(mrv_reg->img_eff_mat_3); ++ u32 ul_ie_mat4 = REG_READ(mrv_reg->img_eff_mat_4); ++ u32 ul_ie_mat5 = REG_READ(mrv_reg->img_eff_mat_5); ++ ++ /* overall operation mode */ ++ switch (ie_config->mode) { ++ case CI_ISP_IE_MODE_OFF: ++ REG_SET_SLICE(ul_ie_ctrl, MRV_IMGEFF_BYPASS_MODE, ++ MRV_IMGEFF_BYPASS_MODE_BYPASS); ++ break; ++ case CI_ISP_IE_MODE_GRAYSCALE: ++ REG_SET_SLICE(ul_ie_ctrl, MRV_IMGEFF_EFFECT_MODE, ++ MRV_IMGEFF_EFFECT_MODE_GRAY); ++ REG_SET_SLICE(ul_ie_ctrl, MRV_IMGEFF_BYPASS_MODE, ++ MRV_IMGEFF_BYPASS_MODE_PROCESS); ++ break; ++ case CI_ISP_IE_MODE_NEGATIVE: ++ REG_SET_SLICE(ul_ie_ctrl, MRV_IMGEFF_EFFECT_MODE, ++ MRV_IMGEFF_EFFECT_MODE_NEGATIVE); ++ REG_SET_SLICE(ul_ie_ctrl, MRV_IMGEFF_BYPASS_MODE, ++ MRV_IMGEFF_BYPASS_MODE_PROCESS); ++ break; ++ case CI_ISP_IE_MODE_SEPIA: ++ REG_SET_SLICE(ul_ie_ctrl, MRV_IMGEFF_EFFECT_MODE, ++ MRV_IMGEFF_EFFECT_MODE_SEPIA); ++ REG_SET_SLICE(ul_ie_ctrl, MRV_IMGEFF_BYPASS_MODE, ++ MRV_IMGEFF_BYPASS_MODE_PROCESS); ++ break; ++ case CI_ISP_IE_MODE_COLOR_SEL: ++ REG_SET_SLICE(ul_ie_ctrl, MRV_IMGEFF_EFFECT_MODE, ++ MRV_IMGEFF_EFFECT_MODE_COLOR_SEL); ++ REG_SET_SLICE(ul_ie_ctrl, MRV_IMGEFF_BYPASS_MODE, ++ MRV_IMGEFF_BYPASS_MODE_PROCESS); ++ break; ++ case CI_ISP_IE_MODE_EMBOSS: ++ REG_SET_SLICE(ul_ie_ctrl, MRV_IMGEFF_EFFECT_MODE, ++ MRV_IMGEFF_EFFECT_MODE_EMBOSS); ++ REG_SET_SLICE(ul_ie_ctrl, MRV_IMGEFF_BYPASS_MODE, ++ MRV_IMGEFF_BYPASS_MODE_PROCESS); ++ break; ++ case CI_ISP_IE_MODE_SKETCH: ++ REG_SET_SLICE(ul_ie_ctrl, MRV_IMGEFF_EFFECT_MODE, ++ MRV_IMGEFF_EFFECT_MODE_SKETCH); ++ REG_SET_SLICE(ul_ie_ctrl, MRV_IMGEFF_BYPASS_MODE, ++ MRV_IMGEFF_BYPASS_MODE_PROCESS); ++ break; ++ default: ++ return CI_STATUS_OUTOFRANGE; ++ } ++ ++ /* use next frame sync update */ ++ REG_SET_SLICE(ul_ie_ctrl, MRV_IMGEFF_CFG_UPD, ON); ++ ++ /* color selection settings */ ++ REG_SET_SLICE(ul_ie_csel, MRV_IMGEFF_COLOR_THRESHOLD, ++ (u32) (ie_config->color_thres)); ++ REG_SET_SLICE(ul_ie_csel, MRV_IMGEFF_COLOR_SELECTION, ++ (u32) (ie_config->color_sel)); ++ ++ /* tint color settings */ ++ REG_SET_SLICE(ul_ie_tint, MRV_IMGEFF_INCR_CB, ++ ci_isp_ie_tint_cx2_reg_val(ie_config->tint_cb)); ++ REG_SET_SLICE(ul_ie_tint, MRV_IMGEFF_INCR_CR, ++ ci_isp_ie_tint_cx2_reg_val(ie_config->tint_cr)); ++ ++ /* matrix coefficients */ ++ REG_SET_SLICE(ul_ie_mat1, MRV_IMGEFF_EMB_COEF_11_4, ++ ci_isp_ie_mx_dec2_reg_val(ie_config->mat_emboss. ++ coeff_11)); ++ REG_SET_SLICE(ul_ie_mat1, MRV_IMGEFF_EMB_COEF_12_4, ++ ci_isp_ie_mx_dec2_reg_val(ie_config->mat_emboss. ++ coeff_12)); ++ REG_SET_SLICE(ul_ie_mat1, MRV_IMGEFF_EMB_COEF_13_4, ++ ci_isp_ie_mx_dec2_reg_val(ie_config->mat_emboss. ++ coeff_13)); ++ REG_SET_SLICE(ul_ie_mat1, MRV_IMGEFF_EMB_COEF_21_4, ++ ci_isp_ie_mx_dec2_reg_val(ie_config->mat_emboss. ++ coeff_21)); ++ REG_SET_SLICE(ul_ie_mat2, MRV_IMGEFF_EMB_COEF_22_4, ++ ci_isp_ie_mx_dec2_reg_val(ie_config->mat_emboss. ++ coeff_22)); ++ REG_SET_SLICE(ul_ie_mat2, MRV_IMGEFF_EMB_COEF_23_4, ++ ci_isp_ie_mx_dec2_reg_val(ie_config->mat_emboss. ++ coeff_23)); ++ REG_SET_SLICE(ul_ie_mat2, MRV_IMGEFF_EMB_COEF_31_4, ++ ci_isp_ie_mx_dec2_reg_val(ie_config->mat_emboss. ++ coeff_31)); ++ REG_SET_SLICE(ul_ie_mat2, MRV_IMGEFF_EMB_COEF_32_4, ++ ci_isp_ie_mx_dec2_reg_val(ie_config->mat_emboss. ++ coeff_32)); ++ REG_SET_SLICE(ul_ie_mat3, MRV_IMGEFF_EMB_COEF_33_4, ++ ci_isp_ie_mx_dec2_reg_val(ie_config->mat_emboss. ++ coeff_33)); ++ REG_SET_SLICE(ul_ie_mat3, MRV_IMGEFF_SKET_COEF_11_4, ++ ci_isp_ie_mx_dec2_reg_val(ie_config->mat_sketch. ++ coeff_11)); ++ REG_SET_SLICE(ul_ie_mat3, MRV_IMGEFF_SKET_COEF_12_4, ++ ci_isp_ie_mx_dec2_reg_val(ie_config->mat_sketch. ++ coeff_12)); ++ REG_SET_SLICE(ul_ie_mat3, MRV_IMGEFF_SKET_COEF_13_4, ++ ci_isp_ie_mx_dec2_reg_val(ie_config->mat_sketch. ++ coeff_13)); ++ REG_SET_SLICE(ul_ie_mat4, MRV_IMGEFF_SKET_COEF_21_4, ++ ci_isp_ie_mx_dec2_reg_val(ie_config->mat_sketch. ++ coeff_21)); ++ REG_SET_SLICE(ul_ie_mat4, MRV_IMGEFF_SKET_COEF_22_4, ++ ci_isp_ie_mx_dec2_reg_val(ie_config->mat_sketch. ++ coeff_22)); ++ REG_SET_SLICE(ul_ie_mat4, MRV_IMGEFF_SKET_COEF_23_4, ++ ci_isp_ie_mx_dec2_reg_val(ie_config->mat_sketch. ++ coeff_23)); ++ REG_SET_SLICE(ul_ie_mat4, MRV_IMGEFF_SKET_COEF_31_4, ++ ci_isp_ie_mx_dec2_reg_val(ie_config->mat_sketch. ++ coeff_31)); ++ REG_SET_SLICE(ul_ie_mat5, MRV_IMGEFF_SKET_COEF_32_4, ++ ci_isp_ie_mx_dec2_reg_val(ie_config->mat_sketch. ++ coeff_32)); ++ REG_SET_SLICE(ul_ie_mat5, MRV_IMGEFF_SKET_COEF_33_4, ++ ci_isp_ie_mx_dec2_reg_val(ie_config->mat_sketch. ++ coeff_33)); ++ ++ /* write changed values back to registers */ ++ REG_WRITE(mrv_reg->img_eff_ctrl, ul_ie_ctrl); ++ REG_WRITE(mrv_reg->img_eff_color_sel, ul_ie_csel); ++ REG_WRITE(mrv_reg->img_eff_tint, ul_ie_tint); ++ REG_WRITE(mrv_reg->img_eff_mat_1, ul_ie_mat1); ++ REG_WRITE(mrv_reg->img_eff_mat_2, ul_ie_mat2); ++ REG_WRITE(mrv_reg->img_eff_mat_3, ul_ie_mat3); ++ REG_WRITE(mrv_reg->img_eff_mat_4, ul_ie_mat4); ++ REG_WRITE(mrv_reg->img_eff_mat_5, ul_ie_mat5); ++ ++ /* frame synchronous update of shadow registers */ ++ REG_SET_SLICE(mrv_reg->isp_ctrl, MRV_ISP_ISP_GEN_CFG_UPD, ON); ++ } ++ ++ return CI_STATUS_SUCCESS; ++} ++ ++/* ++ * Applies the new image stabilisation settings to the module. ++ */ ++int ci_isp_is_set_config(const struct ci_isp_is_config *is_config) ++{ ++ struct isp_register *mrv_reg = (struct isp_register *) MEM_MRV_REG_BASE; ++ ++ if (!is_config) { ++ eprintk("is_config NULL"); ++ return CI_STATUS_NULL_POINTER; ++ } ++ ++ /* set maximal margin distance for X */ ++ if (is_config->max_dx > MRV_IS_IS_MAX_DX_MAX) { ++ REG_SET_SLICE(mrv_reg->isp_is_max_dx, MRV_IS_IS_MAX_DX, ++ (u32) (MRV_IS_IS_MAX_DX_MAX)); ++ } else { ++ REG_SET_SLICE(mrv_reg->isp_is_max_dx, MRV_IS_IS_MAX_DX, ++ (u32) (is_config->max_dx)); ++ } ++ ++ /* set maximal margin distance for Y */ ++ if (is_config->max_dy > MRV_IS_IS_MAX_DY_MAX) { ++ REG_SET_SLICE(mrv_reg->isp_is_max_dy, MRV_IS_IS_MAX_DY, ++ (u32) (MRV_IS_IS_MAX_DY_MAX)); ++ } else { ++ REG_SET_SLICE(mrv_reg->isp_is_max_dy, MRV_IS_IS_MAX_DY, ++ (u32) (is_config->max_dy)); ++ } ++ ++ /* set H offset */ ++ REG_SET_SLICE(mrv_reg->isp_is_h_offs, MRV_IS_IS_H_OFFS, ++ (u32) (is_config->mrv_is_window.hoffs)); ++ /* set V offset */ ++ REG_SET_SLICE(mrv_reg->isp_is_v_offs, MRV_IS_IS_V_OFFS, ++ (u32) (is_config->mrv_is_window.voffs)); ++ /* set H size */ ++ REG_SET_SLICE(mrv_reg->isp_is_h_size, MRV_IS_IS_H_SIZE, ++ (u32) (is_config->mrv_is_window.hsize)); ++ /* set V size */ ++ REG_SET_SLICE(mrv_reg->isp_is_v_size, MRV_IS_IS_V_SIZE, ++ (u32) (is_config->mrv_is_window.vsize)); ++ ++ return CI_STATUS_SUCCESS; ++} ++ ++static int ci_isp_bls_set_fixed_values(const struct ci_isp_bls_subtraction ++ *bls_subtraction) ++{ ++ struct isp_register *mrv_reg = (struct isp_register *) MEM_MRV_REG_BASE; ++ ++ if (!bls_subtraction) ++ return CI_STATUS_NULL_POINTER; ++ ++ if ((bls_subtraction->fixed_a > MRV_ISP_BLS_FIX_SUB_MAX) || ++ (bls_subtraction->fixed_b > MRV_ISP_BLS_FIX_SUB_MAX) || ++ (bls_subtraction->fixed_c > MRV_ISP_BLS_FIX_SUB_MAX) || ++ (bls_subtraction->fixed_d > MRV_ISP_BLS_FIX_SUB_MAX) || ++ (bls_subtraction->fixed_a < (s16) MRV_ISP_BLS_FIX_SUB_MIN) || ++ (bls_subtraction->fixed_b < (s16) MRV_ISP_BLS_FIX_SUB_MIN) || ++ (bls_subtraction->fixed_c < (s16) MRV_ISP_BLS_FIX_SUB_MIN) || ++ (bls_subtraction->fixed_d < (s16) MRV_ISP_BLS_FIX_SUB_MIN)) { ++ return CI_STATUS_OUTOFRANGE; ++ } else { ++ /* we are in this path */ ++ REG_SET_SLICE(mrv_reg->isp_bls_a_fixed, MRV_BLS_BLS_A_FIXED, ++ bls_subtraction->fixed_a); ++ REG_SET_SLICE(mrv_reg->isp_bls_b_fixed, MRV_BLS_BLS_B_FIXED, \ ++ bls_subtraction->fixed_b); ++ REG_SET_SLICE(mrv_reg->isp_bls_c_fixed, MRV_BLS_BLS_C_FIXED, ++ bls_subtraction->fixed_c); ++ REG_SET_SLICE(mrv_reg->isp_bls_d_fixed, MRV_BLS_BLS_D_FIXED, ++ bls_subtraction->fixed_d); ++ } ++ ++ return CI_STATUS_SUCCESS; ++} ++ ++/* ++ * Sets the desired configuration values to the BLS registers, ++ * if possible. In the case the parameter (bls_config == NULL) ++ * the BLS module will be deactivated. ++ */ ++int ci_isp_bls_set_config(const struct ci_isp_bls_config *bls_config) ++{ ++ struct isp_register *mrv_reg = (struct isp_register *) MEM_MRV_REG_BASE; ++ u32 isp_bls_ctrl = 0; ++ ++ int error = CI_STATUS_FAILURE; ++ ++ if (!bls_config) { ++ /* disable the BLS module */ ++ REG_SET_SLICE(mrv_reg->isp_bls_ctrl, ++ MRV_BLS_BLS_ENABLE, DISABLE); ++ return CI_STATUS_SUCCESS; ++ } ++ ++ /* measurement window 2, enable_window =0 */ ++ if (bls_config->isp_bls_window2.enable_window) { ++ if ((bls_config->isp_bls_window2.start_h > ++ MRV_BLS_BLS_H2_START_MAX) ++ || (bls_config->isp_bls_window2.stop_h > ++ MRV_BLS_BLS_H2_STOP_MAX) ++ || (bls_config->isp_bls_window2.start_v > ++ MRV_BLS_BLS_V2_START_MAX) ++ || (bls_config->isp_bls_window2.stop_v > ++ MRV_BLS_BLS_V2_STOP_MAX)) { ++ return CI_STATUS_OUTOFRANGE; ++ } else { ++ REG_SET_SLICE(mrv_reg->isp_bls_h2_start, ++ MRV_BLS_BLS_H2_START, ++ bls_config->isp_bls_window2.start_h); ++ REG_SET_SLICE(mrv_reg->isp_bls_h2_stop, ++ MRV_BLS_BLS_H2_STOP, ++ bls_config->isp_bls_window2.stop_h); ++ REG_SET_SLICE(mrv_reg->isp_bls_v2_start, ++ MRV_BLS_BLS_V2_START, ++ bls_config->isp_bls_window2.start_v); ++ REG_SET_SLICE(mrv_reg->isp_bls_v2_stop, ++ MRV_BLS_BLS_V2_STOP, ++ bls_config->isp_bls_window2.stop_v); ++ } ++ } ++ ++ /* measurement window 1, enable_window=0 */ ++ if (bls_config->isp_bls_window1.enable_window) { ++ if ((bls_config->isp_bls_window1.start_h > ++ MRV_BLS_BLS_H1_START_MAX) ++ || (bls_config->isp_bls_window1.stop_h > ++ MRV_BLS_BLS_H1_STOP_MAX) ++ || (bls_config->isp_bls_window1.start_v > ++ MRV_BLS_BLS_V1_START_MAX) ++ || (bls_config->isp_bls_window1.stop_v > ++ MRV_BLS_BLS_V1_STOP_MAX)) { ++ return CI_STATUS_OUTOFRANGE; ++ } else { ++ REG_SET_SLICE(mrv_reg->isp_bls_h1_start, ++ MRV_BLS_BLS_H1_START, ++ bls_config->isp_bls_window1.start_h); ++ REG_SET_SLICE(mrv_reg->isp_bls_h1_stop, ++ MRV_BLS_BLS_H1_STOP, ++ bls_config->isp_bls_window1.stop_h); ++ REG_SET_SLICE(mrv_reg->isp_bls_v1_start, ++ MRV_BLS_BLS_V1_START, ++ bls_config->isp_bls_window1.start_v); ++ REG_SET_SLICE(mrv_reg->isp_bls_v1_stop, ++ MRV_BLS_BLS_V1_STOP, ++ bls_config->isp_bls_window1.stop_v); ++ } ++ } ++ ++ if (bls_config->bls_samples > MRV_BLS_BLS_SAMPLES_MAX) { ++ return CI_STATUS_OUTOFRANGE; ++ } else { ++ REG_SET_SLICE(mrv_reg->isp_bls_samples, MRV_BLS_BLS_SAMPLES, ++ bls_config->bls_samples); ++ } ++ ++ /* fixed subtraction values, enable_automatic=0 */ ++ if (!bls_config->enable_automatic) { ++ error = ci_isp_bls_set_fixed_values( ++ &(bls_config->bls_subtraction)); ++ if (error != CI_STATUS_SUCCESS) ++ return error; ++ } ++ ++ if ((bls_config->disable_h) || (bls_config->disable_v)) ++ return CI_STATUS_OUTOFRANGE; ++ ++ isp_bls_ctrl = REG_READ(mrv_reg->isp_bls_ctrl); ++ ++ /* enable measurement window(s) */ ++ REG_SET_SLICE(isp_bls_ctrl, MRV_BLS_WINDOW_ENABLE, ++ ((bls_config->isp_bls_window1.enable_window) ++ ? MRV_BLS_WINDOW_ENABLE_WND1 : 0) | ++ ((bls_config->isp_bls_window2.enable_window) ++ ? MRV_BLS_WINDOW_ENABLE_WND2 : 0)); ++ ++ /* set Mode */ ++ REG_SET_SLICE(isp_bls_ctrl, MRV_BLS_BLS_MODE, ++ (bls_config->enable_automatic) ? MRV_BLS_BLS_MODE_MEAS : ++ MRV_BLS_BLS_MODE_FIX); ++ ++ /* enable module */ ++ REG_SET_SLICE(isp_bls_ctrl, MRV_BLS_BLS_ENABLE, ENABLE); ++ ++ /* write into register */ ++ REG_WRITE(mrv_reg->isp_bls_ctrl, isp_bls_ctrl); ++ ++ return CI_STATUS_SUCCESS; ++} ++ ++#define RSZ_FLAGS_MASK (RSZ_UPSCALE_ENABLE | RSZ_SCALER_BYPASS) ++ ++/* ++ * writes the scaler values to the appropriate Marvin registers. ++ */ ++void ci_isp_res_set_main_resize(const struct ci_isp_scale *scale, ++ enum ci_isp_conf_update_time update_time, ++ const struct ci_isp_rsz_lut *rsz_lut) ++{ ++ struct isp_register *mrv_reg = (struct isp_register *) MEM_MRV_REG_BASE; ++ u32 mrsz_ctrl = REG_READ(mrv_reg->mrsz_ctrl); ++ u32 i; ++ int upscaling = false; ++ ++ /* flags must be "outside" scaler value */ ++ WARN_ON(!((RSZ_FLAGS_MASK & MRV_RSZ_SCALE_MASK) == 0)); ++ WARN_ON(!((scale->scale_hy & ~RSZ_FLAGS_MASK) <= MRV_RSZ_SCALE_MAX)); ++ WARN_ON(!((scale->scale_hcb & ~RSZ_FLAGS_MASK) <= MRV_RSZ_SCALE_MAX)); ++ WARN_ON(!((scale->scale_hcr & ~RSZ_FLAGS_MASK) <= MRV_RSZ_SCALE_MAX)); ++ WARN_ON(!((scale->scale_vy & ~RSZ_FLAGS_MASK) <= MRV_RSZ_SCALE_MAX)); ++ WARN_ON(!((scale->scale_vc & ~RSZ_FLAGS_MASK) <= MRV_RSZ_SCALE_MAX)); ++ ++ /* horizontal luminance scale factor */ ++ dprintk(1, "scale_hy = %d( %x )", scale->scale_hy, scale->scale_hy); ++ ++ if (scale->scale_hy & RSZ_SCALER_BYPASS) { ++ /* disable (bypass) scaler */ ++ REG_SET_SLICE(mrsz_ctrl, MRV_MRSZ_SCALE_HY_ENABLE, DISABLE); ++ } else { ++ /* enable scaler */ ++ REG_SET_SLICE(mrsz_ctrl, MRV_MRSZ_SCALE_HY_ENABLE, ENABLE); ++ /* program scale factor and phase */ ++ REG_SET_SLICE(mrv_reg->mrsz_scale_hy, MRV_MRSZ_SCALE_HY, ++ (u32) scale->scale_hy); ++ REG_SET_SLICE(mrv_reg->mrsz_phase_hy, MRV_MRSZ_PHASE_HY, ++ (u32) scale->phase_hy); ++ ++ if (scale->scale_hy & RSZ_UPSCALE_ENABLE) { ++ /* enable upscaling mode */ ++ dprintk(1, "enable up scale"); ++ REG_SET_SLICE(mrsz_ctrl, MRV_MRSZ_SCALE_HY_UP, ++ MRV_MRSZ_SCALE_HY_UP_UPSCALE); ++ /* scaler and upscaling enabled */ ++ upscaling = true; ++ } else ++ /* disable upscaling mode */ ++ REG_SET_SLICE(mrsz_ctrl, MRV_MRSZ_SCALE_HY_UP, ++ MRV_MRSZ_SCALE_HY_UP_DOWNSCALE); ++ } ++ ++ /* horizontal chrominance scale factors */ ++ WARN_ON(!((scale->scale_hcb & RSZ_FLAGS_MASK) == (scale->scale_hcr & ++ RSZ_FLAGS_MASK))); ++ dprintk(1, "scale_hcb = %d( %x )", scale->scale_hcb, scale->scale_hcb); ++ ++ if (scale->scale_hcb & RSZ_SCALER_BYPASS) { ++ /* disable (bypass) scaler */ ++ REG_SET_SLICE(mrsz_ctrl, MRV_MRSZ_SCALE_HC_ENABLE, DISABLE); ++ } else { ++ /* enable scaler */ ++ REG_SET_SLICE(mrsz_ctrl, MRV_MRSZ_SCALE_HC_ENABLE, ENABLE); ++ /* program scale factor and phase */ ++ REG_SET_SLICE(mrv_reg->mrsz_scale_hcb, MRV_MRSZ_SCALE_HCB, ++ (u32) scale->scale_hcb); ++ REG_SET_SLICE(mrv_reg->mrsz_scale_hcr, MRV_MRSZ_SCALE_HCB, ++ (u32) scale->scale_hcr); ++ REG_SET_SLICE(mrv_reg->mrsz_phase_hc, MRV_MRSZ_PHASE_HC, ++ (u32) scale->phase_hc); ++ ++ if (scale->scale_hcb & RSZ_UPSCALE_ENABLE) { ++ /* enable upscaling mode */ ++ REG_SET_SLICE(mrsz_ctrl, MRV_MRSZ_SCALE_HC_UP, ++ MRV_MRSZ_SCALE_HC_UP_UPSCALE); ++ /* scaler and upscaling enabled */ ++ upscaling = true; ++ } else { ++ /* disable upscaling mode */ ++ REG_SET_SLICE(mrsz_ctrl, MRV_MRSZ_SCALE_HC_UP, ++ MRV_MRSZ_SCALE_HC_UP_DOWNSCALE); ++ } ++ } ++ ++ /* vertical luminance scale factor */ ++ dprintk(1, "scale_vy = %d ( %x )", scale->scale_vy, scale->scale_vy); ++ ++ if (scale->scale_vy & RSZ_SCALER_BYPASS) { ++ /* disable (bypass) scaler */ ++ REG_SET_SLICE(mrsz_ctrl, MRV_MRSZ_SCALE_VY_ENABLE, ++ DISABLE); ++ } else { ++ /* enable scaler */ ++ REG_SET_SLICE(mrsz_ctrl, MRV_MRSZ_SCALE_VY_ENABLE, ENABLE); ++ /* program scale factor and phase */ ++ REG_SET_SLICE(mrv_reg->mrsz_scale_vy, MRV_MRSZ_SCALE_VY, ++ (u32) scale->scale_vy); ++ REG_SET_SLICE(mrv_reg->mrsz_phase_vy, MRV_MRSZ_PHASE_VY, ++ (u32) scale->phase_vy); ++ ++ if (scale->scale_vy & RSZ_UPSCALE_ENABLE) { ++ /* enable upscaling mode */ ++ REG_SET_SLICE(mrsz_ctrl, MRV_MRSZ_SCALE_VY_UP, ++ MRV_MRSZ_SCALE_VY_UP_UPSCALE); ++ /* scaler and upscaling enabled */ ++ upscaling = true; ++ } else { ++ /* disable upscaling mode */ ++ REG_SET_SLICE(mrsz_ctrl, MRV_MRSZ_SCALE_VY_UP, ++ MRV_MRSZ_SCALE_VY_UP_DOWNSCALE); ++ } ++ } ++ ++ /* vertical chrominance scale factor */ ++ dprintk(1, "scale_vc = %d( %x )", scale->scale_vc, scale->scale_vc); ++ ++ if (scale->scale_vc & RSZ_SCALER_BYPASS) { ++ /* disable (bypass) scaler */ ++ REG_SET_SLICE(mrsz_ctrl, MRV_MRSZ_SCALE_VC_ENABLE, ++ DISABLE); ++ } else { ++ /* enable scaler */ ++ REG_SET_SLICE(mrsz_ctrl, MRV_MRSZ_SCALE_VC_ENABLE, ENABLE); ++ /* program scale factor and phase */ ++ REG_SET_SLICE(mrv_reg->mrsz_scale_vc, MRV_MRSZ_SCALE_VC, ++ (u32) scale->scale_vc); ++ REG_SET_SLICE(mrv_reg->mrsz_phase_vc, MRV_MRSZ_PHASE_VC, ++ (u32) scale->phase_vc); ++ ++ if (scale->scale_vc & RSZ_UPSCALE_ENABLE) { ++ /* enable upscaling mode */ ++ REG_SET_SLICE(mrsz_ctrl, MRV_MRSZ_SCALE_VC_UP, ++ MRV_MRSZ_SCALE_VC_UP_UPSCALE); ++ /* scaler and upscaling enabled */ ++ upscaling = true; ++ } else { ++ /* disable upscaling mode */ ++ REG_SET_SLICE(mrsz_ctrl, MRV_MRSZ_SCALE_VC_UP, ++ MRV_MRSZ_SCALE_VC_UP_DOWNSCALE); ++ } ++ } ++ ++ /* apply upscaling lookup table */ ++ if (rsz_lut) { ++ for (i = 0; i <= MRV_MRSZ_SCALE_LUT_ADDR_MASK; i++) { ++ REG_SET_SLICE(mrv_reg->mrsz_scale_lut_addr, ++ MRV_MRSZ_SCALE_LUT_ADDR, i); ++ REG_SET_SLICE(mrv_reg->mrsz_scale_lut, ++ MRV_MRSZ_SCALE_LUT, ++ rsz_lut->rsz_lut[i]); ++ } ++ } else if (upscaling) { ++ eprintk("Upscaling requires lookup table!"); ++ WARN_ON(1); ++ } ++ ++ /* handle immediate update flag and write mrsz_ctrl */ ++ switch (update_time) { ++ case CI_ISP_CFG_UPDATE_FRAME_SYNC: ++ /* frame synchronous update of shadow registers */ ++ REG_WRITE(mrv_reg->mrsz_ctrl, mrsz_ctrl); ++ REG_SET_SLICE(mrv_reg->isp_ctrl, MRV_ISP_ISP_GEN_CFG_UPD, ON); ++ break; ++ case CI_ISP_CFG_UPDATE_IMMEDIATE: ++ /* immediate update of shadow registers */ ++ REG_SET_SLICE(mrsz_ctrl, MRV_MRSZ_CFG_UPD, ON); ++ REG_WRITE(mrv_reg->mrsz_ctrl, mrsz_ctrl); ++ break; ++ case CI_ISP_CFG_UPDATE_LATER: ++ default: ++ /* no update from within this function */ ++ REG_WRITE(mrv_reg->mrsz_ctrl, mrsz_ctrl); ++ break; ++ } ++} ++ ++/* ++ * writes the scaler values to the appropriate Marvin registers. ++ */ ++void ci_isp_res_set_self_resize(const struct ci_isp_scale *scale, ++ enum ci_isp_conf_update_time update_time, ++ const struct ci_isp_rsz_lut *rsz_lut) ++{ ++ struct isp_register *mrv_reg = (struct isp_register *) MEM_MRV_REG_BASE; ++ u32 srsz_ctrl = REG_READ(mrv_reg->srsz_ctrl); ++ u32 i; ++ int upscaling = false; ++ ++ /* flags must be "outside" scaler value */ ++ WARN_ON(!((RSZ_FLAGS_MASK & MRV_RSZ_SCALE_MASK) == 0)); ++ WARN_ON(!((scale->scale_hy & ~RSZ_FLAGS_MASK) <= MRV_RSZ_SCALE_MAX)); ++ WARN_ON(!((scale->scale_hcb & ~RSZ_FLAGS_MASK) <= MRV_RSZ_SCALE_MAX)); ++ WARN_ON(!((scale->scale_hcr & ~RSZ_FLAGS_MASK) <= MRV_RSZ_SCALE_MAX)); ++ WARN_ON(!((scale->scale_vy & ~RSZ_FLAGS_MASK) <= MRV_RSZ_SCALE_MAX)); ++ WARN_ON(!((scale->scale_vc & ~RSZ_FLAGS_MASK) <= MRV_RSZ_SCALE_MAX)); ++ ++ /* horizontal luminance scale factor */ ++ dprintk(1, "scale_hy = %d,%x", scale->scale_hy, scale->scale_hy); ++ ++ if (scale->scale_hy & RSZ_SCALER_BYPASS) { ++ /* disable (bypass) scaler */ ++ REG_SET_SLICE(srsz_ctrl, MRV_SRSZ_SCALE_HY_ENABLE, ++ DISABLE); ++ } else { ++ /* enable scaler */ ++ REG_SET_SLICE(srsz_ctrl, MRV_SRSZ_SCALE_HY_ENABLE, ENABLE); ++ /* program scale factor and phase */ ++ REG_SET_SLICE(mrv_reg->srsz_scale_hy, MRV_SRSZ_SCALE_HY, ++ (u32) scale->scale_hy); ++ REG_SET_SLICE(mrv_reg->srsz_phase_hy, MRV_SRSZ_PHASE_HY, ++ (u32) scale->phase_hy); ++ ++ if (scale->scale_hy & RSZ_UPSCALE_ENABLE) { ++ /* enable upscaling mode */ ++ REG_SET_SLICE(srsz_ctrl, MRV_SRSZ_SCALE_HY_UP, ++ MRV_SRSZ_SCALE_HY_UP_UPSCALE); ++ /* scaler and upscaling enabled */ ++ upscaling = true; ++ } else { ++ /* disable upscaling mode */ ++ REG_SET_SLICE(srsz_ctrl, MRV_SRSZ_SCALE_HY_UP, ++ MRV_SRSZ_SCALE_HY_UP_DOWNSCALE); ++ } ++ } ++ ++ /* horizontal chrominance scale factors */ ++ WARN_ON(!((scale->scale_hcb & RSZ_FLAGS_MASK) == (scale->scale_hcr & ++ RSZ_FLAGS_MASK))); ++ ++ dprintk(1, "scale_hcb = %d,%x", scale->scale_hcb, scale->scale_hcb); ++ ++ if (scale->scale_hcb & RSZ_SCALER_BYPASS) { ++ /* disable (bypass) scaler */ ++ REG_SET_SLICE(srsz_ctrl, MRV_SRSZ_SCALE_HC_ENABLE, ++ DISABLE); ++ } else { ++ /* enable scaler */ ++ REG_SET_SLICE(srsz_ctrl, MRV_SRSZ_SCALE_HC_ENABLE, ENABLE); ++ /* program scale factor and phase */ ++ REG_SET_SLICE(mrv_reg->srsz_scale_hcb, MRV_SRSZ_SCALE_HCB, ++ (u32) scale->scale_hcb); ++ REG_SET_SLICE(mrv_reg->srsz_scale_hcr, MRV_SRSZ_SCALE_HCB, ++ (u32) scale->scale_hcr); ++ ++ REG_SET_SLICE(mrv_reg->srsz_phase_hc, MRV_SRSZ_PHASE_HC, ++ (u32) scale->phase_hc); ++ ++ if (scale->scale_hcb & RSZ_UPSCALE_ENABLE) { ++ REG_SET_SLICE(srsz_ctrl, MRV_SRSZ_SCALE_HC_UP, ++ MRV_SRSZ_SCALE_HC_UP_UPSCALE); ++ /* scaler and upscaling enabled */ ++ upscaling = true; ++ } else { ++ REG_SET_SLICE(srsz_ctrl, MRV_SRSZ_SCALE_HC_UP, ++ MRV_SRSZ_SCALE_HC_UP_DOWNSCALE); ++ } ++ } ++ ++ /* vertical luminance scale factor */ ++ dprintk(1, "scale_vy = %d,%x", scale->scale_vy, scale->scale_vy); ++ ++ if (scale->scale_vy & RSZ_SCALER_BYPASS) { ++ /* disable (bypass) scaler */ ++ REG_SET_SLICE(srsz_ctrl, MRV_SRSZ_SCALE_VY_ENABLE, ++ DISABLE); ++ } else { ++ /* enable scaler */ ++ REG_SET_SLICE(srsz_ctrl, MRV_SRSZ_SCALE_VY_ENABLE, ENABLE); ++ /* program scale factor and phase */ ++ REG_SET_SLICE(mrv_reg->srsz_scale_vy, MRV_SRSZ_SCALE_VY, ++ (u32) scale->scale_vy); ++ REG_SET_SLICE(mrv_reg->srsz_phase_vy, MRV_SRSZ_PHASE_VY, ++ (u32) scale->phase_vy); ++ ++ if (scale->scale_vy & RSZ_UPSCALE_ENABLE) { ++ /* enable upscaling mode */ ++ REG_SET_SLICE(srsz_ctrl, MRV_SRSZ_SCALE_VY_UP, ++ MRV_SRSZ_SCALE_VY_UP_UPSCALE); ++ /* scaler and upscaling enabled */ ++ upscaling = true; ++ } else { ++ /* disable upscaling mode */ ++ REG_SET_SLICE(srsz_ctrl, MRV_SRSZ_SCALE_VY_UP, ++ MRV_SRSZ_SCALE_VY_UP_DOWNSCALE); ++ } ++ } ++ ++ /* vertical chrominance scale factor */ ++ dprintk(1, "scale_vc = %d,%x", scale->scale_vc, scale->scale_vc); ++ ++ if (scale->scale_vc & RSZ_SCALER_BYPASS) { ++ /* disable (bypass) scaler */ ++ REG_SET_SLICE(srsz_ctrl, MRV_SRSZ_SCALE_VC_ENABLE, ++ DISABLE); ++ } else { ++ /* enable scaler */ ++ REG_SET_SLICE(srsz_ctrl, MRV_SRSZ_SCALE_VC_ENABLE, ENABLE); ++ /* program scale factor and phase */ ++ REG_SET_SLICE(mrv_reg->srsz_scale_vc, MRV_SRSZ_SCALE_VC, ++ (u32) scale->scale_vc); ++ REG_SET_SLICE(mrv_reg->srsz_phase_vc, MRV_SRSZ_PHASE_VC, ++ (u32) scale->phase_vc); ++ ++ if (scale->scale_vc & RSZ_UPSCALE_ENABLE) { ++ /* enable upscaling mode */ ++ REG_SET_SLICE(srsz_ctrl, MRV_SRSZ_SCALE_VC_UP, ++ MRV_SRSZ_SCALE_VC_UP_UPSCALE); ++ /* scaler and upscaling enabled */ ++ upscaling = true; ++ } else { ++ /* disable upscaling mode */ ++ REG_SET_SLICE(srsz_ctrl, MRV_SRSZ_SCALE_VC_UP, ++ MRV_SRSZ_SCALE_VC_UP_DOWNSCALE); ++ } ++ } ++ ++ /* apply upscaling lookup table */ ++ if (rsz_lut) { ++ for (i = 0; i <= MRV_SRSZ_SCALE_LUT_ADDR_MASK; i++) { ++ REG_SET_SLICE(mrv_reg->srsz_scale_lut_addr, ++ MRV_SRSZ_SCALE_LUT_ADDR, i); ++ REG_SET_SLICE(mrv_reg->srsz_scale_lut, ++ MRV_SRSZ_SCALE_LUT, ++ rsz_lut->rsz_lut[i]); ++ } ++ } else if (upscaling) { ++ eprintk("Upscaling requires lookup table!"); ++ WARN_ON(1); ++ } ++ ++ /* handle immediate update flag and write mrsz_ctrl */ ++ switch (update_time) { ++ case CI_ISP_CFG_UPDATE_FRAME_SYNC: ++ /* frame synchronous update of shadow registers */ ++ REG_WRITE(mrv_reg->srsz_ctrl, srsz_ctrl); ++ REG_SET_SLICE(mrv_reg->isp_ctrl, MRV_ISP_ISP_GEN_CFG_UPD, ++ ON); ++ break; ++ case CI_ISP_CFG_UPDATE_IMMEDIATE: ++ /* immediate update of shadow registers */ ++ REG_SET_SLICE(srsz_ctrl, MRV_SRSZ_CFG_UPD, ON); ++ REG_WRITE(mrv_reg->srsz_ctrl, srsz_ctrl); ++ break; ++ case CI_ISP_CFG_UPDATE_LATER: ++ default: ++ /* no update from within this function */ ++ REG_WRITE(mrv_reg->srsz_ctrl, srsz_ctrl); ++ break; ++ } ++} ++ ++#if MRV_SUPPORT_SL ++ ++/* bad pixel table */ ++static struct ci_sensor_bp_table bp_table = { 0 }; ++ ++/* ++ * Initialization of the Bad Pixel Detection and Correction. ++ */ ++int ci_bp_init(const struct ci_isp_bp_corr_config *bp_corr_config, ++ const struct ci_isp_bp_det_config *bp_det_config) ++{ ++ int error = CI_STATUS_SUCCESS; ++ ++ /* number of table elements */ ++ /* number of table elements */ ++#define MRVSLS_BPINIT_MAX_TABLE 2048 ++ ++ /* check the parameters */ ++ if (!bp_corr_config || !bp_det_config) ++ return CI_STATUS_NULL_POINTER; ++ ++ if (bp_corr_config->bp_corr_type == CI_ISP_BP_CORR_TABLE) { ++ /* set badpixel correction */ ++ error |= ci_isp_set_bp_correction(bp_corr_config); ++ /* set badpixel detection */ ++ error |= ci_isp_set_bp_detection(bp_det_config); ++ /* zero element inside */ ++ bp_table.bp_number = 0; ++ if (!bp_table.bp_table_elem) { ++ /* allocate mem space for the table */ ++ bp_table.bp_table_elem = ++ (struct ci_sensor_bp_table_elem *) ++ kmalloc((sizeof(struct ci_sensor_bp_table_elem)* ++ MRVSLS_BPINIT_MAX_TABLE), GFP_KERNEL); ++ if (!bp_table.bp_table_elem) ++ error |= CI_STATUS_FAILURE; ++ } ++ /* max count of elements */ ++ bp_table.bp_table_elem_num = MRVSLS_BPINIT_MAX_TABLE; ++ /* Clear Interrupt Status */ ++ error |= ci_isp_clear_bp_int(); ++ } else { ++ if (bp_corr_config->bp_corr_type == CI_ISP_BP_CORR_DIRECT) { ++ /* set badpixel correction */ ++ error |= ci_isp_set_bp_correction(bp_corr_config); ++ /* set badpixel detection */ ++ error |= ci_isp_set_bp_detection(NULL); ++ } else { ++ return CI_STATUS_NOTSUPP; ++ } ++ } ++ return error; ++} ++ ++/* ++ * Disable the Bad Pixel Detection and Correction. ++ */ ++int ci_bp_end(const struct ci_isp_bp_corr_config *bp_corr_config) ++{ ++ int uiResult = CI_STATUS_SUCCESS; ++ ++ /* check the parameter */ ++ if (!bp_corr_config) ++ return CI_STATUS_NULL_POINTER; ++ ++ /* disable badpixel correction */ ++ uiResult |= ci_isp_set_bp_correction(NULL); ++ ++ /* disable badpixel detection */ ++ uiResult |= ci_isp_set_bp_detection(NULL); ++ ++ if (bp_corr_config->bp_corr_type == CI_ISP_BP_CORR_TABLE) { ++ /* Clear Interrupt Status */ ++ uiResult |= ci_isp_clear_bp_int(); ++ ++ /* deallocate BP Table */ ++ kfree(bp_table.bp_table_elem); ++ bp_table.bp_table_elem = NULL; ++ } ++ return uiResult; ++} ++#endif +diff --git a/drivers/media/video/mrstci/mrstisp/mrstisp_isp.c b/drivers/media/video/mrstci/mrstisp/mrstisp_isp.c +new file mode 100644 +index 0000000..7c96bc4 +--- /dev/null ++++ b/drivers/media/video/mrstci/mrstisp/mrstisp_isp.c +@@ -0,0 +1,1993 @@ ++/* ++ * Support for Moorestown Langwell Camera Imaging ISP subsystem. ++ * ++ * Copyright (c) 2009 Intel Corporation. All Rights Reserved. ++ * ++ * Copyright (c) Silicon Image 2008 www.siliconimage.com ++ * ++ * 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 Street, Fifth Floor, Boston, MA ++ * 02110-1301, USA. ++ * ++ * ++ * Xiaolin Zhang ++ */ ++ ++#include "mrstisp_stdinc.h" ++ ++int mrst_isp_set_color_conversion_ex(void) ++{ ++ struct isp_register *mrv_reg = (struct isp_register *) MEM_MRV_REG_BASE; ++ ++ REG_SET_SLICE(mrv_reg->isp_cc_coeff_0, MRV_ISP_CC_COEFF_0, 0x00001021); ++ REG_SET_SLICE(mrv_reg->isp_cc_coeff_1, MRV_ISP_CC_COEFF_1, 0x00001040); ++ REG_SET_SLICE(mrv_reg->isp_cc_coeff_2, MRV_ISP_CC_COEFF_2, 0x0000100D); ++ REG_SET_SLICE(mrv_reg->isp_cc_coeff_3, MRV_ISP_CC_COEFF_3, 0x00000FED); ++ REG_SET_SLICE(mrv_reg->isp_cc_coeff_4, MRV_ISP_CC_COEFF_4, 0x00000FDB); ++ REG_SET_SLICE(mrv_reg->isp_cc_coeff_5, MRV_ISP_CC_COEFF_5, 0x00001038); ++ REG_SET_SLICE(mrv_reg->isp_cc_coeff_6, MRV_ISP_CC_COEFF_6, 0x00001038); ++ REG_SET_SLICE(mrv_reg->isp_cc_coeff_7, MRV_ISP_CC_COEFF_7, 0x00000FD1); ++ REG_SET_SLICE(mrv_reg->isp_cc_coeff_8, MRV_ISP_CC_COEFF_8, 0x00000FF7); ++ ++ return CI_STATUS_SUCCESS; ++} ++ ++/* ++ * Selects the ISP path that will become active while processing ++ * data coming from an image sensor configured by the given ISI ++ * configuration struct. ++ */ ++enum ci_isp_path ci_isp_select_path(const struct ci_sensor_config *isi_cfg, ++ u8 *words_per_pixel) ++{ ++ u8 words; ++ enum ci_isp_path ret_val; ++ ++ switch (isi_cfg->mode) { ++ case SENSOR_MODE_DATA: ++ ret_val = CI_ISP_PATH_RAW; ++ words = 1; ++ break; ++ case SENSOR_MODE_PICT: ++ ret_val = CI_ISP_PATH_RAW; ++ words = 1; ++ break; ++ case SENSOR_MODE_RGB565: ++ ret_val = CI_ISP_PATH_RAW; ++ words = 2; ++ break; ++ case SENSOR_MODE_BT601: ++ ret_val = CI_ISP_PATH_YCBCR; ++ words = 2; ++ break; ++ case SENSOR_MODE_BT656: ++ ret_val = CI_ISP_PATH_YCBCR; ++ words = 2; ++ break; ++ case SENSOR_MODE_BAYER: ++ ret_val = CI_ISP_PATH_BAYER; ++ words = 1; ++ break; ++ ++ case SENSOR_MODE_SMIA: ++ switch (isi_cfg->smia_mode) { ++ case SENSOR_SMIA_MODE_RAW_12: ++ case SENSOR_SMIA_MODE_RAW_10: ++ case SENSOR_SMIA_MODE_RAW_8: ++ case SENSOR_SMIA_MODE_RAW_8_TO_10_DECOMP: ++ ret_val = CI_ISP_PATH_BAYER; ++ words = 1; ++ break; ++ case SENSOR_SMIA_MODE_YUV_422: ++ ret_val = CI_ISP_PATH_YCBCR; ++ words = 2; ++ break; ++ case SENSOR_SMIA_MODE_YUV_420: ++ case SENSOR_SMIA_MODE_RGB_444: ++ case SENSOR_SMIA_MODE_RGB_565: ++ case SENSOR_SMIA_MODE_RGB_888: ++ case SENSOR_SMIA_MODE_COMPRESSED: ++ case SENSOR_SMIA_MODE_RAW_7: ++ case SENSOR_SMIA_MODE_RAW_6: ++ default: ++ ret_val = CI_ISP_PATH_RAW; ++ words = 1; ++ break; ++ } ++ break; ++ ++ case SENSOR_MODE_MIPI: ++ switch (isi_cfg->mipi_mode) { ++ case SENSOR_MIPI_MODE_RAW_12: ++ case SENSOR_MIPI_MODE_RAW_10: ++ case SENSOR_MIPI_MODE_RAW_8: ++ ret_val = CI_ISP_PATH_BAYER; ++ words = 1; ++ break; ++ case SENSOR_MIPI_MODE_YUV422_8: ++ case SENSOR_MIPI_MODE_YUV422_10: ++ ret_val = CI_ISP_PATH_YCBCR; ++ words = 2; ++ break; ++ case SENSOR_MIPI_MODE_YUV420_8: ++ case SENSOR_MIPI_MODE_YUV420_10: ++ case SENSOR_MIPI_MODE_LEGACY_YUV420_8: ++ case SENSOR_MIPI_MODE_YUV420_CSPS_8: ++ case SENSOR_MIPI_MODE_YUV420_CSPS_10: ++ case SENSOR_MIPI_MODE_RGB444: ++ case SENSOR_MIPI_MODE_RGB555: ++ case SENSOR_MIPI_MODE_RGB565: ++ case SENSOR_MIPI_MODE_RGB666: ++ case SENSOR_MIPI_MODE_RGB888: ++ case SENSOR_MIPI_MODE_RAW_7: ++ case SENSOR_MIPI_MODE_RAW_6: ++ default: ++ ret_val = CI_ISP_PATH_RAW; ++ words = 1; ++ break; ++ } ++ break; ++ case SENSOR_MODE_BAY_BT656: ++ ret_val = CI_ISP_PATH_BAYER; ++ words = 1; ++ break; ++ case SENSOR_MODE_RAW_BT656: ++ ret_val = CI_ISP_PATH_RAW; ++ words = 1; ++ break; ++ default: ++ ret_val = CI_ISP_PATH_UNKNOWN; ++ words = 1; ++ } ++ ++ if (words_per_pixel) ++ *words_per_pixel = words ; ++ return ret_val; ++} ++ ++/* ++ * configures the input acquisition according to the ++ * given config structure ++ */ ++int ci_isp_set_input_aquisition(const struct ci_sensor_config *isi_cfg) ++{ ++ struct isp_register *mrv_reg = (struct isp_register *) MEM_MRV_REG_BASE; ++ u32 isp_ctrl = REG_READ(mrv_reg->isp_ctrl); ++ u32 isp_acq_prop = REG_READ(mrv_reg->isp_acq_prop); ++ /* factor between pixel count and amount of bytes to sample */ ++ u8 sample_factor; ++ /* number of additional black lines at frame start */ ++ u8 black_lines; ++ ++ if (ci_isp_select_path(isi_cfg, &sample_factor) ++ == CI_ISP_PATH_UNKNOWN) { ++ eprintk("failed to select path"); ++ return CI_STATUS_NOTSUPP; ++ } ++ ++ switch (isi_cfg->mode) { ++ case SENSOR_MODE_DATA: ++ REG_SET_SLICE(isp_ctrl, MRV_ISP_ISP_MODE, ++ MRV_ISP_ISP_MODE_DATA); ++ break; ++ case SENSOR_MODE_PICT: ++ REG_SET_SLICE(isp_ctrl, MRV_ISP_ISP_MODE, ++ MRV_ISP_ISP_MODE_RAW); ++ break; ++ case SENSOR_MODE_RGB565: ++ REG_SET_SLICE(isp_ctrl, MRV_ISP_ISP_MODE, ++ MRV_ISP_ISP_MODE_RAW); ++ break; ++ case SENSOR_MODE_BT601: ++ REG_SET_SLICE(isp_ctrl, MRV_ISP_ISP_MODE, ++ MRV_ISP_ISP_MODE_601); ++ break; ++ case SENSOR_MODE_BT656: ++ REG_SET_SLICE(isp_ctrl, MRV_ISP_ISP_MODE, ++ MRV_ISP_ISP_MODE_656); ++ break; ++ case SENSOR_MODE_BAYER: ++ REG_SET_SLICE(isp_ctrl, MRV_ISP_ISP_MODE, ++ MRV_ISP_ISP_MODE_RGB); ++ break; ++ case SENSOR_MODE_BAY_BT656: ++ REG_SET_SLICE(isp_ctrl, MRV_ISP_ISP_MODE, ++ MRV_ISP_ISP_MODE_RGB656); ++ break; ++ case SENSOR_MODE_RAW_BT656: ++ REG_SET_SLICE(isp_ctrl, MRV_ISP_ISP_MODE, ++ MRV_ISP_ISP_MODE_RAW656); ++ break; ++ ++ case SENSOR_MODE_SMIA: ++ switch (isi_cfg->smia_mode) { ++ case SENSOR_SMIA_MODE_RAW_12: ++ case SENSOR_SMIA_MODE_RAW_10: ++ case SENSOR_SMIA_MODE_RAW_8: ++ case SENSOR_SMIA_MODE_RAW_8_TO_10_DECOMP: ++ case SENSOR_SMIA_MODE_RAW_7: ++ case SENSOR_SMIA_MODE_RAW_6: ++ case SENSOR_SMIA_MODE_YUV_422: ++ case SENSOR_SMIA_MODE_YUV_420: ++ case SENSOR_SMIA_MODE_RGB_888: ++ case SENSOR_SMIA_MODE_RGB_565: ++ case SENSOR_SMIA_MODE_RGB_444: ++ case SENSOR_SMIA_MODE_COMPRESSED: ++ return CI_STATUS_SUCCESS; ++ break; ++ default: ++ return CI_STATUS_NOTSUPP; ++ } ++ break; ++ ++ case SENSOR_MODE_MIPI: ++ REG_SET_SLICE(isp_ctrl, MRV_ISP_ISP_MODE, ++ MRV_ISP_ISP_MODE_RGB); ++ REG_WRITE(mrv_reg->mipi_img_data_sel, 0x02b); ++ break; ++ ++ default: ++ return CI_STATUS_NOTSUPP; ++ } ++ ++ switch (isi_cfg->bus_width) { ++ case SENSOR_BUSWIDTH_12BIT: ++ /* 000- 12Bit external Interface */ ++ REG_SET_SLICE(isp_acq_prop, MRV_ISP_INPUT_SELECTION, ++ MRV_ISP_INPUT_SELECTION_12EXT); ++ break; ++ case SENSOR_BUSWIDTH_10BIT_ZZ: ++ /* 001- 10Bit Interface, append 2 zeroes as LSBs */ ++ REG_SET_SLICE(isp_acq_prop, MRV_ISP_INPUT_SELECTION, ++ MRV_ISP_INPUT_SELECTION_10ZERO); ++ break; ++ case SENSOR_BUSWIDTH_10BIT_EX: ++ /* 010- 10Bit Interface, append 2 MSBs as LSBs */ ++ REG_SET_SLICE(isp_acq_prop, MRV_ISP_INPUT_SELECTION, ++ MRV_ISP_INPUT_SELECTION_10MSB); ++ break; ++ case SENSOR_BUSWIDTH_8BIT_ZZ: ++ /* 011- 8Bit Interface, append 4 zeroes as LSBs */ ++ REG_SET_SLICE(isp_acq_prop, MRV_ISP_INPUT_SELECTION, ++ MRV_ISP_INPUT_SELECTION_8ZERO); ++ break; ++ case SENSOR_BUSWIDTH_8BIT_EX: ++ /* 100- 8Bit Interface, append 4 MSBs as LSBs */ ++ REG_SET_SLICE(isp_acq_prop, MRV_ISP_INPUT_SELECTION, ++ MRV_ISP_INPUT_SELECTION_8MSB); ++ break; ++ /* 101...111 reserved */ ++ default: ++ return CI_STATUS_NOTSUPP; ++ } ++ ++ switch (isi_cfg->field_sel) { ++ case SENSOR_FIELDSEL_ODD: ++ REG_SET_SLICE(isp_acq_prop, MRV_ISP_FIELD_SELECTION, ++ MRV_ISP_FIELD_SELECTION_ODD); ++ break; ++ case SENSOR_FIELDSEL_EVEN: ++ REG_SET_SLICE(isp_acq_prop, MRV_ISP_FIELD_SELECTION, ++ MRV_ISP_FIELD_SELECTION_EVEN); ++ break; ++ case SENSOR_FIELDSEL_BOTH: ++ REG_SET_SLICE(isp_acq_prop, MRV_ISP_FIELD_SELECTION, ++ MRV_ISP_FIELD_SELECTION_BOTH); ++ break; ++ default: ++ return CI_STATUS_NOTSUPP; ++ } ++ ++ switch (isi_cfg->ycseq) { ++ case SENSOR_YCSEQ_CRYCBY: ++ REG_SET_SLICE(isp_acq_prop, MRV_ISP_CCIR_SEQ, ++ MRV_ISP_CCIR_SEQ_CRYCBY); ++ break; ++ case SENSOR_YCSEQ_CBYCRY: ++ REG_SET_SLICE(isp_acq_prop, MRV_ISP_CCIR_SEQ, ++ MRV_ISP_CCIR_SEQ_CBYCRY); ++ break; ++ case SENSOR_YCSEQ_YCRYCB: ++ REG_SET_SLICE(isp_acq_prop, MRV_ISP_CCIR_SEQ, ++ MRV_ISP_CCIR_SEQ_YCRYCB); ++ break; ++ case SENSOR_YCSEQ_YCBYCR: ++ REG_SET_SLICE(isp_acq_prop, MRV_ISP_CCIR_SEQ, ++ MRV_ISP_CCIR_SEQ_YCBYCR); ++ break; ++ default: ++ return CI_STATUS_NOTSUPP; ++ } ++ ++ switch (isi_cfg->conv422) { ++ case SENSOR_CONV422_INTER: ++ REG_SET_SLICE(isp_acq_prop, MRV_ISP_CONV_422, ++ MRV_ISP_CONV_422_INTER); ++ break; ++ ++ case SENSOR_CONV422_NOCOSITED: ++ REG_SET_SLICE(isp_acq_prop, MRV_ISP_CONV_422, ++ MRV_ISP_CONV_422_NONCO); ++ break; ++ case SENSOR_CONV422_COSITED: ++ REG_SET_SLICE(isp_acq_prop, MRV_ISP_CONV_422, ++ MRV_ISP_CONV_422_CO); ++ break; ++ default: ++ return CI_STATUS_NOTSUPP; ++ } ++ ++ switch (isi_cfg->bpat) { ++ case SENSOR_BPAT_BGBGGRGR: ++ REG_SET_SLICE(isp_acq_prop, MRV_ISP_BAYER_PAT, ++ MRV_ISP_BAYER_PAT_BG); ++ break; ++ case SENSOR_BPAT_GBGBRGRG: ++ REG_SET_SLICE(isp_acq_prop, MRV_ISP_BAYER_PAT, ++ MRV_ISP_BAYER_PAT_GB); ++ break; ++ case SENSOR_BPAT_GRGRBGBG: ++ REG_SET_SLICE(isp_acq_prop, MRV_ISP_BAYER_PAT, ++ MRV_ISP_BAYER_PAT_GR); ++ break; ++ case SENSOR_BPAT_RGRGGBGB: ++ REG_SET_SLICE(isp_acq_prop, MRV_ISP_BAYER_PAT, ++ MRV_ISP_BAYER_PAT_RG); ++ break; ++ default: ++ return CI_STATUS_NOTSUPP; ++ } ++ ++ switch (isi_cfg->vpol) { ++ case SENSOR_VPOL_POS: ++ REG_SET_SLICE(isp_acq_prop, MRV_ISP_VSYNC_POL, 1); ++ break; ++ case SENSOR_VPOL_NEG: ++ REG_SET_SLICE(isp_acq_prop, MRV_ISP_VSYNC_POL, 0); ++ break; ++ default: ++ return CI_STATUS_NOTSUPP; ++ } ++ ++ switch (isi_cfg->hpol) { ++ /* The trigger edge differs for vsync_pol and hsync_pol. */ ++ /* vsync_pol = 1 triggers on positive edge whereas */ ++ /* hsync_pol = 1 triggers on negative edge and vice versa */ ++ case SENSOR_HPOL_SYNCPOS: ++ /* trigger on negative edge */ ++ REG_SET_SLICE(isp_acq_prop, MRV_ISP_HSYNC_POL, 1); ++ break; ++ case SENSOR_HPOL_SYNCNEG: ++ /* trigger on positive edge */ ++ REG_SET_SLICE(isp_acq_prop, MRV_ISP_HSYNC_POL, 0); ++ break; ++ case SENSOR_HPOL_REFPOS: ++ /* trigger on positive edge */ ++ REG_SET_SLICE(isp_acq_prop, MRV_ISP_HSYNC_POL, 0); ++ break; ++ case SENSOR_HPOL_REFNEG: ++ /* trigger on negative edge */ ++ REG_SET_SLICE(isp_acq_prop, MRV_ISP_HSYNC_POL, 1); ++ break; ++ default: ++ return CI_STATUS_NOTSUPP; ++ } ++ ++ switch (isi_cfg->edge) { ++ case SENSOR_EDGE_RISING: ++ REG_SET_SLICE(isp_acq_prop, MRV_ISP_SAMPLE_EDGE, 1); ++ break; ++ case SENSOR_EDGE_FALLING: ++ REG_SET_SLICE(isp_acq_prop, MRV_ISP_SAMPLE_EDGE, 0); ++ break; ++ default: ++ return CI_STATUS_NOTSUPP; ++ } ++ dprintk(2, "isp_acq_prop = 0x%x", isp_acq_prop); ++ ++ /* now write values to registers */ ++ REG_WRITE(mrv_reg->isp_ctrl, isp_ctrl); ++ REG_WRITE(mrv_reg->isp_acq_prop, isp_acq_prop); ++ ++ /* number of additional black lines at frame start */ ++ switch (isi_cfg->bls) { ++ case SENSOR_BLS_OFF: ++ black_lines = 0; ++ break; ++ case SENSOR_BLS_TWO_LINES: ++ black_lines = 2; ++ break; ++ case SENSOR_BLS_FOUR_LINES: ++ black_lines = 4; ++ break; ++ default: ++ return CI_STATUS_NOTSUPP; ++ } ++ ++ REG_SET_SLICE(mrv_reg->isp_acq_h_offs, MRV_ISP_ACQ_H_OFFS, ++ 0 * sample_factor); ++ REG_SET_SLICE(mrv_reg->isp_acq_v_offs, MRV_ISP_ACQ_V_OFFS, 0); ++ ++ dprintk(2, "res = %x", isi_cfg->res); ++ switch (isi_cfg->res) { ++ /* 88x72 */ ++ case SENSOR_RES_QQCIF: ++ REG_SET_SLICE(mrv_reg->isp_acq_h_size, MRV_ISP_ACQ_H_SIZE, ++ QQCIF_SIZE_H * sample_factor); ++ REG_SET_SLICE(mrv_reg->isp_acq_v_size, MRV_ISP_ACQ_V_SIZE, ++ QQCIF_SIZE_V + black_lines); ++ break; ++ /* 160x120 */ ++ case SENSOR_RES_QQVGA: ++ REG_SET_SLICE(mrv_reg->isp_acq_h_size, MRV_ISP_ACQ_H_SIZE, ++ QQVGA_SIZE_H * sample_factor); ++ REG_SET_SLICE(mrv_reg->isp_acq_v_size, MRV_ISP_ACQ_V_SIZE, ++ QQVGA_SIZE_V + black_lines); ++ break; ++ /* 176x144 */ ++ case SENSOR_RES_QCIF: ++ REG_SET_SLICE(mrv_reg->isp_acq_h_size, MRV_ISP_ACQ_H_SIZE, ++ QCIF_SIZE_H * sample_factor); ++ REG_SET_SLICE(mrv_reg->isp_acq_v_size, MRV_ISP_ACQ_V_SIZE, ++ QCIF_SIZE_V + black_lines); ++ break; ++ /* 320x240 */ ++ case SENSOR_RES_QVGA: ++ REG_SET_SLICE(mrv_reg->isp_acq_h_size, MRV_ISP_ACQ_H_SIZE, ++ QVGA_SIZE_H * sample_factor); ++ REG_SET_SLICE(mrv_reg->isp_acq_v_size, MRV_ISP_ACQ_V_SIZE, ++ QVGA_SIZE_V + black_lines); ++ break; ++ /* 352x288 */ ++ case SENSOR_RES_CIF: ++ REG_SET_SLICE(mrv_reg->isp_acq_h_size, MRV_ISP_ACQ_H_SIZE, ++ CIF_SIZE_H * sample_factor); ++ REG_SET_SLICE(mrv_reg->isp_acq_v_size, MRV_ISP_ACQ_V_SIZE, ++ CIF_SIZE_V + black_lines); ++ break; ++ /* 640x480 */ ++ case SENSOR_RES_VGA: ++ REG_SET_SLICE(mrv_reg->isp_acq_h_size, MRV_ISP_ACQ_H_SIZE, ++ VGA_SIZE_H * sample_factor); ++ REG_SET_SLICE(mrv_reg->isp_acq_v_size, MRV_ISP_ACQ_V_SIZE, ++ VGA_SIZE_V + black_lines); ++ break; ++ /* 800x600 */ ++ case SENSOR_RES_SVGA: ++ REG_SET_SLICE(mrv_reg->isp_acq_h_size, MRV_ISP_ACQ_H_SIZE, ++ SVGA_SIZE_H * sample_factor); ++ REG_SET_SLICE(mrv_reg->isp_acq_v_size, MRV_ISP_ACQ_V_SIZE, ++ SVGA_SIZE_V + black_lines); ++ break; ++ /* 1024x768 */ ++ case SENSOR_RES_XGA: ++ REG_SET_SLICE(mrv_reg->isp_acq_h_size, MRV_ISP_ACQ_H_SIZE, ++ XGA_SIZE_H * sample_factor); ++ REG_SET_SLICE(mrv_reg->isp_acq_v_size, MRV_ISP_ACQ_V_SIZE, ++ XGA_SIZE_V + black_lines); ++ break; ++ case SENSOR_RES_720P: ++ REG_SET_SLICE(mrv_reg->isp_acq_h_size, MRV_ISP_ACQ_H_SIZE, ++ RES_720P_SIZE_H * sample_factor); ++ REG_SET_SLICE(mrv_reg->isp_acq_v_size, MRV_ISP_ACQ_V_SIZE, ++ RES_720P_SIZE_V + black_lines); ++ break; ++ /* 1280x960 */ ++ case SENSOR_RES_XGA_PLUS: ++ REG_SET_SLICE(mrv_reg->isp_acq_h_size, MRV_ISP_ACQ_H_SIZE, ++ XGA_PLUS_SIZE_H * sample_factor); ++ REG_SET_SLICE(mrv_reg->isp_acq_v_size, MRV_ISP_ACQ_V_SIZE, ++ XGA_PLUS_SIZE_V + black_lines); ++ break; ++ /* 1280x1024 */ ++ case SENSOR_RES_SXGA: ++ REG_SET_SLICE(mrv_reg->isp_acq_h_size, MRV_ISP_ACQ_H_SIZE, ++ SXGA_SIZE_H * sample_factor); ++ REG_SET_SLICE(mrv_reg->isp_acq_v_size, MRV_ISP_ACQ_V_SIZE, ++ SXGA_SIZE_V + black_lines); ++ break; ++ /* 1600x1200 */ ++ case SENSOR_RES_UXGA: ++ REG_SET_SLICE(mrv_reg->isp_acq_h_size, MRV_ISP_ACQ_H_SIZE, ++ QSVGA_SIZE_H * sample_factor); ++ REG_SET_SLICE(mrv_reg->isp_acq_v_size, MRV_ISP_ACQ_V_SIZE, ++ QSVGA_SIZE_V + black_lines); ++ break; ++ /* 1920x1280 */ ++ case SENSOR_RES_1080P: ++ REG_SET_SLICE(mrv_reg->isp_acq_h_size, MRV_ISP_ACQ_H_SIZE, ++ 1920 * sample_factor); ++ REG_SET_SLICE(mrv_reg->isp_acq_v_size, MRV_ISP_ACQ_V_SIZE, ++ 1080 + black_lines); ++ break; ++ /* 2048x1536 */ ++ case SENSOR_RES_QXGA: ++ REG_SET_SLICE(mrv_reg->isp_acq_h_size, MRV_ISP_ACQ_H_SIZE, ++ QXGA_SIZE_H * sample_factor); ++ REG_SET_SLICE(mrv_reg->isp_acq_v_size, MRV_ISP_ACQ_V_SIZE, ++ QXGA_SIZE_V + black_lines); ++ break; ++ /* 2586x2048 */ ++ case SENSOR_RES_QSXGA: ++ REG_SET_SLICE(mrv_reg->isp_acq_h_size, MRV_ISP_ACQ_H_SIZE, ++ QSXGA_SIZE_H * sample_factor); ++ REG_SET_SLICE(mrv_reg->isp_acq_v_size, MRV_ISP_ACQ_V_SIZE, ++ QSXGA_SIZE_V + black_lines); ++ break; ++ /* 2600x2048 */ ++ case SENSOR_RES_QSXGA_PLUS: ++ REG_SET_SLICE(mrv_reg->isp_acq_h_size, MRV_ISP_ACQ_H_SIZE, ++ QSXGA_PLUS_SIZE_H * sample_factor); ++ REG_SET_SLICE(mrv_reg->isp_acq_v_size, MRV_ISP_ACQ_V_SIZE, ++ QSXGA_PLUS_SIZE_V + black_lines); ++ break; ++ /* 2600x1950 */ ++ case SENSOR_RES_QSXGA_PLUS2: ++ REG_SET_SLICE(mrv_reg->isp_acq_h_size, MRV_ISP_ACQ_H_SIZE, ++ QSXGA_PLUS2_SIZE_H * sample_factor); ++ REG_SET_SLICE(mrv_reg->isp_acq_v_size, MRV_ISP_ACQ_V_SIZE, ++ QSXGA_PLUS2_SIZE_V + black_lines); ++ break; ++ /* 2686x2048, 5.30M */ ++ case SENSOR_RES_QSXGA_PLUS3: ++ REG_SET_SLICE(mrv_reg->isp_acq_h_size, MRV_ISP_ACQ_H_SIZE, ++ QSXGA_PLUS3_SIZE_V * sample_factor); ++ REG_SET_SLICE(mrv_reg->isp_acq_v_size, MRV_ISP_ACQ_V_SIZE, ++ QSXGA_PLUS3_SIZE_V + black_lines); ++ break; ++ /* 2592*1944 5M */ ++ case SENSOR_RES_QXGA_PLUS: ++ REG_SET_SLICE(mrv_reg->isp_acq_h_size, MRV_ISP_ACQ_H_SIZE, ++ QXGA_PLUS_SIZE_H * sample_factor); ++ REG_SET_SLICE(mrv_reg->isp_acq_v_size, MRV_ISP_ACQ_V_SIZE, ++ QXGA_PLUS_SIZE_V + black_lines); ++ break; ++ /* 3200x2048, 6.56M */ ++ case SENSOR_RES_WQSXGA: ++ REG_SET_SLICE(mrv_reg->isp_acq_h_size, MRV_ISP_ACQ_H_SIZE, ++ WQSXGA_SIZE_H * sample_factor); ++ REG_SET_SLICE(mrv_reg->isp_acq_v_size, MRV_ISP_ACQ_V_SIZE, ++ WQSXGA_SIZE_V + black_lines); ++ break; ++ /* 3200x2400, 7.68M */ ++ case SENSOR_RES_QUXGA: ++ REG_SET_SLICE(mrv_reg->isp_acq_h_size, MRV_ISP_ACQ_H_SIZE, ++ QUXGA_SIZE_H * sample_factor); ++ REG_SET_SLICE(mrv_reg->isp_acq_v_size, MRV_ISP_ACQ_V_SIZE, ++ QUXGA_SIZE_V + black_lines); ++ break; ++ /* 3840x2400, 9.22M */ ++ case SENSOR_RES_WQUXGA: ++ REG_SET_SLICE(mrv_reg->isp_acq_h_size, MRV_ISP_ACQ_H_SIZE, ++ WQUXGA_SIZE_H * sample_factor); ++ REG_SET_SLICE(mrv_reg->isp_acq_v_size, MRV_ISP_ACQ_V_SIZE, ++ WQUXGA_SIZE_V + black_lines); ++ break; ++ /* 4096x3072, 12.59M */ ++ case SENSOR_RES_HXGA: ++ REG_SET_SLICE(mrv_reg->isp_acq_h_size, MRV_ISP_ACQ_H_SIZE, ++ HXGA_SIZE_H * sample_factor); ++ REG_SET_SLICE(mrv_reg->isp_acq_v_size, MRV_ISP_ACQ_V_SIZE, ++ HXGA_SIZE_V + black_lines); ++ break; ++ /* 4080x1024 */ ++ case SENSOR_RES_YUV_HMAX: ++ REG_SET_SLICE(mrv_reg->isp_acq_h_size, MRV_ISP_ACQ_H_SIZE, ++ YUV_HMAX_SIZE_H * sample_factor); ++ REG_SET_SLICE(mrv_reg->isp_acq_v_size, MRV_ISP_ACQ_V_SIZE, ++ YUV_HMAX_SIZE_V); ++ break; ++ /* 1024x4080 */ ++ case SENSOR_RES_YUV_VMAX: ++ REG_SET_SLICE(mrv_reg->isp_acq_h_size, MRV_ISP_ACQ_H_SIZE, ++ YUV_VMAX_SIZE_H * sample_factor); ++ REG_SET_SLICE(mrv_reg->isp_acq_v_size, MRV_ISP_ACQ_V_SIZE, ++ YUV_VMAX_SIZE_V); ++ break; ++ /* 4096x2048 */ ++ case SENSOR_RES_RAWMAX: ++ REG_SET_SLICE(mrv_reg->isp_acq_h_size, MRV_ISP_ACQ_H_SIZE, ++ RAWMAX_SIZE_H); ++ REG_SET_SLICE(mrv_reg->isp_acq_v_size, MRV_ISP_ACQ_V_SIZE, ++ RAWMAX_SIZE_V); ++ break; ++ /* 352x240 */ ++ case SENSOR_RES_BP1: ++ REG_SET_SLICE(mrv_reg->isp_acq_h_size, MRV_ISP_ACQ_H_SIZE, ++ BP1_SIZE_H * sample_factor); ++ REG_SET_SLICE(mrv_reg->isp_acq_v_size, MRV_ISP_ACQ_V_SIZE, ++ BP1_SIZE_V); ++ break; ++ /* 720x480 */ ++ case SENSOR_RES_L_AFM: ++ REG_SET_SLICE(mrv_reg->isp_acq_h_size, MRV_ISP_ACQ_H_SIZE, ++ L_AFM_SIZE_H * sample_factor); ++ REG_SET_SLICE(mrv_reg->isp_acq_v_size, MRV_ISP_ACQ_V_SIZE, ++ L_AFM_SIZE_V); ++ break; ++ /* 128x96 */ ++ case SENSOR_RES_M_AFM: ++ REG_SET_SLICE(mrv_reg->isp_acq_h_size, MRV_ISP_ACQ_H_SIZE, ++ M_AFM_SIZE_H * sample_factor); ++ REG_SET_SLICE(mrv_reg->isp_acq_v_size, MRV_ISP_ACQ_V_SIZE, ++ M_AFM_SIZE_V); ++ break; ++ /* 64x32 */ ++ case SENSOR_RES_S_AFM: ++ REG_SET_SLICE(mrv_reg->isp_acq_h_size, MRV_ISP_ACQ_H_SIZE, ++ S_AFM_SIZE_H * sample_factor); ++ REG_SET_SLICE(mrv_reg->isp_acq_v_size, MRV_ISP_ACQ_V_SIZE, ++ S_AFM_SIZE_V); ++ break; ++ /* 1304x980 */ ++ case SENSOR_RES_VGA_PLUS: ++ REG_SET_SLICE(mrv_reg->isp_acq_h_size, MRV_ISP_ACQ_H_SIZE, ++ VGA_PLUS_SIZE_H * sample_factor); ++ REG_SET_SLICE(mrv_reg->isp_acq_v_size, MRV_ISP_ACQ_V_SIZE, ++ VGA_PLUS_SIZE_V); ++ break; ++ ++ default: ++ return CI_STATUS_NOTSUPP; ++ } ++ ++ return CI_STATUS_SUCCESS; ++} ++ ++/* ++ * sets output window ++ */ ++void ci_isp_set_output_formatter(const struct ci_isp_window *window, ++ enum ci_isp_conf_update_time update_time) ++{ ++ struct isp_register *mrv_reg = (struct isp_register *) MEM_MRV_REG_BASE; ++ ++ if (window) { ++ /* set output window */ ++ REG_SET_SLICE(mrv_reg->isp_out_h_offs, MRV_IS_IS_H_OFFS, ++ window->hoffs); ++ REG_SET_SLICE(mrv_reg->isp_out_v_offs, MRV_IS_IS_V_OFFS, ++ window->voffs); ++ REG_SET_SLICE(mrv_reg->isp_out_h_size, MRV_IS_IS_H_SIZE, ++ window->hsize); ++ REG_SET_SLICE(mrv_reg->isp_out_v_size, MRV_IS_IS_V_SIZE, ++ window->vsize); ++ ++ REG_SET_SLICE(mrv_reg->isp_is_h_offs, MRV_IS_IS_H_OFFS, 0); ++ REG_SET_SLICE(mrv_reg->isp_is_v_offs, MRV_IS_IS_V_OFFS, 0); ++ REG_SET_SLICE(mrv_reg->isp_is_h_size, MRV_IS_IS_H_SIZE, ++ window->hsize); ++ REG_SET_SLICE(mrv_reg->isp_is_v_size, MRV_IS_IS_V_SIZE, ++ window->vsize); ++ ++ switch (update_time) { ++ case CI_ISP_CFG_UPDATE_FRAME_SYNC: ++ /* frame synchronous update of shadow registers */ ++ REG_SET_SLICE(mrv_reg->isp_ctrl, ++ MRV_ISP_ISP_GEN_CFG_UPD, ON); ++ break; ++ case CI_ISP_CFG_UPDATE_IMMEDIATE: ++ /* immediate update of shadow registers */ ++ REG_SET_SLICE(mrv_reg->isp_ctrl, ++ MRV_ISP_ISP_CFG_UPD, ON); ++ break; ++ case CI_ISP_CFG_UPDATE_LATER: ++ /* no update from within this function */ ++ break; ++ default: ++ break; ++ } ++ } ++} ++ ++/* ++ * programs the given Bayer pattern demosaic parameters ++ */ ++void ci_isp_set_demosaic(enum ci_isp_demosaic_mode demosaic_mode, ++ u8 demosaic_th) ++{ ++ struct isp_register *mrv_reg = (struct isp_register *) MEM_MRV_REG_BASE; ++ u32 isp_demosaic = REG_READ(mrv_reg->isp_demosaic); ++ ++ /* set demosaic mode */ ++ switch (demosaic_mode) { ++ case CI_ISP_DEMOSAIC_STANDARD: ++ REG_SET_SLICE(isp_demosaic, MRV_ISP_DEMOSAIC_MODE, ++ MRV_ISP_DEMOSAIC_MODE_STD); ++ break; ++ case CI_ISP_DEMOSAIC_ENHANCED: ++ REG_SET_SLICE(isp_demosaic, MRV_ISP_DEMOSAIC_MODE, ++ MRV_ISP_DEMOSAIC_MODE_ENH); ++ break; ++ default: ++ WARN_ON(!(false)); ++ } ++ ++ /* set demosaic threshold */ ++ REG_SET_SLICE(isp_demosaic, MRV_ISP_DEMOSAIC_TH, demosaic_th); ++ REG_WRITE(mrv_reg->isp_demosaic, isp_demosaic); ++} ++ ++/* ++ * Sets the dedicated AWB block mode. ++ */ ++int ci_isp_set_wb_mode(enum ci_isp_awb_mode wb_mode) ++{ ++ struct isp_register *mrv_reg = (struct isp_register *) MEM_MRV_REG_BASE; ++ ++ switch (wb_mode) { ++ case CI_ISP_AWB_COMPLETELY_OFF: ++ /* manual WB, no measurements*/ ++ REG_SET_SLICE(mrv_reg->isp_awb_prop, MRV_ISP_AWB_MODE, ++ MRV_ISP_AWB_MODE_NOMEAS); ++ /* switch ABW block off */ ++ REG_SET_SLICE(mrv_reg->isp_ctrl, MRV_ISP_ISP_AWB_ENABLE, ++ DISABLE); ++ break; ++ case CI_ISP_AWB_MAN_MEAS: ++ case CI_ISP_AWB_AUTO: ++ case CI_ISP_AWB_MAN_PUSH_AUTO: ++ case CI_ISP_AWB_ONLY_MEAS: ++ /* manual white balance, measure YCbCr means */ ++ REG_SET_SLICE(mrv_reg->isp_awb_prop, MRV_ISP_AWB_MODE, ++ MRV_ISP_AWB_MODE_MEAS); ++ /* switch ABW block on */ ++ REG_SET_SLICE(mrv_reg->isp_ctrl, MRV_ISP_ISP_AWB_ENABLE, ++ ENABLE); ++ break; ++ case CI_ISP_AWB_MAN_NOMEAS: ++ /* manual white balance, no measurements */ ++ REG_SET_SLICE(mrv_reg->isp_awb_prop, MRV_ISP_AWB_MODE, ++ MRV_ISP_AWB_MODE_NOMEAS); ++ /* switch ABW block on */ ++ REG_SET_SLICE(mrv_reg->isp_ctrl, MRV_ISP_ISP_AWB_ENABLE, ++ ENABLE); ++ break; ++ default: ++ /* to be sure that a regular value is set: */ ++ /* manual white balance, no measurements */ ++ REG_SET_SLICE(mrv_reg->isp_awb_prop, MRV_ISP_AWB_MODE, ++ MRV_ISP_AWB_MODE_NOMEAS); ++ /* switch ABW block off */ ++ REG_SET_SLICE(mrv_reg->isp_ctrl, MRV_ISP_ISP_AWB_ENABLE, ++ DISABLE); ++ return CI_STATUS_FAILURE; ++ } ++ ++ return CI_STATUS_SUCCESS; ++} ++ ++int ci_isp_get_wb_mode(enum ci_isp_awb_mode *wb_mode) ++{ ++ struct isp_register *mrv_reg = (struct isp_register *) MEM_MRV_REG_BASE; ++ ++ if (!wb_mode) ++ return CI_STATUS_NULL_POINTER; ++ ++ if (REG_GET_SLICE(mrv_reg->isp_ctrl, MRV_ISP_ISP_AWB_ENABLE) == ++ DISABLE) { ++ *wb_mode = CI_ISP_AWB_COMPLETELY_OFF; ++ } else { ++ ++ switch (REG_GET_SLICE(mrv_reg->isp_awb_prop, ++ MRV_ISP_AWB_MODE)) { ++ case MRV_ISP_AWB_MODE_MEAS: ++ *wb_mode = CI_ISP_AWB_MAN_MEAS; ++ break; ++ case MRV_ISP_AWB_MODE_NOMEAS: ++ *wb_mode = CI_ISP_AWB_MAN_NOMEAS; ++ break; ++ default: ++ *wb_mode = CI_ISP_AWB_COMPLETELY_OFF; ++ return CI_STATUS_FAILURE; ++ } ++ } ++ return CI_STATUS_SUCCESS; ++} ++int ci_isp_set_wb_meas_config(const struct ci_isp_wb_meas_config ++ *wb_meas_config) ++{ ++ struct isp_register *mrv_reg = (struct isp_register *) MEM_MRV_REG_BASE; ++ u32 isp_awb_thresh = REG_READ(mrv_reg->isp_awb_thresh); ++ ++ if (!wb_meas_config) ++ return CI_STATUS_NULL_POINTER; ++ ++ /* measurement window */ ++ REG_SET_SLICE(mrv_reg->isp_awb_h_size, MRV_ISP_AWB_H_SIZE, ++ (u32) wb_meas_config->awb_window.hsize); ++ REG_SET_SLICE(mrv_reg->isp_awb_v_size, MRV_ISP_AWB_V_SIZE, ++ (u32) wb_meas_config->awb_window.vsize); ++ REG_SET_SLICE(mrv_reg->isp_awb_h_offs, MRV_ISP_AWB_H_OFFS, ++ (u32) wb_meas_config->awb_window.hoffs); ++ REG_SET_SLICE(mrv_reg->isp_awb_v_offs, MRV_ISP_AWB_V_OFFS, ++ (u32) wb_meas_config->awb_window.voffs); ++ ++ /* adjust awb properties (Y_MAX compare) */ ++ if (wb_meas_config->max_y == 0) { ++ REG_SET_SLICE(mrv_reg->isp_awb_prop, MRV_ISP_AWB_MAX_EN, ++ DISABLE); ++ } else { ++ REG_SET_SLICE(mrv_reg->isp_awb_prop, MRV_ISP_AWB_MAX_EN, ++ ENABLE); ++ } ++ ++ /* measurement thresholds */ ++ REG_SET_SLICE(isp_awb_thresh, MRV_ISP_AWB_MAX_Y, ++ (u32) wb_meas_config->max_y); ++ REG_SET_SLICE(isp_awb_thresh, MRV_ISP_AWB_MIN_Y__MAX_G, ++ (u32) wb_meas_config->minY_MaxG); ++ REG_SET_SLICE(isp_awb_thresh, MRV_ISP_AWB_MAX_CSUM, ++ (u32) wb_meas_config->max_csum); ++ REG_SET_SLICE(isp_awb_thresh, MRV_ISP_AWB_MIN_C, ++ (u32) wb_meas_config->min_c); ++ REG_WRITE(mrv_reg->isp_awb_thresh, isp_awb_thresh); ++ REG_SET_SLICE(mrv_reg->isp_awb_ref, MRV_ISP_AWB_REF_CR__MAX_R, ++ (u32)(wb_meas_config->ref_cr_MaxR)); ++ REG_SET_SLICE(mrv_reg->isp_awb_ref, MRV_ISP_AWB_REF_CB__MAX_B, ++ (u32)(wb_meas_config->ref_cb_MaxB)); ++ ++ /* amount of measurement frames */ ++ REG_SET_SLICE(mrv_reg->isp_awb_frames, MRV_ISP_AWB_FRAMES, ++ (u32) wb_meas_config->frames); ++ ++ /* set measurement mode */ ++ REG_SET_SLICE(mrv_reg->isp_awb_prop, MRV_ISP_AWB_MEAS_MODE, ++ (u32)(wb_meas_config->meas_mode)); ++ ++ return CI_STATUS_SUCCESS; ++} ++ ++int ci_isp_get_wb_meas_config(struct ci_isp_wb_meas_config *wb_meas_config) ++{ ++ struct isp_register *mrv_reg = (struct isp_register *) MEM_MRV_REG_BASE; ++ ++ if (!wb_meas_config) ++ return CI_STATUS_NULL_POINTER; ++ ++ /* measurement window */ ++ wb_meas_config->awb_window.hsize = ++ (u16) REG_GET_SLICE(mrv_reg->isp_awb_h_size, MRV_ISP_AWB_H_SIZE); ++ wb_meas_config->awb_window.vsize = ++ (u16) REG_GET_SLICE(mrv_reg->isp_awb_v_size, MRV_ISP_AWB_V_SIZE); ++ wb_meas_config->awb_window.hoffs = ++ (u16) REG_GET_SLICE(mrv_reg->isp_awb_h_offs, MRV_ISP_AWB_H_OFFS); ++ wb_meas_config->awb_window.voffs = ++ (u16) REG_GET_SLICE(mrv_reg->isp_awb_v_offs, MRV_ISP_AWB_V_OFFS); ++ ++ /* measurement thresholds */ ++ wb_meas_config->min_c = ++ (u8) REG_GET_SLICE(mrv_reg->isp_awb_thresh, MRV_ISP_AWB_MIN_C); ++ wb_meas_config->max_csum = ++ (u8) REG_GET_SLICE(mrv_reg->isp_awb_thresh, MRV_ISP_AWB_MAX_CSUM); ++ wb_meas_config->minY_MaxG = ++ (u8) REG_GET_SLICE(mrv_reg->isp_awb_thresh, ++ MRV_ISP_AWB_MIN_Y__MAX_G); ++ wb_meas_config->max_y = ++ (u8) REG_GET_SLICE(mrv_reg->isp_awb_thresh, MRV_ISP_AWB_MAX_Y); ++ wb_meas_config->ref_cb_MaxB = ++ (u8)REG_GET_SLICE(mrv_reg->isp_awb_ref, MRV_ISP_AWB_REF_CB__MAX_B); ++ wb_meas_config->ref_cr_MaxR = ++ (u8)REG_GET_SLICE(mrv_reg->isp_awb_ref, MRV_ISP_AWB_REF_CR__MAX_R); ++ ++ /* amount of measurement frames */ ++ wb_meas_config->frames = ++ (u8) REG_GET_SLICE(mrv_reg->isp_awb_frames, MRV_ISP_AWB_FRAMES); ++ ++ /* overwrite max_y if the feature is disabled */ ++ if (REG_GET_SLICE(mrv_reg->isp_awb_prop, MRV_ISP_AWB_MAX_EN) == ++ DISABLE) { ++ wb_meas_config->max_y = 0; ++ } ++ ++ /* get measurement mode */ ++ wb_meas_config->meas_mode = REG_GET_SLICE(mrv_reg->isp_awb_prop, ++ MRV_ISP_AWB_MEAS_MODE); ++ return CI_STATUS_SUCCESS; ++} ++ ++int ci_isp_get_wb_meas(struct ci_sensor_awb_mean *awb_mean) ++{ ++ struct isp_register *mrv_reg = (struct isp_register *) MEM_MRV_REG_BASE; ++ ++ if (awb_mean == NULL) ++ return CI_STATUS_NULL_POINTER; ++ ++ awb_mean->white = REG_GET_SLICE(mrv_reg->isp_awb_white_cnt, ++ MRV_ISP_AWB_WHITE_CNT); ++ awb_mean->mean_Y__G = (u8) REG_GET_SLICE(mrv_reg->isp_awb_mean, ++ MRV_ISP_AWB_MEAN_Y__G); ++ awb_mean->mean_cb__B = (u8) REG_GET_SLICE(mrv_reg->isp_awb_mean, ++ MRV_ISP_AWB_MEAN_CB__B); ++ awb_mean->mean_cr__R = (u8) REG_GET_SLICE(mrv_reg->isp_awb_mean, ++ MRV_ISP_AWB_MEAN_CR__R); ++ return CI_STATUS_SUCCESS; ++} ++ ++/* ++ * calculates left-top and right-bottom register values ++ * for a given AF measurement window ++ */ ++static int ci_isp_afm_wnd2_regs(const struct ci_isp_window *wnd, u32 *lt, ++ u32 *rb) ++{ ++ WARN_ON(!((wnd != NULL) && (lt != NULL) && (rb != NULL))); ++ ++ if (wnd->hsize && wnd->vsize) { ++ u32 left = wnd->hoffs; ++ u32 top = wnd->voffs; ++ u32 right = left + wnd->hsize - 1; ++ u32 bottom = top + wnd->vsize - 1; ++ ++ if ((left < MRV_AFM_A_H_L_MIN) ++ || (left > MRV_AFM_A_H_L_MAX) ++ || (top < MRV_AFM_A_V_T_MIN) ++ || (top > MRV_AFM_A_V_T_MAX) ++ || (right < MRV_AFM_A_H_R_MIN) ++ || (right > MRV_AFM_A_H_R_MAX) ++ || (bottom < MRV_AFM_A_V_B_MIN) ++ || (bottom > MRV_AFM_A_V_B_MAX)) { ++ return CI_STATUS_OUTOFRANGE; ++ } ++ ++ /* combine the values and return */ ++ REG_SET_SLICE(*lt, MRV_AFM_A_H_L, left); ++ REG_SET_SLICE(*lt, MRV_AFM_A_V_T, top); ++ REG_SET_SLICE(*rb, MRV_AFM_A_H_R, right); ++ REG_SET_SLICE(*rb, MRV_AFM_A_V_B, bottom); ++ } else { ++ *lt = 0; ++ *rb = 0; ++ } ++ ++ return CI_STATUS_SUCCESS; ++} ++ ++int ci_isp_set_auto_focus(const struct ci_isp_af_config *af_config) ++{ ++ ++ struct isp_register *mrv_reg = (struct isp_register *) MEM_MRV_REG_BASE; ++ u32 result = CI_STATUS_SUCCESS; ++ ++ /* disable measurement module */ ++ REG_SET_SLICE(mrv_reg->isp_afm_ctrl, MRV_AFM_AFM_EN, DISABLE); ++ ++ if (af_config) { ++ u32 lt; ++ u32 rb; ++ result = ci_isp_afm_wnd2_regs(&(af_config->wnd_pos_a), ++ <, &rb); ++ /* set measurement window boundaries */ ++ if (result != CI_STATUS_SUCCESS) ++ return result; ++ ++ REG_WRITE(mrv_reg->isp_afm_lt_a, lt); ++ REG_WRITE(mrv_reg->isp_afm_rb_a, rb); ++ ++ result = ci_isp_afm_wnd2_regs(&(af_config->wnd_pos_b), ++ <, &rb); ++ ++ if (result != CI_STATUS_SUCCESS) ++ return result; ++ ++ REG_WRITE(mrv_reg->isp_afm_lt_b, lt); ++ REG_WRITE(mrv_reg->isp_afm_rb_b, rb); ++ ++ result = ci_isp_afm_wnd2_regs(&(af_config->wnd_pos_c), ++ <, &rb); ++ ++ if (result != CI_STATUS_SUCCESS) ++ return result; ++ ++ REG_WRITE(mrv_reg->isp_afm_lt_c, lt); ++ REG_WRITE(mrv_reg->isp_afm_rb_c, rb); ++ ++ /* set other af measurement paraneters */ ++ REG_SET_SLICE(mrv_reg->isp_afm_thres, MRV_AFM_AFM_THRES, ++ af_config->threshold); ++ REG_SET_SLICE(mrv_reg->isp_afm_var_shift, MRV_AFM_LUM_VAR_SHIFT, ++ (af_config->var_shift >> 16)); ++ REG_SET_SLICE(mrv_reg->isp_afm_var_shift, MRV_AFM_AFM_VAR_SHIFT, ++ (af_config->var_shift >> 0)); ++ ++ /* enable measurement module */ ++ REG_SET_SLICE(mrv_reg->isp_afm_ctrl, MRV_AFM_AFM_EN, ENABLE); ++ } ++ ++ return result; ++} ++ ++ ++void ci_isp_get_auto_focus_meas(struct ci_isp_af_meas *af_meas) ++{ ++ struct isp_register *mrv_reg = (struct isp_register *) MEM_MRV_REG_BASE; ++ ++ WARN_ON(!(af_meas != NULL)); ++ ++ af_meas->afm_sum_a = ++ REG_GET_SLICE(mrv_reg->isp_afm_sum_a, MRV_AFM_AFM_SUM_A); ++ af_meas->afm_sum_b = ++ REG_GET_SLICE(mrv_reg->isp_afm_sum_b, MRV_AFM_AFM_SUM_B); ++ af_meas->afm_sum_c = ++ REG_GET_SLICE(mrv_reg->isp_afm_sum_c, MRV_AFM_AFM_SUM_C); ++ af_meas->afm_lum_a = ++ REG_GET_SLICE(mrv_reg->isp_afm_lum_a, MRV_AFM_AFM_LUM_A); ++ af_meas->afm_lum_b = ++ REG_GET_SLICE(mrv_reg->isp_afm_lum_b, MRV_AFM_AFM_LUM_B); ++ af_meas->afm_lum_c = ++ REG_GET_SLICE(mrv_reg->isp_afm_lum_c, MRV_AFM_AFM_LUM_C); ++} ++ ++int ci_isp_set_ls_correction(struct ci_sensor_ls_corr_config *ls_corr_config) ++{ ++ struct isp_register *mrv_reg = (struct isp_register *) MEM_MRV_REG_BASE; ++ u32 i, n; ++ u32 data = 0; ++ int enabled = false; ++ ++ if (!ls_corr_config) { ++ /* disable lens shading module */ ++ REG_SET_SLICE(mrv_reg->isp_lsc_ctrl, MRV_LSC_LSC_EN, DISABLE); ++ } else { ++ /* test if lens shading correction is enabled */ ++ if (REG_GET_SLICE(mrv_reg->isp_lsc_ctrl, MRV_LSC_LSC_EN)) { ++ /* switch off lens shading correction */ ++ REG_SET_SLICE(mrv_reg->isp_lsc_ctrl, ++ MRV_LSC_LSC_EN, DISABLE); ++ /* wait 1ms to make sure that ++ * the LSC have time enough to switch off */ ++ /* wait over 1 ms */ ++ /*mdelay(1000);*/ ++ msleep(1000); ++ enabled = true; ++ } ++ ++ /* clear address counters */ ++ REG_WRITE(mrv_reg->isp_lsc_r_table_addr, 0); ++ REG_WRITE(mrv_reg->isp_lsc_g_table_addr, 0); ++ REG_WRITE(mrv_reg->isp_lsc_b_table_addr, 0); ++ ++ /* program data tables (table size is 9 * 17 = 153; ++ * see also MRV_LSC_?_RAM_ADDR_MAX) */ ++ WARN_ON(!(((CI_ISP_MAX_LSC_SECTORS + 1) * ++ ((CI_ISP_MAX_LSC_SECTORS + 2) / 2)) == ++ (MRV_LSC_R_RAM_ADDR_MAX + 1))); ++ ++ /* 17 steps */ ++ for (n = 0; ++ n < ((CI_ISP_MAX_LSC_SECTORS + 1) * ++ (CI_ISP_MAX_LSC_SECTORS + 1)); ++ n += CI_ISP_MAX_LSC_SECTORS + 1) { ++ dprintk(2, "set ls correct step n = %d", n); ++ /* 17 sectors with 2 values in one DWORD = 9 ++ * DWORDs (8 steps + 1 outside loop) */ ++ for (i = 0; i < (CI_ISP_MAX_LSC_SECTORS); i += 2) { ++ REG_SET_SLICE(data, MRV_LSC_R_SAMPLE_0, ++ ls_corr_config->ls_rdata_tbl[n + i]); ++ REG_SET_SLICE(data, MRV_LSC_R_SAMPLE_1, ++ ls_corr_config->ls_rdata_tbl ++ [n + i + 1]); ++ REG_WRITE(mrv_reg->isp_lsc_r_table_data, data); ++ REG_SET_SLICE(data, MRV_LSC_G_SAMPLE_0, ++ ls_corr_config->ls_gdata_tbl ++ [n + i]); ++ REG_SET_SLICE(data, MRV_LSC_G_SAMPLE_1, ++ ls_corr_config->ls_gdata_tbl ++ [n + i + 1]); ++ REG_WRITE(mrv_reg->isp_lsc_g_table_data, data); ++ REG_SET_SLICE(data, MRV_LSC_B_SAMPLE_0, ++ ls_corr_config->ls_bdata_tbl[n + i]); ++ REG_SET_SLICE(data, MRV_LSC_B_SAMPLE_1, ++ ls_corr_config->ls_bdata_tbl ++ [n + i + 1]); ++ REG_WRITE(mrv_reg->isp_lsc_b_table_data, data); ++ } ++ REG_SET_SLICE(data, MRV_LSC_R_SAMPLE_0, ++ ls_corr_config->ls_rdata_tbl ++ [n + CI_ISP_MAX_LSC_SECTORS]); ++ REG_SET_SLICE(data, MRV_LSC_R_SAMPLE_1, 0); ++ REG_WRITE(mrv_reg->isp_lsc_r_table_data, data); ++ REG_SET_SLICE(data, MRV_LSC_G_SAMPLE_0, ++ ls_corr_config->ls_gdata_tbl ++ [n + CI_ISP_MAX_LSC_SECTORS]); ++ REG_SET_SLICE(data, MRV_LSC_G_SAMPLE_1, 0); ++ REG_WRITE(mrv_reg->isp_lsc_g_table_data, data); ++ REG_SET_SLICE(data, MRV_LSC_B_SAMPLE_0, ++ ls_corr_config->ls_bdata_tbl ++ [n + CI_ISP_MAX_LSC_SECTORS]); ++ REG_SET_SLICE(data, MRV_LSC_B_SAMPLE_1, 0); ++ REG_WRITE(mrv_reg->isp_lsc_b_table_data, data); ++ } ++ ++ /* program x size tables */ ++ REG_SET_SLICE(mrv_reg->isp_lsc_xsize_01, MRV_LSC_X_SECT_SIZE_0, ++ ls_corr_config->ls_xsize_tbl[0]); ++ REG_SET_SLICE(mrv_reg->isp_lsc_xsize_01, MRV_LSC_X_SECT_SIZE_1, ++ ls_corr_config->ls_xsize_tbl[1]); ++ REG_SET_SLICE(mrv_reg->isp_lsc_xsize_23, MRV_LSC_X_SECT_SIZE_2, ++ ls_corr_config->ls_xsize_tbl[2]); ++ REG_SET_SLICE(mrv_reg->isp_lsc_xsize_23, MRV_LSC_X_SECT_SIZE_3, ++ ls_corr_config->ls_xsize_tbl[3]); ++ REG_SET_SLICE(mrv_reg->isp_lsc_xsize_45, MRV_LSC_X_SECT_SIZE_4, ++ ls_corr_config->ls_xsize_tbl[4]); ++ REG_SET_SLICE(mrv_reg->isp_lsc_xsize_45, MRV_LSC_X_SECT_SIZE_5, ++ ls_corr_config->ls_xsize_tbl[5]); ++ REG_SET_SLICE(mrv_reg->isp_lsc_xsize_67, MRV_LSC_X_SECT_SIZE_6, ++ ls_corr_config->ls_xsize_tbl[6]); ++ REG_SET_SLICE(mrv_reg->isp_lsc_xsize_67, MRV_LSC_X_SECT_SIZE_7, ++ ls_corr_config->ls_xsize_tbl[7]); ++ ++ /* program y size tables */ ++ REG_SET_SLICE(mrv_reg->isp_lsc_ysize_01, MRV_LSC_Y_SECT_SIZE_0, ++ ls_corr_config->ls_ysize_tbl[0]); ++ REG_SET_SLICE(mrv_reg->isp_lsc_ysize_01, MRV_LSC_Y_SECT_SIZE_1, ++ ls_corr_config->ls_ysize_tbl[1]); ++ REG_SET_SLICE(mrv_reg->isp_lsc_ysize_23, MRV_LSC_Y_SECT_SIZE_2, ++ ls_corr_config->ls_ysize_tbl[2]); ++ REG_SET_SLICE(mrv_reg->isp_lsc_ysize_23, MRV_LSC_Y_SECT_SIZE_3, ++ ls_corr_config->ls_ysize_tbl[3]); ++ REG_SET_SLICE(mrv_reg->isp_lsc_ysize_45, MRV_LSC_Y_SECT_SIZE_4, ++ ls_corr_config->ls_ysize_tbl[4]); ++ REG_SET_SLICE(mrv_reg->isp_lsc_ysize_45, MRV_LSC_Y_SECT_SIZE_5, ++ ls_corr_config->ls_ysize_tbl[5]); ++ REG_SET_SLICE(mrv_reg->isp_lsc_ysize_67, MRV_LSC_Y_SECT_SIZE_6, ++ ls_corr_config->ls_ysize_tbl[6]); ++ REG_SET_SLICE(mrv_reg->isp_lsc_ysize_67, MRV_LSC_Y_SECT_SIZE_7, ++ ls_corr_config->ls_ysize_tbl[7]); ++ ++ /* program x grad tables */ ++ REG_SET_SLICE(mrv_reg->isp_lsc_xgrad_01, MRV_LSC_XGRAD_0, ++ ls_corr_config->ls_xgrad_tbl[0]); ++ REG_SET_SLICE(mrv_reg->isp_lsc_xgrad_01, MRV_LSC_XGRAD_1, ++ ls_corr_config->ls_xgrad_tbl[1]); ++ REG_SET_SLICE(mrv_reg->isp_lsc_xgrad_23, MRV_LSC_XGRAD_2, ++ ls_corr_config->ls_xgrad_tbl[2]); ++ REG_SET_SLICE(mrv_reg->isp_lsc_xgrad_23, MRV_LSC_XGRAD_3, ++ ls_corr_config->ls_xgrad_tbl[3]); ++ REG_SET_SLICE(mrv_reg->isp_lsc_xgrad_45, MRV_LSC_XGRAD_4, ++ ls_corr_config->ls_xgrad_tbl[4]); ++ REG_SET_SLICE(mrv_reg->isp_lsc_xgrad_45, MRV_LSC_XGRAD_5, ++ ls_corr_config->ls_xgrad_tbl[5]); ++ REG_SET_SLICE(mrv_reg->isp_lsc_xgrad_67, MRV_LSC_XGRAD_6, ++ ls_corr_config->ls_xgrad_tbl[6]); ++ REG_SET_SLICE(mrv_reg->isp_lsc_xgrad_67, MRV_LSC_XGRAD_7, ++ ls_corr_config->ls_xgrad_tbl[7]); ++ ++ /* program y grad tables */ ++ REG_SET_SLICE(mrv_reg->isp_lsc_ygrad_01, MRV_LSC_YGRAD_0, ++ ls_corr_config->ls_ygrad_tbl[0]); ++ REG_SET_SLICE(mrv_reg->isp_lsc_ygrad_01, MRV_LSC_YGRAD_1, ++ ls_corr_config->ls_ygrad_tbl[1]); ++ REG_SET_SLICE(mrv_reg->isp_lsc_ygrad_23, MRV_LSC_YGRAD_2, ++ ls_corr_config->ls_ygrad_tbl[2]); ++ REG_SET_SLICE(mrv_reg->isp_lsc_ygrad_23, MRV_LSC_YGRAD_3, ++ ls_corr_config->ls_ygrad_tbl[3]); ++ REG_SET_SLICE(mrv_reg->isp_lsc_ygrad_45, MRV_LSC_YGRAD_4, ++ ls_corr_config->ls_ygrad_tbl[4]); ++ REG_SET_SLICE(mrv_reg->isp_lsc_ygrad_45, MRV_LSC_YGRAD_5, ++ ls_corr_config->ls_ygrad_tbl[5]); ++ REG_SET_SLICE(mrv_reg->isp_lsc_ygrad_67, MRV_LSC_YGRAD_6, ++ ls_corr_config->ls_ygrad_tbl[6]); ++ REG_SET_SLICE(mrv_reg->isp_lsc_ygrad_67, MRV_LSC_YGRAD_7, ++ ls_corr_config->ls_ygrad_tbl[7]); ++ ++ if (enabled) { ++ /* switch on lens chading correction */ ++ REG_SET_SLICE(mrv_reg->isp_lsc_ctrl, ++ MRV_LSC_LSC_EN, ENABLE); ++ } ++ } ++ ++ return CI_STATUS_SUCCESS; ++} ++ ++int ci_isp_ls_correction_on_off(int ls_corr_on_off) ++{ ++ struct isp_register *mrv_reg = (struct isp_register *) MEM_MRV_REG_BASE; ++ ++ if (ls_corr_on_off) { ++ /* switch on lens chading correction */ ++ REG_SET_SLICE(mrv_reg->isp_lsc_ctrl, MRV_LSC_LSC_EN, ENABLE); ++ } else { ++ /* switch off lens chading correction */ ++ REG_SET_SLICE(mrv_reg->isp_lsc_ctrl, MRV_LSC_LSC_EN, DISABLE); ++ } ++ ++ return CI_STATUS_SUCCESS; ++} ++ ++/* ++ * Sets the Bad Pixel Correction configuration ++ */ ++int ci_isp_set_bp_correction(const struct ci_isp_bp_corr_config ++ *bp_corr_config) ++{ ++ struct isp_register *mrv_reg = (struct isp_register *) MEM_MRV_REG_BASE; ++ u32 isp_bp_ctrl = REG_READ(mrv_reg->isp_bp_ctrl); ++ ++ if (!bp_corr_config) { ++ /* disable correction module */ ++ REG_SET_SLICE(isp_bp_ctrl, MRV_BP_HOT_COR_EN, DISABLE); ++ REG_SET_SLICE(isp_bp_ctrl, MRV_BP_DEAD_COR_EN, DISABLE); ++ } else { ++ /* set bad pixel configuration */ ++ if (bp_corr_config->bp_corr_type == CI_ISP_BP_CORR_DIRECT) { ++ /* direct detection */ ++ u32 isp_bp_cfg1 = REG_READ(mrv_reg->isp_bp_cfg1); ++ u32 isp_bp_cfg2 = REG_READ(mrv_reg->isp_bp_cfg2); ++ ++ REG_SET_SLICE(isp_bp_ctrl, MRV_BP_COR_TYPE, ++ MRV_BP_COR_TYPE_DIRECT); ++ ++ WARN_ON(!(!REG_GET_SLICE(mrv_reg->isp_bp_ctrl, ++ MRV_BP_BP_DET_EN))); ++ ++ /* threshold register only used for direct mode */ ++ REG_SET_SLICE(isp_bp_cfg1, MRV_BP_HOT_THRES, ++ bp_corr_config->bp_abs_hot_thres); ++ REG_SET_SLICE(isp_bp_cfg1, MRV_BP_DEAD_THRES, ++ bp_corr_config->bp_abs_dead_thres); ++ REG_WRITE(mrv_reg->isp_bp_cfg1, isp_bp_cfg1); ++ REG_SET_SLICE(isp_bp_cfg2, MRV_BP_DEV_HOT_THRES, ++ bp_corr_config->bp_dev_hot_thres); ++ REG_SET_SLICE(isp_bp_cfg2, MRV_BP_DEV_DEAD_THRES, ++ bp_corr_config->bp_dev_dead_thres); ++ REG_WRITE(mrv_reg->isp_bp_cfg2, isp_bp_cfg2); ++ } else { ++ /* use bad pixel table */ ++ REG_SET_SLICE(isp_bp_ctrl, MRV_BP_COR_TYPE, ++ MRV_BP_COR_TYPE_TABLE); ++ } ++ ++ if (bp_corr_config->bp_corr_rep == CI_ISP_BP_CORR_REP_LIN) { ++ /* use linear approch */ ++ REG_SET_SLICE(isp_bp_ctrl, MRV_BP_REP_APPR, ++ MRV_BP_REP_APPR_INTERPOL); ++ } else { ++ /* use best neighbour */ ++ REG_SET_SLICE(isp_bp_ctrl, MRV_BP_REP_APPR, ++ MRV_BP_REP_APPR_NEAREST); ++ } ++ ++ switch (bp_corr_config->bp_corr_mode) { ++ case CI_ISP_BP_CORR_HOT_EN: ++ /* enable Hot */ ++ REG_SET_SLICE(isp_bp_ctrl, MRV_BP_HOT_COR_EN, ENABLE); ++ /* disable Dead */ ++ REG_SET_SLICE(isp_bp_ctrl, MRV_BP_DEAD_COR_EN, DISABLE); ++ break; ++ case CI_ISP_BP_CORR_DEAD_EN: ++ /* disable Hot */ ++ REG_SET_SLICE(isp_bp_ctrl, MRV_BP_HOT_COR_EN, DISABLE); ++ /* enable Dead */ ++ REG_SET_SLICE(isp_bp_ctrl, MRV_BP_DEAD_COR_EN, ENABLE); ++ break; ++ case CI_ISP_BP_CORR_HOT_DEAD_EN: ++ default: ++ /* enable Hot */ ++ REG_SET_SLICE(isp_bp_ctrl, MRV_BP_HOT_COR_EN, ENABLE); ++ /* enable Dead */ ++ REG_SET_SLICE(isp_bp_ctrl, MRV_BP_DEAD_COR_EN, ENABLE); ++ break; ++ } ++ } ++ ++ REG_WRITE(mrv_reg->isp_bp_ctrl, isp_bp_ctrl); ++ ++ return CI_STATUS_SUCCESS; ++ ++} ++ ++/* ++ * Sets the Bad Pixel configuration for detection ++ */ ++int ci_isp_set_bp_detection(const struct ci_isp_bp_det_config *bp_det_config) ++{ ++ struct isp_register *mrv_reg = (struct isp_register *) MEM_MRV_REG_BASE; ++ ++ if (!bp_det_config) { ++ /* disable measurement module */ ++ REG_SET_SLICE(mrv_reg->isp_bp_ctrl, MRV_BP_BP_DET_EN, DISABLE); ++ } else { ++ WARN_ON(!(REG_GET_SLICE(mrv_reg->isp_bp_ctrl, MRV_BP_COR_TYPE) ++ == MRV_BP_COR_TYPE_TABLE)); ++ ++ /* set dead threshold for bad pixel detection */ ++ REG_SET_SLICE(mrv_reg->isp_bp_cfg1, MRV_BP_DEAD_THRES, ++ bp_det_config->bp_dead_thres); ++ ++ /* enable measurement module */ ++ REG_SET_SLICE(mrv_reg->isp_bp_ctrl, MRV_BP_BP_DET_EN, ENABLE); ++ } ++ ++ return CI_STATUS_SUCCESS; ++} ++ ++int ci_isp_clear_bp_int(void) ++{ ++ struct isp_register *mrv_reg = (struct isp_register *) MEM_MRV_REG_BASE; ++ ++ /* clear bp_det irq (only if it is signalled to prevent loss of irqs) */ ++ if (REG_GET_SLICE(mrv_reg->isp_ris, MRV_ISP_RIS_BP_DET)) ++ REG_SET_SLICE(mrv_reg->isp_icr, MRV_ISP_ICR_BP_DET, 1); ++ ++ return CI_STATUS_SUCCESS; ++} ++ ++/* ++ * Initializes Isp filter registers with default reset values. ++ */ ++static int ci_isp_initialize_filter_registers(void) ++{ ++ struct isp_register *mrv_reg = (struct isp_register *) MEM_MRV_REG_BASE; ++ ++ mrv_reg->isp_filt_mode = 0x00000000; ++ mrv_reg->isp_filt_fac_sh1 = 0x00000010; ++ mrv_reg->isp_filt_fac_sh0 = 0x0000000C; ++ mrv_reg->isp_filt_fac_mid = 0x0000000A; ++ mrv_reg->isp_filt_fac_bl0 = 0x00000006; ++ mrv_reg->isp_filt_fac_bl1 = 0x00000002; ++ mrv_reg->isp_filt_thresh_bl0 = 0x0000000D; ++ mrv_reg->isp_filt_thresh_bl1 = 0x00000005; ++ mrv_reg->isp_filt_thresh_sh0 = 0x0000001A; ++ mrv_reg->isp_filt_thresh_sh1 = 0x0000002C; ++ mrv_reg->isp_filt_lum_weight = 0x00032040; ++ ++ return CI_STATUS_SUCCESS; ++} ++ ++int ci_isp_activate_filter(int activate_filter) ++{ ++ struct isp_register *mrv_reg = (struct isp_register *) MEM_MRV_REG_BASE; ++ int retval = CI_STATUS_SUCCESS; ++ ++ /* Initialize ISP filter control registers first */ ++ retval = ci_isp_initialize_filter_registers(); ++ if (retval != CI_STATUS_SUCCESS) ++ return retval; ++ ++ /* Activate or deactivate filter algorythm */ ++ REG_SET_SLICE(mrv_reg->isp_filt_mode, MRV_FILT_FILT_ENABLE, ++ (activate_filter) ? ENABLE : DISABLE); ++ ++ return retval; ++} ++ ++/* ++ * Write coefficient and threshold values into Isp filter ++ * registers for noise, sharpness and blurring filtering. ++ */ ++int ci_isp_set_filter_params(u8 noise_reduc_level, u8 sharp_level) ++{ ++ struct isp_register *mrv_reg = (struct isp_register *) MEM_MRV_REG_BASE; ++ u32 isp_filt_mode = 0; ++ ++ if (!REG_GET_SLICE(mrv_reg->isp_filt_mode, MRV_FILT_FILT_ENABLE)) ++ return CI_STATUS_CANCELED; ++ ++ REG_WRITE(mrv_reg->isp_filt_mode, isp_filt_mode); ++ ++ if (((noise_reduc_level <= 10) || (noise_reduc_level == 99)) ++ && (sharp_level <= 10)) { ++ switch (noise_reduc_level) { ++ /* Test Mode */ ++ case 99: ++ /* 10 bit max value */ ++ REG_SET_SLICE(mrv_reg->isp_filt_thresh_sh1, ++ MRV_FILT_FILT_THRESH_SH1, 0x000003FF); ++ /* 10 bit max value */ ++ REG_SET_SLICE(mrv_reg->isp_filt_thresh_sh0, ++ MRV_FILT_FILT_THRESH_SH0, 0x000003FF); ++ /* 10 bit max value */ ++ REG_SET_SLICE(mrv_reg->isp_filt_thresh_bl0, ++ MRV_FILT_FILT_THRESH_BL0, 0x000003FF); ++ /* 10 bit max value */ ++ REG_SET_SLICE(mrv_reg->isp_filt_thresh_bl1, ++ MRV_FILT_FILT_THRESH_BL1, 0x000003FF); ++ REG_SET_SLICE(isp_filt_mode, MRV_FILT_STAGE1_SELECT, 0 ++ /* MRV_FILT_STAGE1_SELECT_MAX_BLUR */); ++ REG_SET_SLICE(isp_filt_mode, MRV_FILT_FILT_CHR_V_MODE, ++ MRV_FILT_FILT_CHR_V_MODE_BYPASS); ++ REG_SET_SLICE(isp_filt_mode, MRV_FILT_FILT_CHR_H_MODE, ++ MRV_FILT_FILT_CHR_H_MODE_BYPASS); ++ break; ++ ++ case 0: ++ /* NoiseReductionLevel = 0 */ ++ REG_SET_SLICE(mrv_reg->isp_filt_thresh_sh1, ++ MRV_FILT_FILT_THRESH_SH1, 0x000000); ++ REG_SET_SLICE(mrv_reg->isp_filt_thresh_sh0, ++ MRV_FILT_FILT_THRESH_SH0, 0x000000); ++ REG_SET_SLICE(mrv_reg->isp_filt_thresh_bl0, ++ MRV_FILT_FILT_THRESH_BL0, 0x000000); ++ REG_SET_SLICE(mrv_reg->isp_filt_thresh_bl1, ++ MRV_FILT_FILT_THRESH_BL1, 0x000000); ++ REG_SET_SLICE(isp_filt_mode, MRV_FILT_STAGE1_SELECT, 6); ++ REG_SET_SLICE(isp_filt_mode, MRV_FILT_FILT_CHR_V_MODE, ++ MRV_FILT_FILT_CHR_V_MODE_STATIC8); ++ REG_SET_SLICE(isp_filt_mode, MRV_FILT_FILT_CHR_H_MODE, ++ MRV_FILT_FILT_CHR_H_MODE_BYPASS); ++ break; ++ ++ case 1: ++ /* NoiseReductionLevel = 1; */ ++ REG_SET_SLICE(mrv_reg->isp_filt_thresh_sh1, ++ MRV_FILT_FILT_THRESH_SH1, 33); ++ REG_SET_SLICE(mrv_reg->isp_filt_thresh_sh0, ++ MRV_FILT_FILT_THRESH_SH0, 18); ++ REG_SET_SLICE(mrv_reg->isp_filt_thresh_bl0, ++ MRV_FILT_FILT_THRESH_BL0, 8); ++ REG_SET_SLICE(mrv_reg->isp_filt_thresh_bl1, ++ MRV_FILT_FILT_THRESH_BL1, 2); ++ REG_SET_SLICE(isp_filt_mode, MRV_FILT_STAGE1_SELECT, 6); ++ REG_SET_SLICE(isp_filt_mode, MRV_FILT_FILT_CHR_V_MODE, ++ MRV_FILT_FILT_CHR_V_MODE_STATIC12); ++ REG_SET_SLICE(isp_filt_mode, MRV_FILT_FILT_CHR_H_MODE, ++ MRV_FILT_FILT_CHR_H_MODE_DYN_2); ++ break; ++ ++ case 2: ++ /* NoiseReductionLevel = 2; */ ++ REG_SET_SLICE(mrv_reg->isp_filt_thresh_sh1, ++ MRV_FILT_FILT_THRESH_SH1, 44); ++ REG_SET_SLICE(mrv_reg->isp_filt_thresh_sh0, ++ MRV_FILT_FILT_THRESH_SH0, 26); ++ REG_SET_SLICE(mrv_reg->isp_filt_thresh_bl0, ++ MRV_FILT_FILT_THRESH_BL0, 13); ++ REG_SET_SLICE(mrv_reg->isp_filt_thresh_bl1, ++ MRV_FILT_FILT_THRESH_BL1, 5); ++ REG_SET_SLICE(isp_filt_mode, MRV_FILT_STAGE1_SELECT, 4 ++ /* MRV_FILT_STAGE1_SELECT_DEFAULT */); ++ REG_SET_SLICE(isp_filt_mode, MRV_FILT_FILT_CHR_V_MODE, ++ MRV_FILT_FILT_CHR_V_MODE_STATIC12); ++ REG_SET_SLICE(isp_filt_mode, MRV_FILT_FILT_CHR_H_MODE, ++ MRV_FILT_FILT_CHR_H_MODE_DYN_2); ++ break; ++ ++ case 3: ++ /* NoiseReductionLevel = 3; */ ++ REG_SET_SLICE(mrv_reg->isp_filt_thresh_sh1, ++ MRV_FILT_FILT_THRESH_SH1, 51); ++ REG_SET_SLICE(mrv_reg->isp_filt_thresh_sh0, ++ MRV_FILT_FILT_THRESH_SH0, 36); ++ REG_SET_SLICE(mrv_reg->isp_filt_thresh_bl0, ++ MRV_FILT_FILT_THRESH_BL0, 23); ++ REG_SET_SLICE(mrv_reg->isp_filt_thresh_bl1, ++ MRV_FILT_FILT_THRESH_BL1, 10); ++ REG_SET_SLICE(isp_filt_mode, MRV_FILT_STAGE1_SELECT, 4 ++ /* MRV_FILT_STAGE1_SELECT_DEFAULT */); ++ REG_SET_SLICE(isp_filt_mode, MRV_FILT_FILT_CHR_V_MODE, ++ MRV_FILT_FILT_CHR_V_MODE_STATIC12); ++ REG_SET_SLICE(isp_filt_mode, MRV_FILT_FILT_CHR_H_MODE, ++ MRV_FILT_FILT_CHR_H_MODE_DYN_2); ++ break; ++ ++ case 4: ++ /* NoiseReductionLevel = 4; */ ++ REG_SET_SLICE(mrv_reg->isp_filt_thresh_sh1, ++ MRV_FILT_FILT_THRESH_SH1, 67); ++ REG_SET_SLICE(mrv_reg->isp_filt_thresh_sh0, ++ MRV_FILT_FILT_THRESH_SH0, 41); ++ REG_SET_SLICE(mrv_reg->isp_filt_thresh_bl0, ++ MRV_FILT_FILT_THRESH_BL0, 26); ++ REG_SET_SLICE(mrv_reg->isp_filt_thresh_bl1, ++ MRV_FILT_FILT_THRESH_BL1, 15); ++ REG_SET_SLICE(isp_filt_mode, MRV_FILT_STAGE1_SELECT, 3); ++ REG_SET_SLICE(isp_filt_mode, MRV_FILT_FILT_CHR_V_MODE, ++ MRV_FILT_FILT_CHR_V_MODE_STATIC12); ++ REG_SET_SLICE(isp_filt_mode, MRV_FILT_FILT_CHR_H_MODE, ++ MRV_FILT_FILT_CHR_H_MODE_DYN_2); ++ break; ++ ++ case 5: ++ /* NoiseReductionLevel = 5; */ ++ REG_SET_SLICE(mrv_reg->isp_filt_thresh_sh1, ++ MRV_FILT_FILT_THRESH_SH1, 100); ++ REG_SET_SLICE(mrv_reg->isp_filt_thresh_sh0, ++ MRV_FILT_FILT_THRESH_SH0, 75); ++ REG_SET_SLICE(mrv_reg->isp_filt_thresh_bl0, ++ MRV_FILT_FILT_THRESH_BL0, 50); ++ REG_SET_SLICE(mrv_reg->isp_filt_thresh_bl1, ++ MRV_FILT_FILT_THRESH_BL1, 20); ++ REG_SET_SLICE(isp_filt_mode, MRV_FILT_STAGE1_SELECT, 3); ++ REG_SET_SLICE(isp_filt_mode, MRV_FILT_FILT_CHR_V_MODE, ++ MRV_FILT_FILT_CHR_V_MODE_STATIC12); ++ REG_SET_SLICE(isp_filt_mode, MRV_FILT_FILT_CHR_H_MODE, ++ MRV_FILT_FILT_CHR_H_MODE_DYN_2); ++ break; ++ ++ case 6: ++ /* NoiseReductionLevel = 6; */ ++ REG_SET_SLICE(mrv_reg->isp_filt_thresh_sh1, ++ MRV_FILT_FILT_THRESH_SH1, 120); ++ REG_SET_SLICE(mrv_reg->isp_filt_thresh_sh0, ++ MRV_FILT_FILT_THRESH_SH0, 90); ++ REG_SET_SLICE(mrv_reg->isp_filt_thresh_bl0, ++ MRV_FILT_FILT_THRESH_BL0, 60); ++ REG_SET_SLICE(mrv_reg->isp_filt_thresh_bl1, ++ MRV_FILT_FILT_THRESH_BL1, 26); ++ REG_SET_SLICE(isp_filt_mode, MRV_FILT_STAGE1_SELECT, 2); ++ REG_SET_SLICE(isp_filt_mode, MRV_FILT_FILT_CHR_V_MODE, ++ MRV_FILT_FILT_CHR_V_MODE_STATIC12); ++ REG_SET_SLICE(isp_filt_mode, MRV_FILT_FILT_CHR_H_MODE, ++ MRV_FILT_FILT_CHR_H_MODE_DYN_2); ++ break; ++ ++ case 7: ++ /* NoiseReductionLevel = 7; */ ++ REG_SET_SLICE(mrv_reg->isp_filt_thresh_sh1, ++ MRV_FILT_FILT_THRESH_SH1, 150); ++ REG_SET_SLICE(mrv_reg->isp_filt_thresh_sh0, ++ MRV_FILT_FILT_THRESH_SH0, 120); ++ REG_SET_SLICE(mrv_reg->isp_filt_thresh_bl0, ++ MRV_FILT_FILT_THRESH_BL0, 80); ++ REG_SET_SLICE(mrv_reg->isp_filt_thresh_bl1, ++ MRV_FILT_FILT_THRESH_BL1, 51); ++ REG_SET_SLICE(isp_filt_mode, MRV_FILT_STAGE1_SELECT, 2); ++ REG_SET_SLICE(isp_filt_mode, MRV_FILT_FILT_CHR_V_MODE, ++ MRV_FILT_FILT_CHR_V_MODE_STATIC12); ++ REG_SET_SLICE(isp_filt_mode, MRV_FILT_FILT_CHR_H_MODE, ++ MRV_FILT_FILT_CHR_H_MODE_DYN_2); ++ break; ++ ++ case 8: ++ /* NoiseReductionLevel = 8; */ ++ REG_SET_SLICE(mrv_reg->isp_filt_thresh_sh1, ++ MRV_FILT_FILT_THRESH_SH1, 200); ++ REG_SET_SLICE(mrv_reg->isp_filt_thresh_sh0, ++ MRV_FILT_FILT_THRESH_SH0, 170); ++ REG_SET_SLICE(mrv_reg->isp_filt_thresh_bl0, ++ MRV_FILT_FILT_THRESH_BL0, 140); ++ REG_SET_SLICE(mrv_reg->isp_filt_thresh_bl1, ++ MRV_FILT_FILT_THRESH_BL1, 100); ++ REG_SET_SLICE(isp_filt_mode, MRV_FILT_STAGE1_SELECT, 2); ++ REG_SET_SLICE(isp_filt_mode, MRV_FILT_FILT_CHR_V_MODE, ++ MRV_FILT_FILT_CHR_V_MODE_STATIC12); ++ REG_SET_SLICE(isp_filt_mode, MRV_FILT_FILT_CHR_H_MODE, ++ MRV_FILT_FILT_CHR_H_MODE_DYN_2); ++ break; ++ ++ case 9: ++ /* NoiseReductionLevel = 9; */ ++ REG_SET_SLICE(mrv_reg->isp_filt_thresh_sh1, ++ MRV_FILT_FILT_THRESH_SH1, 300); ++ REG_SET_SLICE(mrv_reg->isp_filt_thresh_sh0, ++ MRV_FILT_FILT_THRESH_SH0, 250); ++ REG_SET_SLICE(mrv_reg->isp_filt_thresh_bl0, ++ MRV_FILT_FILT_THRESH_BL0, 180); ++ REG_SET_SLICE(mrv_reg->isp_filt_thresh_bl1, ++ MRV_FILT_FILT_THRESH_BL1, 150); ++ REG_SET_SLICE(isp_filt_mode, MRV_FILT_STAGE1_SELECT, ++ (sharp_level > 3) ? 2 : 1); ++ REG_SET_SLICE(isp_filt_mode, MRV_FILT_FILT_CHR_V_MODE, ++ MRV_FILT_FILT_CHR_V_MODE_STATIC12); ++ REG_SET_SLICE(isp_filt_mode, MRV_FILT_FILT_CHR_H_MODE, ++ MRV_FILT_FILT_CHR_H_MODE_DYN_2); ++ break; ++ ++ case 10: ++ /* NoiseReductionLevel = 10; extrem noise */ ++ REG_SET_SLICE(mrv_reg->isp_filt_thresh_sh1, ++ MRV_FILT_FILT_THRESH_SH1, 1023); ++ REG_SET_SLICE(mrv_reg->isp_filt_thresh_sh0, ++ MRV_FILT_FILT_THRESH_SH0, 1023); ++ REG_SET_SLICE(mrv_reg->isp_filt_thresh_bl0, ++ MRV_FILT_FILT_THRESH_BL0, 1023); ++ REG_SET_SLICE(mrv_reg->isp_filt_thresh_bl1, ++ MRV_FILT_FILT_THRESH_BL1, 1023); ++ REG_SET_SLICE(isp_filt_mode, MRV_FILT_STAGE1_SELECT, ++ (sharp_level > 5) ? 2 : ++ ((sharp_level > 3) ? 1 : 0)); ++ REG_SET_SLICE(isp_filt_mode, MRV_FILT_FILT_CHR_V_MODE, ++ MRV_FILT_FILT_CHR_V_MODE_STATIC12); ++ REG_SET_SLICE(isp_filt_mode, MRV_FILT_FILT_CHR_H_MODE, ++ MRV_FILT_FILT_CHR_H_MODE_DYN_2); ++ break; ++ ++ default: ++ return CI_STATUS_OUTOFRANGE; ++ } ++ ++ switch (sharp_level) { ++ /* SharpLevel = 0; no sharp enhancement */ ++ case 0: ++ REG_SET_SLICE(mrv_reg->isp_filt_fac_sh1, ++ MRV_FILT_FILT_FAC_SH1, 0x00000004); ++ REG_SET_SLICE(mrv_reg->isp_filt_fac_sh0, ++ MRV_FILT_FILT_FAC_SH0, 0x00000004); ++ REG_SET_SLICE(mrv_reg->isp_filt_fac_mid, ++ MRV_FILT_FILT_FAC_MID, 0x00000004); ++ REG_SET_SLICE(mrv_reg->isp_filt_fac_bl0, ++ MRV_FILT_FILT_FAC_BL0, 0x00000002); ++ REG_SET_SLICE(mrv_reg->isp_filt_fac_bl1, ++ MRV_FILT_FILT_FAC_BL1, 0x00000000); ++ break; ++ ++ /* SharpLevel = 1; */ ++ case 1: ++ REG_SET_SLICE(mrv_reg->isp_filt_fac_sh1, ++ MRV_FILT_FILT_FAC_SH1, 0x00000008); ++ REG_SET_SLICE(mrv_reg->isp_filt_fac_sh0, ++ MRV_FILT_FILT_FAC_SH0, 0x00000007); ++ REG_SET_SLICE(mrv_reg->isp_filt_fac_mid, ++ MRV_FILT_FILT_FAC_MID, 0x00000006); ++ REG_SET_SLICE(mrv_reg->isp_filt_fac_bl0, ++ MRV_FILT_FILT_FAC_BL0, 0x00000002); ++ REG_SET_SLICE(mrv_reg->isp_filt_fac_bl1, ++ MRV_FILT_FILT_FAC_BL1, 0x00000000); ++ break; ++ ++ /* SharpLevel = 2; */ ++ case 2: ++ REG_SET_SLICE(mrv_reg->isp_filt_fac_sh1, ++ MRV_FILT_FILT_FAC_SH1, 0x0000000C); ++ REG_SET_SLICE(mrv_reg->isp_filt_fac_sh0, ++ MRV_FILT_FILT_FAC_SH0, 0x0000000A); ++ REG_SET_SLICE(mrv_reg->isp_filt_fac_mid, ++ MRV_FILT_FILT_FAC_MID, 0x00000008); ++ REG_SET_SLICE(mrv_reg->isp_filt_fac_bl0, ++ MRV_FILT_FILT_FAC_BL0, 0x00000004); ++ REG_SET_SLICE(mrv_reg->isp_filt_fac_bl1, ++ MRV_FILT_FILT_FAC_BL1, 0x00000000); ++ break; ++ ++ /* SharpLevel = 3; */ ++ case 3: ++ REG_SET_SLICE(mrv_reg->isp_filt_fac_sh1, ++ MRV_FILT_FILT_FAC_SH1, 0x00000010); ++ REG_SET_SLICE(mrv_reg->isp_filt_fac_sh0, ++ MRV_FILT_FILT_FAC_SH0, 0x0000000C); ++ REG_SET_SLICE(mrv_reg->isp_filt_fac_mid, ++ MRV_FILT_FILT_FAC_MID, 0x0000000A); ++ REG_SET_SLICE(mrv_reg->isp_filt_fac_bl0, ++ MRV_FILT_FILT_FAC_BL0, 0x00000006); ++ REG_SET_SLICE(mrv_reg->isp_filt_fac_bl1, ++ MRV_FILT_FILT_FAC_BL1, 0x00000002); ++ break; ++ ++ /* SharpLevel = 4; */ ++ case 4: ++ REG_SET_SLICE(mrv_reg->isp_filt_fac_sh1, ++ MRV_FILT_FILT_FAC_SH1, 0x00000016); ++ REG_SET_SLICE(mrv_reg->isp_filt_fac_sh0, ++ MRV_FILT_FILT_FAC_SH0, 0x00000010); ++ REG_SET_SLICE(mrv_reg->isp_filt_fac_mid, ++ MRV_FILT_FILT_FAC_MID, 0x0000000C); ++ REG_SET_SLICE(mrv_reg->isp_filt_fac_bl0, ++ MRV_FILT_FILT_FAC_BL0, 0x00000008); ++ REG_SET_SLICE(mrv_reg->isp_filt_fac_bl1, ++ MRV_FILT_FILT_FAC_BL1, 0x00000004); ++ break; ++ ++ /* SharpLevel = 5; */ ++ case 5: ++ REG_SET_SLICE(mrv_reg->isp_filt_fac_sh1, ++ MRV_FILT_FILT_FAC_SH1, 0x0000001B); ++ REG_SET_SLICE(mrv_reg->isp_filt_fac_sh0, ++ MRV_FILT_FILT_FAC_SH0, 0x00000014); ++ REG_SET_SLICE(mrv_reg->isp_filt_fac_mid, ++ MRV_FILT_FILT_FAC_MID, 0x00000010); ++ REG_SET_SLICE(mrv_reg->isp_filt_fac_bl0, ++ MRV_FILT_FILT_FAC_BL0, 0x0000000A); ++ REG_SET_SLICE(mrv_reg->isp_filt_fac_bl1, ++ MRV_FILT_FILT_FAC_BL1, 0x00000004); ++ break; ++ ++ /* SharpLevel = 6; */ ++ case 6: ++ REG_SET_SLICE(mrv_reg->isp_filt_fac_sh1, ++ MRV_FILT_FILT_FAC_SH1, 0x00000020); ++ REG_SET_SLICE(mrv_reg->isp_filt_fac_sh0, ++ MRV_FILT_FILT_FAC_SH0, 0x0000001A); ++ REG_SET_SLICE(mrv_reg->isp_filt_fac_mid, ++ MRV_FILT_FILT_FAC_MID, 0x00000013); ++ REG_SET_SLICE(mrv_reg->isp_filt_fac_bl0, ++ MRV_FILT_FILT_FAC_BL0, 0x0000000C); ++ REG_SET_SLICE(mrv_reg->isp_filt_fac_bl1, ++ MRV_FILT_FILT_FAC_BL1, 0x00000006); ++ break; ++ ++ /* SharpLevel = 7; */ ++ case 7: ++ REG_SET_SLICE(mrv_reg->isp_filt_fac_sh1, ++ MRV_FILT_FILT_FAC_SH1, 0x00000026); ++ REG_SET_SLICE(mrv_reg->isp_filt_fac_sh0, ++ MRV_FILT_FILT_FAC_SH0, 0x0000001E); ++ REG_SET_SLICE(mrv_reg->isp_filt_fac_mid, ++ MRV_FILT_FILT_FAC_MID, 0x00000017); ++ REG_SET_SLICE(mrv_reg->isp_filt_fac_bl0, ++ MRV_FILT_FILT_FAC_BL0, 0x00000010); ++ REG_SET_SLICE(mrv_reg->isp_filt_fac_bl1, ++ MRV_FILT_FILT_FAC_BL1, 0x00000008); ++ break; ++ ++ /* SharpLevel = 8; */ ++ case 8: ++ REG_SET_SLICE(mrv_reg->isp_filt_thresh_sh0, ++ MRV_FILT_FILT_THRESH_SH0, 0x00000013); ++ if (REG_GET_SLICE(mrv_reg->isp_filt_thresh_sh1, ++ MRV_FILT_FILT_THRESH_SH1) > 0x0000008A) { ++ REG_SET_SLICE(mrv_reg->isp_filt_thresh_sh1, ++ MRV_FILT_FILT_THRESH_SH1, ++ 0x0000008A); ++ } ++ /* 43 */ ++ REG_SET_SLICE(mrv_reg->isp_filt_fac_sh1, ++ MRV_FILT_FILT_FAC_SH1, 0x0000002C); ++ /* 36 */ ++ REG_SET_SLICE(mrv_reg->isp_filt_fac_sh0, ++ MRV_FILT_FILT_FAC_SH0, 0x00000024); ++ /* 29 */ ++ REG_SET_SLICE(mrv_reg->isp_filt_fac_mid, ++ MRV_FILT_FILT_FAC_MID, 0x0000001D); ++ /* 21 */ ++ REG_SET_SLICE(mrv_reg->isp_filt_fac_bl0, ++ MRV_FILT_FILT_FAC_BL0, 0x00000015); ++ /* 14 */ ++ REG_SET_SLICE(mrv_reg->isp_filt_fac_bl1, ++ MRV_FILT_FILT_FAC_BL1, 0x0000000D); ++ break; ++ ++ /* SharpLevel = 9; */ ++ case 9: ++ REG_SET_SLICE(mrv_reg->isp_filt_thresh_sh0, ++ MRV_FILT_FILT_THRESH_SH0, 0x00000013); ++ if (REG_GET_SLICE(mrv_reg->isp_filt_thresh_sh1, ++ MRV_FILT_FILT_THRESH_SH1) > 0x0000008A) { ++ REG_SET_SLICE(mrv_reg->isp_filt_thresh_sh1, ++ MRV_FILT_FILT_THRESH_SH1, ++ 0x0000008A); ++ } ++ /* 48 */ ++ REG_SET_SLICE(mrv_reg->isp_filt_fac_sh1, ++ MRV_FILT_FILT_FAC_SH1, 0x00000030); ++ /* 42 */ ++ REG_SET_SLICE(mrv_reg->isp_filt_fac_sh0, ++ MRV_FILT_FILT_FAC_SH0, 0x0000002A); ++ /* 34 */ ++ REG_SET_SLICE(mrv_reg->isp_filt_fac_mid, ++ MRV_FILT_FILT_FAC_MID, 0x00000022); ++ /* 26 */ ++ REG_SET_SLICE(mrv_reg->isp_filt_fac_bl0, ++ MRV_FILT_FILT_FAC_BL0, 0x0000001A); ++ /* 20 */ ++ REG_SET_SLICE(mrv_reg->isp_filt_fac_bl1, ++ MRV_FILT_FILT_FAC_BL1, 0x00000014); ++ break; ++ ++ /* SharpLevel = 10; */ ++ case 10: ++ /* REG_SET_SLICE(mrv_reg->isp_filt_thresh_sh0, ++ * MRV_FILT_FILT_THRESH_SH0, 0x00000013); */ ++ /* if (REG_GET_SLICE(mrv_reg->isp_filt_thresh_sh1, ++ * MRV_FILT_FILT_THRESH_SH1) > 0x0000008A) */ ++ /* { */ ++ /* REG_SET_SLICE(mrv_reg->isp_filt_thresh_sh1, ++ * MRV_FILT_FILT_THRESH_SH1, 0x0000008A); */ ++ /* } */ ++ ++ /* 63 */ ++ REG_SET_SLICE(mrv_reg->isp_filt_fac_sh1, ++ MRV_FILT_FILT_FAC_SH1, 0x0000003F); ++ /* 48 */ ++ REG_SET_SLICE(mrv_reg->isp_filt_fac_sh0, ++ MRV_FILT_FILT_FAC_SH0, 0x00000030); ++ /* 40 */ ++ REG_SET_SLICE(mrv_reg->isp_filt_fac_mid, ++ MRV_FILT_FILT_FAC_MID, 0x00000028); ++ /* 36 */ ++ REG_SET_SLICE(mrv_reg->isp_filt_fac_bl0, ++ MRV_FILT_FILT_FAC_BL0, 0x00000024); ++ /* 32 */ ++ REG_SET_SLICE(mrv_reg->isp_filt_fac_bl1, ++ MRV_FILT_FILT_FAC_BL1, 0x00000020); ++ break; ++ ++ default: ++ return CI_STATUS_OUTOFRANGE; ++ } ++ ++ if (noise_reduc_level > 7) { ++ if (sharp_level > 7) { ++ u32 filt_fac_bl0 = REG_GET_SLICE ++ (mrv_reg->isp_filt_fac_bl0, ++ MRV_FILT_FILT_FAC_BL0); ++ u32 filt_fac_bl1 = ++ REG_GET_SLICE(mrv_reg->isp_filt_fac_bl1, ++ MRV_FILT_FILT_FAC_BL1); ++ /* * 0.50 */ ++ REG_SET_SLICE(mrv_reg->isp_filt_fac_bl0, ++ MRV_FILT_FILT_FAC_BL0, ++ (filt_fac_bl0) >> 1); ++ /* * 0.25 */ ++ REG_SET_SLICE(mrv_reg->isp_filt_fac_bl1, ++ MRV_FILT_FILT_FAC_BL1, ++ (filt_fac_bl1) >> 2); ++ } else if (sharp_level > 4) { ++ u32 filt_fac_bl0 = ++ REG_GET_SLICE(mrv_reg->isp_filt_fac_bl0, ++ MRV_FILT_FILT_FAC_BL0); ++ u32 filt_fac_bl1 = ++ REG_GET_SLICE(mrv_reg-> ++ isp_filt_fac_bl1, ++ MRV_FILT_FILT_FAC_BL1); ++ /* * 0.75 */ ++ REG_SET_SLICE(mrv_reg->isp_filt_fac_bl0, ++ MRV_FILT_FILT_FAC_BL0, ++ (filt_fac_bl0 * 3) >> 2); ++ /* * 0.50 */ ++ REG_SET_SLICE(mrv_reg->isp_filt_fac_bl1, ++ MRV_FILT_FILT_FAC_BL1, ++ (filt_fac_bl1) >> 1); ++ } ++ } ++ ++ /* Set ISP filter mode register values */ ++ REG_SET_SLICE(isp_filt_mode, MRV_FILT_FILT_MODE, ++ MRV_FILT_FILT_MODE_DYNAMIC); ++ ++ /* enable filter */ ++ REG_SET_SLICE(isp_filt_mode, MRV_FILT_FILT_ENABLE, ENABLE); ++ REG_WRITE(mrv_reg->isp_filt_mode, isp_filt_mode); ++ ++ return CI_STATUS_SUCCESS; ++ } else { ++ /* At least one function parameter is out of range */ ++ return CI_STATUS_OUTOFRANGE; ++ } ++} ++ ++int ci_isp_meas_exposure_initialize_module(void) ++{ ++ struct isp_register *mrv_reg = (struct isp_register *) MEM_MRV_REG_BASE; ++ ++ REG_SET_SLICE(mrv_reg->isp_exp_h_size, MRV_AE_ISP_EXP_H_SIZE, 0); ++ REG_SET_SLICE(mrv_reg->isp_exp_v_size, MRV_AE_ISP_EXP_V_SIZE, 0); ++ REG_SET_SLICE(mrv_reg->isp_exp_h_offset, MRV_AE_ISP_EXP_H_OFFSET, 0); ++ REG_SET_SLICE(mrv_reg->isp_exp_v_offset, MRV_AE_ISP_EXP_V_OFFSET, 0); ++ ++ return CI_STATUS_SUCCESS; ++ ++} ++ ++/* ++ * Configures the exposure measurement module. ++ */ ++int ci_isp_meas_exposure_set_config(const struct ci_isp_window *wnd, ++ const struct ci_isp_exp_ctrl *isp_exp_ctrl) ++{ ++ struct isp_register *mrv_reg = (struct isp_register *) MEM_MRV_REG_BASE; ++ ++ if (!wnd) { ++ /* stop loop if running */ ++ REG_SET_SLICE(mrv_reg->isp_exp_ctrl, MRV_AE_AUTOSTOP, ON); ++ /* required? */ ++ REG_SET_SLICE(mrv_reg->isp_exp_ctrl, MRV_AE_EXP_START, OFF); ++ return CI_STATUS_SUCCESS; ++ } ++ ++ /* range check */ ++ if ((wnd->hoffs > MRV_AE_ISP_EXP_H_OFFSET_MAX) ++ || (wnd->hsize > MRV_AE_ISP_EXP_H_SIZE_MAX) ++ || (wnd->voffs > MRV_AE_ISP_EXP_V_OFFSET_MAX) ++ || (wnd->vsize > MRV_AE_ISP_EXP_V_SIZE_MAX) ++ || (wnd->vsize & ~MRV_AE_ISP_EXP_V_SIZE_VALID_MASK)) ++ return CI_STATUS_OUTOFRANGE; ++ ++ /* configure measurement windows */ ++ REG_SET_SLICE(mrv_reg->isp_exp_h_size, MRV_AE_ISP_EXP_H_SIZE, ++ wnd->hsize); ++ REG_SET_SLICE(mrv_reg->isp_exp_v_size, MRV_AE_ISP_EXP_V_SIZE, ++ wnd->vsize); ++ REG_SET_SLICE(mrv_reg->isp_exp_h_offset, MRV_AE_ISP_EXP_H_OFFSET, ++ wnd->hoffs); ++ REG_SET_SLICE(mrv_reg->isp_exp_v_offset, MRV_AE_ISP_EXP_V_OFFSET, ++ wnd->voffs); ++ ++ /* set exposure measurement mode */ ++ REG_SET_SLICE(mrv_reg->isp_exp_ctrl, MRV_AE_EXP_MEAS_MODE, ++ (isp_exp_ctrl->exp_meas_mode) ? ON : OFF); ++ ++ /* set or clear AE autostop bit */ ++ REG_SET_SLICE(mrv_reg->isp_exp_ctrl, MRV_AE_AUTOSTOP, ++ (isp_exp_ctrl->auto_stop) ? ON : OFF); ++ REG_SET_SLICE(mrv_reg->isp_exp_ctrl, MRV_AE_EXP_START, ++ (isp_exp_ctrl->exp_start) ? ON : OFF); ++ ++ return CI_STATUS_SUCCESS; ++} ++ ++/* ++ * Programs the given gamma curve for the input gamma ++ * block. Enables or disables gamma processing for the ++ * input gamma block. ++ */ ++void ci_isp_set_gamma(const struct ci_sensor_gamma_curve *r, ++ const struct ci_sensor_gamma_curve *g, ++ const struct ci_sensor_gamma_curve *b) ++{ ++ struct isp_register *mrv_reg = (struct isp_register *)MEM_MRV_REG_BASE; ++ /* values stored as 16bit - use MSBs if cambuswidth is smaller */ ++ const u8 shift_val = 16 - MARVIN_FEATURE_CAMBUSWIDTH; ++ /* used to round up values */ ++ const u16 round_ofs = 0 << (shift_val - 1); ++ s32 i; ++ ++ if (r) { ++ ++ /* ++ * Note: gamma curve increments are already register conform, ++ * so REG_WRITE is used instead of REG_SET_SLICE ++ */ ++ ++ /* ++ * better would be split into 16 separate values to be ++ * register independant ++ */ ++ ++ /* gamma curve dx1..dx16 increments (each nibble of */ ++ REG_WRITE(mrv_reg->isp_gamma_dx_lo, r->gamma_dx0); ++ /* the 32bit-values hold 3 valid bits, see register) */ ++ REG_WRITE(mrv_reg->isp_gamma_dx_hi, r->gamma_dx1); ++ ++ for (i = 0; i < MRV_ISP_GAMMA_R_Y_ARR_SIZE; i++) { ++ REG_SET_SLICE(mrv_reg->isp_gamma_r_y[i], ++ MRV_ISP_GAMMA_R_Y, ++ (r->isp_gamma_y[i] + round_ofs) >> shift_val); ++ REG_SET_SLICE(mrv_reg->isp_gamma_g_y[i], ++ MRV_ISP_GAMMA_G_Y, ++ (g->isp_gamma_y[i] + round_ofs) >> shift_val); ++ REG_SET_SLICE(mrv_reg->isp_gamma_b_y[i], ++ MRV_ISP_GAMMA_B_Y, ++ (b->isp_gamma_y[i] + round_ofs) >> shift_val); ++ } ++ ++ REG_SET_SLICE(mrv_reg->isp_ctrl, ++ MRV_ISP_ISP_GAMMA_IN_ENABLE, ENABLE); ++ } else { ++ REG_SET_SLICE(mrv_reg->isp_ctrl, ++ MRV_ISP_ISP_GAMMA_IN_ENABLE, DISABLE); ++ } ++} ++ ++/* ++ * Programs the given gamma curve for the output gamma ++ * block. Enables or disables gamma processing for the ++ * output gamma block. ++ */ ++void ci_isp_set_gamma2(const struct ci_isp_gamma_out_curve *gamma) ++{ ++ struct isp_register *mrv_reg = (struct isp_register *) MEM_MRV_REG_BASE; ++ s32 i; ++ ++ if (gamma) { ++ WARN_ON(!(MRV_ISP_GAMMA_OUT_Y_ARR_SIZE == ++ CI_ISP_GAMMA_OUT_CURVE_ARR_SIZE)); ++ ++ for (i = 0; i < MRV_ISP_GAMMA_OUT_Y_ARR_SIZE; i++) { ++ REG_SET_SLICE(mrv_reg->isp_gamma_out_y[i], ++ MRV_ISP_ISP_GAMMA_OUT_Y, ++ gamma->isp_gamma_y[i]); ++ } ++ ++ /* gamma curve linear or log */ ++ REG_SET_SLICE(mrv_reg->isp_gamma_out_mode, MRV_ISP_EQU_SEGM, ++ gamma->gamma_segmentation); ++ REG_SET_SLICE(mrv_reg->isp_ctrl, MRV_ISP_ISP_GAMMA_OUT_ENABLE, ++ ENABLE); ++ } else { ++ REG_SET_SLICE(mrv_reg->isp_ctrl, ++ MRV_ISP_ISP_GAMMA_OUT_ENABLE, DISABLE); ++ } ++ ++} +diff --git a/drivers/media/video/mrstci/mrstisp/mrstisp_jpe.c b/drivers/media/video/mrstci/mrstisp/mrstisp_jpe.c +new file mode 100644 +index 0000000..c042e06 +--- /dev/null ++++ b/drivers/media/video/mrstci/mrstisp/mrstisp_jpe.c +@@ -0,0 +1,569 @@ ++/* ++ * Support for Moorestown Langwell Camera Imaging ISP subsystem. ++ * ++ * Copyright (c) 2009 Intel Corporation. All Rights Reserved. ++ * ++ * Copyright (c) Silicon Image 2008 www.siliconimage.com ++ * ++ * 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 Street, Fifth Floor, Boston, MA ++ * 02110-1301, USA. ++ * ++ * ++ * Xiaolin Zhang ++ */ ++ ++#include "mrstisp_stdinc.h" ++ ++int ci_isp_jpe_init_ex(u16 hsize, u16 vsize, u8 compression_ratio, u8 jpe_scale) ++{ ++ struct isp_register *mrv_reg = (struct isp_register *) MEM_MRV_REG_BASE; ++ ++ /* ++ * Reset JPEG-Encoder. In contrast to other software resets ++ * this triggers the modules asynchronous reset resulting ++ * in loss of all data. ++ */ ++ ++ REG_SET_SLICE(mrv_reg->vi_ircl, MRV_VI_JPEG_SOFT_RST, ON); ++ REG_SET_SLICE(mrv_reg->vi_ircl, MRV_VI_JPEG_SOFT_RST, OFF); ++ ++ /* set configuration for the Jpeg capturing */ ++ ci_isp_jpe_set_config(hsize, vsize, jpe_scale); ++ ++ /* ++ * Sleep a while before setting up tables because of the 400 ++ * clock cycles required to initialize the table RAM after a ++ * reset was issued. On FPGA we are running with only 30MHz, ++ * so at least 13us are required. ++ */ ++ ++ ++ /* ++ * Note: this func is called when holding spin lock, ++ * so can not change to msleep. ++ */ ++ mdelay(15); ++ ++ /* program tables */ ++ ci_isp_jpe_set_tables(compression_ratio); ++ ++ /* choose tables */ ++ ci_isp_jpe_select_tables(); ++ ++ return CI_STATUS_SUCCESS; ++} ++ ++/* ++ * initialization of JPEG encoder ++ */ ++int ci_isp_jpe_init(u32 resolution, u8 compression_ratio, int jpe_scale) ++{ ++ u16 hsize = 0; ++ u16 vsize = 0; ++ ++ switch (resolution) { ++ case SENSOR_RES_BP1: ++ /* 352; */ ++ hsize = BP1_SIZE_H; ++ /* 240; */ ++ vsize = BP1_SIZE_V; ++ break; ++ case SENSOR_RES_S_AFM: ++ /* 64; */ ++ hsize = S_AFM_SIZE_H; ++ /* 32; */ ++ vsize = S_AFM_SIZE_V; ++ break; ++ case SENSOR_RES_M_AFM: ++ /* 128; */ ++ hsize = M_AFM_SIZE_H; ++ /* 96; */ ++ vsize = M_AFM_SIZE_V; ++ break; ++ case SENSOR_RES_L_AFM: ++ /* 720; */ ++ hsize = L_AFM_SIZE_H; ++ /* 480; */ ++ vsize = L_AFM_SIZE_V; ++ break; ++ case SENSOR_RES_QQCIF: ++ /* 88; */ ++ hsize = QQCIF_SIZE_H; ++ /* 72; */ ++ vsize = QQCIF_SIZE_V; ++ break; ++ case SENSOR_RES_QQVGA: ++ /* 160; */ ++ hsize = QQVGA_SIZE_H; ++ /* 120; */ ++ vsize = QQVGA_SIZE_V; ++ break; ++ case SENSOR_RES_QCIF: ++ /* 176; */ ++ hsize = QCIF_SIZE_H; ++ /* 144; */ ++ vsize = QCIF_SIZE_V; ++ break; ++ case SENSOR_RES_QVGA: ++ /* 320; */ ++ hsize = QVGA_SIZE_H; ++ /* 240; */ ++ vsize = QVGA_SIZE_V; ++ break; ++ case SENSOR_RES_CIF: ++ /* 352; */ ++ hsize = CIF_SIZE_H; ++ /* 288; */ ++ vsize = CIF_SIZE_V; ++ break; ++ case SENSOR_RES_VGA: ++ /* 640; */ ++ hsize = VGA_SIZE_H; ++ /* 480; */ ++ vsize = VGA_SIZE_V; ++ break; ++ case SENSOR_RES_SVGA: ++ /* 800; */ ++ hsize = SVGA_SIZE_H; ++ /* 600; */ ++ vsize = SVGA_SIZE_V; ++ break; ++ case SENSOR_RES_XGA: ++ /* 1024; */ ++ hsize = XGA_SIZE_H; ++ /* 768; */ ++ vsize = XGA_SIZE_V; ++ break; ++ case SENSOR_RES_XGA_PLUS: ++ /* 1280; */ ++ hsize = XGA_PLUS_SIZE_H; ++ /* 960; */ ++ vsize = XGA_PLUS_SIZE_V; ++ break; ++ case SENSOR_RES_SXGA: ++ /* 1280; */ ++ hsize = SXGA_SIZE_H; ++ /* 1024; */ ++ vsize = SXGA_SIZE_V; ++ break; ++ case SENSOR_RES_UXGA: ++ /* 1600; */ ++ hsize = UXGA_SIZE_H; ++ /* 1200; */ ++ vsize = UXGA_SIZE_V; ++ break; ++ case SENSOR_RES_QXGA: ++ /* 2048; */ ++ hsize = QXGA_SIZE_H; ++ /* 1536; */ ++ vsize = QXGA_SIZE_V; ++ break; ++ case SENSOR_RES_QSXGA: ++ /* 2586; */ ++ hsize = QSXGA_SIZE_H; ++ /* 2048; */ ++ vsize = QSXGA_SIZE_V; ++ break; ++ case SENSOR_RES_QSXGA_PLUS: ++ /* 2600; */ ++ hsize = QSXGA_PLUS_SIZE_H; ++ /* 2048; */ ++ vsize = QSXGA_PLUS_SIZE_V; ++ break; ++ case SENSOR_RES_QSXGA_PLUS2: ++ /* 2600; */ ++ hsize = QSXGA_PLUS2_SIZE_H; ++ /* 1950; */ ++ vsize = QSXGA_PLUS2_SIZE_V; ++ break; ++ case SENSOR_RES_QSXGA_PLUS3: ++ /* 2686; */ ++ hsize = QSXGA_PLUS3_SIZE_H; ++ /* 2048; */ ++ vsize = QSXGA_PLUS3_SIZE_V; ++ break; ++ case SENSOR_RES_WQSXGA: ++ /* 3200 */ ++ hsize = WQSXGA_SIZE_H; ++ /* 2048 */ ++ vsize = WQSXGA_SIZE_V; ++ break; ++ case SENSOR_RES_QUXGA: ++ /* 3200 */ ++ hsize = QUXGA_SIZE_H; ++ /* 2400 */ ++ vsize = QUXGA_SIZE_V; ++ break; ++ case SENSOR_RES_WQUXGA: ++ /* 3840 */ ++ hsize = WQUXGA_SIZE_H; ++ /* 2400 */ ++ vsize = WQUXGA_SIZE_V; ++ break; ++ case SENSOR_RES_HXGA: ++ /* 4096 */ ++ hsize = HXGA_SIZE_H; ++ /* 3072 */ ++ vsize = HXGA_SIZE_V; ++ break; ++ default: ++ eprintk("resolution not supported"); ++ return CI_STATUS_NOTSUPP; ++ } ++ ++ return ci_isp_jpe_init_ex(hsize, vsize, compression_ratio, jpe_scale); ++} ++ ++void ci_isp_jpe_set_tables(u8 compression_ratio) ++{ ++ struct isp_register *mrv_reg = (struct isp_register *) MEM_MRV_REG_BASE; ++ /* required because auto-increment register */ ++ u32 jpe_table_data = 0; ++ ++ u8 idx, size; ++ const u8 *yqtable = NULL; ++ const u8 *uvqtable = NULL; ++ ++ switch (compression_ratio) { ++ case CI_ISP_JPEG_LOW_COMPRESSION: ++ yqtable = ci_isp_yq_table_low_comp1; ++ uvqtable = ci_isp_uv_qtable_low_comp1; ++ break; ++ case CI_ISP_JPEG_01_PERCENT: ++ yqtable = ci_isp_yq_table01_per_cent; ++ uvqtable = ci_isp_uv_qtable01_per_cent; ++ break; ++ case CI_ISP_JPEG_20_PERCENT: ++ yqtable = ci_isp_yq_table20_per_cent; ++ uvqtable = ci_isp_uv_qtable20_per_cent; ++ break; ++ case CI_ISP_JPEG_30_PERCENT: ++ yqtable = ci_isp_yq_table30_per_cent; ++ uvqtable = ci_isp_uv_qtable30_per_cent; ++ break; ++ case CI_ISP_JPEG_40_PERCENT: ++ yqtable = ci_isp_yq_table40_per_cent; ++ uvqtable = ci_isp_uv_qtable40_per_cent; ++ break; ++ case CI_ISP_JPEG_50_PERCENT: ++ yqtable = ci_isp_yq_table50_per_cent; ++ uvqtable = ci_isp_uv_qtable50_per_cent; ++ break; ++ case CI_ISP_JPEG_60_PERCENT: ++ yqtable = ci_isp_yq_table60_per_cent; ++ uvqtable = ci_isp_uv_qtable60_per_cent; ++ break; ++ case CI_ISP_JPEG_70_PERCENT: ++ yqtable = ci_isp_yq_table70_per_cent; ++ uvqtable = ci_isp_uv_qtable70_per_cent; ++ break; ++ case CI_ISP_JPEG_80_PERCENT: ++ yqtable = ci_isp_yq_table80_per_cent; ++ uvqtable = ci_isp_uv_qtable80_per_cent; ++ break; ++ case CI_ISP_JPEG_90_PERCENT: ++ yqtable = ci_isp_yq_table90_per_cent; ++ uvqtable = ci_isp_uv_qtable90_per_cent; ++ break; ++ case CI_ISP_JPEG_99_PERCENT: ++ yqtable = ci_isp_yq_table99_per_cent; ++ uvqtable = ci_isp_uv_qtable99_per_cent; ++ break; ++ case CI_ISP_JPEG_HIGH_COMPRESSION: ++ default: ++ /* ++ * in the case an unknown value is set, ++ * use CI_JPEG_HIGH_COMPRESSION ++ */ ++ yqtable = ci_isp_yq_table75_per_cent; ++ uvqtable = ci_isp_uv_qtable75_per_cent; ++ break; ++ } ++ ++ /* Y q-table 0 programming */ ++ ++ /* all possible assigned tables have same size */ ++ size = sizeof(ci_isp_yq_table75_per_cent)/ ++ sizeof(ci_isp_yq_table75_per_cent[0]); ++ REG_SET_SLICE(mrv_reg->jpe_table_id, MRV_JPE_TABLE_ID, ++ MRV_JPE_TABLE_ID_QUANT0); ++ for (idx = 0; idx < (size - 1); idx += 2) { ++ REG_SET_SLICE(jpe_table_data, MRV_JPE_TABLE_WDATA_H, ++ yqtable[idx]); ++ REG_SET_SLICE(jpe_table_data, MRV_JPE_TABLE_WDATA_L, ++ yqtable[idx + 1]); ++ /* auto-increment register! */ ++ REG_WRITE(mrv_reg->jpe_table_data, jpe_table_data); ++ } ++ ++ /* U/V q-table 0 programming */ ++ ++ /* all possible assigned tables have same size */ ++ size = sizeof(ci_isp_uv_qtable75_per_cent) / ++ sizeof(ci_isp_uv_qtable75_per_cent[0]); ++ REG_SET_SLICE(mrv_reg->jpe_table_id, MRV_JPE_TABLE_ID, ++ MRV_JPE_TABLE_ID_QUANT1); ++ for (idx = 0; idx < (size - 1); idx += 2) { ++ REG_SET_SLICE(jpe_table_data, MRV_JPE_TABLE_WDATA_H, ++ uvqtable[idx]); ++ REG_SET_SLICE(jpe_table_data, MRV_JPE_TABLE_WDATA_L, ++ uvqtable[idx + 1]); ++ /* auto-increment register! */ ++ REG_WRITE(mrv_reg->jpe_table_data, jpe_table_data); ++ } ++ ++ /* Y AC-table 0 programming */ ++ ++ size = sizeof(ci_isp_ac_luma_table_annex_k) / ++ sizeof(ci_isp_ac_luma_table_annex_k[0]); ++ REG_SET_SLICE(mrv_reg->jpe_table_id, MRV_JPE_TABLE_ID, ++ MRV_JPE_TABLE_ID_VLC_AC0); ++ REG_SET_SLICE(mrv_reg->jpe_tac0_len, MRV_JPE_TAC0_LEN, size); ++ for (idx = 0; idx < (size - 1); idx += 2) { ++ REG_SET_SLICE(jpe_table_data, MRV_JPE_TABLE_WDATA_H, ++ ci_isp_ac_luma_table_annex_k[idx]); ++ REG_SET_SLICE(jpe_table_data, MRV_JPE_TABLE_WDATA_L, ++ ci_isp_ac_luma_table_annex_k[idx + 1]); ++ /* auto-increment register! */ ++ REG_WRITE(mrv_reg->jpe_table_data, jpe_table_data); ++ } ++ ++ /* U/V AC-table 1 programming */ ++ ++ size = sizeof(ci_isp_ac_chroma_table_annex_k) / ++ sizeof(ci_isp_ac_chroma_table_annex_k[0]); ++ REG_SET_SLICE(mrv_reg->jpe_table_id, MRV_JPE_TABLE_ID, ++ MRV_JPE_TABLE_ID_VLC_AC1); ++ REG_SET_SLICE(mrv_reg->jpe_tac1_len, MRV_JPE_TAC1_LEN, size); ++ for (idx = 0; idx < (size - 1); idx += 2) { ++ REG_SET_SLICE(jpe_table_data, MRV_JPE_TABLE_WDATA_H, ++ ci_isp_ac_chroma_table_annex_k[idx]); ++ REG_SET_SLICE(jpe_table_data, MRV_JPE_TABLE_WDATA_L, ++ ci_isp_ac_chroma_table_annex_k[idx + 1]); ++ /* auto-increment register! */ ++ REG_WRITE(mrv_reg->jpe_table_data, jpe_table_data); ++ } ++ ++ /* Y DC-table 0 programming */ ++ ++ size = sizeof(ci_isp_dc_luma_table_annex_k) / ++ sizeof(ci_isp_dc_luma_table_annex_k[0]); ++ REG_SET_SLICE(mrv_reg->jpe_table_id, MRV_JPE_TABLE_ID, ++ MRV_JPE_TABLE_ID_VLC_DC0); ++ REG_SET_SLICE(mrv_reg->jpe_tdc0_len, MRV_JPE_TDC0_LEN, size); ++ for (idx = 0; idx < (size - 1); idx += 2) { ++ REG_SET_SLICE(jpe_table_data, MRV_JPE_TABLE_WDATA_H, ++ ci_isp_dc_luma_table_annex_k[idx]); ++ REG_SET_SLICE(jpe_table_data, MRV_JPE_TABLE_WDATA_L, ++ ci_isp_dc_luma_table_annex_k[idx + 1]); ++ /* auto-increment register! */ ++ REG_WRITE(mrv_reg->jpe_table_data, jpe_table_data); ++ } ++ ++ /* U/V DC-table 1 programming */ ++ ++ size = sizeof(ci_isp_dc_chroma_table_annex_k) / ++ sizeof(ci_isp_dc_chroma_table_annex_k[0]); ++ REG_SET_SLICE(mrv_reg->jpe_table_id, MRV_JPE_TABLE_ID, ++ MRV_JPE_TABLE_ID_VLC_DC1); ++ REG_SET_SLICE(mrv_reg->jpe_tdc1_len, MRV_JPE_TDC1_LEN, size); ++ for (idx = 0; idx < (size - 1); idx += 2) { ++ REG_SET_SLICE(jpe_table_data, MRV_JPE_TABLE_WDATA_H, ++ ci_isp_dc_chroma_table_annex_k[idx]); ++ REG_SET_SLICE(jpe_table_data, MRV_JPE_TABLE_WDATA_L, ++ ci_isp_dc_chroma_table_annex_k[idx + 1]); ++ /* auto-increment register! */ ++ REG_WRITE(mrv_reg->jpe_table_data, jpe_table_data); ++ } ++} ++ ++/* ++ * selects tables to be used by encoder ++ */ ++void ci_isp_jpe_select_tables(void) ++{ ++ struct isp_register *mrv_reg = (struct isp_register *) MEM_MRV_REG_BASE; ++ ++ /* selects quantization table for Y */ ++ REG_SET_SLICE(mrv_reg->jpe_tq_y_select, MRV_JPE_TQ0_SELECT, ++ MRV_JPE_TQ_SELECT_TAB0); ++ /* selects quantization table for U */ ++ REG_SET_SLICE(mrv_reg->jpe_tq_u_select, MRV_JPE_TQ1_SELECT, ++ MRV_JPE_TQ_SELECT_TAB1); ++ /* selects quantization table for V */ ++ REG_SET_SLICE(mrv_reg->jpe_tq_v_select, MRV_JPE_TQ2_SELECT, ++ MRV_JPE_TQ_SELECT_TAB1); ++ /* selects Huffman DC table */ ++ REG_SET_SLICE(mrv_reg->jpe_dc_table_select, ++ MRV_JPE_DC_TABLE_SELECT_Y, 0); ++ REG_SET_SLICE(mrv_reg->jpe_dc_table_select, ++ MRV_JPE_DC_TABLE_SELECT_U, 1); ++ REG_SET_SLICE(mrv_reg->jpe_dc_table_select, ++ MRV_JPE_DC_TABLE_SELECT_V, 1); ++ /* selects Huffman AC table */ ++ REG_SET_SLICE(mrv_reg->jpe_ac_table_select, ++ MRV_JPE_AC_TABLE_SELECT_Y, 0); ++ REG_SET_SLICE(mrv_reg->jpe_ac_table_select, ++ MRV_JPE_AC_TABLE_SELECT_U, 1); ++ REG_SET_SLICE(mrv_reg->jpe_ac_table_select, ++ MRV_JPE_AC_TABLE_SELECT_V, 1); ++} ++ ++/* ++ * configure JPEG encoder ++ */ ++void ci_isp_jpe_set_config(u16 hsize, u16 vsize, int jpe_scale) ++{ ++ struct isp_register *mrv_reg = (struct isp_register *) MEM_MRV_REG_BASE; ++ ++ /* JPEG image size */ ++ ++ REG_SET_SLICE(mrv_reg->jpe_enc_hsize, MRV_JPE_ENC_HSIZE, hsize); ++ REG_SET_SLICE(mrv_reg->jpe_enc_vsize, MRV_JPE_ENC_VSIZE, vsize); ++ ++ if (jpe_scale) { ++ /* upscaling of BT601 color space to full range 0..255 */ ++ REG_SET_SLICE(mrv_reg->jpe_y_scale_en, MRV_JPE_Y_SCALE_EN, ++ ENABLE); ++ REG_SET_SLICE(mrv_reg->jpe_cbcr_scale_en, ++ MRV_JPE_CBCR_SCALE_EN, ENABLE); ++ } else { ++ /* bypass scaler */ ++ REG_SET_SLICE(mrv_reg->jpe_y_scale_en, ++ MRV_JPE_Y_SCALE_EN, DISABLE); ++ REG_SET_SLICE(mrv_reg->jpe_cbcr_scale_en, ++ MRV_JPE_CBCR_SCALE_EN, DISABLE); ++ } ++ ++ /* picture format YUV 422 */ ++ REG_SET_SLICE(mrv_reg->jpe_pic_format, MRV_JPE_ENC_PIC_FORMAT, ++ MRV_JPE_ENC_PIC_FORMAT_422); ++ REG_SET_SLICE(mrv_reg->jpe_table_flush, MRV_JPE_TABLE_FLUSH, 0); ++} ++ ++int ci_isp_jpe_generate_header(struct mrst_isp_device *intel, u8 header_mode) ++{ ++ struct isp_register *mrv_reg = (struct isp_register *) MEM_MRV_REG_BASE; ++ ++ WARN_ON(!((header_mode == MRV_JPE_HEADER_MODE_JFIF) ++ || (header_mode == MRV_JPE_HEADER_MODE_NO))); ++ ++ /* clear jpeg gen_header_done interrupt */ ++ /* since we poll them later to detect command completion */ ++ ++ REG_SET_SLICE(mrv_reg->jpe_status_icr, MRV_JPE_GEN_HEADER_DONE, 1); ++ REG_SET_SLICE(mrv_reg->jpe_header_mode, MRV_JPE_HEADER_MODE, ++ header_mode); ++ ++ /* start header generation */ ++ REG_SET_SLICE(mrv_reg->jpe_gen_header, MRV_JPE_GEN_HEADER, ON); ++ ++ return ci_isp_jpe_wait_for_header_gen_done(intel); ++} ++ ++void ci_isp_jpe_prep_enc(enum ci_isp_jpe_enc_mode jpe_enc_mode) ++{ ++ struct isp_register *mrv_reg = (struct isp_register *) MEM_MRV_REG_BASE; ++ u32 jpe_encode = REG_READ(mrv_reg->jpe_encode); ++ ++ /* clear jpeg encode_done interrupt */ ++ /* since we poll them later to detect command completion */ ++ ++ REG_SET_SLICE(mrv_reg->jpe_status_icr, MRV_JPE_ENCODE_DONE, 1); ++ REG_SET_SLICE(jpe_encode, MRV_JPE_ENCODE, ON); ++ ++ switch (jpe_enc_mode) { ++ case CI_ISP_JPE_LARGE_CONT_MODE: ++ /* motion JPEG with header generation */ ++ REG_SET_SLICE(jpe_encode, MRV_JPE_CONT_MODE, ++ MRV_JPE_CONT_MODE_HEADER); ++ break; ++ case CI_ISP_JPE_SHORT_CONT_MODE: ++ /* motion JPEG only first frame with header */ ++ REG_SET_SLICE(jpe_encode, MRV_JPE_CONT_MODE, ++ MRV_JPE_CONT_MODE_NEXT); ++ break; ++ default: ++ /* single shot JPEG */ ++ REG_SET_SLICE(jpe_encode, MRV_JPE_CONT_MODE, ++ MRV_JPE_CONT_MODE_STOP); ++ break; ++ } ++ ++ REG_WRITE(mrv_reg->jpe_encode, jpe_encode); ++ REG_SET_SLICE(mrv_reg->jpe_init, MRV_JPE_JP_INIT, 1); ++} ++ ++/* ++ * wait until JPG Header is generated (MRV_JPGINT_GEN_HEADER_DONE ++ * interrupt occurs) ++ * waiting for JPG Header to be generated ++ */ ++int ci_isp_jpe_wait_for_header_gen_done(struct mrst_isp_device *intel) ++{ ++ struct isp_register *mrv_reg = (struct isp_register *) MEM_MRV_REG_BASE; ++ ++ mrst_timer_start(); ++ ++ while (!REG_GET_SLICE(mrv_reg->jpe_status_ris, ++ MRV_JPE_GEN_HEADER_DONE)) { ++ if (mrst_get_micro_sec() > 2000000) { ++ mrst_timer_stop(); ++ eprintk("timeout"); ++ return CI_STATUS_FAILURE; ++ } ++ } ++ ++ mrst_timer_stop(); ++ ++ return CI_STATUS_SUCCESS; ++} ++ ++/* ++ * wait until JPG Encoder is done (MRV_JPGINT_ENCODE_DONE ++ * interrupt occurs) waiting for the JPG Encoder to be done ++ */ ++int ci_isp_jpe_wait_for_encode_done(struct mrst_isp_device *intel) ++{ ++#if 0 ++ int ret = 0; ++ INIT_COMPLETION(intel->jpe_complete); ++ ret = wait_for_completion_interruptible_timeout(&intel->jpe_complete, ++ 100 * HZ); ++ if ((ret == 0) | (intel->irq_stat == IRQ_JPE_ERROR)) { ++ eprintk("timeout"); ++ return CI_STATUS_FAILURE; ++ } ++ ++ return CI_STATUS_SUCCESS; ++#endif ++ struct isp_register *mrv_reg = (struct isp_register *) MEM_MRV_REG_BASE; ++ mrst_timer_start(); ++ ++ while (!REG_GET_SLICE(mrv_reg->jpe_status_ris, ++ MRV_JPE_ENCODE_DONE)) { ++ if (mrst_get_micro_sec() > 200000) { ++ mrst_timer_stop(); ++ eprintk("timeout"); ++ return CI_STATUS_FAILURE; ++ } ++ } ++ ++ mrst_timer_stop(); ++ ++ /* clear jpeg encode_done interrupt */ ++ REG_SET_SLICE(mrv_reg->jpe_status_icr, MRV_JPE_ENCODE_DONE, 1); ++ ++ return CI_STATUS_SUCCESS; ++} +diff --git a/drivers/media/video/mrstci/mrstisp/mrstisp_main.c b/drivers/media/video/mrstci/mrstisp/mrstisp_main.c +new file mode 100644 +index 0000000..e37b3d1 +--- /dev/null ++++ b/drivers/media/video/mrstci/mrstisp/mrstisp_main.c +@@ -0,0 +1,2977 @@ ++/* ++ * Support for Moorestown Langwell Camera Imaging ISP subsystem. ++ * ++ * Copyright (c) 2009 Intel Corporation. All Rights Reserved. ++ * ++ * 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 Street, Fifth Floor, Boston, MA ++ * 02110-1301, USA. ++ * ++ * ++ * Xiaolin Zhang ++ */ ++ ++#include "mrstisp_stdinc.h" ++#include "ci_isp_fmts_common.h" ++ ++#define GPIO_SCLK_25 44 ++#define GPIO_STDBY1_PIN 48 ++#define GPIO_STDBY2_PIN 49 ++#define GPIO_RESET_PIN 50 ++ ++int mrstisp_debug; ++module_param(mrstisp_debug, int, 0644); ++ ++/*XXX*/ ++static int frame_cnt; ++static long mipi_error_num; ++static u32 mipi_error_flag; ++static long isp_error_num; ++static u32 isp_error_flag; ++static unsigned long jiffies_start; ++static int mipi_flag; ++ ++void intel_timer_start(void) ++{ ++ jiffies_start = jiffies; ++} ++void intel_timer_stop(void) ++{ ++ jiffies_start = 0; ++} ++unsigned long intel_get_micro_sec(void) ++{ ++ unsigned long time_diff = 0; ++ ++ time_diff = jiffies - jiffies_start; ++ ++ return jiffies_to_msecs(time_diff); ++} ++ ++ ++static inline struct mrst_isp_device *to_isp(struct v4l2_device *dev) ++{ ++ return container_of(dev, struct mrst_isp_device, v4l2_dev); ++} ++ ++static struct mrst_camera mrst_camera_table[] = { ++ { ++ .type = MRST_CAMERA_SOC, ++ .name = "ov2650", ++ .sensor_addr = 0x30, ++ }, ++ { ++ .type = MRST_CAMERA_SOC, ++ .name = "ov9665", ++ .sensor_addr = 0x30, ++ }, ++ { ++ .type = MRST_CAMERA_RAW, ++ .name = "ov5630", ++ .sensor_addr = 0x36, ++ .motor_name = "ov5630_motor", ++ .motor_addr = (0x18 >> 1), ++ }, ++ { ++ .type = MRST_CAMERA_RAW, ++ .name = "s5k4e1", ++ .sensor_addr = 0x36, ++ .motor_name = "s5k4e1_motor", ++ .motor_addr = (0x18 >> 1), ++ }, ++}; ++ ++#define N_CAMERA (ARRAY_SIZE(mrst_camera_table)) ++ ++struct videobuf_dma_contig_memory { ++ u32 magic; ++ void *vaddr; ++ dma_addr_t dma_handle; ++ unsigned long size; ++ int is_userptr; ++}; ++#define MAGIC_DC_MEM 0x0733ac61 ++#define MAGIC_CHECK(is, should) \ ++ if (unlikely((is) != (should))) { \ ++ pr_err("magic mismatch: %x expected %x\n", (is), (should)); \ ++ BUG(); \ ++ } ++/* flag to determine whether to do the handler of mblk_line irq */ ++int mrst_isp_to_do_mblk_line; ++unsigned char *mrst_isp_regs; ++ ++static inline struct ci_sensor_config *to_sensor_config(struct v4l2_subdev *sd) ++{ ++ return container_of(sd, struct ci_sensor_config, sd); ++} ++ ++/* g45-th20-b5 gamma out curve with enhanced black level */ ++static struct ci_isp_gamma_out_curve g45_th20_b5 = { ++ { ++ 0x0000, 0x0014, 0x003C, 0x0064, ++ 0x00A0, 0x0118, 0x0171, 0x01A7, ++ 0x01D8, 0x0230, 0x027A, 0x02BB, ++ 0x0323, 0x0371, 0x03AD, 0x03DB, ++ 0x03FF} ++ , ++ 0 ++}; ++ ++static void print_snr_cfg(struct ci_sensor_config *cfg) ++{ ++ dprintk(2, "bus width = %x", cfg->bus_width); ++ dprintk(2, "mode = %x", cfg->mode); ++ dprintk(2, "field_inv = %x", cfg->field_inv); ++ dprintk(2, "field_sel = %x", cfg->field_sel); ++ dprintk(2, "ycseq = %x", cfg->ycseq); ++ dprintk(2, "conv422 = %x", cfg->conv422); ++ dprintk(2, "bpat = %x", cfg->bpat); ++ dprintk(2, "hpol = %x", cfg->hpol); ++ dprintk(2, "vpol = %x", cfg->vpol); ++ dprintk(2, "edge = %x", cfg->edge); ++ dprintk(2, "bls = %x", cfg->bls); ++ dprintk(2, "gamma = %x", cfg->gamma); ++ dprintk(2, "cconv = %x", cfg->cconv); ++ dprintk(2, "res = %x", cfg->res); ++ dprintk(2, "blc = %x", cfg->blc); ++ dprintk(2, "agc = %x", cfg->agc); ++ dprintk(2, "awb = %x", cfg->awb); ++ dprintk(2, "aec = %x", cfg->aec); ++ dprintk(2, "cie_profile = %x", cfg->cie_profile); ++ dprintk(2, "flicker_freq = %x", cfg->flicker_freq); ++ dprintk(2, "smia_mode = %x", cfg->smia_mode); ++ dprintk(2, "mipi_mode = %x", cfg->mipi_mode); ++ dprintk(2, "type = %x", cfg->type); ++ dprintk(2, "name = %s", cfg->name); ++} ++ ++static int mrst_isp_defcfg_all_load(struct ci_isp_config *isp_config) ++{ ++ ++ DBG_entering; ++ ++ /* demosaic mode */ ++ isp_config->demosaic_mode = CI_ISP_DEMOSAIC_ENHANCED; ++ ++ /* bpc */ ++ isp_config->bpc_cfg.bp_corr_type = CI_ISP_BP_CORR_DIRECT; ++ isp_config->bpc_cfg.bp_corr_rep = CI_ISP_BP_CORR_REP_NB; ++ isp_config->bpc_cfg.bp_corr_mode = CI_ISP_BP_CORR_HOT_DEAD_EN; ++ isp_config->bpc_cfg.bp_abs_hot_thres = 496; ++ isp_config->bpc_cfg.bp_abs_dead_thres = 20; ++ isp_config->bpc_cfg.bp_dev_hot_thres = 328; ++ isp_config->bpc_cfg.bp_dev_dead_thres = 328; ++ isp_config->bpd_cfg.bp_dead_thres = 1; ++ ++ /* WB */ ++ isp_config->wb_config.mrv_wb_mode = CI_ISP_AWB_AUTO; ++ isp_config->wb_config.mrv_wb_sub_mode = CI_ISP_AWB_AUTO_ON; ++ isp_config->wb_config.awb_pca_damping = 16; ++ isp_config->wb_config.awb_prior_exp_damping = 12; ++ isp_config->wb_config.awb_pca_push_damping = 16; ++ isp_config->wb_config.awb_prior_exp_push_damping = 12; ++ isp_config->wb_config.awb_auto_max_y = 254; ++ isp_config->wb_config.awb_push_max_y = 250; ++ isp_config->wb_config.awb_measure_max_y = 200; ++ isp_config->wb_config.awb_underexp_det = 10; ++ isp_config->wb_config.awb_push_underexp_det = 170; ++ ++ /* CAC */ ++ isp_config->cac_config.hsize = 2048; ++ isp_config->cac_config.vsize = 1536; ++ isp_config->cac_config.hcenter_offset = 0; ++ isp_config->cac_config.vcenter_offset = 0; ++ isp_config->cac_config.hclip_mode = 1; ++ isp_config->cac_config.vclip_mode = 2; ++ isp_config->cac_config.ablue = 24; ++ isp_config->cac_config.ared = 489; ++ isp_config->cac_config.bblue = 450; ++ isp_config->cac_config.bred = 53; ++ isp_config->cac_config.cblue = 40; ++ isp_config->cac_config.cred = 479; ++ isp_config->cac_config.aspect_ratio = 0.000000; ++ ++ /* BLS */ ++ isp_config->bls_cfg.enable_automatic = 0; ++ isp_config->bls_cfg.disable_h = 0; ++ isp_config->bls_cfg.disable_v = 0; ++ isp_config->bls_cfg.isp_bls_window1.enable_window = 0; ++ isp_config->bls_cfg.isp_bls_window1.start_h = 0; ++ isp_config->bls_cfg.isp_bls_window1.stop_h = 0; ++ isp_config->bls_cfg.isp_bls_window1.start_v = 0; ++ isp_config->bls_cfg.isp_bls_window1.stop_v = 0; ++ isp_config->bls_cfg.isp_bls_window2.enable_window = 0; ++ isp_config->bls_cfg.isp_bls_window2.start_h = 0; ++ isp_config->bls_cfg.isp_bls_window2.stop_h = 0; ++ isp_config->bls_cfg.isp_bls_window2.start_v = 0; ++ isp_config->bls_cfg.isp_bls_window2.stop_v = 0; ++ isp_config->bls_cfg.bls_samples = 5; ++ isp_config->bls_cfg.bls_subtraction.fixed_a = 0x100; ++ isp_config->bls_cfg.bls_subtraction.fixed_b = 0x100; ++ isp_config->bls_cfg.bls_subtraction.fixed_c = 0x100; ++ isp_config->bls_cfg.bls_subtraction.fixed_d = 0x100; ++ ++ /* AF */ ++ isp_config->af_cfg.wnd_pos_a.hoffs = 874; ++ isp_config->af_cfg.wnd_pos_a.voffs = 618; ++ isp_config->af_cfg.wnd_pos_a.hsize = 300; ++ isp_config->af_cfg.wnd_pos_a.vsize = 300; ++ isp_config->af_cfg.wnd_pos_b.hoffs = 0; ++ isp_config->af_cfg.wnd_pos_b.voffs = 0; ++ isp_config->af_cfg.wnd_pos_b.hsize = 0; ++ isp_config->af_cfg.wnd_pos_b.vsize = 0; ++ isp_config->af_cfg.wnd_pos_c.hoffs = 0; ++ isp_config->af_cfg.wnd_pos_c.voffs = 0; ++ isp_config->af_cfg.wnd_pos_c.hsize = 0; ++ isp_config->af_cfg.wnd_pos_c.vsize = 0; ++ isp_config->af_cfg.threshold = 0x00000000; ++ ++ /* color */ ++ isp_config->color.contrast = 128; ++ isp_config->color.brightness = 0; ++ isp_config->color.saturation = 128; ++ isp_config->color.hue = 0; ++ ++ /* Img Effect */ ++ isp_config->img_eff_cfg.mode = CI_ISP_IE_MODE_OFF; ++ isp_config->img_eff_cfg.color_sel = 4; ++ isp_config->img_eff_cfg.color_thres = 128; ++ isp_config->img_eff_cfg.tint_cb = 108; ++ isp_config->img_eff_cfg.tint_cr = 141; ++ isp_config->img_eff_cfg.mat_emboss.coeff_11 = 2; ++ isp_config->img_eff_cfg.mat_emboss.coeff_12 = 1; ++ isp_config->img_eff_cfg.mat_emboss.coeff_13 = 0; ++ isp_config->img_eff_cfg.mat_emboss.coeff_21 = 1; ++ isp_config->img_eff_cfg.mat_emboss.coeff_22 = 0; ++ isp_config->img_eff_cfg.mat_emboss.coeff_23 = -1; ++ isp_config->img_eff_cfg.mat_emboss.coeff_31 = 0; ++ isp_config->img_eff_cfg.mat_emboss.coeff_32 = -1; ++ isp_config->img_eff_cfg.mat_emboss.coeff_33 = -2; ++ isp_config->img_eff_cfg.mat_sketch.coeff_11 = -1; ++ isp_config->img_eff_cfg.mat_sketch.coeff_12 = -1; ++ isp_config->img_eff_cfg.mat_sketch.coeff_13 = -1; ++ isp_config->img_eff_cfg.mat_sketch.coeff_21 = -1; ++ isp_config->img_eff_cfg.mat_sketch.coeff_22 = 8; ++ isp_config->img_eff_cfg.mat_sketch.coeff_23 = -1; ++ isp_config->img_eff_cfg.mat_sketch.coeff_31 = -1; ++ isp_config->img_eff_cfg.mat_sketch.coeff_32 = -1; ++ isp_config->img_eff_cfg.mat_sketch.coeff_33 = -1; ++ ++ /* Framefun */ ++ isp_config->flags.bls = 0; ++ isp_config->flags.lsc = 0; ++ isp_config->flags.bpc = 0; ++ isp_config->flags.awb = 0; ++ isp_config->flags.aec = 0; ++ isp_config->flags.af = 0; ++ isp_config->flags.cp = 0; ++ isp_config->flags.gamma = 0; ++ isp_config->flags.cconv = 0; ++ isp_config->flags.demosaic = 0; ++ isp_config->flags.gamma2 = 0; ++ isp_config->flags.isp_filters = 0; ++ isp_config->flags.cac = 0; ++ isp_config->flags.cconv_basic = 0; ++ isp_config->demosaic_th = 4; ++ ++ isp_config->view_finder.flags = VFFLAG_HWRGB; ++ ++ isp_config->afm_mode = 1; ++ isp_config->filter_level_noise_reduc = 4; ++ isp_config->filter_level_sharp = 4; ++ ++ isp_config->jpeg_enc_ratio = 1; ++ ++ DBG_leaving; ++ return 0; ++} ++ ++static void mrst_isp_update_marvinvfaddr(struct mrst_isp_device *isp, ++ u32 buffer_base, ++ enum ci_isp_conf_update_time update_time) ++{ ++ struct ci_isp_mi_path_conf isp_mi_path_conf; ++ struct ci_isp_mi_path_conf isp_sf_mi_path_conf; ++ static struct v4l2_jpg_review_buffer *jpg_review; ++ u32 bufsize = 0; ++ u32 w; ++ u32 h; ++ ++ jpg_review = &isp->sys_conf.jpg_review; ++ memset(&isp_mi_path_conf, 0, sizeof(struct ci_isp_mi_path_conf)); ++ memset(&isp_sf_mi_path_conf, 0, sizeof(struct ci_isp_mi_path_conf)); ++ ++ w = isp_mi_path_conf.llength = isp->bufwidth; ++ h = isp_mi_path_conf.ypic_height = isp->bufheight; ++ isp_mi_path_conf.ypic_width = isp->bufwidth; ++ ++ /*XXX Zheng: disable jpg review for MIPI sensor */ ++ /*if ((isp->sys_conf.isi_config)->mipi_mode == SENSOR_MIPI_MODE_RAW_10) ++ isp->sys_conf.jpg_review_enable = 0; ++ */ ++ ++ if (isp->sys_conf.jpg_review_enable) { ++ ++ /* for self path, JPEG review */ ++ isp_sf_mi_path_conf.ypic_width = jpg_review->width; ++ isp_sf_mi_path_conf.llength = jpg_review->width; ++ isp_sf_mi_path_conf.ypic_height = jpg_review->height; ++ ++ bufsize = jpg_review->width * jpg_review->height; ++ ++ /* buffer size in bytes */ ++ if (jpg_review->pix_fmt == V4L2_PIX_FMT_YUV420 ++ || jpg_review->pix_fmt == V4L2_PIX_FMT_YVU420) { ++ ++ dprintk(3, "VF yuv420 fmt"); ++ isp_sf_mi_path_conf.ybuffer.size = bufsize; ++ isp_sf_mi_path_conf.cb_buffer.size = bufsize/4; ++ isp_sf_mi_path_conf.cr_buffer.size = bufsize/4; ++ ++ } else if (jpg_review->pix_fmt == V4L2_PIX_FMT_YUV422P) { ++ ++ dprintk(3, "VF yuv422 fmt"); ++ isp_sf_mi_path_conf.ybuffer.size = bufsize; ++ isp_sf_mi_path_conf.cb_buffer.size = bufsize/2; ++ isp_sf_mi_path_conf.cr_buffer.size = bufsize/2; ++ ++ } else if (jpg_review->pix_fmt == V4L2_PIX_FMT_NV12) { ++ ++ dprintk(3, "VF nv12 fmt"); ++ isp_sf_mi_path_conf.ybuffer.size = bufsize; ++ isp_sf_mi_path_conf.cb_buffer.size = bufsize/2; ++ isp_sf_mi_path_conf.cr_buffer.size = 0; ++ ++ } else { ++ printk(KERN_ERR "mrstisp: no support jpg review fmt\n"); ++ } ++ ++ /* buffer address */ ++ if (isp_sf_mi_path_conf.ybuffer.size != 0) { ++ isp_sf_mi_path_conf.ybuffer.pucbuffer = ++ (u8 *)(unsigned long) ++ isp->mb1 + isp->mb1_size - 640*480*2; ++ } ++ ++ if (isp_sf_mi_path_conf.cb_buffer.size != 0) { ++ isp_sf_mi_path_conf.cb_buffer.pucbuffer = ++ isp_sf_mi_path_conf.ybuffer.pucbuffer + ++ isp_sf_mi_path_conf.ybuffer.size; ++ } ++ ++ if (isp_sf_mi_path_conf.cr_buffer.size != 0) { ++ isp_sf_mi_path_conf.cr_buffer.pucbuffer = ++ isp_sf_mi_path_conf.cb_buffer.pucbuffer + ++ isp_sf_mi_path_conf.cb_buffer.size; ++ } ++ ++ if (jpg_review->pix_fmt == V4L2_PIX_FMT_YVU420) { ++ isp_sf_mi_path_conf.cr_buffer.pucbuffer = ++ isp_sf_mi_path_conf.ybuffer.pucbuffer + ++ isp_sf_mi_path_conf.ybuffer.size; ++ isp_sf_mi_path_conf.cb_buffer.pucbuffer = ++ isp_sf_mi_path_conf.cr_buffer.pucbuffer + ++ isp_sf_mi_path_conf.cr_buffer.size; ++ } ++ ++ } ++ ++ if (isp->pixelformat == V4L2_PIX_FMT_YUV420 || ++ isp->pixelformat == V4L2_PIX_FMT_YVU420 || ++ isp->pixelformat == V4L2_PIX_FMT_YUV422P || ++ isp->pixelformat == V4L2_PIX_FMT_NV12) { ++ bufsize = w*h; ++ } else ++ bufsize = isp->frame_size; ++ ++ /* buffer size in bytes */ ++ if (isp->pixelformat == V4L2_PIX_FMT_YUV420 ++ || isp->pixelformat == V4L2_PIX_FMT_YVU420) { ++ ++ dprintk(3, "yuv420 fmt"); ++ isp_mi_path_conf.ybuffer.size = bufsize; ++ isp_mi_path_conf.cb_buffer.size = bufsize/4; ++ isp_mi_path_conf.cr_buffer.size = bufsize/4; ++ } else if (isp->pixelformat == V4L2_PIX_FMT_YUV422P) { ++ ++ dprintk(3, "yuv422 fmt"); ++ isp_mi_path_conf.ybuffer.size = bufsize; ++ isp_mi_path_conf.cb_buffer.size = bufsize/2; ++ isp_mi_path_conf.cr_buffer.size = bufsize/2; ++ } else if (isp->pixelformat == V4L2_PIX_FMT_NV12) { ++ ++ dprintk(3, "nv12 fmt"); ++ isp_mi_path_conf.ybuffer.size = bufsize; ++ isp_mi_path_conf.cb_buffer.size = bufsize/2; ++ isp_mi_path_conf.cr_buffer.size = 0; ++ } else { ++ ++ dprintk(3, "jpeg and rgb fmt"); ++ isp_mi_path_conf.ybuffer.size = bufsize; ++ isp_mi_path_conf.cb_buffer.size = 0; ++ isp_mi_path_conf.cr_buffer.size = 0; ++ } ++ ++ /* buffer address */ ++ if (isp_mi_path_conf.ybuffer.size != 0) { ++ isp_mi_path_conf.ybuffer.pucbuffer = ++ (u8 *)(unsigned long) buffer_base; ++ } ++ ++ if (isp_mi_path_conf.cb_buffer.size != 0) { ++ isp_mi_path_conf.cb_buffer.pucbuffer = ++ isp_mi_path_conf.ybuffer.pucbuffer + ++ isp_mi_path_conf.ybuffer.size; ++ } ++ ++ if (isp_mi_path_conf.cr_buffer.size != 0) { ++ isp_mi_path_conf.cr_buffer.pucbuffer = ++ isp_mi_path_conf.cb_buffer.pucbuffer + ++ isp_mi_path_conf.cb_buffer.size; ++ } ++ ++ if (isp->pixelformat == V4L2_PIX_FMT_YVU420) { ++ isp_mi_path_conf.cr_buffer.pucbuffer = ++ isp_mi_path_conf.ybuffer.pucbuffer + ++ isp_mi_path_conf.ybuffer.size; ++ isp_mi_path_conf.cb_buffer.pucbuffer = ++ isp_mi_path_conf.cr_buffer.pucbuffer + ++ isp_mi_path_conf.cr_buffer.size; ++ } ++ ++ if (isp->sys_conf.isp_cfg.view_finder.flags & VFFLAG_USE_MAINPATH) { ++ ci_isp_mif_set_main_buffer(&isp_mi_path_conf, update_time); ++ if (isp->pixelformat == V4L2_PIX_FMT_JPEG) ++ if (isp->sys_conf.jpg_review_enable) ++ ci_isp_mif_set_self_buffer( ++ &isp_sf_mi_path_conf, update_time); ++ } else { ++ ci_isp_mif_set_self_buffer(&isp_mi_path_conf, update_time); ++ } ++} ++ ++static int mrst_isp_setup_viewfinder_path(struct mrst_isp_device *isp, ++ struct ci_sensor_config *isi_config, ++ int zoom) ++{ ++ int error = CI_STATUS_SUCCESS; ++ struct ci_isp_datapath_desc dp_main; ++ struct ci_isp_datapath_desc dp_self; ++ struct ci_isp_rect self_rect; ++ u16 isi_hsize; ++ u16 isi_vsize; ++ int jpe_scale; ++ struct ci_pl_system_config *sys_conf = &isp->sys_conf; ++ struct ci_isp_config *config = &sys_conf->isp_cfg; ++ struct v4l2_jpg_review_buffer *jpg_review = &sys_conf->jpg_review; ++ u32 dp_mode; ++ ++ DBG_entering; ++ ++ if (sys_conf->isp_cfg.flags.ycbcr_full_range) ++ jpe_scale = false; ++ else ++ jpe_scale = true; ++ ++ memset(&dp_main, 0, sizeof(struct ci_isp_datapath_desc)); ++ memset(&dp_self, 0, sizeof(struct ci_isp_datapath_desc)); ++ ++ self_rect.x = 0; ++ self_rect.y = 0; ++ self_rect.w = isp->bufwidth; /* 640 */ ++ self_rect.h = isp->bufheight; /* 480 */ ++ ++ if (isp->pixelformat == V4L2_PIX_FMT_JPEG) { ++ ++ dprintk(1, "jpeg fmt"); ++ ++ dp_main.flags = CI_ISP_DPD_ENABLE | CI_ISP_DPD_MODE_ISPJPEG; ++ config->view_finder.flags |= VFFLAG_USE_MAINPATH; ++ ++ dp_main.out_w = (u16) isp->bufwidth; ++ dp_main.out_h = (u16) isp->bufheight; ++ ++ if (isp->sys_conf.jpg_review_enable) { ++ ++ dprintk(1, "jpg_review enabled in VF"); ++ ++ self_rect.w = jpg_review->width; ++ self_rect.h = jpg_review->height; ++ ++ dp_self.flags = (CI_ISP_DPD_ENABLE ++ | CI_ISP_DPD_MODE_ISPYC); ++ if (jpg_review->pix_fmt == V4L2_PIX_FMT_YUV420 || ++ jpg_review->pix_fmt == V4L2_PIX_FMT_YVU420) ++ dp_self.flags |= CI_ISP_DPD_YUV_420 ++ | CI_ISP_DPD_CSS_V2; ++ else if (jpg_review->pix_fmt == V4L2_PIX_FMT_YUV422P) ++ dp_self.flags |= CI_ISP_DPD_YUV_422; ++ else if (jpg_review->pix_fmt == V4L2_PIX_FMT_NV12) ++ dp_self.flags |= CI_ISP_DPD_YUV_NV12 ++ | CI_ISP_DPD_CSS_V2; ++ else if (jpg_review->pix_fmt == V4L2_PIX_FMT_YUYV) ++ dp_self.flags |= CI_ISP_DPD_YUV_YUYV; ++ ++ dprintk(1, "dp_self.flags is 0x%x", dp_self.flags); ++ } ++ ++ } else if (isp->pixelformat == INTEL_PIX_FMT_RAW08) { ++ ++ dp_main.flags = CI_ISP_DPD_ENABLE | CI_ISP_DPD_MODE_ISPRAW; ++ config->view_finder.flags |= VFFLAG_USE_MAINPATH; ++ ++ /*just take the output of the sensor without any resizing*/ ++ dp_main.flags |= CI_ISP_DPD_NORESIZE; ++ (void)ci_sensor_res2size(isi_config->res, ++ &(dp_main.out_w), &(dp_main.out_h)); ++ ++ dprintk(1, "RAW08 dp_main.flags is 0x%x", dp_main.flags); ++ ++ } else if (isp->pixelformat == INTEL_PIX_FMT_RAW10 ++ || isp->pixelformat == INTEL_PIX_FMT_RAW12) { ++ ++ dp_main.flags = (CI_ISP_DPD_ENABLE ++ | CI_ISP_DPD_MODE_ISPRAW_16B); ++ config->view_finder.flags |= VFFLAG_USE_MAINPATH; ++ ++ /*just take the output of the sensor without any resizing*/ ++ dp_main.flags |= CI_ISP_DPD_NORESIZE; ++ (void)ci_sensor_res2size(isi_config->res, ++ &(dp_main.out_w), &(dp_main.out_h)); ++ ++ dprintk(1, "RAW10 dp_main.flags is 0x%x", dp_main.flags); ++ ++ } /*else if (isp->bufwidth >= 640 && isp->bufheight >= 480) {*/ ++ else if (isp->bufwidth >= 32 && isp->bufheight >= 16) { ++ ++ dp_main.flags = (CI_ISP_DPD_ENABLE | CI_ISP_DPD_MODE_ISPYC); ++ dp_main.out_w = (u16) isp->bufwidth; ++ dp_main.out_h = (u16) isp->bufheight; ++ config->view_finder.flags |= VFFLAG_USE_MAINPATH; ++ ++ if (isp->pixelformat == V4L2_PIX_FMT_YUV420 || ++ isp->pixelformat == V4L2_PIX_FMT_YVU420) ++ dp_main.flags |= CI_ISP_DPD_YUV_420 | CI_ISP_DPD_CSS_V2; ++ else if (isp->pixelformat == V4L2_PIX_FMT_YUV422P) ++ dp_main.flags |= CI_ISP_DPD_YUV_422; ++ else if (isp->pixelformat == V4L2_PIX_FMT_NV12) { ++ /* to use crop set crop_flag first */ ++ dp_main.flags |= CI_ISP_DPD_YUV_NV12; ++ if (!crop_flag) ++ dp_main.flags |= CI_ISP_DPD_CSS_V2; ++ } else if (isp->pixelformat == V4L2_PIX_FMT_YUYV) ++ dp_main.flags |= CI_ISP_DPD_YUV_YUYV; ++ ++ dprintk(1, "YUV dp_main.flags is 0x%x", dp_main.flags); ++ ++ } /* else if (isp->bufwidth <= 640 && isp->bufheight <= 480) { ++ ++ dp_self.flags = (CI_ISP_DPD_ENABLE | CI_ISP_DPD_MODE_ISPYC); ++ ++ if (isp->pixelformat == V4L2_PIX_FMT_YUV420 || ++ isp->pixelformat == V4L2_PIX_FMT_YVU420) ++ dp_self.flags |= CI_ISP_DPD_YUV_420 | CI_ISP_DPD_CSS_V2; ++ else if (isp->pixelformat == V4L2_PIX_FMT_YUV422P) ++ dp_self.flags |= CI_ISP_DPD_YUV_422; ++ else if (isp->pixelformat == V4L2_PIX_FMT_NV12) ++ dp_self.flags |= CI_ISP_DPD_YUV_NV12 ++ | CI_ISP_DPD_CSS_V2; ++ else if (isp->pixelformat == V4L2_PIX_FMT_YUYV) ++ dp_self.flags |= CI_ISP_DPD_YUV_YUYV; ++ else if (isp->pixelformat == V4L2_PIX_FMT_RGB565) ++ dp_self.flags |= CI_ISP_DPD_HWRGB_565; ++ else if (isp->pixelformat == V4L2_PIX_FMT_BGR32) ++ dp_self.flags |= CI_ISP_DPD_HWRGB_888; ++ ++ dprintk(1, "YUV dp_self.flags is 0x%x", dp_self.flags); ++ } ++ */ ++ ++ dprintk(1, "sensor_res = %x", isi_config->res); ++ ++ (void)ci_sensor_res2size(isi_config->res, &isi_hsize, &isi_vsize); ++ dprintk(1, "self path: w:%d, h:%d; sensor: w:%d, h:%d", ++ self_rect.w, self_rect.h, isi_hsize, isi_vsize); ++ dprintk(1, "main path: out_w:%d, out_h:%d ", ++ dp_main.out_w, dp_main.out_h); ++ ++ /* no stretching/squeezing */ ++ if (dp_self.flags && CI_ISP_DPD_ENABLE) ++ dp_self.flags |= CI_ISP_DPD_KEEPRATIO; ++ else ++ dp_main.flags |= CI_ISP_DPD_KEEPRATIO; ++ ++ /* prepare datapath, 640x480, can changed to the bufsize */ ++ dp_self.out_w = (u16) self_rect.w; ++ dp_self.out_h = (u16) self_rect.h; ++ ++ if (sys_conf->isp_cfg.view_finder.flags & VFFLAG_HWRGB) { ++ /* YCbCr to RGB conversion in hardware */ ++ if (isp->pixelformat == V4L2_PIX_FMT_RGB565) ++ dp_self.flags |= CI_ISP_DPD_HWRGB_565; ++ if (isp->pixelformat == V4L2_PIX_FMT_BGR32) ++ dp_self.flags |= CI_ISP_DPD_HWRGB_888; ++ } ++ ++ if (sys_conf->isp_cfg.view_finder.flags & VFFLAG_MIRROR) ++ dp_self.flags |= CI_ISP_DPD_H_FLIP; ++ ++ ++ if (sys_conf->isp_cfg.view_finder.flags & VFFLAG_V_FLIP) ++ dp_self.flags |= CI_ISP_DPD_V_FLIP; ++ ++ ++ if (sys_conf->isp_cfg.view_finder.flags & VFFLAG_ROT90_CCW) ++ dp_self.flags |= CI_ISP_DPD_90DEG_CCW; ++ ++ /* setup self & main path with zoom */ ++ if (zoom < 0) ++ zoom = sys_conf->isp_cfg.view_finder.zoom; ++ ++ if (sys_conf->isp_cfg.view_finder.flags & VFFLAG_USE_MAINPATH) { ++ /* For RAW snapshots, we have to bypass the ISP too */ ++ dp_mode = dp_main.flags & CI_ISP_DPD_MODE_MASK; ++ if ((dp_mode == CI_ISP_DPD_MODE_ISPRAW) || ++ (dp_mode == CI_ISP_DPD_MODE_ISPRAW_16B)) { ++ struct ci_sensor_config isi_conf; ++ /* isi_conf = *sys_conf->isi_config; */ ++ isi_conf = *isi_config; ++ isi_conf.mode = SENSOR_MODE_PICT; ++ error = ci_isp_set_input_aquisition(&isi_conf); ++ if (error != CI_STATUS_SUCCESS) ++ eprintk("33"); ++ } ++ } ++ /* to use crop mode, set crop_flag */ ++ if (crop_flag) ++ dp_main.flags |= CI_ISP_DPD_NORESIZE; ++ ++ error = ci_datapath_isp(sys_conf, isi_config, &dp_main, &dp_self, zoom); ++ if (error != CI_STATUS_SUCCESS) { ++ printk(KERN_ERR "mrstisp: failed to setup marvins datapath\n"); ++ return error; ++ } ++ ++ DBG_leaving; ++ return error; ++} ++ ++static int mrst_isp_init_mrv_image_effects(struct ci_pl_system_config *sys_conf, ++ int enable) ++{ ++ int res; ++ ++ DBG_entering; ++ ++ if (enable && sys_conf->isp_cfg.img_eff_cfg.mode ++ != CI_ISP_IE_MODE_OFF) { ++ res = ci_isp_ie_set_config(&(sys_conf->isp_cfg.img_eff_cfg)); ++ if (res != CI_STATUS_SUCCESS) ++ printk(KERN_ERR "mrstisp: error setting ie config\n"); ++ } else { ++ (void)ci_isp_ie_set_config(NULL); ++ res = CI_STATUS_SUCCESS; ++ } ++ ++ DBG_leaving; ++ return res; ++} ++ ++static int mrst_isp_init_mrvisp_lensshade(struct ci_pl_system_config *sys_conf, ++ int enable) ++{ ++ if (enable) { ++ ci_isp_set_ls_correction(&sys_conf->isp_cfg.lsc_cfg); ++ ci_isp_ls_correction_on_off(1); ++ } else { ++ ci_isp_ls_correction_on_off(0); ++ } ++ return CI_STATUS_SUCCESS; ++} ++ ++static int mrst_isp_init_mrvisp_badpixel(const struct ci_pl_system_config ++ *sys_conf, int enable) ++{ ++ if ((enable) && (sys_conf->isp_cfg.flags.bpc)) { ++ (void)ci_bp_init(&sys_conf->isp_cfg.bpc_cfg, ++ &sys_conf->isp_cfg.bpd_cfg); ++ } else { ++ (void)ci_bp_end(&sys_conf->isp_cfg.bpc_cfg); ++ (void)ci_isp_set_bp_correction(NULL); ++ (void)ci_isp_set_bp_detection(NULL); ++ } ++ return CI_STATUS_SUCCESS; ++} ++ ++static int mrst_isp_init_mrv_ispfilter(const struct ci_pl_system_config ++ *sys_conf, int enable) ++{ ++ int res; ++ ++ DBG_entering; ++ ++ if ((enable) && (sys_conf->isp_cfg.flags.isp_filters)) { ++ ci_isp_activate_filter(true); ++ res = ci_isp_set_filter_params(sys_conf->isp_cfg. ++ filter_level_noise_reduc, ++ sys_conf->isp_cfg. ++ filter_level_sharp); ++ if (res != CI_STATUS_SUCCESS) ++ printk(KERN_ERR "mrstisp: error set filter param\n"); ++ } else { ++ ci_isp_activate_filter(false); ++ res = CI_STATUS_SUCCESS; ++ } ++ ++ DBG_leaving; ++ return res; ++} ++ ++static int mrst_isp_init_mrvisp_cac(const struct ci_pl_system_config *sys_conf, ++ int enable) ++{ ++ return 0; ++} ++ ++static int mrst_isp_initbls(const struct ci_pl_system_config *sys_conf) ++{ ++ struct ci_isp_bls_config *bls_config = ++ (struct ci_isp_bls_config *)&sys_conf->isp_cfg.bls_cfg; ++ return ci_isp_bls_set_config(bls_config); ++} ++ ++static int mrst_isp_dp_init(struct ci_pl_system_config *sys_conf, ++ struct ci_sensor_config *isi_config) ++{ ++ int error; ++ u8 words_per_pixel; ++ ++ DBG_entering; ++ ++ /* base initialisation of Marvin */ ++ ci_isp_init(); ++ ++ /* setup input acquisition according to image sensor settings */ ++ print_snr_cfg(isi_config); ++ error = ci_isp_set_input_aquisition(isi_config); ++ if (error) { ++ printk(KERN_ERR "mrstisp: error setting input acquisition\n"); ++ return error; ++ } ++ ++ /* setup functional blocks for Bayer pattern processing */ ++ if (ci_isp_select_path(isi_config, &words_per_pixel) ++ == CI_ISP_PATH_BAYER) { ++ ++ /* black level */ ++ if (sys_conf->isp_cfg.flags.bls) { ++ error = mrst_isp_initbls(sys_conf); ++ if (error != CI_STATUS_SUCCESS) { ++ printk(KERN_ERR "mrstisp: error set bls\n"); ++ return error; ++ } ++ } else { ++ ci_isp_bls_set_config(NULL); ++ } ++ ++ /* gamma */ ++ if (sys_conf->isp_cfg.flags.gamma2) { ++ dprintk(1, "setting gamma 2 "); ++ ci_isp_set_gamma2(&g45_th20_b5); ++ } else { ++ dprintk(1, "no setting gamma 2 "); ++ ci_isp_set_gamma2(NULL); ++ } ++ ++ /* demosaic */ ++ ci_isp_set_demosaic(sys_conf->isp_cfg.demosaic_mode, ++ sys_conf->isp_cfg.demosaic_th); ++ ++ /* color convertion */ ++ if (sys_conf->isp_cfg.flags.cconv) { ++ if (!sys_conf->isp_cfg.flags.cconv_basic) { ++ mrst_isp_set_color_conversion_ex(); ++ /* set color converstion skipped by xiaolin, ++ * to be done in libci */ ++ if (error != CI_STATUS_SUCCESS) { ++ printk(KERN_ERR "mrstisp: error set" ++ " color conversion\n"); ++ return error; ++ } ++ } ++ } ++ ++ /* af setting */ ++ if (sys_conf->isp_cfg.flags.af) ++ ci_isp_set_auto_focus(&sys_conf->isp_cfg.af_cfg); ++ else ++ ci_isp_set_auto_focus(NULL); ++ ++ /* filter */ ++ mrst_isp_init_mrv_ispfilter(sys_conf, true); ++ ++ /* cac */ ++ mrst_isp_init_mrvisp_cac(sys_conf, true); ++ } ++ ++ /* ++ * disable color processing for now (will be set under user control ++ * in the main loop) ++ */ ++ ci_isp_col_set_color_processing(NULL); ++ ++ /* configure image effects */ ++ mrst_isp_init_mrv_image_effects(sys_conf, true); ++ ++ /* configure lens shading correction */ ++ if (strcmp(isi_config->name, "s5k4e1") == 0 ++ && (isi_config->res == SENSOR_RES_720P ++ || isi_config->res == SENSOR_RES_QXGA_PLUS)) { ++ dprintk(1, "enabling lsc for kmot 720p and qsxga\n"); ++ mrst_isp_init_mrvisp_lensshade(sys_conf, true); ++ } else ++ mrst_isp_init_mrvisp_lensshade(sys_conf, ++ sys_conf->isp_cfg.flags.lsc); ++ ++ /* configure bad pixel detection/correction */ ++ mrst_isp_init_mrvisp_badpixel(sys_conf, true); ++ ++ DBG_leaving; ++ return CI_STATUS_SUCCESS; ++} ++ ++int ci_jpe_encode(struct mrst_isp_device *intel, ++ enum ci_isp_conf_update_time update_time, ++ enum ci_isp_jpe_enc_mode mrv_jpe_encMode) ++{ ++ u32 mipi_data_id = 1; ++ struct isp_register *mrv_reg = ++ (struct isp_register *) MEM_MRV_REG_BASE; ++ ++ ci_isp_jpe_prep_enc(mrv_jpe_encMode); ++ ++ if (to_sensor_config(intel->sensor_curr)->mipi_mode) { ++ ci_isp_start(1, update_time); ++ v4l2_subdev_call(intel->sensor_curr, video, s_stream, 1); ++ if (mipi_flag) ++ while (mipi_data_id) ++ mipi_data_id = ++ REG_READ_EX(mrv_reg->mipi_cur_data_id); ++ mipi_flag = 0; ++ ++ } else ++ ci_isp_start(1, update_time); ++ ++ return ci_isp_jpe_wait_for_encode_done(intel); ++} ++ ++/* capture one frame */ ++u32 ci_jpe_capture(struct mrst_isp_device *isp, ++ enum ci_isp_conf_update_time update_time) ++{ ++ int retval = CI_STATUS_SUCCESS; ++ ++ /* generate header */ ++ retval = ci_isp_jpe_generate_header(isp, MRV_JPE_HEADER_MODE_JFIF); ++ if (retval != CI_STATUS_SUCCESS) ++ return 0; ++ ++ /* now encode JPEG */ ++ retval = ci_jpe_encode(isp, update_time, CI_ISP_JPE_SINGLE_SHOT); ++ if (retval != CI_STATUS_SUCCESS) ++ return 0; ++ ++ /* return ci_isp_mif_get_byte_cnt(); */ ++ return 0; ++} ++ ++static int mrst_ci_capture(struct mrst_isp_device *isp) ++{ ++ u32 bufbase; ++ u32 mipi_data_id = 1; ++ struct videobuf_buffer *vb; ++ struct isp_register *mrv_reg = ++ (struct isp_register *) MEM_MRV_REG_BASE; ++ ++ bufbase = videobuf_to_dma_contig(isp->active); ++ mrst_isp_update_marvinvfaddr(isp, bufbase, CI_ISP_CFG_UPDATE_IMMEDIATE); ++ ci_isp_mif_reset_offsets(CI_ISP_CFG_UPDATE_IMMEDIATE); ++ ++ ci_isp_reset_interrupt_status(); ++ mrst_isp_enable_interrupt(isp); ++ ++ if (isp->pixelformat == V4L2_PIX_FMT_JPEG) { ++ mrst_isp_disable_interrupt(isp); ++ ci_isp_jpe_init_ex(isp->bufwidth, isp->bufheight, ++ isp->sys_conf.isp_cfg.jpeg_enc_ratio, ++ true); ++ ci_jpe_capture(isp, CI_ISP_CFG_UPDATE_FRAME_SYNC); ++ ++ vb = isp->active; ++ vb->size = ci_isp_mif_get_byte_cnt(); ++ vb->state = VIDEOBUF_DONE; ++ do_gettimeofday(&vb->ts); ++ vb->field_count++; ++ wake_up(&vb->done); ++ isp->active = NULL; ++ ++ dprintk(2, "countcount = %lx", vb->size); ++ } else if (isp->pixelformat == INTEL_PIX_FMT_RAW08 ++ || isp->pixelformat == INTEL_PIX_FMT_RAW10 ++ || isp->pixelformat == INTEL_PIX_FMT_RAW12) { ++ mrst_isp_disable_interrupt(isp); ++ ci_isp_start(1, CI_ISP_CFG_UPDATE_FRAME_SYNC); ++ ci_isp_wait_for_frame_end(isp); ++ ++ /* update captured frame status */ ++ vb = isp->active; ++ /* vb->size = ci_isp_mif_get_byte_cnt(); */ ++ vb->state = VIDEOBUF_DONE; ++ do_gettimeofday(&vb->ts); ++ vb->field_count++; ++ wake_up(&vb->done); ++ isp->active = NULL; ++ /* ci_isp_reg_dump_all(); */ ++ dprintk(3, "captured index = %d", vb->i); ++ } else if (to_sensor_config(isp->sensor_curr)->mipi_mode) { ++ ci_isp_start(0, CI_ISP_CFG_UPDATE_IMMEDIATE); ++ ++ if (mipi_flag) { ++ v4l2_subdev_call(isp->sensor_curr, video, s_stream, 1); ++ ++ while (mipi_data_id) { ++ mipi_data_id = ++ REG_READ_EX(mrv_reg->mipi_cur_data_id); ++ dprintk(5, "mipi_cur_data_id = %x", ++ mipi_data_id); ++ } ++ mipi_flag = 0; ++ } ++ } else ++ ci_isp_start(0, CI_ISP_CFG_UPDATE_FRAME_SYNC); ++ ++ return 0; ++} ++ ++static int buffer_setup(struct videobuf_queue *vq, unsigned int *count, ++ unsigned int *size) ++{ ++ struct mrst_isp_fh *fh = vq->priv_data; ++ struct mrst_isp_device *isp = fh->dev; ++ ++ u32 w = isp->bufwidth; ++ u32 h = isp->bufheight; ++ u32 depth = isp->depth; ++ u32 fourcc = isp->pixelformat; ++ ++ if (fourcc == V4L2_PIX_FMT_JPEG) { ++ *size = PAGE_ALIGN((isp->mb1_size ++ - 640*480*2)/(*count)) - PAGE_SIZE; ++ /* *size = PAGE_ALIGN(2 * 1024 * 1024); */ ++ } else if (fourcc == INTEL_PIX_FMT_RAW08 ++ || fourcc == INTEL_PIX_FMT_RAW10 ++ || fourcc == INTEL_PIX_FMT_RAW12) { ++ *size = (w * h * depth)/8; ++ } else { ++ *size = (w * h * depth)/8; ++ } ++ ++ isp->frame_size = *size; ++ isp->num_frames = *count; ++ ++ if (0 == *count) ++ *count = 3; ++ ++ while (*size * *count > isp->mb1_size) ++ (*count)--; ++ ++ dprintk(1, "count=%d, size=%d", *count, *size); ++ return 0; ++} ++ ++static void free_buffer(struct videobuf_queue *vq, struct mrst_isp_buffer *buf) ++{ ++ struct videobuf_buffer *vb = &buf->vb; ++ ++ dprintk(1, "(vb=0x%p) baddr = 0x%08lx bsize = %d", vb, ++ vb->baddr, vb->bsize); ++ ++ videobuf_dma_contig_free(vq, vb); ++ ++ buf->vb.state = VIDEOBUF_NEEDS_INIT; ++ dprintk(1, "free_buffer: freed"); ++} ++ ++static int buffer_prepare(struct videobuf_queue *vq, ++ struct videobuf_buffer *vb, enum v4l2_field field) ++{ ++ struct mrst_isp_fh *fh = vq->priv_data; ++ struct mrst_isp_device *isp = fh->dev; ++ struct mrst_isp_buffer *buf = container_of(vb, struct mrst_isp_buffer, ++ vb); ++ int ret; ++ ++ if (vb->width != isp->bufwidth || vb->height != isp->bufheight ++ || vb->field != field) { ++ /* buf->fmt = isp->pixelformat; */ ++ vb->width = isp->bufwidth; ++ vb->height = isp->bufheight; ++ vb->field = field; ++ vb->state = VIDEOBUF_NEEDS_INIT; ++ } ++ ++ vb->size = isp->frame_size; ++ ++ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { ++ ret = videobuf_iolock(vq, vb, NULL); ++ if (ret) ++ goto fail; ++ vb->state = VIDEOBUF_PREPARED; ++ } ++ ++ return 0; ++ ++fail: ++ printk(KERN_ERR "mrstisp: error calling videobuf_iolock"); ++ free_buffer(vq, buf); ++ return ret; ++} ++ ++static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) ++{ ++ struct mrst_isp_fh *fh = vq->priv_data; ++ struct mrst_isp_device *isp = fh->dev; ++ u32 bufbase; ++ ++ vb->state = VIDEOBUF_QUEUED; ++ dprintk(1, "buffer %d in buffer querue", vb->i); ++ if (isp->stopflag) { ++ list_add_tail(&vb->queue, &isp->capture); ++ if (isp->active) { ++ /* dprintk(1, "AAAAAAAAAA in flag condition"); */ ++ /* isp->active->state = VIDEOBUF_ACTIVE; */ ++ /* mrst_isp_to_do_mblk_line = 1; */ ++ bufbase = videobuf_to_dma_contig(vb); ++ mrst_isp_update_marvinvfaddr(isp, bufbase, 0); ++ /* mrst_isp_enable_interrupt(isp); */ ++ } else { ++ isp->active = vb; ++ mrst_isp_enable_interrupt(isp); ++ /* ++ dprintk(1, "xxxxxxxxx in flag condition"); ++ isp->active->state = VIDEOBUF_ACTIVE; ++ mrst_isp_to_do_mblk_line = 1; ++ bufbase = videobuf_to_dma_contig(isp->active); ++ mrst_isp_update_marvinvfaddr(isp, bufbase, ++ CI_ISP_CFG_UPDATE_FRAME_SYNC); ++ */ ++ } ++ isp->stopflag = 0; ++ } else if (!isp->active) { ++ dprintk(1, "no active queue"); ++ isp->active = vb; ++ isp->active->state = VIDEOBUF_ACTIVE; ++ mrst_isp_to_do_mblk_line = 1; ++ mrst_ci_capture(isp); ++ } else { ++ dprintk(1, "capture to active queue"); ++ list_add_tail(&vb->queue, &isp->capture); ++ } ++ ++ return; ++} ++ ++static void buffer_release(struct videobuf_queue *vq, ++ struct videobuf_buffer *vb) ++{ ++ struct mrst_isp_buffer *buf = container_of(vb, ++ struct mrst_isp_buffer, vb); ++ DBG_entering; ++ free_buffer(vq, buf); ++ DBG_leaving; ++} ++ ++static struct videobuf_queue_ops mrst_isp_videobuf_qops = { ++ .buf_setup = buffer_setup, ++ .buf_prepare = buffer_prepare, ++ .buf_queue = buffer_queue, ++ .buf_release = buffer_release, ++}; ++ ++static int mrst_isp_open(struct file *file) ++{ ++ struct video_device *vdev = video_devdata(file); ++ struct mrst_isp_device *isp = video_get_drvdata(vdev); ++ struct mrst_isp_fh *fh = NULL; ++ struct v4l2_format sensor_format; ++ int ret; ++ ++ DBG_entering; ++ ++ if (!isp) { ++ printk(KERN_ERR "null in mrst_isp_open\n"); ++ return -ENODEV; ++ } ++ ++ dprintk(2, "open = %d", isp->open); ++ mutex_lock(&isp->mutex); ++ if (isp->open == 0) { ++ if (isp->sensor_soc) { ++ dprintk(0, "cur senfor soc"); ++ isp->sensor_curr = isp->sensor_soc; ++ } else { ++ dprintk(0, "cur sensor raw"); ++ isp->sensor_curr = isp->sensor_raw; ++ } ++ } ++ ++isp->open; ++ ++ ret = v4l2_subdev_call(isp->sensor_curr, video, g_fmt, ++ &sensor_format); ++ if (ret) { ++ printk(KERN_ERR "can't get current pix from sensor!\n"); ++ ret = -EINVAL; ++ goto exit_unlock; ++ } ++ ++ dprintk(1, "current sensor format: %d x %d", ++ sensor_format.fmt.pix.width, ++ sensor_format.fmt.pix.height); ++ ++ fh = kzalloc(sizeof(*fh), GFP_KERNEL); ++ if (NULL == fh) { ++ printk(KERN_ERR "no mem for fh \n"); ++ ret = -ENOMEM; ++ goto exit_unlock; ++ } ++ ++ file->private_data = fh; ++ fh->dev = isp; ++ ++ videobuf_queue_dma_contig_init(&fh->vb_q, &mrst_isp_videobuf_qops, ++ vdev->parent, &isp->lock, ++ V4L2_BUF_TYPE_VIDEO_CAPTURE, ++ V4L2_FIELD_NONE, ++ sizeof(struct mrst_isp_buffer), fh); ++ ++exit_unlock: ++ mutex_unlock(&isp->mutex); ++ DBG_leaving; ++ return 0; ++} ++ ++static int mrst_isp_close(struct file *file) ++{ ++ struct video_device *dev = video_devdata(file); ++ struct mrst_isp_device *isp = video_get_drvdata(dev); ++ struct mrst_isp_fh *fh = file->private_data; ++ unsigned long flags; ++ ++ DBG_entering; ++ mutex_lock(&isp->mutex); ++ --isp->open; ++ dprintk(2, "close = %d", isp->open); ++ if (isp->open == 0) { ++ if (isp->streaming == 1) { ++ videobuf_streamoff(&fh->vb_q); ++ isp->streaming = 0; ++ isp->buffer_required = 0; ++ isp->stopflag = 0; ++ ++ spin_lock_irqsave(&isp->lock, flags); ++ INIT_LIST_HEAD(&isp->capture); ++ isp->active = NULL; ++ isp->next = NULL; ++ isp->sys_conf.isp_hal_enable = 0; ++ isp->sys_conf.jpg_review_enable = 0; ++ spin_unlock_irqrestore(&isp->lock, flags); ++ ++ ci_isp_stop(CI_ISP_CFG_UPDATE_FRAME_SYNC); ++ v4l2_subdev_call(isp->sensor_curr, video, s_stream, 0); ++ isp->sensor_curr = NULL; ++ } ++ if (isp->sensor_soc) ++ v4l2_subdev_call(isp->sensor_soc, core, s_gpio, 1); ++ if (isp->sensor_raw) ++ v4l2_subdev_call(isp->sensor_raw, core, s_gpio, 1); ++ } ++ ++ kfree(file->private_data); ++ ++ mutex_unlock(&isp->mutex); ++ ++ /*XXX zheng*/ ++ if (isp->open == 0) ++ frame_cnt = 0; ++ ++ DBG_leaving; ++ return 0; ++} ++ ++static ssize_t mrst_isp_read(struct file *file, char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ return 0; ++} ++ ++static void mrst_isp_videobuf_vm_open(struct vm_area_struct *vma) ++{ ++ struct videobuf_mapping *map = vma->vm_private_data; ++ ++ dprintk(2, "vm_open %p [count=%u,vma=%08lx-%08lx]\n", ++ map, map->count, vma->vm_start, vma->vm_end); ++ ++ map->count++; ++} ++ ++static void mrst_isp_videobuf_vm_close(struct vm_area_struct *vma) ++{ ++ struct videobuf_mapping *map = vma->vm_private_data; ++ struct videobuf_queue *q = map->q; ++ int i; ++ ++ dprintk(2, "vm_close %p [count=%u,vma=%08lx-%08lx]\n", ++ map, map->count, vma->vm_start, vma->vm_end); ++ ++ map->count--; ++ if (0 == map->count) { ++ struct videobuf_dma_contig_memory *mem; ++ ++ dprintk(2, "munmap %p q=%p\n", map, q); ++ mutex_lock(&q->vb_lock); ++ ++ /* We need first to cancel streams, before unmapping */ ++ if (q->streaming) ++ videobuf_queue_cancel(q); ++ ++ for (i = 0; i < VIDEO_MAX_FRAME; i++) { ++ if (NULL == q->bufs[i]) ++ continue; ++ ++ if (q->bufs[i]->map != map) ++ continue; ++ ++ mem = q->bufs[i]->priv; ++ if (mem) { ++ /* This callback is called only if kernel has ++ allocated memory and this memory is mmapped. ++ In this case, memory should be freed, ++ in order to do memory unmap. ++ */ ++ ++ MAGIC_CHECK(mem->magic, MAGIC_DC_MEM); ++ ++ /* vfree is not atomic - can't be ++ called with IRQ's disabled ++ */ ++ dprintk(2, "buf[%d] freeing %p\n", ++ i, mem->vaddr); ++ ++ /* ++ dma_free_coherent(q->dev, mem->size, ++ mem->vaddr, mem->dma_handle); ++ */ ++ mem->vaddr = NULL; ++ } ++ ++ q->bufs[i]->map = NULL; ++ q->bufs[i]->baddr = 0; ++ } ++ ++ kfree(map); ++ ++ mutex_unlock(&q->vb_lock); ++ } ++} ++ ++static struct vm_operations_struct mrst_isp_videobuf_vm_ops = { ++ .open = mrst_isp_videobuf_vm_open, ++ .close = mrst_isp_videobuf_vm_close, ++}; ++ ++static int mrst_isp_mmap_mapper(struct videobuf_queue *q, ++ struct vm_area_struct *vma) ++{ ++ struct videobuf_dma_contig_memory *mem; ++ struct videobuf_mapping *map; ++ unsigned int first; ++ int retval; ++ unsigned long size, offset = vma->vm_pgoff << PAGE_SHIFT; ++ ++ struct mrst_isp_fh *fh = q->priv_data; ++ struct mrst_isp_device *isp = fh->dev; ++ ++ DBG_entering; ++ ++ if (!(vma->vm_flags & VM_WRITE) || !(vma->vm_flags & VM_SHARED)) ++ return -EINVAL; ++ ++ /* look for first buffer to map */ ++ for (first = 0; first < VIDEO_MAX_FRAME; first++) { ++ if (!q->bufs[first]) ++ continue; ++ ++ if (V4L2_MEMORY_MMAP != q->bufs[first]->memory) ++ continue; ++ if (q->bufs[first]->boff == offset) { ++ dprintk(1, "buff id %d is mapped", first); ++ break; ++ } ++ } ++ if (VIDEO_MAX_FRAME == first) { ++ eprintk("invalid user space offset [offset=0x%lx]", offset); ++ return -EINVAL; ++ } ++ ++ /* create mapping + update buffer list */ ++ map = kzalloc(sizeof(struct videobuf_mapping), GFP_KERNEL); ++ if (!map) ++ return -ENOMEM; ++ ++ q->bufs[first]->map = map; ++ map->start = vma->vm_start; ++ map->end = vma->vm_end; ++ map->q = q; ++ ++ q->bufs[first]->baddr = vma->vm_start; ++ ++ mem = q->bufs[first]->priv; ++ BUG_ON(!mem); ++ MAGIC_CHECK(mem->magic, MAGIC_DC_MEM); ++ ++ mem->size = PAGE_ALIGN(q->bufs[first]->bsize); ++ mem->dma_handle = isp->mb1 + (mem->size * first); ++ mem->vaddr = (void *)0x1; ++ /* ++ mem->vaddr = dma_alloc_coherent(q->dev, mem->size, ++ &mem->dma_handle, GFP_KERNEL); ++ */ ++ if (mem->size > isp->mb1_size) { ++ eprintk("to big size, can not be mmapped"); ++ return -EINVAL; ++ } ++ ++ /* Try to remap memory */ ++ ++ size = vma->vm_end - vma->vm_start; ++ size = (size < mem->size) ? size : mem->size; ++ ++ dprintk(1, "vm_end - vm_start = %ld, mem-size = %ld", size, mem->size); ++ ++ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); ++ retval = remap_pfn_range(vma, vma->vm_start, ++ mem->dma_handle >> PAGE_SHIFT, ++ size, vma->vm_page_prot); ++ if (retval) { ++ eprintk("mmap: remap failed with error %d. ", retval); ++ goto error; ++ } ++ ++ vma->vm_ops = &mrst_isp_videobuf_vm_ops; ++ vma->vm_flags |= VM_DONTEXPAND; ++ vma->vm_private_data = map; ++ ++ dprintk(1, "mmap %p: q=%p %08lx-%08lx (%lx) pgoff %08lx buf %d\n", ++ map, q, vma->vm_start, vma->vm_end, ++ (long int) q->bufs[first]->bsize, ++ vma->vm_pgoff, first); ++ ++ mrst_isp_videobuf_vm_open(vma); ++ ++ return 0; ++ ++error: ++ kfree(map); ++ return -ENOMEM; ++} ++int mrst_isp_videobuf_mmap_mapper(struct videobuf_queue *q, ++ struct vm_area_struct *vma) ++{ ++ MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS); ++ ++ mutex_lock(&q->vb_lock); ++ mrst_isp_mmap_mapper(q, vma); ++ /* retval = CALL(q, mmap_mapper, q, vma); */ ++ q->is_mmapped = 1; ++ mutex_unlock(&q->vb_lock); ++ ++ return 0; ++} ++static int mrst_isp_mmap(struct file *file, struct vm_area_struct *vma) ++{ ++ int ret; ++ int map_by_myself; ++ struct mrst_isp_fh *fh; ++ struct video_device *dev = video_devdata(file); ++ struct mrst_isp_device *isp = video_get_drvdata(dev); ++ unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; ++ unsigned long size = vma->vm_end-vma->vm_start; ++ unsigned long page; ++ ++ DBG_entering; ++ ++ /* temporarily put here */ ++ if (isp->open > 1) { ++ printk(KERN_ERR "ISP already opened..."); ++ return -EINVAL; ++ } ++ ++ fh = file->private_data; ++ ++ if (!(vma->vm_flags & (VM_WRITE | VM_READ)) ++ || !(vma->vm_flags & VM_SHARED)) { ++ printk(KERN_ERR "mrstisp: wrong vma flag"); ++ return -EINVAL; ++ } ++ ++ /* to check whether if it is ISP bar 0 map */ ++ if (offset == isp->mb0_size + isp->mb1_size) { ++ dprintk(1, "---- map bar0 ----"); ++ page = isp->mb0; ++ map_by_myself = 1; ++ } else if (offset == 0 && size == isp->mb1_size) { ++ dprintk(1, "---- map bar1 ----"); ++ page = isp->mb1; ++ map_by_myself = 1; ++ } else if (isp->pixelformat == V4L2_PIX_FMT_JPEG ++ && isp->sys_conf.jpg_review_enable == 1 ++ && offset == isp->sys_conf.jpg_review.offset) { ++ dprintk(1, "---- map jpeg review buffer----"); ++ page = isp->mb1 + isp->sys_conf.jpg_review.offset; ++ map_by_myself = 1; ++ } else { ++ dprintk(1, "----map one certain buffer----"); ++ map_by_myself = 0; ++ } ++ ++ if (map_by_myself) { ++ vma->vm_flags |= VM_IO; ++ vma->vm_flags |= VM_RESERVED; /* avoid to swap out this VMA */ ++ ++ page = page >> PAGE_SHIFT; ++ ++ if (remap_pfn_range(vma, vma->vm_start, page, size, ++ PAGE_SHARED)) { ++ printk(KERN_ERR "fail to put MMAP buffer to user space\n"); ++ return -EAGAIN; ++ } ++ ++ return 0; ++ } ++ ++ if (size > isp->num_frames * PAGE_ALIGN(isp->frame_size)) { ++ eprintk("length is larger than num * size"); ++ return -EINVAL; ++ } ++ ++ ret = mrst_isp_videobuf_mmap_mapper(&fh->vb_q, vma); ++ ++ dprintk(1, "vma start=0x%08lx, size=%ld, offset=%ld ret=%d", ++ (unsigned long)vma->vm_start, ++ (unsigned long)vma->vm_end-(unsigned long)vma->vm_start, ++ (unsigned long)offset, ret); ++ ++ return ret; ++} ++ ++static int mrst_isp_g_fmt_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct video_device *dev = video_devdata(file); ++ struct mrst_isp_device *isp = video_get_drvdata(dev); ++ int ret; ++ ++ WARN_ON(priv != file->private_data); ++ ++ DBG_entering; ++ ++ if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { ++ f->fmt.pix.width = isp->bufwidth; ++ f->fmt.pix.height = isp->bufheight; ++ f->fmt.pix.pixelformat = isp->pixelformat; ++ f->fmt.pix.bytesperline = (f->fmt.pix.width * isp->depth) >> 3; ++ f->fmt.pix.sizeimage = f->fmt.pix.height ++ * f->fmt.pix.bytesperline; ++ ret = 0; ++ } else { ++ ret = -EINVAL; ++ } ++ ++ dprintk(1, "get fmt %d x %d ", f->fmt.pix.width, f->fmt.pix.height); ++ DBG_leaving; ++ return ret; ++} ++ ++static struct intel_fmt *fmt_by_fourcc(unsigned int fourcc) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < NUM_FORMATS; i++) ++ if (fmts[i].fourcc == fourcc) ++ return fmts+i; ++ return NULL; ++} ++ ++static int mrst_isp_try_fmt_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct video_device *dev = video_devdata(file); ++ struct mrst_isp_device *isp = video_get_drvdata(dev); ++ ++ struct intel_fmt *fmt; ++ int w, h; ++ int ret; ++ ++ WARN_ON(priv != file->private_data); ++ ++ DBG_entering; ++ ++ mutex_lock(&isp->mutex); ++ ++ fmt = fmt_by_fourcc(f->fmt.pix.pixelformat); ++ if (NULL == fmt && f->fmt.pix.pixelformat != V4L2_PIX_FMT_MPEG) { ++ printk(KERN_ERR "mrstisp: fmt not found\n"); ++ ret = -EINVAL; ++ goto exit_unlock; ++ } ++ ++ w = f->fmt.pix.width; ++ h = f->fmt.pix.height; ++ ++ dprintk(1, "sensor name %s: before w = %d, h = %d", ++ isp->sensor_curr->name, w, h); ++ ++ ret = v4l2_subdev_call(isp->sensor_curr, video, try_fmt, f); ++ if (ret) ++ goto exit_unlock; ++ ++ ++ w = f->fmt.pix.width; ++ h = f->fmt.pix.height; ++ ++ if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_RGB565 || ++ f->fmt.pix.pixelformat == V4L2_PIX_FMT_BGR32) { ++ if (w < INTEL_MIN_WIDTH) ++ w = INTEL_MIN_WIDTH; ++ if (w > INTEL_MAX_WIDTH) ++ w = INTEL_MAX_WIDTH; ++ if (h < INTEL_MIN_HEIGHT) ++ h = INTEL_MIN_HEIGHT; ++ if (h > INTEL_MAX_HEIGHT) ++ h = INTEL_MAX_HEIGHT; ++ f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; ++ } else { ++ if (w < INTEL_MIN_WIDTH) ++ w = INTEL_MIN_WIDTH; ++ if (w > INTEL_MAX_WIDTH_MP) ++ w = INTEL_MAX_WIDTH_MP; ++ if (h < INTEL_MIN_HEIGHT) ++ h = INTEL_MIN_HEIGHT; ++ if (h > INTEL_MAX_HEIGHT_MP) ++ h = INTEL_MAX_HEIGHT_MP; ++ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; ++ } ++ ++ f->fmt.pix.width = w; ++ f->fmt.pix.height = h; ++ ++ f->fmt.pix.field = V4L2_FIELD_NONE; ++ f->fmt.pix.bytesperline = (w * h)/8; ++ if (fmt) ++ f->fmt.pix.sizeimage = (w * h * fmt->depth)/8; ++ if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_JPEG) ++ f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; ++ f->fmt.pix.priv = 0; ++ ++ dprintk(3, "after w = %d, h = %d", w, h); ++ ret = 0; ++ ++exit_unlock: ++ mutex_unlock(&isp->mutex); ++ ++ DBG_leaving; ++ return ret; ++} ++ ++static int mrst_isp_s_fmt_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct video_device *dev = video_devdata(file); ++ struct mrst_isp_device *isp = video_get_drvdata(dev); ++ struct intel_fmt *fmt; ++ int ret; ++ unsigned int width_o, height_o; ++ unsigned short width_sensor, height_sensor; ++ unsigned int w, h; ++ ++ WARN_ON(priv != file->private_data); ++ ++ DBG_entering; ++ ++ mipi_flag = 1; ++ ++ w = f->fmt.pix.width; ++ h = f->fmt.pix.height; ++ ++ if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_RGB565 || ++ f->fmt.pix.pixelformat == V4L2_PIX_FMT_BGR32) { ++ if (w < INTEL_MIN_WIDTH) ++ w = INTEL_MIN_WIDTH; ++ if (w > INTEL_MAX_WIDTH) ++ w = INTEL_MAX_WIDTH; ++ if (h < INTEL_MIN_HEIGHT) ++ h = INTEL_MIN_HEIGHT; ++ if (h > INTEL_MAX_HEIGHT) ++ h = INTEL_MAX_HEIGHT; ++ f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; ++ } else { ++ if (w < INTEL_MIN_WIDTH) ++ w = INTEL_MIN_WIDTH; ++ if (w > INTEL_MAX_WIDTH_MP) ++ w = INTEL_MAX_WIDTH_MP; ++ if (h < INTEL_MIN_HEIGHT) ++ h = INTEL_MIN_HEIGHT; ++ if (h > INTEL_MAX_HEIGHT_MP) ++ h = INTEL_MAX_HEIGHT_MP; ++ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; ++ } ++ ++ f->fmt.pix.width = w; ++ f->fmt.pix.height = h; ++ ++ width_o = f->fmt.pix.width; ++ height_o = f->fmt.pix.height; ++ ++ (void)ci_sensor_res2size(to_sensor_config(isp->sensor_curr)->res, ++ &width_sensor, &height_sensor); ++ ++ ret = mrst_isp_try_fmt_cap(file, priv, f); ++ if (0 != ret) { ++ printk(KERN_ERR "mrstisp: set format failed\n"); ++ return ret; ++ } ++ ++ /* set fmt for only sensor */ ++ if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_MPEG) { ++ ret = v4l2_subdev_call(isp->sensor_curr, video, s_fmt, f); ++ dprintk(1, "------------set fmt only for sensor (%d x %d)", ++ f->fmt.pix.width, f->fmt.pix.height); ++ return ret; ++ } ++ ++ if (isp->sys_conf.isp_hal_enable) { ++ /* set fmt for isp */ ++ mutex_lock(&isp->mutex); ++ fmt = fmt_by_fourcc(f->fmt.pix.pixelformat); ++ ++ isp->pixelformat = fmt->fourcc; ++ isp->depth = fmt->depth; ++ ++ dprintk(1, "sensor (%d x %d)", width_sensor, height_sensor); ++ if (width_o < f->fmt.pix.width && ++ height_o < f->fmt.pix.height) { ++ isp->bufwidth = width_o; ++ isp->bufheight = height_o; ++ } else if (width_sensor < f->fmt.pix.width && ++ height_sensor < f->fmt.pix.height) { ++ isp->bufwidth = width_sensor; ++ isp->bufheight = height_sensor; ++ f->fmt.pix.width = width_sensor; ++ f->fmt.pix.height = height_sensor; ++ } else { ++ isp->bufwidth = f->fmt.pix.width; ++ isp->bufheight = f->fmt.pix.height; ++ } ++ ++ /* FIXME ++ * check if buf res is larger than ++ * sensor real res(1304x980) ++ * if yes, down buf res to VGA ++ */ ++ if (to_sensor_config(isp->sensor_curr)->res == ++ SENSOR_RES_VGA_PLUS) ++ if (isp->bufwidth >= VGA_SIZE_H && ++ isp->bufheight >= VGA_SIZE_V) { ++ isp->bufwidth = VGA_SIZE_H; ++ isp->bufheight = VGA_SIZE_V; ++ } ++ ++ mutex_unlock(&isp->mutex); ++ ++ dprintk(1, "----------set fmt only to isp: w %d, h%d, " ++ "fourcc: %lx", isp->bufwidth, ++ isp->bufheight, fmt->fourcc); ++ } else { ++ ++ /* set fmt for both isp and sensor */ ++ mutex_lock(&isp->mutex); ++ fmt = fmt_by_fourcc(f->fmt.pix.pixelformat); ++ ++ isp->pixelformat = fmt->fourcc; ++ isp->depth = fmt->depth; ++ isp->bufwidth = width_o; ++ isp->bufheight = height_o; ++ ++ mutex_unlock(&isp->mutex); ++ ++ dprintk(1, "--------set fmt for isp : w%d, h%d, fourcc: %lx", ++ isp->bufwidth, isp->bufheight, fmt->fourcc); ++ dprintk(1, "--------set fmt for sesnro : w%d, h%d, fourcc: %lx", ++ f->fmt.pix.width, f->fmt.pix.height, fmt->fourcc); ++ ++ ret = v4l2_subdev_call(isp->sensor_curr, video, s_fmt, f); ++ } ++ ++ DBG_leaving; ++ return ret; ++} ++ ++static int mrst_isp_enum_framesizes(struct file *file, void *priv, ++ struct v4l2_frmsizeenum *arg) ++{ ++ struct video_device *dev = video_devdata(file); ++ struct mrst_isp_device *isp = video_get_drvdata(dev); ++ int ret; ++ ++ DBG_entering; ++ ++ WARN_ON(priv != file->private_data); ++ ++ ret = v4l2_subdev_call(isp->sensor_curr, video, enum_framesizes, arg); ++ ++ DBG_leaving; ++ return ret; ++} ++ ++static int mrst_isp_enum_frameintervals(struct file *file, void *priv, ++ struct v4l2_frmivalenum *arg) ++{ ++ struct video_device *dev = video_devdata(file); ++ struct mrst_isp_device *isp = video_get_drvdata(dev); ++ int ret; ++ ++ DBG_entering; ++ ++ WARN_ON(priv != file->private_data); ++ ++ ret = v4l2_subdev_call(isp->sensor_curr, video, enum_frameintervals, ++ arg); ++ DBG_leaving; ++ return ret; ++} ++ ++static int mrst_isp_queryctrl(struct file *file, void *priv, ++ struct v4l2_queryctrl *c) ++{ ++ struct video_device *vdev = video_devdata(file); ++ struct mrst_isp_device *isp = video_get_drvdata(vdev); ++ ++ WARN_ON(priv != file->private_data); ++ ++ DBG_entering; ++ ++ if (!v4l2_subdev_call(isp->sensor_curr, core, queryctrl, c)) ++ return 0; ++ else if (!v4l2_subdev_call(isp->motor, core, queryctrl, c)) ++ return 0; ++ ++ /* No controls supported */ ++ return -EINVAL; ++} ++ ++static int mrst_isp_g_ctrl(struct file *file, void *priv, ++ struct v4l2_control *c) ++{ ++ struct video_device *dev = video_devdata(file); ++ struct mrst_isp_device *isp = video_get_drvdata(dev); ++ int ret; ++ ++ WARN_ON(priv != file->private_data); ++ ++ DBG_entering; ++ if (c->id == V4L2_CID_FOCUS_ABSOLUTE) { ++ ret = v4l2_subdev_call(isp->motor, core, g_ctrl, c); ++ dprintk(2, "get focus from motor : %d", c->value); ++ return ret; ++ } else { ++ ret = v4l2_subdev_call(isp->sensor_curr, core, g_ctrl, c); ++ dprintk(2, "get other cotrol from senrsor : %d", c->value); ++ return ret; ++ } ++} ++ ++static int mrst_isp_s_ctrl(struct file *file, void *fh, struct v4l2_control *c) ++{ ++ struct video_device *dev = video_devdata(file); ++ struct mrst_isp_device *isp = video_get_drvdata(dev); ++ ++ DBG_entering; ++ ++ if (c->id == V4L2_CID_FOCUS_ABSOLUTE) { ++ dprintk(2, "setting focus %d to motor", c->value); ++ return v4l2_subdev_call(isp->motor, core, s_ctrl, c); ++ } else { ++ dprintk(2, "setting other ctrls, value = %d", c->value); ++ return v4l2_subdev_call(isp->sensor_curr, core, s_ctrl, c); ++ } ++} ++ ++static int mrst_isp_index_to_camera(struct mrst_isp_device *isp, u32 index) ++{ ++ int camera = MRST_CAMERA_NONE; ++ ++ if (isp->sensor_soc && isp->sensor_raw) { ++ switch (index) { ++ case 0: ++ camera = isp->sensor_soc_index; ++ break; ++ case 1: ++ camera = isp->sensor_raw_index; ++ break; ++ } ++ } else if (isp->sensor_soc) { ++ switch (index) { ++ case 0: ++ camera = isp->sensor_soc_index; ++ break; ++ } ++ } else if (isp->sensor_raw) { ++ switch (index) { ++ case 0: ++ camera = isp->sensor_raw_index; ++ break; ++ } ++ } ++ ++ return camera; ++} ++ ++static int mrst_isp_enum_input(struct file *file, void *priv, ++ struct v4l2_input *i) ++{ ++ struct video_device *vdev = video_devdata(file); ++ struct mrst_isp_device *isp = video_get_drvdata(vdev); ++ int camera; ++ ++ DBG_entering; ++ ++ WARN_ON(priv != file->private_data); ++ ++ camera = mrst_isp_index_to_camera(isp, i->index); ++ if (MRST_CAMERA_NONE == camera) ++ return -EINVAL; ++ ++ i->type = V4L2_INPUT_TYPE_CAMERA; ++ i->std = V4L2_STD_UNKNOWN; ++ strcpy(i->name, mrst_camera_table[camera].name); ++ ++ DBG_leaving; ++ return 0; ++} ++static int mrst_isp_g_input(struct file *file, void *priv, unsigned int *i) ++{ ++ struct video_device *vdev = video_devdata(file); ++ struct mrst_isp_device *isp = video_get_drvdata(vdev); ++ ++ DBG_entering; ++ ++ WARN_ON(priv != file->private_data); ++ ++ if (isp->sensor_soc && isp->sensor_raw) ++ if (isp->sensor_curr == isp->sensor_soc) ++ *i = 0; ++ else ++ *i = 1; ++ else ++ *i = 0; ++ ++ DBG_leaving; ++ return 0; ++} ++ ++static int mrst_isp_s_input(struct file *file, void *priv, unsigned int i) ++{ ++ struct video_device *vdev = video_devdata(file); ++ struct mrst_isp_device *isp = video_get_drvdata(vdev); ++ ++ int camera; ++ ++ DBG_entering; ++ ++ if (isp->streaming) { ++ printk(KERN_WARNING "VIDIOC_S_INPUT error: ISP is streaming\n"); ++ return -EBUSY; ++ } ++ ++ camera = mrst_isp_index_to_camera(isp, i); ++ if (MRST_CAMERA_NONE == camera) ++ return -EINVAL; ++ ++ if (mrst_camera_table[camera].type == MRST_CAMERA_SOC) ++ isp->sensor_curr = isp->sensor_soc; ++ else ++ isp->sensor_curr = isp->sensor_raw; ++ ++ dprintk(1, "set sensor %s as input", isp->sensor_curr->name); ++ ++ DBG_leaving; ++ return 0; ++} ++ ++static int mrst_isp_g_ext_ctrls(struct file *file, ++ void *fh, ++ struct v4l2_ext_controls *c) ++{ ++ struct video_device *dev = video_devdata(file); ++ struct mrst_isp_device *isp = video_get_drvdata(dev); ++ ++ int ret = -EINVAL; ++ ++ DBG_entering; ++ ++ if (c->ctrl_class != V4L2_CTRL_CLASS_CAMERA) { ++ printk(KERN_ERR "Invalid control class\n"); ++ return ret; ++ } ++ ++ c->error_idx = 0; ++ if (isp->motor) { ++ ret = v4l2_subdev_call(isp->motor, core, g_ext_ctrls, c); ++ if (c->error_idx) { ++ printk(KERN_ERR "mrst: error call g_ext_ctrls\n"); ++ return ret; ++ } ++ } ++ ++ DBG_leaving; ++ return 0; ++} ++ ++static int mrst_isp_s_ext_ctrls(struct file *file, void *fh, ++ struct v4l2_ext_controls *c) ++{ ++ struct video_device *dev = video_devdata(file); ++ struct mrst_isp_device *isp = video_get_drvdata(dev); ++ ++ int ret = -EINVAL; ++ ++ DBG_entering; ++ ++ if (c->ctrl_class != V4L2_CTRL_CLASS_CAMERA) { ++ printk(KERN_INFO "Invalid control class\n"); ++ return ret; ++ } ++ ++ c->error_idx = 0; ++ if (isp->motor) { ++ ret = v4l2_subdev_call(isp->motor, core, s_ext_ctrls, c); ++ if (c->error_idx) { ++ printk(KERN_ERR "mrst: error call s_ext_ctrls\n"); ++ return ret; ++ } ++ } ++ ++ DBG_leaving; ++ return 0; ++} ++ ++static int mrst_isp_s_std(struct file *filp, void *priv, v4l2_std_id *a) ++{ ++ DBG_entering; ++ DBG_leaving; ++ return 0; ++} ++ ++static int mrst_isp_querycap(struct file *file, void *priv, ++ struct v4l2_capability *cap) ++{ ++ struct video_device *dev = video_devdata(file); ++ ++ DBG_entering; ++ ++ strlcpy(cap->driver, DRIVER_NAME, sizeof(cap->driver)); ++ strlcpy(cap->card, dev->name, sizeof(cap->card)); ++ ++ cap->version = INTEL_VERSION(0, 5, 0); ++ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; ++ ++ DBG_leaving; ++ ++ return 0; ++} ++ ++static int mrst_isp_cropcap(struct file *file, void *priv, ++ struct v4l2_cropcap *cap) ++{ ++ struct video_device *dev = video_devdata(file); ++ struct mrst_isp_device *isp = video_get_drvdata(dev); ++ ++ WARN_ON(priv != file->private_data); ++ ++ DBG_entering; ++ ++ if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ++ return -EINVAL; ++ ++ cap->bounds.left = 0; ++ cap->bounds.top = 0; ++ cap->bounds.width = isp->bufwidth; ++ cap->bounds.height = isp->bufheight; ++ cap->defrect = cap->bounds; ++ cap->pixelaspect.numerator = 1; ++ cap->pixelaspect.denominator = 1; ++ ++ DBG_leaving; ++ ++ return 0; ++} ++ ++static int mrst_isp_enum_fmt_cap(struct file *file, void *priv, ++ struct v4l2_fmtdesc *f) ++{ ++ struct video_device *dev = video_devdata(file); ++ struct mrst_isp_device *isp = video_get_drvdata(dev); ++ ++ unsigned int index; ++ ++ DBG_entering; ++ ++ index = f->index; ++ ++ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ++ return -EINVAL; ++ else { ++ if (isp->sensor_curr == isp->sensor_soc) ++ if (index >= 8) ++ return -EINVAL; ++ if (index >= sizeof(fmts) / sizeof(*fmts)) ++ return -EINVAL; ++ ++ f->index = index; ++ f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ strlcpy(f->description, fmts[index].name, ++ sizeof(f->description)); ++ f->pixelformat = fmts[index].fourcc; ++ if (fmts[index].fourcc == V4L2_PIX_FMT_JPEG) ++ f->flags = V4L2_FMT_FLAG_COMPRESSED; ++ } ++ ++ DBG_leaving; ++ ++ return 0; ++ ++} ++ ++#define ALIGN4(x) ((((long)(x)) & 0x3) == 0) ++ ++static int mrst_isp_reqbufs(struct file *file, void *priv, ++ struct v4l2_requestbuffers *req) ++{ ++ int ret; ++ struct mrst_isp_fh *fh = file->private_data; ++ struct video_device *dev = video_devdata(file); ++ struct mrst_isp_device *isp = video_get_drvdata(dev); ++ ++ WARN_ON(priv != file->private_data); ++ ++ DBG_entering; ++ if (req->count == 0) ++ return 0; ++ ++ /* ++ * if (req->count > 3) ++ req->count = 3; ++ */ ++ ++ if (req->memory != V4L2_MEMORY_MMAP) { ++ eprintk("wrong memory type"); ++ return -EINVAL; ++ } ++ ret = videobuf_reqbufs(&fh->vb_q, req); ++ if (ret) ++ eprintk("err calling videobuf_reqbufs ret = %d", ret); ++ ++ if (!ret) ++ isp->buffer_required = 1; ++ ++ DBG_leaving; ++ return ret; ++} ++ ++static int mrst_isp_querybuf(struct file *file, void *priv, ++ struct v4l2_buffer *buf) ++{ ++ int ret; ++ struct mrst_isp_fh *fh = file->private_data; ++ ++ WARN_ON(priv != file->private_data); ++ ++ DBG_entering; ++ ret = videobuf_querybuf(&fh->vb_q, buf); ++ ++ DBG_leaving; ++ return ret; ++} ++ ++static int mrst_isp_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) ++{ ++ int ret; ++ struct mrst_isp_fh *fh = file->private_data; ++ ++ WARN_ON(priv != file->private_data); ++ ++ DBG_entering; ++ ret = videobuf_qbuf(&fh->vb_q, buf); ++ /* identify which video buffer was q-ed */ ++ if (ret == 0) ++ fh->qbuf_flag |= (1<index); ++ dprintk(1, "q-ed index = %d", buf->index); ++ ++ DBG_leaving; ++ ++ return ret; ++} ++ ++static int mrst_isp_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) ++{ ++ int ret; ++ struct mrst_isp_fh *fh = file->private_data; ++ ++ WARN_ON(priv != file->private_data); ++ ++ /*XXX zheng*/ ++ /* ++ if (frame_cnt == 0) { ++ printk(KERN_WARNING "timer start\n"); ++ intel_timer_start(); ++ } ++ */ ++ ++ DBG_entering; ++ ++ if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ++ return -EINVAL; ++ if (b->memory != V4L2_MEMORY_MMAP) ++ return -EINVAL; ++ if (fh->qbuf_flag == 0) { ++ dprintk(1, "no buffer can be dq-ed\n"); ++ return -EINVAL; ++ } ++ ++ /*dprintk(3, "entering");*/ ++ /* ret = videobuf_dqbuf(&fh->vb_q, b, file->f_flags & O_NONBLOCK); */ ++ ret = videobuf_dqbuf(&fh->vb_q, b, 0); ++ /* identify which video buffer was dq-ed */ ++ if (ret == 0) ++ fh->qbuf_flag &= ~(1<index); ++ ++ /*XXX zheng*/ ++ ++frame_cnt; ++ /* ++ if (frame_cnt % 10 == 0) ++ printk(KERN_WARNING "%d frames takes %dms to go, fps = %d\n", ++ frame_cnt, intel_get_micro_sec(), ++ frame_cnt * 1000 / intel_get_micro_sec()); ++ */ ++ ++ dprintk(1, "dq-ed index = %d", b->index); ++ DBG_leaving; ++ return ret; ++} ++ ++static int mrst_isp_streamon(struct file *file, void *priv, ++ enum v4l2_buf_type type) ++{ ++ struct mrst_isp_fh *fh = file->private_data; ++ struct video_device *dev = video_devdata(file); ++ struct mrst_isp_device *isp = video_get_drvdata(dev); ++ int ret; ++ ++ DBG_entering; ++ ++ if (!isp->buffer_required) { ++ eprintk("buffer is not required, can not stream on "); ++ return -EINVAL; ++ } ++ ++ dprintk(2, "gamma2 = %d", isp->sys_conf.isp_cfg.flags.gamma2); ++ WARN_ON(priv != file->private_data); ++ ++ if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ++ return -EINVAL; ++ ++ mutex_lock(&isp->mutex); ++ ++ if (!to_sensor_config(isp->sensor_curr)->mipi_mode) ++ v4l2_subdev_call(isp->sensor_curr, video, s_stream, 1); ++ ++ mrst_isp_dp_init(&isp->sys_conf, to_sensor_config(isp->sensor_curr)); ++ mrst_isp_setup_viewfinder_path(isp, ++ to_sensor_config(isp->sensor_curr), -1); ++ ++ ret = videobuf_streamon(&fh->vb_q); ++ isp->streaming = 1; ++ ++ mutex_unlock(&isp->mutex); ++ ++ dprintk(1, "isp->active = %p", isp->active); ++ DBG_leaving; ++ return ret; ++} ++ ++static int mrst_isp_streamoff(struct file *file, void *priv, ++ enum v4l2_buf_type type) ++{ ++ struct mrst_isp_fh *fh = file->private_data; ++ struct video_device *dev = video_devdata(file); ++ struct mrst_isp_device *isp = video_get_drvdata(dev); ++ ++ unsigned long flags; ++ int ret; ++ ++ DBG_entering; ++ ++ WARN_ON(priv != file->private_data); ++ ++ if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ++ return -EINVAL; ++ ++ mutex_lock(&isp->mutex); ++ ++ ret = videobuf_streamoff(&fh->vb_q); ++ dprintk(1, "ret of videobuf_streamoff = %d", ret); ++ isp->streaming = 0; ++ ++ spin_lock_irqsave(&isp->lock, flags); ++ INIT_LIST_HEAD(&isp->capture); ++ isp->active = NULL; ++ isp->next = NULL; ++ isp->stopflag = 0; ++ isp->sys_conf.isp_hal_enable = 0; ++ isp->sys_conf.jpg_review_enable = 0; ++ isp->sys_conf.isp_cfg.img_eff_cfg.mode = CI_ISP_IE_MODE_OFF; ++ isp->sys_conf.isp_cfg.jpeg_enc_ratio = 1; ++ ++ spin_unlock_irqrestore(&isp->lock, flags); ++ ++ v4l2_subdev_call(isp->sensor_curr, video, s_stream, 0); ++ ci_isp_stop(CI_ISP_CFG_UPDATE_FRAME_SYNC); ++ ++ mutex_unlock(&isp->mutex); ++ ++ DBG_leaving; ++ return ret; ++} ++ ++static const struct v4l2_file_operations mrst_isp_fops = { ++ .owner = THIS_MODULE, ++ .open = mrst_isp_open, ++ .release = mrst_isp_close, ++ .read = mrst_isp_read, ++ .mmap = mrst_isp_mmap, ++ .ioctl = video_ioctl2, ++}; ++ ++static const struct v4l2_ioctl_ops mrst_isp_ioctl_ops = { ++ .vidioc_querycap = mrst_isp_querycap, ++ .vidioc_enum_fmt_vid_cap = mrst_isp_enum_fmt_cap, ++ .vidioc_g_fmt_vid_cap = mrst_isp_g_fmt_cap, ++ /* .vidioc_g_fmt_vid_out = ++ * mrst_isp_g_fmt_cap_for_sensor_hal, */ ++ .vidioc_try_fmt_vid_cap = mrst_isp_try_fmt_cap, ++ .vidioc_s_fmt_vid_cap = mrst_isp_s_fmt_cap, ++ .vidioc_cropcap = mrst_isp_cropcap, ++ .vidioc_reqbufs = mrst_isp_reqbufs, ++ .vidioc_querybuf = mrst_isp_querybuf, ++ .vidioc_qbuf = mrst_isp_qbuf, ++ .vidioc_dqbuf = mrst_isp_dqbuf, ++ .vidioc_enum_input = mrst_isp_enum_input, ++ .vidioc_g_input = mrst_isp_g_input, ++ .vidioc_s_input = mrst_isp_s_input, ++ .vidioc_s_std = mrst_isp_s_std, ++ .vidioc_queryctrl = mrst_isp_queryctrl, ++ .vidioc_streamon = mrst_isp_streamon, ++ .vidioc_streamoff = mrst_isp_streamoff, ++ .vidioc_g_ctrl = mrst_isp_g_ctrl, ++ .vidioc_s_ctrl = mrst_isp_s_ctrl, ++ .vidioc_enum_framesizes = mrst_isp_enum_framesizes, ++ .vidioc_enum_frameintervals = mrst_isp_enum_frameintervals, ++ .vidioc_g_ext_ctrls = mrst_isp_g_ext_ctrls, ++ .vidioc_s_ext_ctrls = mrst_isp_s_ext_ctrls, ++ /* FIXME private ioctls */ ++ .vidioc_default = mrst_isp_vidioc_default, ++}; ++ ++static struct video_device mrst_isp_vdev = { ++ .name = "mrst_isp", ++ .minor = -1, ++ .fops = &mrst_isp_fops, ++ .ioctl_ops = &mrst_isp_ioctl_ops, ++ .release = video_device_release_empty, ++}; ++ ++static int mrst_ci_sensor_probe(struct mrst_isp_device *isp) ++{ ++ struct v4l2_subdev *sensor = NULL, *motor = NULL; ++ int i; ++ char *name; ++ u8 addr; ++ ++ isp->adapter_sensor = i2c_get_adapter(MRST_I2C_BUS_SENSOR); ++ if (NULL == isp->adapter_sensor) { ++ printk(KERN_ERR "mrstisp: no sensor i2c adapter\n"); ++ return -ENODEV; ++ } ++ ++ dprintk(1, "got sensor i2c adapter: %s", isp->adapter_sensor->name); ++ ++ gpio_request(GPIO_STDBY1_PIN, "Sensor Standby1"); ++ gpio_request(GPIO_STDBY2_PIN, "Sensor Standby2"); ++ gpio_request(GPIO_RESET_PIN, "Sensor Reset"); ++ gpio_request(GPIO_SCLK_25, "Sensor clock"); ++ gpio_request(95, "Camera Motor"); ++ ++ /* Enable sensor related GPIO in system */ ++ gpio_direction_output(GPIO_STDBY1_PIN, 0); ++ gpio_direction_output(GPIO_STDBY2_PIN, 0); ++ gpio_direction_output(GPIO_RESET_PIN, 1); ++ gpio_direction_output(GPIO_SCLK_25, 0); ++ /* gpio_direction_output(GPIO_AF_PD, 1); */ ++ ++ /* ++ gpio_alt_func(GPIO_STDBY1_PIN, 0); ++ gpio_alt_func(GPIO_STDBY2_PIN, 0); ++ gpio_alt_func(GPIO_RESET_PIN, 0); ++ gpio_alt_func(GPIO_SCLK_25, 1); ++ */ ++ ++ for (i = 0; i < N_CAMERA; i++) { ++ name = mrst_camera_table[i].name; ++ addr = mrst_camera_table[i].sensor_addr; ++ if (mrst_camera_table[i].type == MRST_CAMERA_SOC) { ++#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 31)) ++ sensor = v4l2_i2c_new_subdev(&isp->v4l2_dev, ++ isp->adapter_sensor, ++ name, name, addr); ++#else ++ sensor = v4l2_i2c_new_subdev(&isp->v4l2_dev, ++ isp->adapter_sensor, ++ name, name, addr, NULL); ++#endif ++ ++ if (sensor == NULL) { ++ dprintk(2, "sensor %s not found", name); ++ continue; ++ } ++ isp->sensor_soc = sensor; ++ isp->sensor_soc_index = i; ++ dprintk(0, "soc camera sensor %s-%s successfully found", ++ name, sensor->name); ++ } ++ ++ if (mrst_camera_table[i].type == MRST_CAMERA_RAW) { ++#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 31)) ++ sensor = v4l2_i2c_new_subdev(&isp->v4l2_dev, ++ isp->adapter_sensor, ++ name, name, addr); ++#else ++ sensor = v4l2_i2c_new_subdev(&isp->v4l2_dev, ++ isp->adapter_sensor, ++ name, name, addr, NULL); ++#endif ++ ++ if (sensor == NULL) { ++ dprintk(2, "sensor %s not found", name); ++ continue; ++ } ++ isp->sensor_raw = sensor; ++ isp->sensor_raw_index = i; ++ dprintk(0, "raw camera sensor %s successfully found", ++ name); ++ name = mrst_camera_table[i].motor_name; ++ addr = mrst_camera_table[i].motor_addr; ++#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 31)) ++ motor = v4l2_i2c_new_subdev(&isp->v4l2_dev, ++ isp->adapter_sensor, ++ name, name, addr); ++#else ++ motor = v4l2_i2c_new_subdev(&isp->v4l2_dev, ++ isp->adapter_sensor, ++ name, name, addr, NULL); ++#endif ++ ++ if (motor == NULL) ++ dprintk(2, "motor %s not found", name); ++ else { ++ isp->motor = motor; ++ dprintk(0, "motor %s successfully found", name); ++ } ++ } ++ } ++ ++ if (!isp->sensor_soc && !isp->sensor_raw) { ++ dprintk(0, "no camera sensor device attached"); ++ return -ENODEV; ++ } else { ++ if (isp->sensor_soc) ++ isp->sensor_curr = isp->sensor_soc; ++ else ++ isp->sensor_curr = isp->sensor_raw; ++ return 0; ++ } ++} ++ ++static int mrst_ci_flash_probe(struct mrst_isp_device *isp) ++{ ++ struct v4l2_subdev *flash = NULL; ++ char *name = "mrst_camera_flash"; ++ ++ gpio_request(45, "Camera Flash"); ++ gpio_direction_output(45, 0); ++ ++ isp->adapter_flash = i2c_get_adapter(MRST_I2C_BUS_FLASH); ++ if (NULL == isp->adapter_flash) { ++ dprintk(0, "no flash i2c adapter\n"); ++ return -ENODEV; ++ } ++ ++ dprintk(1, "got flash i2c adapter: %s", isp->adapter_flash->name); ++ ++#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 31)) ++ flash = v4l2_i2c_new_subdev(&isp->v4l2_dev, ++ isp->adapter_flash, ++ name, name, 0x53); ++#else ++ flash = v4l2_i2c_new_subdev(&isp->v4l2_dev, ++ isp->adapter_flash, ++ name, name, 0x53, NULL); ++#endif ++ ++ if (flash == NULL) { ++ dprintk(0, "no flash IC found\n"); ++ return -ENODEV; ++ } ++ ++ dprintk(0, "flash IC found"); ++ return 0; ++} ++ ++#if IRQ ++static irqreturn_t mrst_isp_irq_handler(int this_irq, void *dev_id) ++{ ++ struct isp_register *mrv_reg = ++ (struct isp_register *) MEM_MRV_REG_BASE; ++ struct mrst_isp_device *isp = dev_id; ++ struct videobuf_buffer *vb; ++ unsigned long flags; ++ ++ u32 mi_mask = ci_isp_get_frame_end_irq_mask_isp(); ++ u32 isp_mask = MRV_ISP_RIS_DATA_LOSS_MASK ++ | MRV_ISP_RIS_PIC_SIZE_ERR_MASK; ++ u32 jpe_status_mask = MRV_JPE_ALL_STAT_MASK; ++ u32 jpe_error_mask = MRV_JPE_ALL_ERR_MASK; ++ u32 mblk_line_mask = MRV_MI_MBLK_LINE_MASK; ++ ++ u32 isp_irq; ++ u32 mi_irq; ++ u32 jpe_status_irq; ++ u32 jpe_error_irq; ++ u32 mipi_irq; ++ u32 mblk_line; ++ u32 bufbase; ++ ++ isp_irq = REG_READ_EX(mrv_reg->isp_ris) & isp_mask; ++ mi_irq = REG_READ_EX(mrv_reg->mi_ris) & mi_mask; ++ ++ mblk_line = REG_READ_EX(mrv_reg->mi_ris) & mblk_line_mask; ++ ++ jpe_status_irq = REG_READ_EX(mrv_reg->jpe_status_ris) & jpe_status_mask; ++ jpe_error_irq = REG_READ_EX(mrv_reg->jpe_error_ris) & jpe_error_mask; ++ ++ mipi_irq = REG_READ_EX(mrv_reg->mipi_ris) & 0x00f00000; ++ ++ dprintk(3, "IRQ: mblk_line = %x, mi_irq = %x, jpe_status_irq = %x," ++ " jpe_error_irq = %x, isp_irq = %x", mblk_line, mi_irq, ++ jpe_status_irq, jpe_error_irq, isp_irq); ++ ++ if (!(isp_irq | mi_irq | jpe_status_irq | jpe_error_irq | mblk_line ++ | mipi_irq)) { ++ dprintk(2, "unknown interrupt"); ++ return IRQ_HANDLED; ++ } ++ ++ REG_SET_SLICE_EX(mrv_reg->isp_icr, MRV_ISP_ICR_ALL, ON); ++ REG_SET_SLICE_EX(mrv_reg->mi_icr, MRV_MI_ALLIRQS, ON); ++ REG_SET_SLICE_EX(mrv_reg->jpe_error_icr, MRV_JPE_ALL_ERR, ON); ++ REG_SET_SLICE_EX(mrv_reg->jpe_status_icr, MRV_JPE_ALL_STAT, ON); ++ REG_WRITE_EX(mrv_reg->mipi_icr, 0xffffffff); ++ ++ if (isp_irq) { ++ /* Currently we don't reset hardware even error detect */ ++ dprintk(3, "ISP error IRQ received %x", isp_irq); ++ isp_error_num++; ++ isp_error_flag |= isp_irq; ++ return IRQ_HANDLED; ++ } ++ ++ if (mipi_irq) { ++ dprintk(3, "error in mipi_irq %x", mipi_irq); ++ mipi_error_num++; ++ mipi_error_flag |= mipi_irq; ++ return IRQ_HANDLED; ++ } ++ ++ if (mblk_line && mrst_isp_to_do_mblk_line) { ++ REG_SET_SLICE(mrv_reg->mi_imsc, MRV_MI_MBLK_LINE, OFF); ++ dprintk(3, "enter mblk_line irq"); ++ ++ if (!(isp->active && !isp->next)) { ++ dprintk(3, "wrong isq status"); ++ if (isp->active) ++ dprintk(2, "actie->i = %d", isp->active->i); ++ else ++ dprintk(2, "actie = NULL"); ++ if (isp->next) ++ dprintk(2, "next->i = %d", isp->next->i); ++ else ++ dprintk(2, "next = NULL"); ++ return IRQ_HANDLED; ++ } ++ ++ spin_lock_irqsave(&isp->lock, flags); ++ ++ if (!list_empty(&isp->capture)) { ++ isp->next = list_entry(isp->capture.next, ++ struct videobuf_buffer, queue); ++ isp->next->state = VIDEOBUF_ACTIVE; ++ bufbase = videobuf_to_dma_contig(isp->next); ++ mrst_isp_update_marvinvfaddr(isp, bufbase, ++ CI_ISP_CFG_UPDATE_FRAME_SYNC); ++ dprintk(1, "updating new addr, next = %d", ++ isp->next->i); ++ } else { ++ isp->stopflag = 1; ++ dprintk(0, "stop isp"); ++ } ++ ++ mrst_isp_to_do_mblk_line = 0; ++ ++ spin_unlock_irqrestore(&isp->lock, flags); ++ ++ /* return IRQ_HANDLED; */ ++ } ++ ++ if (mi_irq && isp->pixelformat != V4L2_PIX_FMT_JPEG && ++ !jpe_status_irq) { ++ dprintk(1, "view finding case"); ++ ++ if (!isp->active) { ++ dprintk(0, "no active queue, You should not go here"); ++ mrst_isp_to_do_mblk_line = 1; ++ REG_SET_SLICE(mrv_reg->mi_imsc, MRV_MI_MBLK_LINE, ON); ++ return IRQ_HANDLED; ++ } ++ ++ spin_lock_irqsave(&isp->lock, flags); ++ ++ /* update captured frame status */ ++ vb = isp->active; ++ /* vb->size = ci_isp_mif_get_byte_cnt(); */ ++ /* if this buffer has been dq-ed, set nothing to state*/ ++ if (vb->state != VIDEOBUF_IDLE) ++ vb->state = VIDEOBUF_DONE; ++ vb->field_count++; ++ ++ isp->active = NULL; ++ dprintk(1, "buf %d size = %lx", vb->i, vb->size); ++ do_gettimeofday(&vb->ts); ++ wake_up(&vb->done); ++ ++ if (!isp->next) { ++ if (!list_empty(&isp->capture)) { ++ isp->active = list_entry(isp->capture.next, ++ struct videobuf_buffer, queue); ++ list_del_init(&isp->active->queue); ++ isp->active->state = VIDEOBUF_ACTIVE; ++ dprintk(3, "start next frame %d", ++ isp->active->i); ++ mrst_isp_to_do_mblk_line = 1; ++ REG_SET_SLICE(mrv_reg->mi_imsc, ++ MRV_MI_MBLK_LINE, ON); ++ } else { ++ mrst_isp_to_do_mblk_line = 1; ++ REG_SET_SLICE(mrv_reg->mi_imsc, ++ MRV_MI_MBLK_LINE, ON); ++ mrst_isp_disable_interrupt(isp); ++ dprintk(3, "no frame right now"); ++ } ++ } else { ++ isp->active = isp->next; ++ list_del_init(&isp->next->queue); ++ isp->next = NULL; ++ dprintk(1, "active = next = %d, next = NULL", ++ isp->active->i); ++ mrst_isp_to_do_mblk_line = 1; ++ REG_SET_SLICE(mrv_reg->mi_imsc, MRV_MI_MBLK_LINE, ON); ++ } ++ ++ spin_unlock_irqrestore(&isp->lock, flags); ++ return IRQ_HANDLED; ++ } ++ ++ if (jpe_status_irq) { ++ dprintk(2, "jpeg capture case"); ++ ++ if (!isp->active) ++ return IRQ_HANDLED; ++ ++ spin_lock_irqsave(&isp->lock, flags); ++ ++ vb = isp->active; ++ vb->size = ci_isp_mif_get_byte_cnt(); ++ vb->state = VIDEOBUF_DONE; ++ do_gettimeofday(&vb->ts); ++ vb->field_count++; ++ wake_up(&vb->done); ++ isp->active = NULL; ++ ++ dprintk(2, "index =%d, bufsize = %lx", vb->i, vb->size); ++ ++ spin_unlock_irqrestore(&isp->lock, flags); ++ ++ return IRQ_HANDLED; ++ } ++ ++ if (jpe_error_irq) ++ dprintk(2, "entered jpe_error_irq"); ++ ++ return IRQ_HANDLED; ++} ++#endif ++ ++static void __devexit mrst_isp_pci_remove(struct pci_dev *pdev) ++{ ++ struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev); ++ struct mrst_isp_device *isp = to_isp(v4l2_dev); ++ ++ DBG_entering; ++ ++ ci_isp_stop(CI_ISP_CFG_UPDATE_FRAME_SYNC); ++ mrst_isp_disable_interrupt(isp); ++ ++#if IRQ ++ free_irq(pdev->irq, isp); ++#endif ++ ++ if (isp->vdev) { ++ dprintk(2, "isp->vdev = %p", isp->vdev); ++ video_unregister_device(isp->vdev); ++ } ++ ++ dma_release_declared_memory(&pdev->dev); ++ ++ iounmap(isp->regs); ++ ++ pci_release_regions(pdev); ++ ++ pci_disable_device(pdev); ++ ++ v4l2_device_unregister(&isp->v4l2_dev); ++ ++ kfree(isp); ++ ++ DBG_leaving; ++} ++ ++static int __devinit mrst_isp_pci_probe(struct pci_dev *pdev, ++ const struct pci_device_id *pci_id) ++{ ++ struct mrst_isp_device *isp; ++ unsigned int start = 0; ++ unsigned int len = 0; ++ int ret = 0; ++ ++ DBG_entering; ++ ++ /* alloc device struct */ ++ isp = kzalloc(sizeof(struct mrst_isp_device), GFP_KERNEL); ++ if (NULL == isp) { ++ printk(KERN_ERR "mrstisp: fail to kzalloc mrst_isp_device\n"); ++ ret = -ENOMEM; ++ goto exit; ++ } ++ ++ /* register v4l2 device */ ++ ret = v4l2_device_register(&pdev->dev, &isp->v4l2_dev); ++ if (ret) { ++ printk(KERN_ERR "mrstisp: fail to register v4l2 device\n"); ++ goto exit_free_isp; ++ } ++ ++ /* PCI operations */ ++ ret = pci_enable_device(pdev); ++ if (ret) { ++ printk(KERN_ERR "mrstisp: can't enable isp\n"); ++ goto exit_unregister_v4l2; ++ } ++ ++ pci_set_master(pdev); ++ ++ ret = pci_request_regions(pdev, "mrst isp"); ++ if (ret) { ++ printk(KERN_ERR "mrstisp: can't request regions\n"); ++ goto exit_disable_isp; ++ } ++ ++ /* mem bar 0 */ ++ start = isp->mb0 = pci_resource_start(pdev, 0); ++ len = isp->mb0_size = pci_resource_len(pdev, 0); ++ ++ isp->regs = ioremap_nocache(start, len); ++ mrst_isp_regs = isp->regs; ++ if (isp->regs == NULL) { ++ printk(KERN_ERR "mrstisp: fail to ioremap isp registers\n"); ++ goto exit_release_regions; ++ } ++ ++ dprintk(1, "isp mb0 = %lx, mb0_size = %lx, regs = %p", ++ isp->mb0, isp->mb0_size, isp->regs); ++ ++ /* mem bar 1 */ ++ start = isp->mb1 = pci_resource_start(pdev, 1); ++ len = isp->mb1_size = pci_resource_len(pdev, 1); ++ ++ dprintk(1, "isp mb1 = %lx, mb1_size = %lx", isp->mb1, isp->mb1_size); ++ ++ ret = dma_declare_coherent_memory(&pdev->dev, start, ++ /* start, len - 640 * 480 * 2, */ ++ start, len, ++ DMA_MEMORY_MAP); ++ /* ++ DMA_MEMORY_MAP ++ | DMA_MEMORY_EXCLUSIVE); ++ */ ++ if (!ret) { ++ dprintk(0, "failed to declare dma memory"); ++ ret = -ENXIO; ++ goto exit_iounmap; ++ } ++ ++ /* init device struct */ ++ INIT_LIST_HEAD(&isp->capture); ++ spin_lock_init(&isp->lock); ++ mutex_init(&isp->mutex); ++ ++ pci_read_config_word(pdev, PCI_VENDOR_ID, &isp->vendorID); ++ pci_read_config_word(pdev, PCI_DEVICE_ID, &isp->deviceID); ++ ++ mrst_isp_defcfg_all_load(&isp->sys_conf.isp_cfg); ++ ++ isp->bufwidth = 640; ++ isp->bufheight = 480; ++ isp->depth = 12; ++ isp->pixelformat = V4L2_PIX_FMT_YVU420; ++ isp->streaming = 0; ++ isp->buffer_required = 0; ++ ++ ++ /* probe sensor */ ++ ret = mrst_ci_sensor_probe(isp); ++ if (ret) { ++ dprintk(0, "failed to sensor probe\n"); ++ goto exit_dma_release; ++ } ++ ++ /* regiter video device */ ++ isp->vdev = &mrst_isp_vdev; ++ isp->vdev->parent = &pdev->dev; ++ video_set_drvdata(isp->vdev, isp); ++ ++ ret = video_register_device(isp->vdev, VFL_TYPE_GRABBER, -1); ++ if (ret) { ++ dprintk(0, "fail to register video deivice"); ++ goto exit_dma_release; ++ } ++ ++ dprintk(0, "registered dev/video%d", isp->vdev->num); ++ dprintk(0, "isp->vdev = %p", isp->vdev); ++ ++#if IRQ ++ /* request irq */ ++ ret = request_irq(pdev->irq, mrst_isp_irq_handler, IRQF_SHARED, ++ /* pci_name(pdev), isp); */ ++ "mrst_camera_imaging", isp); ++ if (ret) { ++ dprintk(0, "fail to request irq"); ++ goto exit_unregister_video; ++ } ++ ++ mrst_isp_disable_interrupt(isp); ++#endif ++ ++ /* probe flash */ ++ mrst_ci_flash_probe(isp); ++ ++ mrst_isp_to_do_mblk_line = 0; ++ ++ dprintk(0, "mrstisp driver module successfully loaded"); ++ return 0; ++ ++exit_unregister_video: ++ video_unregister_device(isp->vdev); ++exit_dma_release: ++ dma_release_declared_memory(&pdev->dev); ++exit_iounmap: ++ iounmap(isp->regs); ++exit_release_regions: ++ pci_release_regions(pdev); ++exit_disable_isp: ++ pci_disable_device(pdev); ++exit_unregister_v4l2: ++ v4l2_device_unregister(&isp->v4l2_dev); ++exit_free_isp: ++ kfree(isp); ++exit: ++ return ret; ++} ++ ++#ifdef CONFIG_PM ++static int mrst_isp_pci_suspend(struct pci_dev *pdev, pm_message_t state) ++{ ++ struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev); ++ struct mrst_isp_device *isp = to_isp(v4l2_dev); ++ int ret; ++ ++ DBG_entering; ++ ++ ci_isp_off(); ++ ++ ret = pci_save_state(pdev); ++ if (ret) { ++ printk(KERN_ERR "mrstisp: pci_save_state failed %d\n", ret); ++ return ret; ++ } ++ ++ ret = pci_set_power_state(pdev, PCI_D3cold); ++ if (ret) { ++ printk(KERN_ERR "mrstisp: fail to set power state\n"); ++ return ret; ++ } ++ ++/* ++ ret = ci_sensor_suspend(); ++ if (ret) { ++ printk(KERN_ERR "mrstisp: Fail to suspend sensor\n"); ++ return ret; ++ } ++*/ ++ if (isp->sensor_soc) ++ v4l2_subdev_call(isp->sensor_soc, core, s_gpio, 1); ++ if (isp->sensor_raw) ++ v4l2_subdev_call(isp->sensor_raw, core, s_gpio, 1); ++ ++ DBG_leaving; ++ return 0; ++} ++ ++static int mrst_isp_pci_resume(struct pci_dev *pdev) ++{ ++ struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev); ++ struct mrst_isp_device *isp = to_isp(v4l2_dev); ++ int ret; ++ ++ DBG_entering; ++ ++ pci_set_power_state(pdev, PCI_D0); ++ pci_restore_state(pdev); ++ ++ ret = pci_enable_device(pdev); ++ if (ret) { ++ printk(KERN_ERR "mrstisp: fail to enable device in resume\n"); ++ return ret; ++ } ++ ++/* ++ ret = ci_sensor_resume(); ++ if (ret) { ++ printk(KERN_ERR "mrstisp: Fail to resume sensor\n"); ++ return ret; ++ } ++*/ ++ if (isp->sensor_soc) ++ v4l2_subdev_call(isp->sensor_soc, core, s_gpio, 0); ++ if (isp->sensor_raw) ++ v4l2_subdev_call(isp->sensor_raw, core, s_gpio, 0); ++ ++ ci_isp_init(); ++ ++ DBG_leaving; ++ return 0; ++} ++#endif ++ ++static struct pci_device_id mrst_isp_pci_tbl[] __devinitdata = { ++ { PCI_DEVICE(0x8086, 0x080B) }, ++ {0,} ++}; ++ ++MODULE_DEVICE_TABLE(pci, mrst_isp_pci_tbl); ++ ++static struct pci_driver mrst_isp_pci_driver = { ++ .name = "mrstisp", ++ .id_table = mrst_isp_pci_tbl, ++ .probe = mrst_isp_pci_probe, ++ .remove = mrst_isp_pci_remove, ++ #ifdef CONFIG_PM ++ .suspend = mrst_isp_pci_suspend, ++ .resume = mrst_isp_pci_resume, ++ #endif ++}; ++ ++static int __init mrst_isp_pci_init(void) ++{ ++ int ret; ++ ++ DBG_entering; ++ ++ ret = pci_register_driver(&mrst_isp_pci_driver); ++ if (ret) { ++ printk(KERN_ERR "mrstisp: Unable to register driver\n"); ++ return ret; ++ } ++ ++ if (ret) ++ dprintk(1, "Unable to register flash driver"); ++ ++ DBG_leaving; ++ return 0; ++} ++ ++static void __exit mrst_isp_pci_exit(void) ++{ ++ DBG_entering; ++ ++ pci_unregister_driver(&mrst_isp_pci_driver); ++ ++ DBG_leaving; ++} ++ ++module_init(mrst_isp_pci_init); ++/* late_initcall(mrst_isp_pci_init); */ ++module_exit(mrst_isp_pci_exit); ++ ++MODULE_DESCRIPTION("Intel Moorestown ISP driver"); ++MODULE_AUTHOR("Xiaolin Zhang "); ++MODULE_LICENSE("GPL"); ++MODULE_SUPPORTED_DEVICE("video"); ++ +diff --git a/drivers/media/video/mrstci/mrstisp/mrstisp_mif.c b/drivers/media/video/mrstci/mrstisp/mrstisp_mif.c +new file mode 100644 +index 0000000..a05731a +--- /dev/null ++++ b/drivers/media/video/mrstci/mrstisp/mrstisp_mif.c +@@ -0,0 +1,763 @@ ++/* ++ * Support for Moorestown Langwell Camera Imaging ISP subsystem. ++ * ++ * Copyright (c) 2009 Intel Corporation. All Rights Reserved. ++ * ++ * Copyright (c) Silicon Image 2008 www.siliconimage.com ++ * ++ * 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 Street, Fifth Floor, Boston, MA ++ * 02110-1301, USA. ++ * ++ * ++ * Xiaolin Zhang ++ */ ++ ++#include "mrstisp_stdinc.h" ++ ++/* ++ * sets all main picture and self picture buffer offsets back to 0 ++ */ ++void ci_isp_mif_reset_offsets(enum ci_isp_conf_update_time update_time) ++{ ++ struct isp_register *mrv_reg = (struct isp_register *) MEM_MRV_REG_BASE; ++ ++ REG_SET_SLICE(mrv_reg->mi_mp_y_offs_cnt_init, ++ MRV_MI_MP_Y_OFFS_CNT_INIT, 0); ++ REG_SET_SLICE(mrv_reg->mi_mp_cb_offs_cnt_init, ++ MRV_MI_MP_CB_OFFS_CNT_INIT, 0); ++ REG_SET_SLICE(mrv_reg->mi_mp_cr_offs_cnt_init, ++ MRV_MI_MP_CR_OFFS_CNT_INIT, 0); ++ ++ REG_SET_SLICE(mrv_reg->mi_sp_y_offs_cnt_init, ++ MRV_MI_SP_Y_OFFS_CNT_INIT, 0); ++ REG_SET_SLICE(mrv_reg->mi_sp_cb_offs_cnt_init, ++ MRV_MI_SP_CB_OFFS_CNT_INIT, 0); ++ REG_SET_SLICE(mrv_reg->mi_sp_cr_offs_cnt_init, ++ MRV_MI_SP_CR_OFFS_CNT_INIT, 0); ++ ++ REG_SET_SLICE(mrv_reg->mi_ctrl, MRV_MI_INIT_OFFSET_EN, ON); ++ REG_SET_SLICE(mrv_reg->mi_ctrl, MRV_MI_INIT_BASE_EN, ON); ++ ++ switch (update_time) { ++ case CI_ISP_CFG_UPDATE_FRAME_SYNC: ++ break; ++ case CI_ISP_CFG_UPDATE_IMMEDIATE: ++ REG_SET_SLICE(mrv_reg->mi_init, MRV_MI_MI_CFG_UPD, ON); ++ break; ++ case CI_ISP_CFG_UPDATE_LATER: ++ break; ++ default: ++ break; ++ } ++} ++ ++/* ++ * This function get the byte count from the last JPEG or raw data transfer ++ */ ++u32 ci_isp_mif_get_byte_cnt(void) ++{ ++ struct isp_register *mrv_reg = (struct isp_register *) MEM_MRV_REG_BASE; ++ ++ return (u32) REG_GET_SLICE(mrv_reg->mi_byte_cnt, MRV_MI_BYTE_CNT); ++} ++ ++/* ++ * Sets the desired self picture orientation, if possible. ++ */ ++static int ci_isp_mif_set_self_pic_orientation(enum ci_isp_mif_sp_mode ++ mrv_mif_sp_mode, ++ int activate_self_path) ++{ ++ ++ struct isp_register *mrv_reg = (struct isp_register *) MEM_MRV_REG_BASE; ++ u32 mi_ctrl = REG_READ(mrv_reg->mi_ctrl); ++ ++ u32 output_format = REG_GET_SLICE(mi_ctrl, MRV_MI_SP_OUTPUT_FORMAT); ++ ++ /* apply the desired self picture orientation, if possible */ ++ switch (mrv_mif_sp_mode) { ++ case CI_ISP_MIF_SP_ORIGINAL: ++ REG_SET_SLICE(mi_ctrl, MRV_MI_ROT_AND_FLIP, 0); ++ break; ++ ++ case CI_ISP_MIF_SP_HORIZONTAL_FLIP: ++ REG_SET_SLICE(mi_ctrl, MRV_MI_ROT_AND_FLIP, ++ MRV_MI_ROT_AND_FLIP_H_FLIP); ++ break; ++ ++ case CI_ISP_MIF_SP_VERTICAL_FLIP: ++ REG_SET_SLICE(mi_ctrl, MRV_MI_ROT_AND_FLIP, ++ MRV_MI_ROT_AND_FLIP_V_FLIP); ++ break; ++ ++ case CI_ISP_MIF_SP_ROTATION_090_DEG: ++ REG_SET_SLICE(mi_ctrl, MRV_MI_ROT_AND_FLIP, ++ MRV_MI_ROT_AND_FLIP_ROTATE); ++ break; ++ ++ case CI_ISP_MIF_SP_ROTATION_180_DEG: ++ REG_SET_SLICE(mi_ctrl, MRV_MI_ROT_AND_FLIP, ++ MRV_MI_ROT_AND_FLIP_H_FLIP | ++ MRV_MI_ROT_AND_FLIP_V_FLIP); ++ break; ++ ++ case CI_ISP_MIF_SP_ROTATION_270_DEG: ++ REG_SET_SLICE(mi_ctrl, MRV_MI_ROT_AND_FLIP, ++ MRV_MI_ROT_AND_FLIP_H_FLIP | ++ MRV_MI_ROT_AND_FLIP_V_FLIP | ++ MRV_MI_ROT_AND_FLIP_ROTATE); ++ break; ++ ++ case CI_ISP_MIF_SP_ROT_090_V_FLIP: ++ REG_SET_SLICE(mi_ctrl, MRV_MI_ROT_AND_FLIP, ++ MRV_MI_ROT_AND_FLIP_V_FLIP | ++ MRV_MI_ROT_AND_FLIP_ROTATE); ++ break; ++ ++ case CI_ISP_MIF_SP_ROT_270_V_FLIP: ++ REG_SET_SLICE(mi_ctrl, MRV_MI_ROT_AND_FLIP, ++ MRV_MI_ROT_AND_FLIP_H_FLIP | ++ MRV_MI_ROT_AND_FLIP_ROTATE); ++ break; ++ ++ default: ++ eprintk("unknown value for mrv_mif_sp_mode"); ++ return CI_STATUS_NOTSUPP; ++ } ++ ++ if (REG_GET_SLICE(mi_ctrl, MRV_MI_ROT_AND_FLIP) & ++ MRV_MI_ROT_AND_FLIP_ROTATE) { ++ switch (output_format) { ++ case MRV_MI_SP_OUTPUT_FORMAT_RGB888: ++ case MRV_MI_SP_OUTPUT_FORMAT_RGB666: ++ case MRV_MI_SP_OUTPUT_FORMAT_RGB565: ++ /* rotation supported on this output modes */ ++ break; ++ default: ++ eprintk("rotation is only allowed for RGB modes."); ++ return CI_STATUS_NOTSUPP; ++ } ++ } ++ ++ REG_SET_SLICE(mi_ctrl, MRV_MI_SP_ENABLE, ++ (activate_self_path) ? ENABLE : DISABLE); ++ REG_WRITE(mrv_reg->mi_ctrl, mi_ctrl); ++ REG_SET_SLICE(mrv_reg->mi_init, MRV_MI_MI_CFG_UPD, ON); ++ ++ return CI_STATUS_SUCCESS; ++} ++ ++/* ++ * Checks the main or self picture path buffer structure. ++ */ ++static int ci_isp_mif_check_mi_path_conf(const struct ci_isp_mi_path_conf ++ *isp_mi_path_conf, int main_buffer) ++{ ++ if (!isp_mi_path_conf) { ++ eprintk("isp_mi_path_conf is NULL"); ++ return CI_STATUS_NULL_POINTER; ++ } ++ ++ if (!isp_mi_path_conf->ybuffer.pucbuffer) { ++ eprintk("isp_mi_path_conf->ybuffer.pucbuffer is NULL"); ++ return CI_STATUS_NULL_POINTER; ++ } ++ ++ if (main_buffer) { ++ if ((((unsigned long)(isp_mi_path_conf->ybuffer.pucbuffer) ++ & ~(MRV_MI_MP_Y_BASE_AD_INIT_VALID_MASK)) != 0) ++ || ++ ((isp_mi_path_conf->ybuffer.size ++ & ~(MRV_MI_MP_Y_SIZE_INIT_VALID_MASK)) != 0) ++ || ++ ((isp_mi_path_conf->ybuffer.size ++ & (MRV_MI_MP_Y_SIZE_INIT_VALID_MASK)) == 0) ++ || ++ ((isp_mi_path_conf->ybuffer.offs ++ & ~(MRV_MI_MP_Y_OFFS_CNT_INIT_VALID_MASK)) != 0)) { ++ return CI_STATUS_OUTOFRANGE; ++ } ++ } else { ++ if ((((unsigned long) isp_mi_path_conf->ybuffer.pucbuffer ++ & ~(MRV_MI_SP_Y_BASE_AD_INIT_VALID_MASK)) != 0) ++ || ++ ((isp_mi_path_conf->ybuffer.size & ++ ~(MRV_MI_SP_Y_SIZE_INIT_VALID_MASK)) != 0) ++ || ++ ((isp_mi_path_conf->ybuffer.size & ++ (MRV_MI_SP_Y_SIZE_INIT_VALID_MASK)) == 0) ++ || ++ ((isp_mi_path_conf->ybuffer.offs & ++ ~(MRV_MI_SP_Y_OFFS_CNT_INIT_VALID_MASK)) != ++ 0) ++ || ++ ((isp_mi_path_conf->llength & ++ ~(MRV_MI_SP_Y_LLENGTH_VALID_MASK)) != 0) ++ || ++ ((isp_mi_path_conf-> ++ llength & (MRV_MI_SP_Y_LLENGTH_VALID_MASK)) == 0)) { ++ return CI_STATUS_OUTOFRANGE; ++ } ++ } ++ ++ if (isp_mi_path_conf->cb_buffer.pucbuffer != 0) { ++ if (main_buffer) { ++ if ((((unsigned long) ++ isp_mi_path_conf->cb_buffer.pucbuffer ++ & ~(MRV_MI_MP_CB_BASE_AD_INIT_VALID_MASK)) != ++ 0) ++ || ++ ((isp_mi_path_conf->cb_buffer.size & ++ ~(MRV_MI_MP_CB_SIZE_INIT_VALID_MASK)) != 0) ++ || ++ ((isp_mi_path_conf->cb_buffer.size & ++ (MRV_MI_MP_CB_SIZE_INIT_VALID_MASK)) == 0) ++ || ++ ((isp_mi_path_conf->cb_buffer.offs & ++ ~(MRV_MI_MP_CB_OFFS_CNT_INIT_VALID_MASK)) != ++ 0)) { ++ return CI_STATUS_OUTOFRANGE; ++ } ++ } else { ++ if ((((unsigned long) ++ isp_mi_path_conf->cb_buffer.pucbuffer ++ & ~(MRV_MI_SP_CB_BASE_AD_INIT_VALID_MASK)) != ++ 0) ++ || ++ ((isp_mi_path_conf->cb_buffer.size & ++ ~(MRV_MI_SP_CB_SIZE_INIT_VALID_MASK)) != 0) ++ || ++ ((isp_mi_path_conf->cb_buffer.size & ++ (MRV_MI_SP_CB_SIZE_INIT_VALID_MASK)) == 0) ++ || ++ ((isp_mi_path_conf->cb_buffer.offs & ++ ~(MRV_MI_SP_CB_OFFS_CNT_INIT_VALID_MASK)) != ++ 0)) { ++ return CI_STATUS_OUTOFRANGE; ++ } ++ } ++ } ++ ++ if (isp_mi_path_conf->cr_buffer.pucbuffer != 0) { ++ if (main_buffer) { ++ if ((((unsigned long) ++ isp_mi_path_conf->cr_buffer.pucbuffer ++ & ~(MRV_MI_MP_CR_BASE_AD_INIT_VALID_MASK)) != ++ 0) ++ || ++ ((isp_mi_path_conf->cr_buffer.size & ++ ~(MRV_MI_MP_CR_SIZE_INIT_VALID_MASK)) != 0) ++ || ++ ((isp_mi_path_conf->cr_buffer.size & ++ (MRV_MI_MP_CR_SIZE_INIT_VALID_MASK)) == 0) ++ || ++ ((isp_mi_path_conf->cr_buffer.offs & ++ ~(MRV_MI_MP_CR_OFFS_CNT_INIT_VALID_MASK)) != ++ 0)){ ++ return CI_STATUS_OUTOFRANGE; ++ } ++ } else { ++ if ((((unsigned long) ++ isp_mi_path_conf->cr_buffer.pucbuffer ++ & ~(MRV_MI_SP_CR_BASE_AD_INIT_VALID_MASK)) ++ != 0) ++ || ++ ((isp_mi_path_conf->cr_buffer.size & ++ ~(MRV_MI_SP_CR_SIZE_INIT_VALID_MASK)) != 0) ++ || ++ ((isp_mi_path_conf->cr_buffer.size & ++ (MRV_MI_SP_CR_SIZE_INIT_VALID_MASK)) == 0) ++ || ++ ((isp_mi_path_conf->cr_buffer.offs & ++ ~(MRV_MI_SP_CR_OFFS_CNT_INIT_VALID_MASK)) != 0)) { ++ return CI_STATUS_OUTOFRANGE; ++ } ++ } ++ } ++ ++ return CI_STATUS_SUCCESS; ++} ++ ++/* ++ * Configures the main picture path buffers of the MI. ++ */ ++int ci_isp_mif_set_main_buffer(const struct ci_isp_mi_path_conf ++ *isp_mi_path_conf, ++ enum ci_isp_conf_update_time update_time) ++{ ++ struct isp_register *mrv_reg = (struct isp_register *) MEM_MRV_REG_BASE; ++ int error = CI_STATUS_FAILURE; ++ ++ error = ci_isp_mif_check_mi_path_conf(isp_mi_path_conf, true); ++ if (error != CI_STATUS_SUCCESS) ++ return error; ++ ++ /* set register values */ ++ REG_SET_SLICE(mrv_reg->mi_mp_y_base_ad_init, ++ MRV_MI_MP_Y_BASE_AD_INIT, ++ (u32)(unsigned long)isp_mi_path_conf->ybuffer.pucbuffer); ++ REG_SET_SLICE(mrv_reg->mi_mp_y_size_init, MRV_MI_MP_Y_SIZE_INIT, ++ isp_mi_path_conf->ybuffer.size); ++ REG_SET_SLICE(mrv_reg->mi_mp_y_offs_cnt_init, ++ MRV_MI_MP_Y_OFFS_CNT_INIT, ++ isp_mi_path_conf->ybuffer.offs); ++ ++ if (isp_mi_path_conf->cb_buffer.pucbuffer != 0) { ++ REG_SET_SLICE(mrv_reg->mi_mp_cb_base_ad_init, ++ MRV_MI_MP_CB_BASE_AD_INIT, ++ (u32)(unsigned long) isp_mi_path_conf->cb_buffer. ++ pucbuffer); ++ REG_SET_SLICE(mrv_reg->mi_mp_cb_size_init, ++ MRV_MI_MP_CB_SIZE_INIT, ++ isp_mi_path_conf->cb_buffer.size); ++ REG_SET_SLICE(mrv_reg->mi_mp_cb_offs_cnt_init, ++ MRV_MI_MP_CB_OFFS_CNT_INIT, ++ isp_mi_path_conf->cb_buffer.offs); ++ } ++ ++ if (isp_mi_path_conf->cr_buffer.pucbuffer != 0) { ++ REG_SET_SLICE(mrv_reg->mi_mp_cr_base_ad_init, ++ MRV_MI_MP_CR_BASE_AD_INIT, ++ (u32)(unsigned long) isp_mi_path_conf->cr_buffer. ++ pucbuffer); ++ REG_SET_SLICE(mrv_reg->mi_mp_cr_size_init, ++ MRV_MI_MP_CR_SIZE_INIT, ++ isp_mi_path_conf->cr_buffer.size); ++ REG_SET_SLICE(mrv_reg->mi_mp_cr_offs_cnt_init, ++ MRV_MI_MP_CR_OFFS_CNT_INIT, ++ isp_mi_path_conf->cr_buffer.offs); ++ } ++ ++ /* ++ * update base and offset registers during next immediate or ++ * automatic update request ++ */ ++ REG_SET_SLICE(mrv_reg->mi_ctrl, MRV_MI_INIT_OFFSET_EN, ENABLE); ++ REG_SET_SLICE(mrv_reg->mi_ctrl, MRV_MI_INIT_BASE_EN, ENABLE); ++ ++ switch (update_time) { ++ case CI_ISP_CFG_UPDATE_FRAME_SYNC: ++ /* ++ * frame synchronous update of shadow registers, ++ * update is done after the curr frame ++ */ ++ REG_SET_SLICE(mrv_reg->isp_ctrl, MRV_ISP_ISP_GEN_CFG_UPD, ON); ++ break; ++ case CI_ISP_CFG_UPDATE_IMMEDIATE: ++ /* ++ * immediate update of shadow registers ++ * (will disturb an ongoing frame processing) ++ */ ++ REG_SET_SLICE(mrv_reg->mi_init, MRV_MI_MI_CFG_UPD, ON); ++ break; ++ case CI_ISP_CFG_UPDATE_LATER: ++ /* no update from within this function */ ++ break; ++ default: ++ break; ++ } ++ ++ return error; ++} ++ ++/* ++ * Configures the self picture path buffers of the MI. ++ * ++ */ ++int ci_isp_mif_set_self_buffer(const struct ci_isp_mi_path_conf ++ *isp_mi_path_conf, ++ enum ci_isp_conf_update_time update_time) ++{ ++ struct isp_register *mrv_reg = (struct isp_register *) MEM_MRV_REG_BASE; ++ int error = CI_STATUS_FAILURE; ++ ++ error = ci_isp_mif_check_mi_path_conf(isp_mi_path_conf, false); ++ if (error != CI_STATUS_SUCCESS) ++ return error; ++ ++ /* set register values */ ++ REG_SET_SLICE(mrv_reg->mi_sp_y_base_ad_init, ++ MRV_MI_SP_Y_BASE_AD_INIT, ++ (u32)(unsigned long)isp_mi_path_conf->ybuffer.pucbuffer); ++ REG_SET_SLICE(mrv_reg->mi_sp_y_size_init, MRV_MI_SP_Y_SIZE_INIT, ++ isp_mi_path_conf->ybuffer.size); ++ REG_SET_SLICE(mrv_reg->mi_sp_y_offs_cnt_init, ++ MRV_MI_SP_Y_OFFS_CNT_INIT, ++ isp_mi_path_conf->ybuffer.offs); ++ ++ /* ++ * llength is counted in pixels and this value could be stored ++ * directly into the register ++ */ ++ REG_SET_SLICE(mrv_reg->mi_sp_y_llength, MRV_MI_SP_Y_LLENGTH, ++ isp_mi_path_conf->llength); ++ ++ if (isp_mi_path_conf->cb_buffer.pucbuffer) { ++ REG_SET_SLICE(mrv_reg->mi_sp_cb_base_ad_init, ++ MRV_MI_SP_CB_BASE_AD_INIT, ++ (u32) (unsigned long)isp_mi_path_conf->cb_buffer. ++ pucbuffer); ++ REG_SET_SLICE(mrv_reg->mi_sp_cb_size_init, ++ MRV_MI_SP_CB_SIZE_INIT, ++ isp_mi_path_conf->cb_buffer.size); ++ REG_SET_SLICE(mrv_reg->mi_sp_cb_offs_cnt_init, ++ MRV_MI_SP_CB_OFFS_CNT_INIT, ++ isp_mi_path_conf->cb_buffer.offs); ++ } ++ ++ if (isp_mi_path_conf->cr_buffer.pucbuffer) { ++ REG_SET_SLICE(mrv_reg->mi_sp_cr_base_ad_init, ++ MRV_MI_SP_CR_BASE_AD_INIT, ++ (u32) (unsigned long)isp_mi_path_conf->cr_buffer. ++ pucbuffer); ++ REG_SET_SLICE(mrv_reg->mi_sp_cr_size_init, ++ MRV_MI_SP_CR_SIZE_INIT, ++ isp_mi_path_conf->cr_buffer.size); ++ REG_SET_SLICE(mrv_reg->mi_sp_cr_offs_cnt_init, ++ MRV_MI_SP_CR_OFFS_CNT_INIT, ++ isp_mi_path_conf->cr_buffer.offs); ++ } ++ ++ if ((!isp_mi_path_conf->ypic_width) ++ || (!isp_mi_path_conf->ypic_height)) { ++ return CI_STATUS_FAILURE; ++ } ++ ++ REG_SET_SLICE(mrv_reg->mi_sp_y_pic_width, MRV_MI_SP_Y_PIC_WIDTH, ++ isp_mi_path_conf->ypic_width); ++ REG_SET_SLICE(mrv_reg->mi_sp_y_pic_height, MRV_MI_SP_Y_PIC_HEIGHT, ++ isp_mi_path_conf->ypic_height); ++ REG_SET_SLICE(mrv_reg->mi_sp_y_pic_size, MRV_MI_SP_Y_PIC_SIZE, ++ isp_mi_path_conf->ypic_height * ++ isp_mi_path_conf->llength); ++ ++ /* ++ * update base and offset registers during next immediate or ++ * automatic update request ++ */ ++ REG_SET_SLICE(mrv_reg->mi_ctrl, MRV_MI_INIT_OFFSET_EN, ENABLE); ++ REG_SET_SLICE(mrv_reg->mi_ctrl, MRV_MI_INIT_BASE_EN, ENABLE); ++ ++ switch (update_time) { ++ case CI_ISP_CFG_UPDATE_FRAME_SYNC: ++ REG_SET_SLICE(mrv_reg->isp_ctrl, MRV_ISP_ISP_GEN_CFG_UPD, ++ ON); ++ break; ++ case CI_ISP_CFG_UPDATE_IMMEDIATE: ++ REG_SET_SLICE(mrv_reg->mi_init, MRV_MI_MI_CFG_UPD, ON); ++ break; ++ case CI_ISP_CFG_UPDATE_LATER: ++ break; ++ default: ++ break; ++ } ++ ++ return error; ++} ++ ++/* ++ * Configures the DMA path of the MI. ++ * ++ */ ++int ci_isp_mif_set_path_and_orientation(const struct ci_isp_mi_ctrl ++ *mrv_mi_ctrl) ++{ ++ struct isp_register *mrv_reg = (struct isp_register *) MEM_MRV_REG_BASE; ++ int error = CI_STATUS_OUTOFRANGE; ++ u32 mi_ctrl = 0; ++ ++ if (!mrv_mi_ctrl) { ++ eprintk("mrv_mi_ctrl is NULL"); ++ return CI_STATUS_NULL_POINTER; ++ } ++ ++ if ((mrv_mi_ctrl->irq_offs_init & ++ ~(MRV_MI_MP_Y_IRQ_OFFS_INIT_VALID_MASK)) != 0) { ++ eprintk("bad mrv_mi_ctrl->irq_offs_init value"); ++ return error; ++ } ++ ++ REG_SET_SLICE(mrv_reg->mi_mp_y_irq_offs_init, ++ MRV_MI_MP_Y_IRQ_OFFS_INIT, mrv_mi_ctrl->irq_offs_init); ++ ++ /* main picture path */ ++ switch (mrv_mi_ctrl->main_path) { ++ case CI_ISP_PATH_OFF: ++ REG_SET_SLICE(mi_ctrl, MRV_MI_MP_ENABLE, OFF); ++ break; ++ case CI_ISP_PATH_ON: ++ REG_SET_SLICE(mi_ctrl, MRV_MI_MP_ENABLE, ON); ++ break; ++ case CI_ISP_PATH_JPE: ++ REG_SET_SLICE(mi_ctrl, MRV_MI_JPEG_ENABLE, ON); ++ break; ++ case CI_ISP_PATH_RAW8: ++ REG_SET_SLICE(mi_ctrl, MRV_MI_RAW_ENABLE, ON); ++ break; ++ case CI_ISP_PATH_RAW816: ++ REG_SET_SLICE(mi_ctrl, MRV_MI_RAW_ENABLE, ON); ++ REG_SET_SLICE(mi_ctrl, MRV_MI_MP_WRITE_FORMAT, ++ MRV_MI_MP_WRITE_FORMAT_INTERLEAVED); ++ break; ++ default: ++ eprintk("bad mrv_mi_ctrl->main_path value"); ++ return error; ++ } ++ ++ /* self picture path output format */ ++ switch (mrv_mi_ctrl->mrv_mif_sp_out_form) { ++ case CI_ISP_MIF_COL_FORMAT_YCBCR_422: ++ REG_SET_SLICE(mi_ctrl, MRV_MI_SP_OUTPUT_FORMAT, ++ MRV_MI_SP_OUTPUT_FORMAT_YUV422); ++ break; ++ case CI_ISP_MIF_COL_FORMAT_YCBCR_444: ++ REG_SET_SLICE(mi_ctrl, MRV_MI_SP_OUTPUT_FORMAT, ++ MRV_MI_SP_OUTPUT_FORMAT_YUV444); ++ break; ++ case CI_ISP_MIF_COL_FORMAT_YCBCR_420: ++ REG_SET_SLICE(mi_ctrl, MRV_MI_SP_OUTPUT_FORMAT, ++ MRV_MI_SP_OUTPUT_FORMAT_YUV420); ++ break; ++ case CI_ISP_MIF_COL_FORMAT_YCBCR_400: ++ REG_SET_SLICE(mi_ctrl, MRV_MI_SP_OUTPUT_FORMAT, ++ MRV_MI_SP_OUTPUT_FORMAT_YUV400); ++ break; ++ case CI_ISP_MIF_COL_FORMAT_RGB_565: ++ REG_SET_SLICE(mi_ctrl, MRV_MI_SP_OUTPUT_FORMAT, ++ MRV_MI_SP_OUTPUT_FORMAT_RGB565); ++ break; ++ case CI_ISP_MIF_COL_FORMAT_RGB_888: ++ REG_SET_SLICE(mi_ctrl, MRV_MI_SP_OUTPUT_FORMAT, ++ MRV_MI_SP_OUTPUT_FORMAT_RGB888); ++ break; ++ case CI_ISP_MIF_COL_FORMAT_RGB_666: ++ REG_SET_SLICE(mi_ctrl, MRV_MI_SP_OUTPUT_FORMAT, ++ MRV_MI_SP_OUTPUT_FORMAT_RGB666); ++ break; ++ ++ default: ++ eprintk("bad mrv_mi_ctrl->mrv_mif_sp_out_form value"); ++ return error; ++ } ++ ++ /* self picture path input format */ ++ switch (mrv_mi_ctrl->mrv_mif_sp_in_form) { ++ case CI_ISP_MIF_COL_FORMAT_YCBCR_422: ++ REG_SET_SLICE(mi_ctrl, MRV_MI_SP_INPUT_FORMAT, ++ MRV_MI_SP_INPUT_FORMAT_YUV422); ++ break; ++ case CI_ISP_MIF_COL_FORMAT_YCBCR_444: ++ REG_SET_SLICE(mi_ctrl, MRV_MI_SP_INPUT_FORMAT, ++ MRV_MI_SP_INPUT_FORMAT_YUV444); ++ break; ++ case CI_ISP_MIF_COL_FORMAT_YCBCR_420: ++ REG_SET_SLICE(mi_ctrl, MRV_MI_SP_INPUT_FORMAT, ++ MRV_MI_SP_INPUT_FORMAT_YUV420); ++ break; ++ case CI_ISP_MIF_COL_FORMAT_YCBCR_400: ++ REG_SET_SLICE(mi_ctrl, MRV_MI_SP_INPUT_FORMAT, ++ MRV_MI_SP_INPUT_FORMAT_YUV400); ++ break; ++ case CI_ISP_MIF_COL_FORMAT_RGB_565: ++ case CI_ISP_MIF_COL_FORMAT_RGB_666: ++ case CI_ISP_MIF_COL_FORMAT_RGB_888: ++ default: ++ eprintk("bad mrv_mi_ctrl->mrv_mif_sp_in_form value"); ++ return error; ++ } ++ ++ error = CI_STATUS_SUCCESS; ++ ++ /* self picture path write format */ ++ switch (mrv_mi_ctrl->mrv_mif_sp_pic_form) { ++ case CI_ISP_MIF_PIC_FORM_PLANAR: ++ REG_SET_SLICE(mi_ctrl, MRV_MI_SP_WRITE_FORMAT, ++ MRV_MI_SP_WRITE_FORMAT_PLANAR); ++ break; ++ case CI_ISP_MIF_PIC_FORM_SEMI_PLANAR: ++ if ((mrv_mi_ctrl->mrv_mif_sp_out_form == ++ CI_ISP_MIF_COL_FORMAT_YCBCR_422) ++ || (mrv_mi_ctrl->mrv_mif_sp_out_form == ++ CI_ISP_MIF_COL_FORMAT_YCBCR_420)) { ++ REG_SET_SLICE(mi_ctrl, MRV_MI_SP_WRITE_FORMAT, ++ MRV_MI_SP_WRITE_FORMAT_SEMIPLANAR); ++ } else { ++ error = CI_STATUS_NOTSUPP; ++ } ++ break; ++ case CI_ISP_MIF_PIC_FORM_INTERLEAVED: ++ if (mrv_mi_ctrl->mrv_mif_sp_out_form == ++ CI_ISP_MIF_COL_FORMAT_YCBCR_422) { ++ REG_SET_SLICE(mi_ctrl, MRV_MI_SP_WRITE_FORMAT, ++ MRV_MI_SP_WRITE_FORMAT_INTERLEAVED); ++ } else { ++ error = CI_STATUS_NOTSUPP; ++ } ++ break; ++ default: ++ error = CI_STATUS_OUTOFRANGE; ++ break; ++ ++ } ++ ++ if (error != CI_STATUS_SUCCESS) { ++ eprintk("bad mrv_mi_ctrl->mrv_mif_sp_pic_form value"); ++ return error; ++ } ++ ++ if (mrv_mi_ctrl->main_path == CI_ISP_PATH_ON) { ++ /* for YCbCr mode only, permitted for raw mode */ ++ /* main picture path write format */ ++ switch (mrv_mi_ctrl->mrv_mif_mp_pic_form) { ++ case CI_ISP_MIF_PIC_FORM_PLANAR: ++ REG_SET_SLICE(mi_ctrl, MRV_MI_MP_WRITE_FORMAT, ++ MRV_MI_MP_WRITE_FORMAT_PLANAR); ++ break; ++ case CI_ISP_MIF_PIC_FORM_SEMI_PLANAR: ++ REG_SET_SLICE(mi_ctrl, MRV_MI_MP_WRITE_FORMAT, ++ MRV_MI_MP_WRITE_FORMAT_SEMIPLANAR); ++ break; ++ case CI_ISP_MIF_PIC_FORM_INTERLEAVED: ++ REG_SET_SLICE(mi_ctrl, MRV_MI_MP_WRITE_FORMAT, ++ MRV_MI_MP_WRITE_FORMAT_INTERLEAVED); ++ break; ++ default: ++ error = CI_STATUS_OUTOFRANGE; ++ break; ++ } ++ } ++ ++ if (error != CI_STATUS_SUCCESS) { ++ eprintk("bad mrv_mi_ctrl->mrv_mif_mp_pic_form value"); ++ return error; ++ } ++ ++ /* burst length for chrominance for write port */ ++ /* setting burst mode to 16 bits ++ switch (mrv_mi_ctrl->burst_length_chrom) { ++ case CI_ISP_MIF_BURST_LENGTH_4: ++ REG_SET_SLICE(mi_ctrl, MRV_MI_BURST_LEN_CHROM, ++ MRV_MI_BURST_LEN_CHROM_4); ++ break; ++ case CI_ISP_MIF_BURST_LENGTH_8: ++ REG_SET_SLICE(mi_ctrl, MRV_MI_BURST_LEN_CHROM, ++ MRV_MI_BURST_LEN_CHROM_8); ++ break; ++ case CI_ISP_MIF_BURST_LENGTH_16: ++ REG_SET_SLICE(mi_ctrl, MRV_MI_BURST_LEN_CHROM, ++ MRV_MI_BURST_LEN_CHROM_16); ++ break; ++ default: ++ error = CI_STATUS_OUTOFRANGE; ++ break; ++ } ++ */ ++ REG_SET_SLICE(mi_ctrl, MRV_MI_BURST_LEN_CHROM, ++ MRV_MI_BURST_LEN_CHROM_16); ++ ++ if (error != CI_STATUS_SUCCESS) { ++ eprintk("bad mrv_mi_ctrl->burst_length_chrom value"); ++ return error; ++ } ++ ++ /* burst length for luminance for write port */ ++ /* setting burst mode to 16 bits ++ switch (mrv_mi_ctrl->burst_length_lum) { ++ case CI_ISP_MIF_BURST_LENGTH_4: ++ REG_SET_SLICE(mi_ctrl, MRV_MI_BURST_LEN_LUM, ++ MRV_MI_BURST_LEN_LUM_4); ++ break; ++ case CI_ISP_MIF_BURST_LENGTH_8: ++ REG_SET_SLICE(mi_ctrl, MRV_MI_BURST_LEN_LUM, ++ MRV_MI_BURST_LEN_LUM_8); ++ break; ++ case CI_ISP_MIF_BURST_LENGTH_16: ++ REG_SET_SLICE(mi_ctrl, MRV_MI_BURST_LEN_LUM, ++ MRV_MI_BURST_LEN_LUM_16); ++ break; ++ default: ++ error = CI_STATUS_OUTOFRANGE; ++ break; ++ } ++ */ ++ REG_SET_SLICE(mi_ctrl, MRV_MI_BURST_LEN_LUM, ++ MRV_MI_BURST_LEN_LUM_16); ++ ++ if (error != CI_STATUS_SUCCESS) { ++ eprintk("bad mrv_mi_ctrl->burst_length_lum value"); ++ return error; ++ } ++ ++ /* enable updating of the shadow registers for main and self picture ++ * to their init values ++ */ ++ switch (mrv_mi_ctrl->init_vals) { ++ case CI_ISP_MIF_NO_INIT_VALS: ++ break; ++ case CI_ISP_MIF_INIT_OFFS: ++ REG_SET_SLICE(mi_ctrl, MRV_MI_INIT_OFFSET_EN, ENABLE); ++ break; ++ case CI_ISP_MIF_INIT_BASE: ++ REG_SET_SLICE(mi_ctrl, MRV_MI_INIT_BASE_EN, ENABLE); ++ break; ++ case CI_ISP_MIF_INIT_OFFSAndBase: ++ REG_SET_SLICE(mi_ctrl, MRV_MI_INIT_OFFSET_EN, ENABLE); ++ REG_SET_SLICE(mi_ctrl, MRV_MI_INIT_BASE_EN, ENABLE); ++ break; ++ default: ++ error = CI_STATUS_OUTOFRANGE; ++ break; ++ } ++ ++ if (error != CI_STATUS_SUCCESS) { ++ eprintk("bad mrv_mi_ctrl->init_vals value"); ++ return error; ++ } ++ ++ /* enable change of byte order for write port */ ++ REG_SET_SLICE(mi_ctrl, MRV_MI_BYTE_SWAP, ++ (mrv_mi_ctrl->byte_swap_enable) ? ON : OFF); ++ ++ /* enable or disable the last pixel signalization */ ++ REG_SET_SLICE(mi_ctrl, MRV_MI_LAST_PIXEL_SIG_EN, ++ (mrv_mi_ctrl->last_pixel_enable) ? ON : OFF); ++ ++ /* now write settings into register */ ++ REG_WRITE(mrv_reg->mi_ctrl, mi_ctrl); ++ ++ dprintk(2, "mi_ctrl = 0x%x", mi_ctrl); ++ ++ /* self picture path operating mode */ ++ if ((mrv_mi_ctrl->self_path == CI_ISP_PATH_ON) || ++ (mrv_mi_ctrl->self_path == CI_ISP_PATH_OFF)) { ++ ++ /* do not call if not supported */ ++ ++ /* support has been restricted to >= MI_V2 && <= MI_V3 in ++ * ci_isp_mif_set_self_pic_orientation, so we do the same here ++ */ ++ ++ error = ci_isp_mif_set_self_pic_orientation( ++ mrv_mi_ctrl->mrv_mif_sp_mode, ++ (int) (mrv_mi_ctrl->self_path ++ == CI_ISP_PATH_ON)); ++ } else { ++ eprintk("bad mrv_mi_ctrl->self_path value"); ++ error = CI_STATUS_OUTOFRANGE; ++ } ++ ++ REG_SET_SLICE(mrv_reg->mi_init, MRV_MI_MI_CFG_UPD, ON); ++ ++ return error; ++} +-- +1.6.0.6 + -- cgit v1.2.3