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