From patchwork Thu May 12 08:16:04 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomoya X-Patchwork-Id: 779352 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter2.kernel.org (8.14.4/8.14.3) with ESMTP id p4C8Bapj030440 for ; Thu, 12 May 2011 08:11:36 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753196Ab1ELIKr (ORCPT ); Thu, 12 May 2011 04:10:47 -0400 Received: from sm-d311v.smileserver.ne.jp ([203.211.202.206]:23716 "EHLO sm-d311v.smileserver.ne.jp" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752169Ab1ELIKi (ORCPT ); Thu, 12 May 2011 04:10:38 -0400 X-Virus-Status: clean(F-Secure/virusgw_smtp/403/viruscheck1-00.private.hosting-pf.net) Received: from localhost.localdomain (113x35x143x224.ap113.ftth.ucom.ne.jp [113.35.143.224]) by sm-d311v.smileserver.ne.jp (mail) with ESMTPSA id 217DA96D94; Thu, 12 May 2011 17:10:30 +0900 (JST) From: Tomoya MORINAGA To: Mauro Carvalho Chehab , Hans Verkuil , Guennadi Liakhovetski , Kyungmin Park , Marek Szyprowski , linux-media@vger.kernel.org, linux-kernel@vger.kernel.org Cc: qi.wang@intel.com, yong.y.wang@intel.com, joel.clark@intel.com, kok.howg.ewe@intel.com, toshiharu-linux@dsn.okisemi.com, Tomoya MORINAGA Subject: [PATCH] Add VIDEO IN driver for OKI SEMICONDUCTOR ML7213/ML7223 IOHs Date: Thu, 12 May 2011 17:16:04 +0900 Message-Id: <1305188164-6372-1-git-send-email-tomoya-linux@dsn.okisemi.com> X-Mailer: git-send-email 1.7.3.4 Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.6 (demeter2.kernel.org [140.211.167.43]); Thu, 12 May 2011 08:11:36 +0000 (UTC) This patch is for Video IN driver of OKI SEMICONDUCTOR ML7213/ML7223 IOHs (Input/Output Hub). These ML7213/ML7223 IOHs are companion chip for Intel Atom E6xx series. ML7213 IOH is for IVI(In-Vehicle Infotainment) use and ML7223 IOH is for MP(Media Phone) use. Signed-off-by: Tomoya MORINAGA --- drivers/media/video/Kconfig | 79 + drivers/media/video/Makefile | 15 + drivers/media/video/ioh_video_in.c | 4704 +++++++++++++++++++++++++ drivers/media/video/ioh_video_in_main.h | 1058 ++++++ drivers/media/video/ioh_video_in_ml86v76651.c | 620 ++++ drivers/media/video/ioh_video_in_ncm13j.c | 584 +++ drivers/media/video/ioh_video_in_ov7620.c | 637 ++++ drivers/media/video/ioh_video_in_ov9653.c | 818 +++++ 8 files changed, 8515 insertions(+), 0 deletions(-) create mode 100644 drivers/media/video/ioh_video_in.c create mode 100644 drivers/media/video/ioh_video_in_main.h create mode 100644 drivers/media/video/ioh_video_in_ml86v76651.c create mode 100644 drivers/media/video/ioh_video_in_ncm13j.c create mode 100644 drivers/media/video/ioh_video_in_ov7620.c create mode 100644 drivers/media/video/ioh_video_in_ov9653.c diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 00f51dd..11a96a8 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -928,6 +928,85 @@ config VIDEO_MX2 Interface +config IOH_VIDEOIN + tristate "OKI SEMICONDUCTOR ML7213/ML7223 IOH VIDEO IN" + depends on PCI && DMADEVICES + select PCH_DMA + help + This driver is for Video IN of OKI SEMICONDUCTOR ML7213/ML7223 IOHs + (Input/Output Hub). + These ML7213/ML7223 IOHs are companion chip for Intel Atom E6xx + series. + ML7213 IOH is for IVI(In-Vehicle Infotainment) use and ML7223 IOH is + for MP(Media Phone) use. + +config IOH_VIDEO_DEVICE_SELECT + boolean + +choice + prompt "Select IOH VIDEO IN Device" + depends on IOH_VIDEOIN + help + This is a selection of used device of the IOH VIDEO. + +config IOH_ML86V76651 + boolean "IOH VIDEO IN(ML86V76651)" + depends on PCI && IOH_VIDEOIN && I2C_EG20T + help + If you say yes to this option, support will be included for the + IOH VIDEO ON Driver(ML86V76651). + +config IOH_ML86V76653 + boolean "IOH VIDEO IN(ML86V76653)" + depends on PCI && IOH_VIDEOIN && I2C_EG20T + help + If you say yes to this option, support will be included for the + IOH VIDEO ON Driver(ML86V76653). + +config IOH_OV7620 + boolean "IOH VIDEO IN(OV7620)" + depends on PCI && IOH_VIDEOIN && I2C_EG20T + help + If you say yes to this option, support will be included for the + IOH VIDEO ON Driver(OV7620). + +config IOH_OV9653 + boolean "IOH VIDEO IN(OV9653)" + depends on PCI && IOH_VIDEOIN && I2C_EG20T + help + If you say yes to this option, support will be included for the + IOH VIDEO ON Driver(OV9653). + +config IOH_NCM13J + boolean "IOH VIDEO IN(NCM13-J)" + depends on PCI && IOH_VIDEOIN && I2C_EG20T + help + If you say yes to this option, support will be included for the + IOH VIDEO ON Driver(NCM13-J). +endchoice + +config IOH_VIDEO_FRAMEWORK_SELECT + boolean + +choice + prompt "Select IOH VIDEO IN Videobuf Freamework" + depends on IOH_VIDEOIN + help + This is a selection of used the method of video buffer framework. + +config IOH_VIDEO_IN_VMALLOC + boolean "VMALLOC Framework" + select VIDEOBUF_VMALLOC + help + If you say yes to this option, VMALLOC framework is used. + +config IOH_VIDEO_IN_DMA_CONTIG + boolean "DMA-CONTIG Framework" + select VIDEOBUF_DMA_CONTIG + help + If you say yes to this option, DMA-CONTIG framework is used. +endchoice + # # USB Multimedia device configuration # diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index ace5d8b..147aec0 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -181,6 +181,21 @@ obj-y += davinci/ obj-$(CONFIG_ARCH_OMAP) += omap/ +obj-$(CONFIG_IOH_ML86V76651) += ioh_video_in_ml86v76651.o +obj-$(CONFIG_IOH_ML86V76653) += ioh_video_in_ml86v76651.o +obj-$(CONFIG_IOH_OV7620) += ioh_video_in_ov7620.o +obj-$(CONFIG_IOH_OV9653) += ioh_video_in_ov9653.o +obj-$(CONFIG_IOH_NCM13J) += ioh_video_in_ncm13j.o +obj-$(CONFIG_IOH_VIDEOIN) += ioh_video_in.o +ifeq ($(CONFIG_IOH_ML86V76653),y) +EXTRA_CFLAGS += -DIOH_VIDEO_IN_ML86V76653 +endif +ifeq ($(CONFIG_IOH_VIDEO_IN_DMA_CONTIG),y) +EXTRA_CFLAGS += -DIOH_VIDEO_IN_DMA_CONTIG +endif + + EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core EXTRA_CFLAGS += -Idrivers/media/dvb/frontends EXTRA_CFLAGS += -Idrivers/media/common/tuners +EXTRA_CFLAGS += -Idrivers/i2c/busses/ diff --git a/drivers/media/video/ioh_video_in.c b/drivers/media/video/ioh_video_in.c new file mode 100644 index 0000000..1bbe728 --- /dev/null +++ b/drivers/media/video/ioh_video_in.c @@ -0,0 +1,4704 @@ +/* + * Copyright (C) 2010 OKI SEMICONDUCTOR CO., LTD. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef IOH_VIDEO_IN_DMA_CONTIG +#include +#else +#include +#endif +#include +#include + +#include "ioh_video_in_main.h" + +#define IOH_VIDEOIN_THREAD_NAME "ioh_videoin_thread" + +#define IOH_VIN_MAJOR_VERSION 1 +#define IOH_VIN_MINOR_VERSION 0 +#define IOH_VIN_RELEASE 0 +#define IOH_VIN_VERSION \ + KERNEL_VERSION(IOH_VIN_MAJOR_VERSION, IOH_VIN_MINOR_VERSION, \ + IOH_VIN_RELEASE) + +#define OV7620_ADDR (0x42 >> 1) +#define OV9653_ADDR (0x60 >> 1) +#define ML86V76651_ADDR (0x80 >> 1) +#define NCM13J_ADDR (0xBA >> 1) + +#define ALLOC_NUMBER (3) +#define ALLOC_ORDER (10)/* (9) */ + +/* Macros for bit positions. */ +#define IOH_BIT_0 (0) +#define IOH_BIT_1 (1) +#define IOH_BIT_2 (2) +#define IOH_BIT_3 (3) +#define IOH_BIT_4 (4) +#define IOH_BIT_5 (5) +#define IOH_BIT_6 (6) +#define IOH_BIT_7 (7) +#define IOH_BIT_8 (8) +#define IOH_BIT_9 (9) +#define IOH_BIT_10 (10) +#define IOH_BIT_11 (11) +#define IOH_BIT_12 (12) +#define IOH_BIT_13 (13) +#define IOH_BIT_14 (14) +#define IOH_BIT_15 (15) + +/* Macros for register offset. */ +#define IOH_VIDEO_IN_VICTRL1 (0x0000U) + +#define IOH_VIDEO_IN_VICTRL2 (0x0004U) + #define IN2S (IOH_BIT_6) + +#define IOH_VIDEO_IN_VOCTRL1 (0x0008U) + #define OUT2S (IOH_BIT_8) + #define LDSEL (IOH_BIT_6) + #define ORGBSEL (IOH_BIT_1) + +#define IOH_VIDEO_IN_VOCTRL2 (0x000CU) + #define O422SEL (IOH_BIT_8) + #define CBON (IOH_BIT_7) + #define BBON (IOH_BIT_6) + #define SBON (IOH_BIT_4) + #define RGBLEV (IOH_BIT_3) + +#define IOH_VIDEO_IN_BLNKTIM (0x0018U) + #define CNTCTL (IOH_BIT_7) + +#define IOH_VIDEO_IN_LUMLEV (0x0020U) + #define NOSIG (IOH_BIT_7) + +#define IOH_VIDEO_IN_GGAIN (0x0024U) + +#define IOH_VIDEO_IN_BGAIN (0x0028U) + +#define IOH_VIDEO_IN_RGAIN (0x002CU) + +#define IOH_VIDEO_IN_THMOD1 (0x00F8U) + +#define IOH_VIDEO_IN_THMOD2 (0x00FCU) + +#define IOH_VIDEO_IN_INTENB (0x0100U) + +#define IOH_VIDEO_IN_INTSTS (0x0104U) + #define INTBITS (0x7) + #define OFINTSTS (0x4) + #define HSINTSTS (0x2) + #define VSINTSTS (0x1) + +#define IOH_VIDEO_IN_VDATA (0x1000U) + +#define IOH_VIDEO_IN_RESET (0x1ffcU) + #define ASSERT_RESET (0x0001U) + #define DE_ASSERT_RESET (0x0000U) + +#define DESC_SIZE (1028 * MAXIMUM_FRAME_BUFFERS) + +#define VSYNC_SYNC 0 +#define VSYNC_NOT_SYNC 1 + +#define DISABLE 0 +#define ENABLE 1 + +#define LIKELY(x) likely(x) +#define UNLIKELY(x) unlikely(x) + +struct reg_intenb { + u8 drevsem; + u8 dmarenb; + u8 ofintenb; + u8 hsintenb; + u8 vsintenb; +}; + +struct ioh_video_in_settings { + /**< The current input data format. */ + struct ioh_video_in_input_format current_input_format; + + /**< The current frame size. */ + struct ioh_video_in_frame_size current_frame_size; + + /**< The current output data format. */ + struct ioh_video_in_output_format current_output_format; + + /**< The current scan mode conversion method. */ + enum ioh_video_in_scan_mode_method current_scan_mode_method; + + /**< The current luminance settings. */ + struct ioh_video_in_luminance_settings current_luminance_settings; + + /**< The current RGB gain settings. */ + struct ioh_video_in_rgb_gain_settings current_rgb_gain_settings; + + /**< The current blanking time settings. */ + struct ioh_video_in_blank_tim_settings current_blank_tim_settings; + + /**< The current Blue background settings. */ + enum ioh_video_in_bb_mode current_bb_mode; + + /**< The current Color bar settings. */ + struct ioh_video_in_cb_settings current_cb_settings; + + /**< The current interrupt settings. */ + /* Used only during suspension and resume operation. */ + struct reg_intenb current_interrupt_settings; +}; + +enum ioh_type { + ML7213_IOH, + ML7223_IOH, +}; + +struct BT656_device { + /* The base (remapped) address of the video device. */ + void __iomem *base_address; + /* The physical address of the video device. */ + u32 physical_address; + + /* The pci_dev structure reference of the device. */ + struct pci_dev *p_device; + /* The IRQ line reserved for the device. */ + int irq; + + /* The DMA channel obtained for capturing data. */ + struct dma_async_tx_descriptor *desc_dma; + struct pch_dma_slave param_dma; + struct dma_chan *chan_dma; + struct scatterlist *sg_dma; /* sg_tx_p */ + int nent; + +#ifdef IOH_VIDEO_IN_DMA_CONTIG + /* Wait queue variable for the thread sleep operation. */ + wait_queue_head_t thread_sleep_queue; + wait_queue_head_t vsync_wait_queue; +#else /* IOH_VIDEO_IN_DMA_CONTIG */ + /* Information of Video Frame Buffers. */ + struct ioh_video_in_frame_buffer_info frame_buffer_info; + + /* Video Frame Buffers. */ + struct ioh_video_in_frame_buffers bufs; + + /* Read Buffer. (The current frame buffer that can be read.) */ + struct ioh_video_in_frame_buffer *read_buffer; + + /* Wait queue variable for the read operation. */ + wait_queue_head_t read_wait_queue; + /* Wait queue variable for the thread sleep operation. */ + wait_queue_head_t thread_sleep_queue; + wait_queue_head_t vsync_wait_queue; +#endif /* IOH_VIDEO_IN_DMA_CONTIG */ + + /* Lock variables. */ +#ifdef IOH_VIDEO_IN_DMA_CONTIG + spinlock_t dev_lock; +#else /* IOH_VIDEO_IN_DMA_CONTIG */ + spinlock_t read_buffer_lock; + spinlock_t dev_lock; +#endif /* IOH_VIDEO_IN_DMA_CONTIG */ + + /* Flag variables. */ + u8 suspend_flag; /* for denoting the suspend action. */ + u8 open_flag; /* for denoting the open action. */ + u8 vsync_waiting_flag; +#ifdef IOH_VIDEO_IN_DMA_CONTIG +#else /* IOH_VIDEO_IN_DMA_CONTIG */ + u8 read_wait_flag; /* for denoting the read wait process. */ +#endif /* IOH_VIDEO_IN_DMA_CONTIG */ + u8 thread_sleep_flag; /* for denoting the thread sleep process. */ + u8 thread_exit_flag; /* for denoting the thread exit process. */ + u8 overflow_flag; /* for denoting the overflow interrupt. */ + s8 dma_flag; /* for denoting the DMA process completion. */ + + struct reg_intenb intenb; + + /* current video setting details of the device. */ + struct ioh_video_in_settings video_settings; + + /* Used in thread */ + + /* The number of frame skip */ + u32 frame_skip_num; + /* DMA buffer virtual address. */ + void *dma_buffer_virt[MAXIMUM_FRAME_BUFFERS]; + /* DMA buffer physical address. */ + dma_addr_t dma_buffer_phy[MAXIMUM_FRAME_BUFFERS]; + + struct scatterlist *sg_dma_list[MAXIMUM_FRAME_BUFFERS]; + int ioh_type; /* ML7213 or ML7223 */ +}; + +#ifdef IOH_VIDEO_IN_DMA_CONTIG +#else /* IOH_VIDEO_IN_DMA_CONTIG */ +#define ioh_video_in_lock_buffer(lock_variable) spin_lock(lock_variable) +#define ioh_video_in_unlock_buffer(lock_variable) spin_unlock(lock_variable) +#endif /* IOH_VIDEO_IN_DMA_CONTIG */ + +struct ioh_vin_fmt { + char *name; + u32 fourcc; /* v4l2 format id */ + int depth; + enum v4l2_mbus_pixelcode mbus_code; +}; + +#ifdef IOH_VIDEO_IN_DMA_CONTIG +enum ioh_video_in_vidq_status { + IOH_VIDEO_IN_VIDQ_IDLE, + IOH_VIDEO_IN_VIDQ_INITIALISING, + IOH_VIDEO_IN_VIDQ_RUNNING, +}; +#else /* IOH_VIDEO_IN_DMA_CONTIG */ +#endif /* IOH_VIDEO_IN_DMA_CONTIG */ + +/* buffer for one video frame */ +struct ioh_vin_buffer { + /* common v4l buffer stuff -- must be first */ + struct videobuf_buffer vb; + + struct ioh_vin_fmt *fmt; + +#ifdef IOH_VIDEO_IN_DMA_CONTIG + u32 channel; + u32 frame_index; +#else /* IOH_VIDEO_IN_DMA_CONTIG */ +#endif /* IOH_VIDEO_IN_DMA_CONTIG */ +}; + +struct ioh_vin_dev { + struct v4l2_device v4l2_dev; + + spinlock_t slock; + struct mutex mutex; + + int users; + + /* various device info */ + struct video_device *vfd; + + struct list_head vidq_active; +#ifdef IOH_VIDEO_IN_DMA_CONTIG + struct ioh_vin_buffer *active_frm; + enum ioh_video_in_vidq_status vidq_status; +#else /* IOH_VIDEO_IN_DMA_CONTIG */ +#endif /* IOH_VIDEO_IN_DMA_CONTIG */ + /* Control 'registers' */ + struct BT656_device *video_in_dev; + + struct v4l2_subdev *sensor; + +}; + +struct ioh_vin_fh { + struct ioh_vin_dev *dev; + + /* video capture */ + struct ioh_vin_fmt *fmt; + struct v4l2_pix_format pix_format; + struct videobuf_queue vb_vidq; + + enum v4l2_buf_type type; +}; + +#define IOH_VIN_DRV_NAME "ioh_videoin" +#define MAX_DEVICE_SUPPORTED (1) +#define PCI_VENDOR_ID_IOH (0x10DB) +#define PCI_DEVICE_ID_IVI_VIDEOIN (0x802A) +#define PCI_DEVICE_ID_MP_VIDEOIN (0x8011) + +#define SET_BIT(value, bit) ((value) |= ((u32)0x1 << (bit))) +#define RESET_BIT(value, bit) ((value) &= ~((u32)0x1 << (bit))) +#define RESET_BIT_RANGE(value, lbit, hbit) \ +((value) &= ~((((u32)2 << ((hbit) - (lbit) + 1)) - 1) << (lbit))) + +/* -- */ +#define ioh_err(device, fmt, arg...) \ + dev_err(&(device)->p_device->dev, fmt, ##arg); +#define ioh_warn(device, fmt, arg...) \ + dev_warn(&(device)->p_device->dev, fmt, ##arg); +#define ioh_info(device, fmt, arg...) \ + dev_info(&(device)->p_device->dev, fmt, ##arg); +#define ioh_dbg(device, fmt, arg...) \ + dev_dbg(&(device)->p_device->dev, fmt, ##arg); +/* -- */ + +#define SLEEP_DELAY (500U) + +static unsigned video_nr = -1; +module_param(video_nr, uint, 0644); +MODULE_PARM_DESC(video_nr, "videoX number, -1 is autodetect"); + +#ifdef IOH_VIDEO_IN_DMA_CONTIG +#define N_FRAME_BUF 2 +#else /* IOH_VIDEO_IN_DMA_CONTIG */ +#define MIN_N_FRAME_BUF 3 +#define MAX_N_FRAME_BUF 5 +static unsigned n_frame_buf = 3; +module_param(n_frame_buf, uint, 0644); +MODULE_PARM_DESC(n_frame_buf, "the number of farme buffer to allocate[3-5]."); +#endif /* IOH_VIDEO_IN_DMA_CONTIG */ + +static unsigned int vid_limit = 16; + +/* prototype */ +static void ioh_videoin_thread_tick(struct ioh_vin_dev *dev); +static int ioh_video_in_open(struct ioh_vin_dev *dev); +static int ioh_video_in_close(struct ioh_vin_dev *dev); + +static long ioh_video_in_ioctl(struct file *p_file, void *priv, + int command, void *param); + +#define sensor_call(dev, o, f, args...) \ + v4l2_subdev_call(dev->sensor, o, f, ##args) + +static struct ioh_vin_fmt formats[] = { + { + .name = "4:2:2, packed, UYVY", + .fourcc = V4L2_PIX_FMT_UYVY, + .depth = 16, + .mbus_code = V4L2_MBUS_FMT_UYVY8_2X8, + }, +}; + +/* ---- HAL ---- */ + +void write_intenb(struct BT656_device *device) +{ + void __iomem *addr; + u32 data; + + addr = device->base_address + IOH_VIDEO_IN_INTENB; + data = device->intenb.drevsem << 5 + | device->intenb.dmarenb << 4 + | device->intenb.ofintenb << 2 + | device->intenb.hsintenb << 1 + | device->intenb.vsintenb << 0; + + iowrite32(data, addr); + + ioh_dbg(device, "In write_intenb -> 0x%04x write", data); +} + +void ioh_video_in_wait_vsync(struct BT656_device *device) +{ + device->vsync_waiting_flag = true; + + ioh_dbg(device, "In %s waiting", __func__); + + wait_event_interruptible(device->vsync_wait_queue, + false == device->vsync_waiting_flag); + + ioh_dbg(device, "In %s wake_up", __func__); + + return; +} + +struct ioh_video_in_input_settings { + /* Input format. */ + enum ioh_video_in_input_data_format format; + /* VICTRL1 register value. */ + u32 victrl1; + /* VICTRL2 register value. */ + u32 victrl2; + /* Frame size. */ + struct ioh_video_in_frame_size frame_size; +}; + +static const struct ioh_video_in_input_settings ioh_input_settings[] = { +/* */ + {NT_SQPX_ITU_R_BT_656_4_8BIT, + 0x00000001, 0x00000000, {640, 480, 640 * 2} }, + {NT_SQPX_ITU_R_BT_656_4_10BIT, + 0x00000001, 0x00000200, {640, 480, 640 * 4} }, + {NT_SQPX_YCBCR_422_8BIT, + 0x00000009, 0x00000000, {640, 480, 640 * 4} }, + {NT_SQPX_YCBCR_422_10BIT, + 0x00000009, 0x00000200, {640, 480, 640 * 4} }, + + {NT_BT601_ITU_R_BT_656_4_8BIT, + 0x00000000, 0x00000000, {720, 480, 768 * 2} }, + {NT_BT601_ITU_R_BT_656_4_10BIT, + 0x00000000, 0x00000200, {720, 480, 768 * 4} }, + {NT_BT601_YCBCR_422_8BIT, + 0x00000008, 0x00000000, {720, 480, 768 * 4} }, + {NT_BT601_YCBCR_422_10BIT, + 0x00000008, 0x00000200, {720, 480, 768 * 4} }, + + {NT_RAW_8BIT, 0x00000000, 0x00000100, {640, 480, 640 * 2} }, + {NT_RAW_10BIT, 0x00000000, 0x00000300, {640, 480, 640 * 2} }, + {NT_RAW_12BIT, 0x00000000, 0x00000500, {640, 480, 640 * 2} }, +}; + +#define DEFAULT_IP_TRANS_SETTINGS (LINE_INTERPOLATION) +#define DEFAULT_BB_SETTINGS (BB_OUTPUT_OFF) + +static const struct ioh_video_in_input_format default_input_data_format = { + NT_SQPX_ITU_R_BT_656_4_8BIT, + OFFSET_BINARY_FORMAT, +}; + +static const struct ioh_video_in_output_format default_output_data_format = { + YCBCR_422_8BIT, + OFFSET_BINARY_FORMAT, + BT601_LUMINANCE_RANGE, + RGB_FULL_SCALE_MODE, +}; + +static const struct ioh_video_in_luminance_settings +default_luminance_settings = { + NOSIG_NORMAL_MODE, + LUMLEV_78_PERCENT, +}; + +static const struct ioh_video_in_rgb_gain_settings +default_rgb_gain_settings = { + (unsigned char)0x00, + (unsigned char)0x00, + (unsigned char)0x00, +}; + +static const struct ioh_video_in_blank_tim_settings +default_blank_tim_settings = { + CNTCTL_STANDARD_SIGNAL, + BLKADJ_0_PIXEL, +}; + +static const struct ioh_video_in_cb_settings default_cb_settings = { + CB_OUTPUT_OFF, + CB_OUTLEV_25_PERCENT, +}; + +static const struct ioh_video_in_input_format invalid_input_format = { + INVALID_INPUT_DATA_FORMAT, + INVALID_NUMERICAL_FORMAT, +}; + +static const struct ioh_video_in_frame_size invalid_frame_size = { + 0, + 0, + 0, +}; + +static const struct ioh_video_in_output_format invalid_output_format = { + INVALID_OUTPUT_DATA_FORMAT, + INVALID_NUMERICAL_FORMAT, + INVALID_LUMINANCE_RANGE, + INVALID_RGB_GAIN_LEVEL, +}; + +#define INVALID_IP_TRANS_MODE (INVALID_SCAN_MODE_METHOD) + +static const struct ioh_video_in_luminance_settings invalid_luminance_level = { + INVALID_LUMINANCE_NOSIG, + INVALID_LUMINANCE_LUMLEV, +}; + +static const struct ioh_video_in_blank_tim_settings invalid_blank_tim = { + INVALID_BLANK_TIM_CNTCTL, + INVALID_BLANK_TIM_BLKADJ, +}; + +#define INVALID_BB_MODE (INVALID_BB_MODE) + +static const struct ioh_video_in_cb_settings invalid_cb_settings = { + INVALID_CB_MODE, + INVALID_CB_OUTLEV, +}; + +#define MAXIMUM_INPUT_FORMAT (sizeof(ioh_input_settings)/(sizeof(struct \ +ioh_video_in_input_settings))) + +static s32 +ioh_video_in_set_input_format(struct BT656_device *device, + struct ioh_video_in_input_format input_format) +{ + u32 victrl2; + u32 counter; + void __iomem *base_address; + s32 retval = IOH_VIDEOIN_SUCCESS; + + /* Obtaining the base address. */ + base_address = device->base_address; + + /* Checking for the input data format. */ + for (counter = 0; counter < MAXIMUM_INPUT_FORMAT; counter++) { + if (input_format.format == ioh_input_settings[counter].format) + break; + } + + /* Data format valid. */ + if (counter >= MAXIMUM_INPUT_FORMAT) { + ioh_err(device, "In %s -> " + "Invalid input data format", __func__); + + retval = IOH_VIDEOIN_FAIL; + + goto out; + } + + /* Obtaining the value of the VICTRL2 register. */ + victrl2 = ioh_input_settings[counter].victrl2; + + /* Setting IN2S bit of VICTRL2 */ + switch (input_format.numerical_format) { + case OFFSET_BINARY_FORMAT: + RESET_BIT(victrl2, IN2S); + break; + + case COMPLEMENTARY_FORMAT_OF_2: + SET_BIT(victrl2, IN2S); + break; + + case DONT_CARE_NUMERICAL_FORMAT: + break; + + default: + ioh_err(device, "In %s -> " + "Invalid numerical format", __func__); + + retval = IOH_VIDEOIN_FAIL; + break; + } + + if (IOH_VIDEOIN_SUCCESS != retval) + goto out; + + /* Setting the VICTRL1 and VICTRL2 regsiter. */ + spin_lock(&device->dev_lock); + + iowrite32((ioh_input_settings[counter].victrl1), + (base_address + IOH_VIDEO_IN_VICTRL1)); + + iowrite32((victrl2), (base_address + IOH_VIDEO_IN_VICTRL2)); + + /* Confirming the write operation. */ + ioh_dbg(device, "In %s -> victrl1 = %08x victrl2 = %08x", + __func__, ioh_input_settings[counter].victrl1, victrl2); + + if (((ioh_input_settings[counter].victrl1) == + (ioread32(base_address + IOH_VIDEO_IN_VICTRL1))) + && (victrl2 == (ioread32(base_address + IOH_VIDEO_IN_VICTRL2)))) { + + ioh_dbg(device, "In %s -> " + "Register write successful", __func__); + + device->video_settings.current_input_format = input_format; + device->video_settings.current_frame_size = + ioh_input_settings[counter].frame_size; + } else { + ioh_err(device, "In %s -> " + "Register write unsuccessful", __func__); + + device->video_settings.current_input_format = + invalid_input_format; + + device->video_settings.current_frame_size = + invalid_frame_size; + + retval = IOH_VIDEOIN_FAIL; + } + spin_unlock(&device->dev_lock); + +out: + ioh_dbg(device, "Function %s ended(%d)", __func__, retval); + + return retval; +} + +static struct ioh_video_in_input_format +ioh_video_in_get_input_format(struct BT656_device *device) +{ + ioh_dbg(device, "Function %s invoked successfully", __func__); + + return device->video_settings.current_input_format; +} + +static s32 +ioh_video_in_set_output_format(struct BT656_device *device, + struct ioh_video_in_output_format output_format) +{ + u32 voctrl1; + u32 voctrl2; + void __iomem *base_address; + struct ioh_video_in_input_format input_format; + s32 retval = IOH_VIDEOIN_FAIL; + + /* Obtaining the device base address for read and write operations. */ + base_address = device->base_address; + + /* Obtaining the currently set input format. */ + input_format = device->video_settings.current_input_format; + + spin_lock(&device->dev_lock); + + /* Reading the VOCTRL1 and VOCTRL2 register value for updation. */ + voctrl1 = ioread32(base_address + IOH_VIDEO_IN_VOCTRL1); + voctrl2 = ioread32(base_address + IOH_VIDEO_IN_VOCTRL2); + + /* Setting the register values depending on the output data format. */ + switch (output_format.format) { + case YCBCR_422_8BIT: + if ((input_format.format == NT_SQPX_ITU_R_BT_656_4_8BIT) + || (input_format.format == NT_BT601_ITU_R_BT_656_4_8BIT) + || (input_format.format == NT_SQPX_YCBCR_422_8BIT) + || (input_format.format == NT_BT601_YCBCR_422_8BIT) + || (input_format.format == NT_RAW_8BIT)) { + /* NT_RAW_8BIT condition is for input setting is raw & + output setting is 422-8bit. */ + + /* Clearing bit1(ORGBSEL) */ + RESET_BIT(voctrl1, ORGBSEL); + + /* Setting bit8 (O422SEL) */ + SET_BIT(voctrl2, O422SEL); + + /* Clearing bits9-10(OBITSEL) */ + RESET_BIT_RANGE(voctrl1, IOH_BIT_9, IOH_BIT_10); + + retval = IOH_VIDEOIN_SUCCESS; + } + break; + + case YCBCR_422_10BIT: + if ((input_format.format == NT_SQPX_ITU_R_BT_656_4_10BIT) + || (input_format.format == NT_BT601_ITU_R_BT_656_4_10BIT) + || (input_format.format == NT_SQPX_YCBCR_422_10BIT) + || (input_format.format == NT_BT601_YCBCR_422_10BIT)) { + /* Clearing bit1(ORGBSEL) */ + RESET_BIT(voctrl1, ORGBSEL); + + /* Setting bit8 (O422SEL) */ + SET_BIT(voctrl2, O422SEL); + + /* Clearing bits9-10(OBITSEL) */ + RESET_BIT_RANGE(voctrl1, IOH_BIT_9, IOH_BIT_10); + + /* Setting bit9 (OBITSEL) */ + SET_BIT(voctrl1, IOH_BIT_9); + + retval = IOH_VIDEOIN_SUCCESS; + } + break; + + case YCBCR_444_8BIT: + if ((input_format.format == NT_SQPX_ITU_R_BT_656_4_8BIT) + || (input_format.format == NT_BT601_ITU_R_BT_656_4_8BIT) + || (input_format.format == NT_SQPX_YCBCR_422_8BIT) + || (input_format.format == NT_BT601_YCBCR_422_8BIT)) { + /* Clearing bit1(ORGBSEL) */ + RESET_BIT(voctrl1, ORGBSEL); + + /* Resetting bit8 (O422SEL) */ + RESET_BIT(voctrl2, O422SEL); + + /* Clearing bits9-10(OBITSEL) */ + RESET_BIT_RANGE(voctrl1, IOH_BIT_9, IOH_BIT_10); + + retval = IOH_VIDEOIN_SUCCESS; + } + break; + + case YCBCR_444_10BIT: + if ((input_format.format == NT_SQPX_ITU_R_BT_656_4_10BIT) + || (input_format.format == NT_BT601_ITU_R_BT_656_4_10BIT) + || (input_format.format == NT_SQPX_YCBCR_422_10BIT) + || (input_format.format == NT_BT601_YCBCR_422_10BIT)) { + /* Clearing bit1(ORGBSEL) */ + RESET_BIT(voctrl1, ORGBSEL); + + /* Resetting bit8 (O422SEL) */ + RESET_BIT(voctrl2, O422SEL); + + /* Clearing bits9-10(OBITSEL) */ + RESET_BIT_RANGE(voctrl1, IOH_BIT_9, IOH_BIT_10); + + /* Setting bit9 (OBITSEL) */ + SET_BIT(voctrl1, IOH_BIT_9); + + retval = IOH_VIDEOIN_SUCCESS; + } + break; + + case RGB888: + if ((input_format.format == NT_SQPX_ITU_R_BT_656_4_8BIT) + || (input_format.format == NT_BT601_ITU_R_BT_656_4_8BIT) + || (input_format.format == NT_SQPX_YCBCR_422_8BIT) + || (input_format.format == NT_BT601_YCBCR_422_8BIT) + || (input_format.format == NT_SQPX_ITU_R_BT_656_4_10BIT) + || (input_format.format == NT_BT601_ITU_R_BT_656_4_10BIT) + || (input_format.format == NT_SQPX_YCBCR_422_10BIT) + || (input_format.format == NT_BT601_YCBCR_422_10BIT)) { + + /* Clearing bits9-10(OBITSEL) */ + RESET_BIT_RANGE(voctrl1, IOH_BIT_9, IOH_BIT_10); + + /* Setting bit1(ORGBSEL) */ + SET_BIT(voctrl1, ORGBSEL); + + retval = IOH_VIDEOIN_SUCCESS; + } + break; + + case RGB666: + if ((input_format.format == NT_SQPX_ITU_R_BT_656_4_8BIT) + || (input_format.format == NT_BT601_ITU_R_BT_656_4_8BIT) + || (input_format.format == NT_SQPX_YCBCR_422_8BIT) + || (input_format.format == NT_BT601_YCBCR_422_8BIT) + || (input_format.format == NT_SQPX_ITU_R_BT_656_4_10BIT) + || (input_format.format == NT_BT601_ITU_R_BT_656_4_10BIT) + || (input_format.format == NT_SQPX_YCBCR_422_10BIT) + || (input_format.format == NT_BT601_YCBCR_422_10BIT)) { + + /* Clearing bits9-10(OBITSEL) */ + RESET_BIT_RANGE(voctrl1, IOH_BIT_9, IOH_BIT_10); + + /* Setting bits9(OBITSEL) */ + SET_BIT(voctrl1, IOH_BIT_9); + + /* Setting bit1(ORGBSEL) */ + SET_BIT(voctrl1, ORGBSEL); + + retval = IOH_VIDEOIN_SUCCESS; + } + break; + + case RGB565: + if ((input_format.format == NT_SQPX_ITU_R_BT_656_4_8BIT) + || (input_format.format == NT_BT601_ITU_R_BT_656_4_8BIT) + || (input_format.format == NT_SQPX_YCBCR_422_8BIT) + || (input_format.format == NT_BT601_YCBCR_422_8BIT) + || (input_format.format == NT_SQPX_ITU_R_BT_656_4_10BIT) + || (input_format.format == NT_BT601_ITU_R_BT_656_4_10BIT) + || (input_format.format == NT_SQPX_YCBCR_422_10BIT) + || (input_format.format == NT_BT601_YCBCR_422_10BIT)) { + /* Clearing bits9-10(OBITSEL) */ + RESET_BIT_RANGE(voctrl1, IOH_BIT_9, IOH_BIT_10); + + /* Setting bits10(OBITSEL) */ + SET_BIT(voctrl1, IOH_BIT_10); + + /* Setting bit1(ORGBSEL) */ + SET_BIT(voctrl1, ORGBSEL); + + retval = IOH_VIDEOIN_SUCCESS; + } + break; + + case RAW_8BIT: + if (input_format.format == NT_RAW_8BIT) { + /* Clearing bits9-10(OBITSEL) */ + RESET_BIT_RANGE(voctrl1, IOH_BIT_9, IOH_BIT_10); + + retval = IOH_VIDEOIN_SUCCESS; + } + break; + + case RAW_10BIT: + if (input_format.format == NT_RAW_10BIT) { + /* Clearing bits9-10(OBITSEL) */ + RESET_BIT_RANGE(voctrl1, IOH_BIT_9, IOH_BIT_10); + + /* Setting bits9(OBITSEL) */ + SET_BIT(voctrl1, IOH_BIT_9); + + retval = IOH_VIDEOIN_SUCCESS; + } + break; + + case RAW_12BIT: + if (input_format.format == NT_RAW_12BIT) { + /* Clearing bits9-10(OBITSEL) */ + RESET_BIT_RANGE(voctrl1, IOH_BIT_9, IOH_BIT_10); + + /* Setting bits10(OBITSEL) */ + SET_BIT(voctrl1, IOH_BIT_10); + + retval = IOH_VIDEOIN_SUCCESS; + } + break; + + default: + break; + } + + if (IOH_VIDEOIN_FAIL == retval) { + ioh_err(device, "In %s -> " + "Invalid Output Data Format", __func__); + goto out; + } + + /* Checking and setting for output numerical format. */ + switch (output_format.numerical_format) { + case OFFSET_BINARY_FORMAT: + /* Clearing bit8(O2S) */ + RESET_BIT(voctrl1, OUT2S); + break; + + case COMPLEMENTARY_FORMAT_OF_2: + /* Setting bit8 (O2S) */ + SET_BIT(voctrl1, OUT2S); + break; + + case DONT_CARE_NUMERICAL_FORMAT: + break; + + default: + ioh_err(device, "In %s -> " + "Invalid output numerical format", __func__); + + retval = IOH_VIDEOIN_FAIL; + break; + } + + if (IOH_VIDEOIN_SUCCESS != retval) + goto out; + + /* Clearing bit4 SBON */ + RESET_BIT(voctrl2, SBON); + + /* Checking and setting for output luminance range. */ + switch (output_format.luminance_range) { + case BT601_LUMINANCE_RANGE: + voctrl2 |= BT601_LUMINANCE_RANGE; + break; + + case EXTENDENDED_LUMINANCE_RANGE: + voctrl2 |= EXTENDENDED_LUMINANCE_RANGE; + break; + + case DONT_CARE_LUMINANNCE_RANGE: + break; + + default: + ioh_err(device, "In %s -> " + "Invalid output luminance range format", __func__); + + retval = IOH_VIDEOIN_FAIL; + break; + } + + if (IOH_VIDEOIN_SUCCESS != retval) + goto out; + + /* Clearing bit3 RGBLEV */ + RESET_BIT(voctrl2, RGBLEV); + + /* Checking and setting for RGB Gain level. */ + switch (output_format.rgb_gain_level) { + case RGB_FULL_SCALE_MODE: + voctrl2 |= RGB_FULL_SCALE_MODE; + break; + + case RGB_BT601_MODE: + voctrl2 |= RGB_BT601_MODE; + break; + + case DONT_CARE_RGBLEV: + break; + + default: + ioh_err(device, "In %s -> " + "Invalid output rgb gain level format", __func__); + + retval = IOH_VIDEOIN_FAIL; + break; + } + + if (IOH_VIDEOIN_SUCCESS != retval) + goto out; + + /* Updating the VOCTRL1 and VOCTRL2 registers. */ + iowrite32(voctrl1, (base_address + IOH_VIDEO_IN_VOCTRL1)); + iowrite32(voctrl2, (base_address + IOH_VIDEO_IN_VOCTRL2)); + + /* Confirming the register write. */ + ioh_dbg(device, "In %s -> voctrl1 = %08x voctrl2 = %08x", + __func__, voctrl1, voctrl2); + + if ((voctrl1 == ioread32(base_address + IOH_VIDEO_IN_VOCTRL1)) + && (voctrl2 == ioread32(base_address + IOH_VIDEO_IN_VOCTRL2))) { + ioh_dbg(device, "In %s -> " + "Register write successful", __func__); + + device->video_settings.current_output_format = output_format; + } else { + ioh_err(device, "In %s -> " + "Register write unsuccessful", __func__); + + device->video_settings.current_output_format = + invalid_output_format; + + retval = IOH_VIDEOIN_FAIL; + } +out: + spin_unlock(&device->dev_lock); + + ioh_dbg(device, "Function %s ended(%d)", __func__, retval); + + return retval; +} + +static struct ioh_video_in_output_format +ioh_video_in_get_output_format(struct BT656_device *device) +{ + ioh_dbg(device, "Function %s invoked successfully", __func__); + + return device->video_settings.current_output_format; +} + +static s32 +ioh_video_in_set_size(struct BT656_device *device, + struct ioh_video_in_frame_size frame_size) +{ + if ((frame_size.pitch_size % 128) != 0) { + + ioh_err(device, "In %s -> " + "Invalid pitch size", __func__); + + return IOH_VIDEOIN_FAIL; + } else { + device->video_settings.current_frame_size = frame_size; + + ioh_dbg(device, "Function %s ended", __func__); + + return IOH_VIDEOIN_SUCCESS; + } +} + +static struct ioh_video_in_frame_size +ioh_video_in_get_size(struct BT656_device *device) +{ + ioh_dbg(device, "Function %s invoked successfully", __func__); + + return device->video_settings.current_frame_size; +} + +static s32 +ioh_video_in_set_ip_trans(struct BT656_device *device, + enum ioh_video_in_scan_mode_method scan_mode_method) +{ + u32 voctrl1; + void __iomem *base_address; + s32 retval = IOH_VIDEOIN_SUCCESS; + + /* Obtaining the device base address. */ + base_address = device->base_address; + + /* Obtaining the value of VOCTRL1 for updation. */ + spin_lock(&device->dev_lock); + voctrl1 = ioread32(base_address + IOH_VIDEO_IN_VOCTRL1); + + /* Clearing bit6(LDSEL) */ + RESET_BIT(voctrl1, LDSEL); + + /* Checking the interpolation type and updating the value. */ + switch (scan_mode_method) { + case LINE_INTERPOLATION: + voctrl1 |= LINE_INTERPOLATION; + break; + + case LINE_DOUBLER: + /* Setting bit6(LDSEL) */ + voctrl1 |= LINE_DOUBLER; + break; + + default: + ioh_err(device, "In %s -> " + "Invalid scan mode method", __func__); + + retval = IOH_VIDEOIN_FAIL; + break; + } + + if (IOH_VIDEOIN_SUCCESS != retval) + goto out; + + /* Updating the register values. */ + iowrite32(voctrl1, (base_address + IOH_VIDEO_IN_VOCTRL1)); + + /* Confirming the register write. */ + if ((voctrl1 == ioread32(base_address + IOH_VIDEO_IN_VOCTRL1))) { + ioh_dbg(device, "In %s -> " + "Register write successful", __func__); + + device->video_settings. + current_scan_mode_method = scan_mode_method; + } else { + ioh_err(device, "In %s -> " + "Register write unsuccessful", __func__); + + device->video_settings.current_scan_mode_method = + INVALID_IP_TRANS_MODE; + + retval = IOH_VIDEOIN_FAIL; + } +out: + spin_unlock(&device->dev_lock); + + ioh_dbg(device, "Function %s ended(%d)", __func__, retval); + + return retval; +} + +static enum ioh_video_in_scan_mode_method +ioh_video_in_get_ip_trans(struct BT656_device *device) +{ + ioh_dbg(device, "Function %s invoked successfully", __func__); + + return device->video_settings.current_scan_mode_method; +} + +static s32 +ioh_video_in_set_luminance_level(struct BT656_device *device, + struct ioh_video_in_luminance_settings + luminance_settings) +{ + u32 lumlev; + void __iomem *base_address; + s32 retval = IOH_VIDEOIN_SUCCESS; + + /* Obtaining the base address of the device. */ + base_address = device->base_address; + + spin_lock(&device->dev_lock); + + /* Reading the luminance register value for updation. */ + lumlev = ioread32(base_address + IOH_VIDEO_IN_LUMLEV); + + /* Resetting bit7(NOSIG) */ + RESET_BIT(lumlev, NOSIG); + + /* Checking and setting the NOSIG value. */ + switch (luminance_settings.luminance_nosig) { + case NOSIG_NORMAL_MODE: + /* Resetting bit7(NOSIG) */ + lumlev |= NOSIG_NORMAL_MODE; + break; + + case NOSIG_NOINMASTER_MODE: + /* Setting bit7(NOSIG) */ + lumlev |= NOSIG_NOINMASTER_MODE; + break; + + default: + ioh_err(device, "In %s -> " + "Invalid luminance NOSIG format", __func__); + + retval = IOH_VIDEOIN_FAIL; + break; + } + + if (IOH_VIDEOIN_SUCCESS != retval) + goto out; + + /* Resetting bits0-3(LUMLEV) */ + RESET_BIT_RANGE(lumlev, IOH_BIT_0, IOH_BIT_3); + + /* Checking and setting the luminance level values. */ + switch (luminance_settings.luminance_lumlev) { + case LUMLEV_78_PERCENT: + lumlev |= LUMLEV_78_PERCENT; + break; + + case LUMLEV_81_PERCENT: + lumlev |= LUMLEV_81_PERCENT; + break; + + case LUMLEV_84_PERCENT: + lumlev |= LUMLEV_84_PERCENT; + break; + + case LUMLEV_87_PERCENT: + lumlev |= LUMLEV_87_PERCENT; + break; + + case LUMLEV_90_PERCENT: + lumlev |= LUMLEV_90_PERCENT; + break; + + case LUMLEV_93_PERCENT: + lumlev |= LUMLEV_93_PERCENT; + break; + + case LUMLEV_96_PERCENT: + lumlev |= LUMLEV_96_PERCENT; + break; + + case LUMLEV_100_PERCENT: + lumlev |= LUMLEV_100_PERCENT; + break; + + case LUMLEV_103_PERCENT: + lumlev |= LUMLEV_103_PERCENT; + break; + + case LUMLEV_106_PERCENT: + lumlev |= LUMLEV_106_PERCENT; + break; + + case LUMLEV_109_PERCENT: + lumlev |= LUMLEV_109_PERCENT; + break; + + case LUMLEV_112_PERCENT: + lumlev |= LUMLEV_112_PERCENT; + break; + + case LUMLEV_115_PERCENT: + lumlev |= LUMLEV_115_PERCENT; + break; + + case LUMLEV_118_PERCENT: + lumlev |= LUMLEV_118_PERCENT; + break; + + case LUMLEV_121_PERCENT: + lumlev |= LUMLEV_121_PERCENT; + break; + + case LUMLEV_125_PERCENT: + lumlev |= LUMLEV_125_PERCENT; + break; + + default: + ioh_err(device, "In %s -> " + "Invalid luminance level format", __func__); + + retval = IOH_VIDEOIN_FAIL; + break; + } + + if (IOH_VIDEOIN_SUCCESS != retval) + goto out; + + /* Updating the register values. */ + iowrite32(lumlev, (base_address + IOH_VIDEO_IN_LUMLEV)); + + /* Confirming the register write. */ + if (lumlev == ioread32(base_address + IOH_VIDEO_IN_LUMLEV)) { + ioh_dbg(device, "In %s -> " + "Register write successful", __func__); + + device->video_settings.current_luminance_settings = + luminance_settings; + + } else { + ioh_err(device, "In %s -> " + "Register write unsuccessful", __func__); + + device->video_settings.current_luminance_settings = + invalid_luminance_level; + + retval = IOH_VIDEOIN_FAIL; + } +out: + spin_unlock(&device->dev_lock); + + ioh_dbg(device, "Function %s ended(%d)", __func__, retval); + + return retval; +} + +static struct ioh_video_in_luminance_settings +ioh_video_in_get_luminance_level(struct BT656_device *device) +{ + ioh_dbg(device, "Function %s invoked successfully", __func__); + + return device->video_settings.current_luminance_settings; +} + +static s32 +ioh_video_in_set_rgb_gain(struct BT656_device *device, + struct ioh_video_in_rgb_gain_settings + rgb_gain_settings) +{ + void __iomem *base_address; + s32 retval = IOH_VIDEOIN_SUCCESS; + + spin_lock(&device->dev_lock); + + /* Obtaining the base address of the device. */ + base_address = device->base_address; + + /* Updating the register values. */ + iowrite32((u32) rgb_gain_settings.r_gain, + (base_address + IOH_VIDEO_IN_RGAIN)); + iowrite32((u32) rgb_gain_settings.g_gain, + (base_address + IOH_VIDEO_IN_GGAIN)); + iowrite32((u32) rgb_gain_settings.b_gain, + (base_address + IOH_VIDEO_IN_BGAIN)); + + /* Confirming the write operation. */ + if (((u32) rgb_gain_settings.r_gain == + ioread32(base_address + IOH_VIDEO_IN_RGAIN)) + && ((u32) rgb_gain_settings.g_gain == + ioread32(base_address + IOH_VIDEO_IN_GGAIN)) + && ((u32) rgb_gain_settings.b_gain == + ioread32(base_address + IOH_VIDEO_IN_BGAIN))) { + ioh_dbg(device, "In %s -> " + "Register write successful", __func__); + + device->video_settings. + current_rgb_gain_settings = rgb_gain_settings; + + } else { + ioh_err(device, "In %s -> " + "Register write unsuccessful", __func__); + + rgb_gain_settings.r_gain = + (u8) ioread32(base_address + IOH_VIDEO_IN_RGAIN); + rgb_gain_settings.g_gain = + (u8) ioread32(base_address + IOH_VIDEO_IN_GGAIN); + rgb_gain_settings.b_gain = + (u8) ioread32(base_address + IOH_VIDEO_IN_BGAIN); + + retval = IOH_VIDEOIN_FAIL; + } + + spin_unlock(&device->dev_lock); + + ioh_dbg(device, "Function %s ended(%d)", __func__, retval); + + return retval; +} + +static struct ioh_video_in_rgb_gain_settings +ioh_video_in_get_rgb_gain(struct BT656_device *device) +{ + ioh_dbg(device, "Function %s invoked successfully", __func__); + + return device->video_settings.current_rgb_gain_settings; +} + +static s32 ioh_video_in_cap_start(struct BT656_device *device) +{ + s32 retval = IOH_VIDEOIN_SUCCESS; + + /* Thread exist and is sleeping. */ + if ((false == device->thread_exit_flag) && + (true == device->thread_sleep_flag)) { + + device->thread_sleep_flag = false; + wake_up(&device->thread_sleep_queue); + + ioh_dbg(device, "In %s -> " + "Video capturing started", __func__); + + } else if (true == device->thread_exit_flag) { + ioh_err(device, "In %s -> " + "Thread exited", __func__); + + retval = IOH_VIDEOIN_FAIL; + } + + ioh_dbg(device, "Function %s ended(%d)", __func__, retval); + + return retval; +} + +static void ioh_video_in_cap_stop(struct BT656_device *device) +{ + /* If thread has not exited and thread is not sleeping. */ + if ((false == device->thread_exit_flag) + && (false == device->thread_sleep_flag)) { + + device->thread_sleep_flag = true; + wake_up(&device->thread_sleep_queue); + + ioh_dbg(device, "In %s -> " + "Video capturing stopped", __func__); + } + + ioh_dbg(device, "Function %s ended", __func__); +} + +static s32 +ioh_video_in_set_blank_tim(struct BT656_device *device, + struct ioh_video_in_blank_tim_settings + blank_tim_settings) +{ + u32 blnkim; + void __iomem *base_address; + s32 retval = IOH_VIDEOIN_SUCCESS; + + /* Obtaining the base address of the device. */ + base_address = device->base_address; + + spin_lock(&device->dev_lock); + + /* Obtaining the BLNKTIM register value for updation. */ + blnkim = ioread32(base_address + IOH_VIDEO_IN_BLNKTIM); + + /* Resetting bit7(CNTCTL) */ + RESET_BIT(blnkim, CNTCTL); + + /* Checking and setting the CNTCTL values. */ + switch (blank_tim_settings.blank_tim_cntctl) { + case CNTCTL_STANDARD_SIGNAL: + /* Resetting bit7(CNTCTL) */ + blnkim |= CNTCTL_STANDARD_SIGNAL; + break; + + case CNTCTL_NON_STANDARD_SIGNAL: + /* Setting bit7(CNTCTL) */ + blnkim |= CNTCTL_NON_STANDARD_SIGNAL; + break; + + default: + ioh_err(device, "In %s -> " + "Invalid CNTCTL format", __func__); + + retval = IOH_VIDEOIN_FAIL; + break; + } + + if (IOH_VIDEOIN_SUCCESS == retval) { + + /* Resetting bit0-3(BLKADJ) */ + RESET_BIT_RANGE(blnkim, IOH_BIT_0, IOH_BIT_3); + + /* Checking and setting the BLKADJ values. */ + switch (blank_tim_settings.blank_tim_blkadj) { + case BLKADJ_MINUS_8_PIXEL: + blnkim |= BLKADJ_MINUS_8_PIXEL; + break; + + case BLKADJ_MINUS_7_PIXEL: + blnkim |= BLKADJ_MINUS_7_PIXEL; + break; + + case BLKADJ_MINUS_6_PIXEL: + blnkim |= BLKADJ_MINUS_6_PIXEL; + break; + + case BLKADJ_MINUS_5_PIXEL: + blnkim |= BLKADJ_MINUS_5_PIXEL; + break; + + case BLKADJ_MINUS_4_PIXEL: + blnkim |= BLKADJ_MINUS_4_PIXEL; + break; + + case BLKADJ_MINUS_3_PIXEL: + blnkim |= BLKADJ_MINUS_3_PIXEL; + break; + + case BLKADJ_MINUS_2_PIXEL: + blnkim |= BLKADJ_MINUS_2_PIXEL; + break; + + case BLKADJ_MINUS_1_PIXEL: + blnkim |= BLKADJ_MINUS_1_PIXEL; + break; + + case BLKADJ_0_PIXEL: + blnkim |= BLKADJ_0_PIXEL; + break; + + case BLKADJ_PLUS_1_PIXEL: + blnkim |= BLKADJ_PLUS_1_PIXEL; + break; + + case BLKADJ_PLUS_2_PIXEL: + blnkim |= BLKADJ_PLUS_2_PIXEL; + break; + + case BLKADJ_PLUS_3_PIXEL: + blnkim |= BLKADJ_PLUS_3_PIXEL; + break; + + case BLKADJ_PLUS_4_PIXEL: + blnkim |= BLKADJ_PLUS_4_PIXEL; + break; + + case BLKADJ_PLUS_5_PIXEL: + blnkim |= BLKADJ_PLUS_5_PIXEL; + break; + + case BLKADJ_PLUS_6_PIXEL: + blnkim |= BLKADJ_PLUS_6_PIXEL; + break; + + case BLKADJ_PLUS_7_PIXEL: + blnkim |= BLKADJ_PLUS_7_PIXEL; + break; + + default: + ioh_err(device, "In %s -> " + "Invalid BLKADJ format", __func__); + + retval = IOH_VIDEOIN_FAIL; + break; + } + } + + if (IOH_VIDEOIN_SUCCESS == retval) { + + /* Updating the register values. */ + iowrite32(blnkim, (base_address + IOH_VIDEO_IN_BLNKTIM)); + + /* confirming the register updation. */ + if ((blnkim == + ioread32(base_address + IOH_VIDEO_IN_BLNKTIM))) { + ioh_dbg(device, "In %s -> " + "Register write successful", __func__); + + device->video_settings.current_blank_tim_settings + = blank_tim_settings; + + } else { + ioh_err(device, "In %s -> " + "Register write unsuccessful", __func__); + + device->video_settings. + current_blank_tim_settings = invalid_blank_tim; + + retval = IOH_VIDEOIN_FAIL; + } + } + + spin_unlock(&device->dev_lock); + + ioh_dbg(device, "Function %s ended(%d)", __func__, retval); + + return retval; +} + +static struct ioh_video_in_blank_tim_settings +ioh_video_in_get_blank_tim(struct BT656_device *device) +{ + ioh_dbg(device, "Function %s invoked successfully", __func__); + + return device->video_settings.current_blank_tim_settings; +} + +static s32 +ioh_video_in_set_bb(struct BT656_device *device, + enum ioh_video_in_bb_mode output_bb_mode) +{ + u32 voctrl2; + void __iomem *base_address; + s32 retval = IOH_VIDEOIN_SUCCESS; + + /* Obtaining the base address of the device. */ + base_address = device->base_address; + + spin_lock(&device->dev_lock); + + /* Reading the VOCTRL2 register value for updation. */ + voctrl2 = ioread32(base_address + IOH_VIDEO_IN_VOCTRL2); + + /* Resetting bit6(BBON) */ + RESET_BIT(voctrl2, BBON); + + /* Checking and setting the BBOB bit value. */ + switch (output_bb_mode) { + case BB_OUTPUT_OFF: + /* Resetting bit6(BBON) */ + voctrl2 |= BB_OUTPUT_OFF; + break; + + case BB_OUTPUT_ON: + /* Setting bit6(BBON) */ + voctrl2 |= BB_OUTPUT_ON; + break; + + default: + ioh_err(device, "In %s -> " + "Invalid Blue background mode", __func__); + + retval = IOH_VIDEOIN_FAIL; + break; + } + + if (IOH_VIDEOIN_SUCCESS != retval) + goto out; + + /* Updationg the VOCTRL2 register value. */ + iowrite32(voctrl2, (base_address + IOH_VIDEO_IN_VOCTRL2)); + + /* Confirming the register updation. */ + if (voctrl2 == ioread32(base_address + IOH_VIDEO_IN_VOCTRL2)) { + ioh_dbg(device, "In %s -> " + "Register write successful", __func__); + + device->video_settings.current_bb_mode = output_bb_mode; + } else { + ioh_err(device, "In %s -> " + "Register write unsuccessful", __func__); + + device->video_settings.current_bb_mode = INVALID_BB_MODE; + + retval = IOH_VIDEOIN_FAIL; + } +out: + spin_unlock(&device->dev_lock); + + ioh_dbg(device, "Function %s ended(%d)", __func__, retval); + + return retval; +} + +static enum ioh_video_in_bb_mode +ioh_video_in_get_bb(struct BT656_device *device) +{ + ioh_dbg(device, "Function %s invoked successfully", __func__); + + return device->video_settings.current_bb_mode; +} + +static s32 +ioh_video_in_set_cb(struct BT656_device *device, + struct ioh_video_in_cb_settings cb_settings) +{ + u32 voctrl2; + void __iomem *base_address; + s32 retval = IOH_VIDEOIN_SUCCESS; + + /* Obtaining the base address of the device. */ + base_address = device->base_address; + + spin_lock(&device->dev_lock); + + /* Reading the VOCTRL2 register value for updation. */ + voctrl2 = ioread32(base_address + IOH_VIDEO_IN_VOCTRL2); + + /* Resetting bit7(CBON) */ + RESET_BIT(voctrl2, CBON); + + /* checking and setting the CB_Mode bits. */ + switch (cb_settings.cb_mode) { + case CB_OUTPUT_OFF: + /* Resetting bit7(CBON) */ + voctrl2 |= CB_OUTPUT_OFF; + cb_settings.cb_outlev = + device->video_settings.current_cb_settings.cb_outlev; + break; + + case CB_OUTPUT_ON: + /* Setting bit7(CBON) */ + voctrl2 |= CB_OUTPUT_ON; + break; + + default: + ioh_err(device, "In %s -> " + "Invalid Color Bar Mode", __func__); + + retval = IOH_VIDEOIN_FAIL; + break; + } + + if ((IOH_VIDEOIN_SUCCESS == retval) + && (CB_OUTPUT_ON == cb_settings.cb_mode)) { + /* Resetting bit0-1(OUTLEV) */ + RESET_BIT_RANGE(voctrl2, IOH_BIT_0, IOH_BIT_1); + + /* Checking and setting the CB outlev values. */ + switch (cb_settings.cb_outlev) { + case CB_OUTLEV_25_PERCENT: + voctrl2 |= CB_OUTLEV_25_PERCENT; + break; + + case CB_OUTLEV_50_PERCENT: + voctrl2 |= CB_OUTLEV_50_PERCENT; + break; + + case CB_OUTLEV_75_PERCENT: + voctrl2 |= CB_OUTLEV_75_PERCENT; + break; + + case CB_OUTLEV_100_PERCENT: + voctrl2 |= CB_OUTLEV_100_PERCENT; + break; + + default: + ioh_err(device, "In %s -> " + "Invalid Color Bar output level", __func__); + + retval = IOH_VIDEOIN_FAIL; + break; + } + } + + if (IOH_VIDEOIN_SUCCESS == retval) { + /* Updating the register values. */ + iowrite32(voctrl2, (base_address + IOH_VIDEO_IN_VOCTRL2)); + + if ((voctrl2 == ioread32( + (base_address + IOH_VIDEO_IN_VOCTRL2)))) { + ioh_dbg(device, "In %s -> " + "Register write successful", __func__); + + device->video_settings. + current_cb_settings = cb_settings; + } else { + ioh_err(device, "In %s -> " + "Register write unsuccessful", __func__); + + device->video_settings. + current_cb_settings = invalid_cb_settings; + + retval = IOH_VIDEOIN_FAIL; + } + } + + spin_unlock(&device->dev_lock); + + ioh_dbg(device, "Function %s ended(%d)", __func__, retval); + + return retval; +} + +static struct +ioh_video_in_cb_settings ioh_video_in_get_cb(struct BT656_device *device) +{ + ioh_dbg(device, "Function %s invoked successfully", __func__); + + return device->video_settings.current_cb_settings; +} + +static u32 ioh_video_in_get_buffer_size(struct BT656_device *device) +{ + u32 retval; + u32 X_comp; + u32 Y_comp; + u32 X_size; + + X_comp = device->video_settings.current_frame_size.X_component; + Y_comp = device->video_settings.current_frame_size.Y_component; + X_size = device->video_settings.current_frame_size.pitch_size; + + retval = X_size * Y_comp; + + ioh_dbg(device, "Function %s invoked successfully(%u)", + __func__, retval); + + return retval; +} + +static u32 ioh_video_in_get_image_size(struct BT656_device *device) +{ + u32 bytes_per_pixel; + u32 X_comp; + u32 Y_comp; + u32 retval; + + X_comp = device->video_settings.current_frame_size.X_component; + Y_comp = device->video_settings.current_frame_size.Y_component; + + switch (device->video_settings.current_output_format.format) { + case YCBCR_422_8BIT: + bytes_per_pixel = 2; + break; + + case YCBCR_422_10BIT: + bytes_per_pixel = 4; + break; + + case YCBCR_444_8BIT: + bytes_per_pixel = 4; + break; + + case YCBCR_444_10BIT: + bytes_per_pixel = 4; + break; + + case RGB888: + bytes_per_pixel = 4; + break; + + case RGB666: + bytes_per_pixel = 4; + break; + + case RGB565: + bytes_per_pixel = 4; + break; + + case RAW_8BIT: + bytes_per_pixel = 2; + break; + + case RAW_10BIT: + bytes_per_pixel = 2; + break; + + case RAW_12BIT: + bytes_per_pixel = 2; + break; + + default: + bytes_per_pixel = 0; + break; + } + + retval = (X_comp * Y_comp * bytes_per_pixel); + + ioh_dbg(device, "Function %s ended(%d)", __func__, retval); + + return retval; +} + +#ifdef IOH_VIDEO_IN_DMA_CONTIG +#else /* IOH_VIDEO_IN_DMA_CONTIG */ +static u32 ioh_video_in_alloc_frame_buffer(struct BT656_device *device, + struct ioh_video_in_frame_buffer_info info) +{ + s32 retval = IOH_VIDEOIN_SUCCESS; + int i; + + if (info.buffer_num > MAXIMUM_FRAME_BUFFERS) { + retval = IOH_VIDEOIN_FAIL; + } else if (info.order <= 0) { + retval = IOH_VIDEOIN_FAIL; + } else { + device->frame_buffer_info.buffer_num = info.buffer_num; + + device->frame_buffer_info.order = info.order; + + for (i = 0; i < info.buffer_num; i++) { + device->bufs.frame_buffer[i].virt_addr = + (u32)dma_alloc_coherent(&device->p_device->dev, + (PAGE_SIZE << + device->frame_buffer_info.order), + (dma_addr_t *)&(device-> + bufs.frame_buffer[i].phy_addr), + GFP_ATOMIC); + + if (0 == device->bufs.frame_buffer[i].virt_addr) { + retval = IOH_VIDEOIN_FAIL; + ioh_err(device, "In %s -> " + "Function dma_alloc_coherent " + "failed for Video Frame Buffer %d", + __func__, i); + break; + } + + device->bufs.frame_buffer[i].index = i; + + device->bufs.frame_buffer[i].data_size = 0; + } + } + + ioh_dbg(device, "Function %s ended(%d)", __func__, retval); + + return retval; +} + +static u32 ioh_video_in_free_frame_buffer(struct BT656_device *device, + struct ioh_video_in_frame_buffer_info info) +{ + s32 retval = IOH_VIDEOIN_SUCCESS; + int i; + + if (info.buffer_num != device->frame_buffer_info.buffer_num) { + retval = IOH_VIDEOIN_FAIL; + } else if (info.order != device->frame_buffer_info.order) { + retval = IOH_VIDEOIN_FAIL; + } else { + for (i = 0; i < info.buffer_num; i++) { + if (device->bufs.frame_buffer[i].virt_addr) { + dma_free_coherent(&device->p_device->dev, + (PAGE_SIZE << + device-> + frame_buffer_info.order), + (void *)device-> + bufs.frame_buffer[i].virt_addr, + (dma_addr_t)device-> + bufs.frame_buffer[i].phy_addr + ); + device->bufs.frame_buffer[i].index = 0; + + device->bufs.frame_buffer[i].virt_addr = 0; + + device->bufs.frame_buffer[i].phy_addr = 0; + + device->bufs.frame_buffer[i].data_size = 0; + } + } + } + + ioh_dbg(device, "Function %s ended(%d)", __func__, retval); + + return retval; +} + +static struct ioh_video_in_frame_buffers +ioh_video_in_get_frame_buffers(struct BT656_device *device) +{ + ioh_dbg(device, "Function %s invoked successfully", __func__); + + return device->bufs; +} + +static ssize_t ioh_video_in_read_sub(struct BT656_device *device, + char __user *p_buffer, + struct ioh_video_in_frame_buffer *r_frame, + size_t size, + unsigned int f_flag) +{ + struct BT656_device *dev; + int repeat_flag; + ssize_t retval = -EAGAIN; + /* Reference to the current frame.*/ + struct ioh_video_in_frame_buffer *frame; + dev = ((struct BT656_device *)device); + + do { + repeat_flag = 0; + + /* Attaining the read lock + and obtaining the size of data that can be read. */ + ioh_video_in_lock_buffer(&(dev->read_buffer_lock)); + + if (NULL != dev->read_buffer) { + ioh_dbg(device, "In %s -> " + "Read Buffer Not Empty", __func__); + + /* Obtaing the current latest frame. */ + frame = dev->read_buffer; + + /* Updating the read buffer. */ + dev->read_buffer = NULL; + + if (frame->data_size < size) + size = frame->data_size; + + if (p_buffer != NULL) { + /* If copying of video data + to the user space failed. */ + if (0 != copy_to_user((void *)p_buffer, + (void *)frame->virt_addr, size)) { + ioh_err(device, + "In %s -> " + "Function copy_to_user " + "failed", __func__); + retval = -EFAULT; + } else { /* Copying successful. */ + ioh_dbg(device, "In %s -> " + "Function copy_to_user " + "invoked successfully", + __func__); + retval = size; + } + } else { + /* read buffer info to r_frame */ + *r_frame = *frame; + retval = size; + } + frame->data_size = 0; + ioh_video_in_unlock_buffer(&(dev->read_buffer_lock)); + } else { + ioh_video_in_unlock_buffer(&(dev->read_buffer_lock)); + + ioh_dbg(device, "In %s -> " + "The video buffer is empty", __func__); + + /* If device has been opened + without non-block mode. */ + if (!(f_flag)) { + /* If video capturing thread + is not sleeping. */ + if (false == dev->thread_sleep_flag) { + + /* Preparing to enter + the wait state for capturing + data. */ + dev->read_wait_flag = true; + + ioh_dbg(device, "In %s -> " + "Read wait flag set to true", + __func__); + + ioh_dbg(device, "In %s -> " + "Entering the wait state", + __func__); + + retval = wait_event_interruptible( + dev->read_wait_queue, + (false == dev->read_wait_flag)); + + /* If wait failed. */ + if (-ERESTARTSYS == retval) { + ioh_err(device, + "In %s -> " + "Read wait failed", + __func__); + retval = -EIO; + } else { + ioh_dbg(device, "In %s -> " + "Exiting from " + "the wait state", + __func__); + + /* Attaining the loack + over the raed + buffer. */ + ioh_video_in_lock_buffer + (&(dev->read_buffer_lock)); + + /* Checking whether read data + has been updated + after wait state. */ + if (NULL != dev->read_buffer) { + ioh_dbg(device, "In %s" + " -> Data capturing " + "successfully after " + "wait", __func__); + + /* Setting flag for + repeating. */ + repeat_flag = 1; + } else { + /* No video data is available + for reading. */ + + ioh_err(device, "In " + "%s -> " + "Video data cannot " + "be captured even " + "after wait", + __func__); + } + + /* Releasing the lock over the + read buffer. */ + ioh_video_in_unlock_buffer(& + (dev->read_buffer_lock)); + } + } else { + /* Video capturing thread is sleeping. */ + + ioh_err(device, "In %s -> " + "Video capturing not " + "initiated", __func__); + + retval = -EIO; + } + } + } + } while (repeat_flag); + + ioh_dbg(device, "Function %s ended(%d)", __func__, retval); + + return retval; +} + +static struct ioh_video_in_frame_buffer +ioh_video_in_read_frame_buffer(struct BT656_device *device) +{ + struct ioh_video_in_frame_buffer r_frame; + ssize_t retval; + + r_frame.index = -1; + r_frame.virt_addr = (unsigned int)NULL; + r_frame.phy_addr = (unsigned int)NULL; + r_frame.data_size = 0; + + retval = ioh_video_in_read_sub(device, + NULL, + &r_frame, + 0, + 0); /* 1 means non blocking */ + + ioh_dbg(device, "Function %s invoked successfully", __func__); + + return r_frame; +} +#endif /* IOH_VIDEO_IN_DMA_CONTIG */ + +static irqreturn_t ioh_video_in_interrupt(int irq, void *dev_id) +{ + u32 insts; + u32 emask; + void __iomem *base_address; + irqreturn_t retval = IRQ_NONE; + struct ioh_vin_dev *dev = dev_id; + struct BT656_device *device = dev->video_in_dev; + + /* Obtaining the base address. */ + base_address = device->base_address; + + /* Reading the interrupt register. */ + insts = ((ioread32(base_address + IOH_VIDEO_IN_INTSTS)) & INTBITS); + emask = ((ioread32(base_address + IOH_VIDEO_IN_INTENB)) & INTBITS); + + if ((emask & insts) == 0) { + ioh_dbg(device, "In %s -> No interrupt source " + "insts = 0x%08x emask = 0x%08x", + __func__, insts, emask); + goto out; + } + + ioh_dbg(device, "In %s -> insts = 0x%08x, emask = 0x%08x", + __func__, insts, emask); + + if (LIKELY(((emask & insts) & VSINTSTS) != 0)) { + + ioh_dbg(device, "In %s -> VSYNC interrupt handled", __func__); + + if (device->vsync_waiting_flag == true) { + device->vsync_waiting_flag = false; + wake_up_interruptible(&(device->vsync_wait_queue)); + + ioh_dbg(device, "In %s -> wake up by vsync", __func__); + } + retval = IRQ_HANDLED; + } + if (((emask & insts) & HSINTSTS) != 0) { + + ioh_dbg(device, "In %s -> HSYNC interrupt handled", __func__); + + retval = IRQ_HANDLED; + } + if (((emask & insts) & OFINTSTS) != 0) { + + ioh_dbg(device, "In %s -> " + "Buffer Overflow interrupt handled", __func__); + + if (device->overflow_flag == false) { + + ioh_dbg(device, "[OVERFLOW]In %s -> " + "overflow_flag is true", __func__); + + device->overflow_flag = true; + } + retval = IRQ_HANDLED; + } + +out: + /* Clearing of interrupts if any interrupt exists */ + iowrite32(insts, (base_address + IOH_VIDEO_IN_INTSTS)); + + ioh_dbg(device, "In %s -> " + "Interrupt handled and cleared.", __func__); + + return retval; +} + +static void ioh_video_in_dma_complete(void *arg) +{ + struct BT656_device *device = arg; + + async_tx_ack(device->desc_dma); + + ioh_dbg(device, "In %s -> " + "dmarenb disable and wake up the thread_sleep_queue", + __func__); + + device->dma_flag = 1; + wake_up(&device->thread_sleep_queue); + + ioh_dbg(device, "Function %s ended", __func__); +} + +static u32 ioh_video_in_get_additional_line_size(struct BT656_device *device) +{ + enum ioh_video_in_input_data_format format; + u32 addition = 0; + + format = (device)-> + video_settings.current_input_format.format; + + if ((format != NT_RAW_8BIT) + && (format != NT_RAW_10BIT) + && (format != NT_RAW_12BIT)) + addition = 3; + + ioh_dbg(device, "Function %s ended(%d)", __func__, addition); + + return addition; +} + +#ifdef IOH_VIDEO_IN_DMA_CONTIG +static s32 ioh_video_in_make_dma_descriptors(struct BT656_device *device, + u32 idx) +#else /* IOH_VIDEO_IN_DMA_CONTIG */ +static s32 ioh_video_in_make_dma_descriptors(struct BT656_device *device) +#endif /* IOH_VIDEO_IN_DMA_CONTIG */ +{ + u32 i, j; /* DMA buffer and descriptor index variable. */ + + struct BT656_device *priv = device; + struct scatterlist *sg; + dma_addr_t dma; + void *buf; + + u32 Y_comp = 0; /* The Y-component. */ + u32 addition = 0; + u32 bytes_per_line_image = 0; /* The bytes per line of image. */ + +#ifdef IOH_VIDEO_IN_DMA_CONTIG +#else /* IOH_VIDEO_IN_DMA_CONTIG */ + u32 idx; + int nent; +#endif /* IOH_VIDEO_IN_DMA_CONTIG */ + + Y_comp = device->video_settings.current_frame_size.Y_component; + bytes_per_line_image = ioh_video_in_get_image_size(device); + bytes_per_line_image = bytes_per_line_image / Y_comp; + addition = ioh_video_in_get_additional_line_size(device); + + ioh_dbg(device, "In %s -> X = %d bytes, Y = %d+%d line", + __func__, bytes_per_line_image, Y_comp, addition); + + spin_lock(&device->dev_lock); + + i = 0; + + priv->nent = (Y_comp + addition); + +#ifdef IOH_VIDEO_IN_DMA_CONTIG + sg = priv->sg_dma + (idx * (Y_comp + addition)); +#else /* IOH_VIDEO_IN_DMA_CONTIG */ + sg = priv->sg_dma; + for (idx = 0; idx < device->frame_buffer_info.buffer_num; idx++) { +#endif /* IOH_VIDEO_IN_DMA_CONTIG */ + + /* Making one descriptor set */ + priv->sg_dma_list[idx] = sg; + + sg_init_table(sg, (Y_comp + addition)); + + for (j = 0; j < Y_comp; j++, sg++) { + dma = device->dma_buffer_phy[idx] + + (j * bytes_per_line_image); +#ifdef IOH_VIDEO_IN_DMA_CONTIG + buf = phys_to_virt(dma); +#else /* IOH_VIDEO_IN_DMA_CONTIG */ + buf = device->dma_buffer_virt[idx] + + (j * bytes_per_line_image); +#endif /* IOH_VIDEO_IN_DMA_CONTIG */ + + sg_set_page(sg, + virt_to_page(buf), + bytes_per_line_image / 4, + (unsigned long)buf & ~PAGE_MASK); + sg_dma_address(sg) = dma; + sg_dma_len(sg) = bytes_per_line_image / 4; + } + + /* Last 3 descriptor are empty transfer if not RAW */ + for (j = 0; j < addition; j++, sg++) { + dma = device->dma_buffer_phy[idx]; +#ifdef IOH_VIDEO_IN_DMA_CONTIG + buf = phys_to_virt(dma); +#else /* IOH_VIDEO_IN_DMA_CONTIG */ + buf = device->dma_buffer_virt[idx]; +#endif /* IOH_VIDEO_IN_DMA_CONTIG */ + + sg_set_page(sg, + virt_to_page(buf), + 0, + (unsigned long)buf & ~PAGE_MASK); + sg_dma_address(sg) = dma; + sg_dma_len(sg) = 0; + } +#ifdef IOH_VIDEO_IN_DMA_CONTIG +/* TODO ? */ +#else /* IOH_VIDEO_IN_DMA_CONTIG */ + nent = dma_map_sg(&priv->p_device->dev, + priv->sg_dma_list[idx], + (Y_comp + addition), + DMA_FROM_DEVICE); + ioh_dbg(device, "In %s -> nent %d", __func__, nent); + } +#endif /* IOH_VIDEO_IN_DMA_CONTIG */ + + spin_unlock(&device->dev_lock); + + ioh_dbg(device, "Function %s ended", __func__); + + return IOH_VIDEOIN_SUCCESS; +} + +static s32 ioh_video_in_dma_submit(struct BT656_device *device, + u32 index) +{ + struct BT656_device *priv = device; + u32 Y_comp = 0; + u32 addition = 0; + struct dma_async_tx_descriptor *desc; + + Y_comp = device->video_settings.current_frame_size.Y_component; + + addition = ioh_video_in_get_additional_line_size(device); + + desc = priv->chan_dma->device->device_prep_slave_sg(priv->chan_dma, + priv->sg_dma_list[index], + ((Y_comp + addition)), + DMA_FROM_DEVICE, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + + if (!desc) { + ioh_err(device, "In %s -> " + "device_prep_slave_sg failed", __func__); + goto out; + } + + priv->desc_dma = desc; + + dma_sync_sg_for_device(&priv->p_device->dev, + priv->sg_dma_list[index], + priv->nent, + DMA_FROM_DEVICE); + + desc->callback = ioh_video_in_dma_complete; + desc->callback_param = priv; + desc->tx_submit(desc); + +/* TODO comment out for unexpexted callback */ +#if 0 + dma_async_issue_pending(priv->chan_dma); +#endif + + spin_lock(&(device->dev_lock)); + device->intenb.dmarenb = ENABLE; + device->intenb.ofintenb = ENABLE; + device->intenb.vsintenb = ENABLE; + write_intenb(device); + spin_unlock(&(device->dev_lock)); + + ioh_dbg(device, "Function %s (index=%d) ended", __func__, index); + + return IOH_VIDEOIN_SUCCESS; +out: + return IOH_VIDEOIN_FAIL; +} + +#ifdef IOH_VIDEO_IN_DMA_CONTIG +static s32 ioh_video_in_schedule_next(struct ioh_vin_dev *dev, + struct ioh_vin_buffer *buf) +{ + struct BT656_device *device = dev->video_in_dev; + static int idx; + + device->dma_buffer_phy[idx] = videobuf_to_dma_contig(&buf->vb); + + ioh_video_in_make_dma_descriptors(device, idx); + + buf->frame_index = idx; + + ioh_dbg(device, "%s -> addr[%d] = 0x%08x", + __func__, idx, (u32)device->dma_buffer_phy[idx]); + + idx++; + if (idx >= N_FRAME_BUF) + idx = 0; + + ioh_dbg(device, "Function %s ended", __func__); + + return IOH_VIDEOIN_SUCCESS; +} +#else /* IOH_VIDEO_IN_DMA_CONTIG */ +#endif /* IOH_VIDEO_IN_DMA_CONTIG */ + +static s32 ioh_video_in_alloc_dma_desc(struct BT656_device *device) +{ + struct BT656_device *priv = device; + int num = DESC_SIZE; + + priv->sg_dma = kzalloc(sizeof(struct scatterlist)*num, GFP_ATOMIC); + if (priv->sg_dma == NULL) { + ioh_err(device, "In %s -> kzalloc failed", __func__); + goto out; + } + ioh_dbg(device, "Function %s ended", __func__); + + return IOH_VIDEOIN_SUCCESS; +out: + return IOH_VIDEOIN_FAIL; +} + +static s32 ioh_video_in_free_dma_desc(struct BT656_device *device) +{ + struct BT656_device *priv = device; +#ifdef IOH_VIDEO_IN_DMA_CONTIG +/* TODO ? */ +#else /* IOH_VIDEO_IN_DMA_CONTIG */ + int idx; + + for (idx = 0; idx < device->frame_buffer_info.buffer_num; idx++) { + dma_unmap_sg(&priv->p_device->dev, + priv->sg_dma_list[idx], + priv->nent, + DMA_FROM_DEVICE); + } +#endif /* IOH_VIDEO_IN_DMA_CONTIG */ + + kfree(priv->sg_dma); + + ioh_dbg(device, "Function %s ended", __func__); + + return 0; +} + +static s32 ioh_video_in_start_setting(struct ioh_vin_dev *dev) +{ +#ifdef IOH_VIDEO_IN_DMA_CONTIG +#else /* IOH_VIDEO_IN_DMA_CONTIG */ + struct BT656_device *device = dev->video_in_dev; + unsigned int i; + u32 frame_index = 0; /* The frame buffer index. */ + int ret_val; + + mutex_lock(&dev->mutex); + + for (i = 0; i < device->frame_buffer_info.buffer_num; i++) { + device->dma_buffer_virt[i] = (void *)device-> + bufs.frame_buffer[i].virt_addr; + device->dma_buffer_phy[i] = device-> + bufs.frame_buffer[i].phy_addr; + } + + ioh_video_in_make_dma_descriptors(device); + + ret_val = ioh_video_in_dma_submit(device, frame_index); + + ioh_dbg(device, "In %s -> ioh_video_in_dma_submit returned(%d)", + __func__, ret_val); + + mutex_unlock(&dev->mutex); + + ioh_dbg(device, "Function %s ended", __func__); + +#endif /* IOH_VIDEO_IN_DMA_CONTIG */ + + return 0; +} + +static s32 ioh_video_in_thread_fn(void *data) +{ + struct ioh_vin_dev *dev = data; + struct BT656_device *device = dev->video_in_dev; + /* Accessing the device buffer and flag variables. */ + /* DMA descriptor start/end physical address */ + u32 thread_1st_flag = false; /* thread 1st flag*/ +#ifdef IOH_VIDEO_IN_DMA_CONTIG +#else /* IOH_VIDEO_IN_DMA_CONTIG */ + u32 frame_index = 0; /* The frame buffer index. */ + u32 frame_count = 0; /* temporary frame count value for debug */ + u32 frame_index_next; +#endif /* IOH_VIDEO_IN_DMA_CONTIG */ + u32 frame_skip_count = 0; + + /* Reference to thread sleep flag. */ + u8 *thread_sleep_flag = &device->thread_sleep_flag; + + /* Reference to thread exit flag. */ + u8 *thread_exit_flag = &device->thread_exit_flag; + + /* Reference to suspend flag. */ + u8 *suspend_flag = &device->suspend_flag; + + /* Reference to dma flag. */ + u8 *dma_flag = &device->dma_flag; + + /* Reference to overflow flag. */ + u8 *overflow_flag = &device->overflow_flag; + + /* The DMA enable re-try counter. */ + + int ret_val; + + /* Obtaining DMA enabled space for descriptors around 8KB. */ + mutex_lock(&dev->mutex); + if (ioh_video_in_alloc_dma_desc(device) != IOH_VIDEOIN_SUCCESS) + goto out; + + ioh_dbg(device, "In %s -> Function " + "ioh_video_in_alloc_dma_desc invoked successfully", __func__); + + mutex_unlock(&dev->mutex); + + while (false == *thread_exit_flag) { + ioh_dbg(device, "In %s -> " + "Thread preparing to sleep if capturing is " + "stopped or suspension is enabled", __func__); + + /* Thread trying to sleep + if conditions are not suitable. */ + wait_event(device->thread_sleep_queue, + (((false == *thread_sleep_flag) + && (false == *suspend_flag)) + || (true == *thread_exit_flag))); + + if (true == *thread_exit_flag) { + ioh_dbg(device, "In %s -> thread_exit_flag was " + "true and break the thread", __func__); + break; + } + + /* Thread trying to capture the video data + by enabling the DMA transfer. */ + *dma_flag = 0; + + if (UNLIKELY(thread_1st_flag == false)) { + ioh_video_in_start_setting(dev); + thread_1st_flag = true; + } + + ioh_dbg(device, "In %s -> " + "thread preparing for capturing", __func__); + + wait_event(device->thread_sleep_queue, + ((true == (*thread_sleep_flag)) + /* Thread should not sleep. */ + || (true == (*thread_exit_flag)) + /* Thread should not exit. */ + || (true == (*suspend_flag)) + /* System should not suspend. */ + || (1 == (*dma_flag)) + /* DMA transaction should complete. */ + )); + + ioh_dbg(device, "In %s -> wait_event end " + "thread_sleep_flag=%d, thread_exit_flag=%d, " + "suspend_flag=%d, dma_flag=%d", + __func__, + *thread_sleep_flag, *thread_exit_flag, + *suspend_flag, *dma_flag); + + if (UNLIKELY(1 != *dma_flag)) { + ioh_dbg(device, "In %s -> dma_flag was not 1 " + "and break the threead", __func__); + break; + } + + spin_lock(&dev->video_in_dev->dev_lock); + dev->video_in_dev->intenb.dmarenb = DISABLE; + write_intenb(dev->video_in_dev); + spin_unlock(&dev->video_in_dev->dev_lock); + + /* if overflow then wait for next vsync */ + /* else dma is started. if vsync is already comes, start at + next frame */ + if (*overflow_flag == true) { + ioh_dbg(device, "[OVERFLOW]In %s -> wait vsync", + __func__); + + ioh_video_in_wait_vsync(device); + ioh_video_in_wait_vsync(device); + } + +#ifdef IOH_VIDEO_IN_DMA_CONTIG +#else /* IOH_VIDEO_IN_DMA_CONTIG */ + /* next index value */ + if (frame_index == (device->frame_buffer_info.buffer_num - 1)) + frame_index_next = 0; + else + frame_index_next = frame_index + 1; + + ret_val = ioh_video_in_dma_submit(device, frame_index_next); + + ioh_dbg(device, "In %s -> ioh_video_in_dma_submit returned(%d)", + __func__, ret_val); + + dma_sync_sg_for_cpu(&device->p_device->dev, + device->sg_dma_list[frame_index], + device->nent, + DMA_FROM_DEVICE); +#endif /* IOH_VIDEO_IN_DMA_CONTIG */ + + if (frame_skip_count == device->frame_skip_num) { + frame_skip_count = 0; +#ifdef IOH_VIDEO_IN_DMA_CONTIG +#else /* IOH_VIDEO_IN_DMA_CONTIG */ + /* Updating the size of the video farme captured */ + device->bufs.frame_buffer[frame_index].data_size + = ioh_video_in_get_buffer_size(device); + + /* Attaining the read buffer lock + for updating the read buffer. */ + ioh_video_in_lock_buffer(& + (device->read_buffer_lock)); + + /* Updating the read buffer. */ + device->read_buffer = &(device-> + bufs.frame_buffer[frame_index]); + + ioh_dbg(device, "In %s -> " + "Captured index is %d(p:0x%08x v:0x%08x)", + __func__, + frame_index, + (u32)device->read_buffer->phy_addr, + (u32)device->read_buffer->virt_addr); + + /* Releasing the read buffer lock. */ + ioh_video_in_unlock_buffer(& + (device->read_buffer_lock)); + + /* Wake up the read system call + if it is waiting for capture data. */ + if ((true == device->read_wait_flag) + && (*overflow_flag == false)) { + device->read_wait_flag = false; + wake_up_interruptible(&device-> + read_wait_queue); + frame_count++; + ioh_dbg(device, "In %s -> " + "frame_count = %d", __func__, frame_count); + } +#endif /* IOH_VIDEO_IN_DMA_CONTIG */ + + } else { + frame_skip_count++; + ioh_dbg(device, "In %s -> " + "Video Data skipped", __func__); + } + + ioh_videoin_thread_tick(dev); + +#ifdef IOH_VIDEO_IN_DMA_CONTIG +#else /* IOH_VIDEO_IN_DMA_CONTIG */ + frame_index = frame_index_next; +#endif /* IOH_VIDEO_IN_DMA_CONTIG */ + + *overflow_flag = false; + + ioh_dbg(device, "In %s -> " + "Video Data captured and " + "updated to the video read buffer", __func__); + + } /* while */ + + /* Disable the DMA mode. */ + spin_lock(&(device->dev_lock)); + device->intenb.dmarenb = DISABLE; + device->intenb.ofintenb = DISABLE; + write_intenb(device); + spin_unlock(&(device->dev_lock)); + + ioh_video_in_free_dma_desc(device); + + ioh_dbg(device, "In %s -> Function " + "ioh_video_in_free_dma_desc invoked", __func__); + +#ifdef IOH_VIDEO_IN_DMA_CONTIG +#else /* IOH_VIDEO_IN_DMA_CONTIG */ + /* Wake up the read system call if it is waiting for capture data. */ + if (true == device->read_wait_flag) { + device->read_wait_flag = false; + wake_up_interruptible(&device->read_wait_queue); + } +#endif /* IOH_VIDEO_IN_DMA_CONTIG */ + +out: + *thread_exit_flag = true; + *thread_sleep_flag = true; + + mutex_unlock(&dev->mutex); + + ret_val = IOH_VIDEOIN_SUCCESS; + ioh_dbg(device, "Function %s ended", __func__); + + return ret_val; +} + +static s32 ioh_video_in_init(struct BT656_device *device) +{ + s32 retval; + + /* Setting the default input format. */ + retval = ioh_video_in_set_input_format(device, + default_input_data_format); + if (IOH_VIDEOIN_SUCCESS != retval) { + ioh_err(device, "In %s -> Function " + "ioh_video_in_set_input_format failed", __func__); + goto out; + } + + /* Setting the default output format. */ + retval = ioh_video_in_set_output_format(device, + default_output_data_format); + if (IOH_VIDEOIN_SUCCESS != retval) { + ioh_err(device, "In %s -> Function " + "ioh_video_in_set_output_format failed", __func__); + goto out; + } + + /* Setting the default I/P conversion mode. */ + retval = ioh_video_in_set_ip_trans(device, DEFAULT_IP_TRANS_SETTINGS); + + if (IOH_VIDEOIN_SUCCESS != retval) { + ioh_err(device, "In %s -> Function " + "ioh_video_in_set_ip_trans failed", __func__); + goto out; + } + + /* Setting the luminance level. */ + retval = ioh_video_in_set_luminance_level(device, + default_luminance_settings); + + if (IOH_VIDEOIN_SUCCESS != retval) { + ioh_err(device, "In %s -> Function " + "ioh_video_in_set_luminance_level failed", __func__); + goto out; + } + + /* Setting the RGB gain settings. */ + retval = ioh_video_in_set_rgb_gain(device, default_rgb_gain_settings); + if (IOH_VIDEOIN_SUCCESS != retval) { + ioh_err(device, "In %s -> Function " + "ioh_video_in_set_rgb_gain failed", __func__); + goto out; + } + + /* Setting the Blnaking Signal Timing settings. */ + retval = ioh_video_in_set_blank_tim(device, + default_blank_tim_settings); + if (IOH_VIDEOIN_SUCCESS != retval) { + ioh_err(device, "In %s -> Function " + "ioh_video_in_set_blank_tim failed", __func__); + goto out; + } + + /* Setting the Blue background settings. */ + retval = ioh_video_in_set_bb(device, DEFAULT_BB_SETTINGS); + if (IOH_VIDEOIN_SUCCESS != retval) { + ioh_err(device, "In %s -> Function " + "ioh_video_in_set_bb invoked failed", __func__); + goto out; + } + + /* Setting the Color bar settings. */ + retval = ioh_video_in_set_cb(device, default_cb_settings); + if (IOH_VIDEOIN_SUCCESS != retval) { + ioh_err(device, "In %s -> Function " + "ioh_video_in_set_cb invoked failed", __func__); + goto out; + } + + spin_lock(&device->dev_lock); + device->intenb.drevsem = VSYNC_SYNC; + device->intenb.dmarenb = DISABLE; + device->intenb.ofintenb = ENABLE; + device->intenb.hsintenb = DISABLE; + device->intenb.vsintenb = ENABLE; + write_intenb(device); + spin_unlock(&device->dev_lock); + + /* Enabling the interrupts. */ + device->dma_flag = -1; + +out: + ioh_dbg(device, "Function %s ended(%d)", __func__, retval); + + return retval; +} + +static void ioh_video_in_exit(struct BT656_device *device) +{ + spin_lock(&device->dev_lock); + (device)->intenb.drevsem = VSYNC_NOT_SYNC; + (device)->intenb.dmarenb = DISABLE; + (device)->intenb.ofintenb = DISABLE; + (device)->intenb.hsintenb = DISABLE; + (device)->intenb.vsintenb = DISABLE; + write_intenb(device); + + iowrite32(ASSERT_RESET, device->base_address + IOH_VIDEO_IN_RESET); + + spin_unlock(&device->dev_lock); + + ioh_dbg(device, "Function %s ended", __func__); +} + +/* ---- V4L2 ---- */ + +/* ------------------------------------------------------------------ + DMA and thread functions + ------------------------------------------------------------------*/ + +static void +ioh_vin_fillbuff(struct ioh_vin_dev *dev, struct ioh_vin_buffer *buf) +{ +#ifdef IOH_VIDEO_IN_DMA_CONTIG + struct BT656_device *device; + struct timeval ts; + + device = dev->video_in_dev; + + /* Advice that buffer was filled */ + buf->vb.field_count++; + do_gettimeofday(&ts); + buf->vb.ts = ts; + buf->vb.state = VIDEOBUF_DONE; +#else /* IOH_VIDEO_IN_DMA_CONTIG */ + struct BT656_device *device; + int pos = 0; + int hmax = buf->vb.height; + int wmax = buf->vb.width; + struct timeval ts; + void *vbuf = videobuf_to_vmalloc(&buf->vb); + + if (!vbuf) + return; + + device = dev->video_in_dev; + + /* -- */ + ioh_video_in_lock_buffer(&(device->read_buffer_lock)); + if (device->read_buffer != NULL) { + memcpy(vbuf + pos, (void *)device->read_buffer->virt_addr, + wmax * 2 * hmax); pos += wmax*2 * hmax; + ioh_dbg(device, "In %s -> read_buffer is copied" + "(p:0x%08x v:0x%08x)", __func__, + (u32)device->read_buffer->phy_addr, + (u32)device->read_buffer->virt_addr); + device->read_buffer = NULL; + } else { + ioh_dbg(device, "In %s -> read_buffer is NULL", __func__); + } + ioh_video_in_unlock_buffer(&(device->read_buffer_lock)); + /* -- */ + + /* Advice that buffer was filled */ + buf->vb.field_count++; + do_gettimeofday(&ts); + buf->vb.ts = ts; + buf->vb.state = VIDEOBUF_DONE; +#endif /* IOH_VIDEO_IN_DMA_CONTIG */ +} + +static void ioh_videoin_thread_tick(struct ioh_vin_dev *dev) +{ +#ifdef IOH_VIDEO_IN_DMA_CONTIG + struct ioh_vin_buffer *buf; + unsigned long flags = 0; + int ret_val; + + spin_lock_irqsave(&dev->slock, flags); + if (UNLIKELY(list_empty(&dev->vidq_active))) { + ioh_dbg(dev->video_in_dev, "In %s -> " + "No active queue to serve", __func__); + goto unlock; + } + if (UNLIKELY(!dev->active_frm)) { + ioh_dbg(dev->video_in_dev, "In %s -> " + "No active frame", __func__); + goto unlock; + } + + buf = dev->active_frm; + + list_del(&buf->vb.queue); + + do_gettimeofday(&buf->vb.ts); + + /* Fill buffer */ + ioh_vin_fillbuff(dev, buf); + + wake_up(&buf->vb.done); + + if (UNLIKELY(list_empty(&dev->vidq_active))) { + /* Stop */ + dev->active_frm = NULL; + dev->vidq_status = IOH_VIDEO_IN_VIDQ_INITIALISING; + ioh_dbg(dev->video_in_dev, "In %s -> vidq_active is empty", + __func__); + goto unlock; + } + + dev->active_frm = list_entry(dev->vidq_active.next, + struct ioh_vin_buffer, vb.queue); + + ret_val = ioh_video_in_dma_submit(dev->video_in_dev, + dev->active_frm->frame_index); + + ioh_dbg(dev->video_in_dev, "In %s -> " + "ioh_video_in_dma_submit returned(%d)", + __func__, ret_val); + + if (UNLIKELY(dev->active_frm->vb.queue.next != &dev->vidq_active)) { + struct ioh_vin_buffer *new = + list_entry(dev->active_frm->vb.queue.next, + struct ioh_vin_buffer, vb.queue); + + ioh_video_in_schedule_next(dev, new); + + ioh_dbg(dev->video_in_dev, "In %s -> " + "ioh_video_in_schedule_next was called", __func__); + } + +unlock: + spin_unlock_irqrestore(&dev->slock, flags); + return; +/* --------------------------------- */ +#else /* IOH_VIDEO_IN_DMA_CONTIG */ + struct ioh_vin_buffer *buf; + unsigned long flags = 0; + + spin_lock_irqsave(&dev->slock, flags); + if (list_empty(&dev->vidq_active)) { + ioh_dbg(dev->video_in_dev, "In %s -> " + "No active queue to serve", __func__); + goto unlock; + } + + buf = list_entry(dev->vidq_active.next, + struct ioh_vin_buffer, vb.queue); + + /* Nobody is waiting on this buffer, return */ + if (!waitqueue_active(&buf->vb.done)) { + ioh_dbg(dev->video_in_dev, "In %s -> " + "No active frame", __func__); + goto unlock; + } + +#if 1 + if (dev->video_in_dev->read_buffer == NULL) { + ioh_dbg(dev->video_in_dev, "In %s -> " + "read_buffer is NULL", __func__); + goto unlock; + } +#endif + + list_del(&buf->vb.queue); + + do_gettimeofday(&buf->vb.ts); + + /* Fill buffer */ + ioh_vin_fillbuff(dev, buf); + + wake_up(&buf->vb.done); +unlock: + spin_unlock_irqrestore(&dev->slock, flags); + return; +#endif /* IOH_VIDEO_IN_DMA_CONTIG */ +} + +/* ------------------------------------------------------------------ + Videobuf operations + ------------------------------------------------------------------*/ +static int +buffer_setup(struct videobuf_queue *vq, + unsigned int *count, unsigned int *size) +{ + struct ioh_vin_fh *fh = vq->priv_data; + struct ioh_vin_dev *dev = fh->dev; + + *size = fh->pix_format.sizeimage; + + if (0 == *count) + *count = 32; + + while (*size * *count > vid_limit * 1024 * 1024) + (*count)--; + + ioh_dbg(dev->video_in_dev, "%s, count=%d, size=%d\n", __func__, + *count, *size); + + return 0; +} + +static void free_buffer(struct videobuf_queue *vq, struct ioh_vin_buffer *buf) +{ + struct ioh_vin_fh *fh = vq->priv_data; + struct ioh_vin_dev *dev = fh->dev; + + ioh_dbg(dev->video_in_dev, "%s, state: %i\n", __func__, buf->vb.state); + + if (in_interrupt()) + BUG(); + +#ifdef IOH_VIDEO_IN_DMA_CONTIG + videobuf_dma_contig_free(vq, &buf->vb); +#else /* IOH_VIDEO_IN_DMA_CONTIG */ + videobuf_vmalloc_free(&buf->vb); +#endif /* IOH_VIDEO_IN_DMA_CONTIG */ + ioh_dbg(dev->video_in_dev, "free_buffer: freed\n"); + buf->vb.state = VIDEOBUF_NEEDS_INIT; +} + +#define norm_minw() (320) +#define norm_minh() (240) +#define norm_maxw() 1280 +#define norm_maxh() (1024 + 3) +static int +buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct ioh_vin_fh *fh = vq->priv_data; + struct ioh_vin_dev *dev = fh->dev; + struct ioh_vin_buffer *buf = + container_of(vb, struct ioh_vin_buffer, vb); + int rc; + + ioh_dbg(dev->video_in_dev, "%s, field=%d\n", __func__, field); + + BUG_ON(NULL == fh->fmt); + + if ((fh->pix_format.width < norm_minw()) || + (fh->pix_format.width > norm_maxw()) || + (fh->pix_format.height < norm_minh()) || + (fh->pix_format.height > norm_maxh())) + return -EINVAL; + + buf->vb.size = fh->pix_format.sizeimage; + if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) + return -EINVAL; + + /* These properties only change when queue is idle, see s_fmt */ + buf->fmt = fh->fmt; + buf->vb.width = fh->pix_format.width; + buf->vb.height = fh->pix_format.height; + buf->vb.field = field; + + if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { + rc = videobuf_iolock(vq, &buf->vb, NULL); + if (rc < 0) + goto fail; + } + + buf->vb.state = VIDEOBUF_PREPARED; + + return 0; + +fail: + free_buffer(vq, buf); + return rc; +} + +static void +buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + struct ioh_vin_buffer *buf = + container_of(vb, struct ioh_vin_buffer, vb); + struct ioh_vin_fh *fh = vq->priv_data; + struct ioh_vin_dev *dev = fh->dev; +#ifdef IOH_VIDEO_IN_DMA_CONTIG + int ret_val; +#else /* IOH_VIDEO_IN_DMA_CONTIG */ +#endif /* IOH_VIDEO_IN_DMA_CONTIG */ + + ioh_dbg(dev->video_in_dev, "%s", __func__); + + buf->vb.state = VIDEOBUF_QUEUED; + list_add_tail(&buf->vb.queue, &dev->vidq_active); + +#ifdef IOH_VIDEO_IN_DMA_CONTIG + if (dev->vidq_status == IOH_VIDEO_IN_VIDQ_RUNNING) { + ioh_dbg(dev->video_in_dev, "%s running" + "(vidq_status=%d)", __func__, dev->vidq_status); + return; + } else if (!dev->active_frm) { + ioh_dbg(dev->video_in_dev, "%s first buffer" + "(vidq_status=%d)", __func__, dev->vidq_status); + dev->active_frm = buf; + /* Ready */ + ioh_video_in_schedule_next(dev, buf); + + spin_unlock_irq(&dev->slock); + + ret_val = ioh_video_in_dma_submit(dev->video_in_dev, + dev->active_frm->frame_index); + + spin_lock_irq(&dev->slock); + + ioh_dbg(dev->video_in_dev, "In %s -> " + "ioh_video_in_dma_submit returned(%d)", + __func__, ret_val); + + } else if (dev->active_frm->vb.queue.next == &buf->vb.queue) { + ioh_dbg(dev->video_in_dev, "%s second buffer" + "(vidq_status=%d)", __func__, dev->vidq_status); + dev->vidq_status = IOH_VIDEO_IN_VIDQ_RUNNING; + + /* Ready for next */ + ioh_video_in_schedule_next(dev, buf); + + } +#else /* IOH_VIDEO_IN_DMA_CONTIG */ +#endif /* IOH_VIDEO_IN_DMA_CONTIG */ +} + +static void buffer_release(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct ioh_vin_buffer *buf = + container_of(vb, struct ioh_vin_buffer, vb); + struct ioh_vin_fh *fh = vq->priv_data; + struct ioh_vin_dev *dev = (struct ioh_vin_dev *)fh->dev; + +#ifdef IOH_VIDEO_IN_DMA_CONTIG + unsigned long flags = 0; + + spin_lock_irqsave(&dev->slock, flags); + if (dev->active_frm == buf) { + spin_lock(&dev->video_in_dev->dev_lock); + dev->video_in_dev->intenb.dmarenb = DISABLE; + write_intenb(dev->video_in_dev); + spin_unlock(&dev->video_in_dev->dev_lock); + } + if ((vb->state == VIDEOBUF_ACTIVE || vb->state == VIDEOBUF_QUEUED)) { + vb->state = VIDEOBUF_ERROR; + list_del(&vb->queue); + } + spin_unlock_irqrestore(&dev->slock, flags); +#else /* IOH_VIDEO_IN_DMA_CONTIG */ +#endif /* IOH_VIDEO_IN_DMA_CONTIG */ + + ioh_dbg(dev->video_in_dev, "%s", __func__); + + free_buffer(vq, buf); +} + +static struct videobuf_queue_ops ioh_vin_video_qops = { + .buf_setup = buffer_setup, + .buf_prepare = buffer_prepare, + .buf_queue = buffer_queue, + .buf_release = buffer_release, +}; + +/* ------------------------------------------------------------------ + IOCTL vidioc handling + ------------------------------------------------------------------*/ +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct ioh_vin_fh *fh = priv; + struct ioh_vin_dev *dev = fh->dev; + + strcpy(cap->driver, "ioh_vin"); + strcpy(cap->card, "ioh_vin"); + strlcpy(cap->bus_info, dev->v4l2_dev.name, sizeof(cap->bus_info)); + cap->version = IOH_VIN_VERSION; + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_STREAMING | + V4L2_CAP_READWRITE; + return 0; +} + +static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct ioh_vin_fmt *fmt; + + if (f->index >= ARRAY_SIZE(formats)) + return -EINVAL; + + fmt = &formats[f->index]; + + strlcpy(f->description, fmt->name, sizeof(f->description)); + f->pixelformat = fmt->fourcc; + return 0; +} + +static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct ioh_vin_fh *fh = priv; + + ioh_dbg(fh->dev->video_in_dev, "%s", __func__); + + f->fmt.pix = fh->pix_format; + + f->fmt.pix.field = fh->vb_vidq.field; + f->fmt.pix.pixelformat = fh->fmt->fourcc; + f->fmt.pix.bytesperline = + (f->fmt.pix.width * fh->fmt->depth) >> 3; + f->fmt.pix.sizeimage = + f->fmt.pix.height * f->fmt.pix.bytesperline; + + return 0; +} + +static struct ioh_vin_fmt *get_format(struct v4l2_format *f) +{ + struct ioh_vin_fmt *fmt; + unsigned int k; + + for (k = 0; k < ARRAY_SIZE(formats); k++) { + fmt = &formats[k]; + if (fmt->fourcc == f->fmt.pix.pixelformat) + break; + } + + if (k == ARRAY_SIZE(formats)) + return NULL; + + return &formats[k]; +} + +static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct ioh_vin_fh *fh = priv; + struct ioh_vin_dev *dev = fh->dev; + struct ioh_vin_fmt *fmt; + struct v4l2_pix_format *pix = &f->fmt.pix; + struct v4l2_mbus_framefmt mbus_fmt; + int ret; + + ioh_dbg(dev->video_in_dev, "%s", __func__); + + fmt = get_format(f); + if (!fmt) { + ioh_warn(dev->video_in_dev, "Fourcc format (0x%08x) invalid", + pix->pixelformat); + return -EINVAL; + } + ioh_dbg(dev->video_in_dev, "%s Fourcc format (0x%08x) valid", + __func__, pix->pixelformat); + + pix->pixelformat = fmt->fourcc; + v4l2_fill_mbus_format(&mbus_fmt, pix, fmt->mbus_code); + + ret = sensor_call(dev, video, try_mbus_fmt, &mbus_fmt); + + v4l2_fill_pix_format(pix, &mbus_fmt); + pix->bytesperline = (pix->width * fmt->depth) >> 3; + pix->sizeimage = pix->height * pix->bytesperline; + + switch (mbus_fmt.field) { + case V4L2_FIELD_NONE: + break; + case V4L2_FIELD_ANY: + pix->field = V4L2_FIELD_NONE; + break; + default: + ioh_err(dev->video_in_dev, "Not supported sensor detected"); + ret = -EINVAL; + } + + return ret; +} + +static int ioh_video_in_setting_s_fmt(struct ioh_vin_dev *dev, + struct v4l2_format *fmt) +{ + struct ioh_video_in_input_format input_format; + struct ioh_video_in_output_format output_format; + struct ioh_video_in_frame_size frame_size; + struct BT656_device *device; + int ret; + + device = dev->video_in_dev; + + /* input format setting for videoin */ + switch (v4l2_i2c_subdev_addr(dev->sensor)) { + case OV7620_ADDR: + ioh_info(dev->video_in_dev, "sensor is OV7620"); + input_format.format = NT_RAW_8BIT; + input_format.numerical_format = DONT_CARE_NUMERICAL_FORMAT; + device->frame_skip_num = 0; + break; + case OV9653_ADDR: + ioh_info(dev->video_in_dev, "sensor is OV9653"); + input_format.format = NT_RAW_8BIT; + input_format.numerical_format = DONT_CARE_NUMERICAL_FORMAT; + device->frame_skip_num = 0; + break; + case ML86V76651_ADDR: + ioh_info(dev->video_in_dev, "sensor is ML86V76651"); + input_format.format = NT_SQPX_ITU_R_BT_656_4_8BIT; + input_format.numerical_format = DONT_CARE_NUMERICAL_FORMAT; + device->frame_skip_num = 0; + break; + case NCM13J_ADDR: + ioh_info(dev->video_in_dev, "sensor is NCM13J"); + input_format.format = NT_RAW_8BIT; + input_format.numerical_format = DONT_CARE_NUMERICAL_FORMAT; + device->frame_skip_num = 0; + break; + default: + ioh_err(dev->video_in_dev, "Not supported sensor detected"); + return -ENODEV; + } + ret = ioh_video_in_set_input_format(device, input_format); + if (ret != 0) + return -ENODEV; + + /* output format setting for videoin */ + output_format.format = YCBCR_422_8BIT; + output_format.numerical_format = DONT_CARE_NUMERICAL_FORMAT; + output_format.luminance_range = DONT_CARE_LUMINANNCE_RANGE; + output_format.rgb_gain_level = DONT_CARE_RGBLEV; + ret = ioh_video_in_set_output_format(device, output_format); + if (ret != 0) + return -ENODEV; + + /* size setting for videoin */ + frame_size.X_component = fmt->fmt.pix.width; + frame_size.Y_component = fmt->fmt.pix.height; + frame_size.pitch_size = fmt->fmt.pix.bytesperline; + ret = ioh_video_in_set_size(device, frame_size); + if (ret != 0) + return -ENODEV; + + ioh_dbg(dev->video_in_dev, "%s X=%d[pix], Y=%d[pix], " + "pitch_size=%d[byte]", __func__, + frame_size.X_component, frame_size.Y_component, + frame_size.pitch_size); + + return 0; +} + +static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct ioh_vin_fh *fh = priv; + struct videobuf_queue *q = &fh->vb_vidq; + struct ioh_vin_dev *dev = fh->dev; + struct v4l2_mbus_framefmt mbus_fmt; + struct ioh_vin_fmt *f; + int ret; + + ioh_dbg(dev->video_in_dev, "%s start", __func__); + + f = get_format(fmt); + + ret = vidioc_try_fmt_vid_cap(file, fh, fmt); + + if (ret < 0) + return ret; + + mutex_lock(&q->vb_lock); + + fh->fmt = get_format(fmt); + fh->pix_format = fmt->fmt.pix; + fh->vb_vidq.field = fmt->fmt.pix.field; + + v4l2_fill_mbus_format(&mbus_fmt, &fh->pix_format, fh->fmt->mbus_code); + + ret = sensor_call(dev, video, s_mbus_fmt, &mbus_fmt); + if (ret != 0) + goto out; + + ret = ioh_video_in_setting_s_fmt(dev, fmt); + if (ret != 0) + goto out; + +out: + mutex_unlock(&q->vb_lock); + + ioh_dbg(dev->video_in_dev, "%s ended(%d)", __func__, ret); + + return ret; +} + +static int vidioc_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *p) +{ + struct ioh_vin_fh *fh = priv; + + return videobuf_reqbufs(&fh->vb_vidq, p); +} + +static int vidioc_querybuf(struct file *file, + void *priv, struct v4l2_buffer *p) +{ + struct ioh_vin_fh *fh = priv; + + return videobuf_querybuf(&fh->vb_vidq, p); +} + +static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + struct ioh_vin_fh *fh = priv; + + return videobuf_qbuf(&fh->vb_vidq, p); +} + +static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + struct ioh_vin_fh *fh = priv; + + return videobuf_dqbuf(&fh->vb_vidq, p, + file->f_flags & O_NONBLOCK); +} + +#ifdef CONFIG_VIDEO_V4L1_COMPAT +static int vidiocgmbuf(struct file *file, void *priv, struct video_mbuf *mbuf) +{ + struct ioh_vin_fh *fh = priv; + + return videobuf_cgmbuf(&fh->vb_vidq, mbuf, 8); +} +#endif + +static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct ioh_vin_fh *fh = priv; + + if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (i != fh->type) + return -EINVAL; + /* -- */ + { + int retval; + struct BT656_device *device; + device = fh->dev->video_in_dev; + + retval = ioh_video_in_cap_start(device); + if (retval != IOH_VIDEOIN_SUCCESS) + return -EINVAL; + } + /* -- */ + return videobuf_streamon(&fh->vb_vidq); +} + +static int vidioc_streamoff(struct file *file, + void *priv, enum v4l2_buf_type i) +{ + struct ioh_vin_fh *fh = priv; + + if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (i != fh->type) + return -EINVAL; + /* -- */ + { + struct BT656_device *device; + device = fh->dev->video_in_dev; + + ioh_video_in_cap_stop(device); + } + /* -- */ + return videobuf_streamoff(&fh->vb_vidq); +} + +static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *i) +{ + return 0; +} + +/* only one input in this driver */ +static int vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *inp) +{ + /* -- */ + if (inp->index != 0) + return -EINVAL; + /* -- */ + + inp->type = V4L2_INPUT_TYPE_CAMERA; + inp->std = V4L2_STD_NTSC_M; + sprintf(inp->name, "Camera %u", inp->index); + + return 0; +} + +static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) +{ + *i = 0; + return 0; +} +static int vidioc_s_input(struct file *file, void *priv, unsigned int i) +{ + if (i != 0) + return -EINVAL; + return 0; +} + + /* --- controls ---------------------------------------------- */ +static int vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + struct ioh_vin_fh *fh = priv; + struct ioh_vin_dev *dev = fh->dev; + int ret; + + ret = sensor_call(dev, core, queryctrl, qc); + return ret; +} + +static int vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct ioh_vin_fh *fh = priv; + struct ioh_vin_dev *dev = fh->dev; + int ret; + + ret = sensor_call(dev, core, g_ctrl, ctrl); + return ret; +} +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct ioh_vin_fh *fh = priv; + struct ioh_vin_dev *dev = fh->dev; + int ret; + + ret = sensor_call(dev, core, s_ctrl, ctrl); + return ret; +} + +/* ------------------------------------------------------------------ + File operations for the device + ------------------------------------------------------------------*/ + +static int ioh_vin_v4l2_open(struct file *file) +{ + struct ioh_vin_dev *dev = video_drvdata(file); + struct ioh_vin_fh *fh = NULL; + int retval = 0; + + ioh_dbg(dev->video_in_dev, "%s start", __func__); + + mutex_lock(&dev->mutex); + dev->users++; + + if (dev->users > 1) { + dev->users--; + mutex_unlock(&dev->mutex); + return -EBUSY; + } + + ioh_info(dev->video_in_dev, "open %s type=%s users=%d\n", + video_device_node_name(dev->vfd), + v4l2_type_names[V4L2_BUF_TYPE_VIDEO_CAPTURE], dev->users); + + /* allocate + initialize per filehandle data */ + fh = kzalloc(sizeof(*fh), GFP_KERNEL); + if (NULL == fh) { + ioh_err(dev->video_in_dev, "In %s -> " + "kzalloc failed", __func__); + dev->users--; + retval = -ENOMEM; + } + + if (retval) { + mutex_unlock(&dev->mutex); + return retval; + } + + file->private_data = fh; + fh->dev = dev; + + fh->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + fh->fmt = &formats[0]; + +#ifdef IOH_VIDEO_IN_DMA_CONTIG + dev->active_frm = NULL; + dev->vidq_status = IOH_VIDEO_IN_VIDQ_IDLE; + + videobuf_queue_dma_contig_init(&fh->vb_vidq, &ioh_vin_video_qops, + NULL, &dev->slock, fh->type, V4L2_FIELD_ANY, + sizeof(struct ioh_vin_buffer), fh, + NULL); +#else /* IOH_VIDEO_IN_DMA_CONTIG */ + videobuf_queue_vmalloc_init(&fh->vb_vidq, &ioh_vin_video_qops, + NULL, &dev->slock, fh->type, V4L2_FIELD_ANY, + sizeof(struct ioh_vin_buffer), fh, + NULL); +#endif /* IOH_VIDEO_IN_DMA_CONTIG */ + + /* -- */ + retval = ioh_video_in_open(dev); + mutex_unlock(&dev->mutex); + if (retval < 0) + return -1; + + /* -- */ + + return 0; +} + +static ssize_t +ioh_vin_v4l2_read(struct file *file, char __user *data, size_t count, + loff_t *ppos) +{ + struct ioh_vin_fh *fh = file->private_data; + + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + return videobuf_read_stream(&fh->vb_vidq, data, count, ppos, 0, + file->f_flags & O_NONBLOCK); + } + return 0; +} + +static unsigned int +ioh_vin_v4l2_poll(struct file *file, struct poll_table_struct *wait) +{ + struct ioh_vin_fh *fh = file->private_data; + struct ioh_vin_dev *dev = fh->dev; + struct videobuf_queue *q = &fh->vb_vidq; + unsigned int ret; + + ioh_dbg(dev->video_in_dev, "%s\n", __func__); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type) + return POLLERR; + + ret = videobuf_poll_stream(file, q, wait); + + ioh_dbg(dev->video_in_dev, "%s ret=0x%04x\n", __func__, ret); + + return ret; +} + +static int ioh_vin_v4l2_close(struct file *file) +{ + struct ioh_vin_fh *fh = file->private_data; + struct ioh_vin_dev *dev = fh->dev; + struct video_device *vdev = video_devdata(file); + + ioh_dbg(dev->video_in_dev, "%s", __func__); + + mutex_lock(&dev->mutex); + + ioh_video_in_close(dev); + +#ifdef IOH_VIDEO_IN_DMA_CONTIG + dev->active_frm = NULL; + dev->vidq_status = IOH_VIDEO_IN_VIDQ_IDLE; +#else /* IOH_VIDEO_IN_DMA_CONTIG */ +#endif /* IOH_VIDEO_IN_DMA_CONTIG */ + + videobuf_stop(&fh->vb_vidq); + videobuf_mmap_free(&fh->vb_vidq); + + kfree(fh); + + dev->users--; + + ioh_info(dev->video_in_dev, "close called (dev=%s, users=%d)\n", + video_device_node_name(vdev), dev->users); + + mutex_unlock(&dev->mutex); + + return 0; +} + +static int ioh_vin_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct ioh_vin_fh *fh = file->private_data; + struct ioh_vin_dev *dev = fh->dev; + int ret; + + ioh_dbg(dev->video_in_dev, "mmap called, vma=0x%08lx\n", + (unsigned long)vma); + + ret = videobuf_mmap_mapper(&fh->vb_vidq, vma); + + ioh_dbg(dev->video_in_dev, "vma start=0x%08lx, size=%ld, ret=%d\n", + (unsigned long)vma->vm_start, + (unsigned long)vma->vm_end-(unsigned long)vma->vm_start, + ret); + + return ret; +} + +static long ioh_vin_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct ioh_vin_dev *dev = video_drvdata(file); + long ret; + + mutex_lock(&dev->mutex); + + ret = video_ioctl2(file, cmd, arg); + + mutex_unlock(&dev->mutex); + + return ret; +} + +static const struct v4l2_file_operations ioh_vin_fops = { + .owner = THIS_MODULE, + .open = ioh_vin_v4l2_open, + .release = ioh_vin_v4l2_close, + .read = ioh_vin_v4l2_read, + .poll = ioh_vin_v4l2_poll, + .unlocked_ioctl = ioh_vin_ioctl, + .mmap = ioh_vin_mmap, +}; + +static const struct v4l2_ioctl_ops ioh_vin_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_s_std = vidioc_s_std, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, +#ifdef CONFIG_VIDEO_V4L1_COMPAT + .vidiocgmbuf = vidiocgmbuf, +#endif + .vidioc_default = ioh_video_in_ioctl, +}; + +static struct video_device ioh_vin_template = { + .name = "ioh_vin", + .fops = &ioh_vin_fops, + .ioctl_ops = &ioh_vin_ioctl_ops, + .release = video_device_release_empty, + + .tvnorms = V4L2_STD_NTSC_M, + .current_norm = V4L2_STD_NTSC_M, +}; + +/* ----------------------------------------------------------------- + Initialization and module stuff + ------------------------------------------------------------------*/ + +static int __devexit ioh_vin_remove(struct ioh_vin_dev *dev) +{ + v4l2_info(&dev->v4l2_dev, "V4L2 device unregistering %s\n", + video_device_node_name(dev->vfd)); + video_unregister_device(dev->vfd); + v4l2_device_unregister(&dev->v4l2_dev); + + ioh_dbg(dev->video_in_dev, "Function %s ended", __func__); + + return 0; +} + +static int __devinit +ioh_video_in_subdev_open(struct ioh_vin_dev *dev) +{ + int ret = 0; + struct i2c_adapter *adap; + static unsigned short addrs[] = { + OV7620_ADDR, + OV9653_ADDR, + ML86V76651_ADDR, + NCM13J_ADDR, + I2C_CLIENT_END + }; + + adap = i2c_get_adapter(0); + dev->sensor = v4l2_i2c_new_subdev(&dev->v4l2_dev, adap, + "ioh_i2c", 0, addrs); + if (!dev->sensor) { + ioh_err(dev->video_in_dev, "%s " + "v4l2_i2c_new_subdev failed\n", __func__); + ret = -ENODEV; + } + return ret; +} + +static int __devinit +ioh_vin_dev_initialize(struct ioh_vin_dev *dev) +{ + struct video_device *vfd; + int ret; + + snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name), + "%s", IOH_VIN_DRV_NAME); + ret = v4l2_device_register(NULL, &dev->v4l2_dev); + if (ret) + goto ini_ret; + + /* init video dma queues */ + INIT_LIST_HEAD(&dev->vidq_active); + + ret = ioh_video_in_subdev_open(dev); + if (ret < 0) + goto unreg_dev; + + ret = -ENOMEM; + vfd = video_device_alloc(); + if (!vfd) + goto unreg_dev; + + *vfd = ioh_vin_template; + + ret = video_register_device(vfd, VFL_TYPE_GRABBER, video_nr); + if (ret < 0) + goto rel_vdev; + + video_set_drvdata(vfd, dev); + + if (video_nr >= 0) + video_nr++; + + dev->vfd = vfd; + v4l2_info(&dev->v4l2_dev, "V4L2 device registered as %s\n", + video_device_node_name(vfd)); + + return 0; + +rel_vdev: + video_device_release(vfd); +unreg_dev: + v4l2_device_unregister(&dev->v4l2_dev); +ini_ret: + return ret; +} + +static bool filter(struct dma_chan *chan, void *slave) +{ + struct pch_dma_slave *param = slave; + + if ((chan->chan_id == param->chan_id) && (param->dma_dev == + chan->device->dev)) { + chan->private = param; + return true; + } else { + return false; + } +} + +static void ioh_request_dma(struct BT656_device *device) +{ + dma_cap_mask_t mask; + struct dma_chan *chan; + struct pci_dev *dma_dev; + struct pch_dma_slave *param; + struct BT656_device *priv = device; + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + if (priv->ioh_type == ML7213_IOH) + dma_dev = pci_get_bus_and_slot(2, PCI_DEVFN(0xa, 0)); /* Get DMA's dev + information */ + else if (priv->ioh_type == ML7223_IOH) + dma_dev = pci_get_bus_and_slot(2, PCI_DEVFN(0xc, 0)); /* Get DMA's dev + information */ + + /* Set Tx DMA */ + param = &priv->param_dma; + param->dma_dev = &dma_dev->dev; + param->width = PCH_DMA_WIDTH_4_BYTES; + + if (priv->ioh_type == ML7213_IOH) + param->chan_id = 6; /* ch2 */ + else if (priv->ioh_type == ML7223_IOH) + param->chan_id = 2; /* ch6 */ + + param->tx_reg = (unsigned int)NULL; + param->rx_reg = device->physical_address + IOH_VIDEO_IN_VDATA; + + chan = dma_request_channel(mask, filter, param); + if (!chan) { + ioh_err(device, "In %s -> dma_request_channel failed", + __func__); + return; + } + priv->chan_dma = chan; + + ioh_dbg(device, "Function %s ended", __func__); + return; +} + +static void ioh_free_dma(struct BT656_device *device) +{ + struct BT656_device *priv = device; + int retval; + + if (priv->chan_dma) { + + retval = priv->chan_dma->device->device_control(priv->chan_dma, + DMA_TERMINATE_ALL, 0); + ioh_dbg(device, "In %s -> device_control returned(%d)", + __func__, retval); + + dma_release_channel(priv->chan_dma); + priv->chan_dma = NULL; + } + + ioh_dbg(device, "Function %s ended", __func__); + return; +} + +/* ---- MAIN ---- */ + +static int ioh_video_in_open(struct ioh_vin_dev *dev) +{ + int retval; + struct BT656_device *device; + + /* Attaining the device specific structure. */ + device = dev->video_in_dev; + + if (true == device->suspend_flag) { + retval = -EAGAIN; + ioh_err(device, "In %s -> The device is in suspend mode(%d)", + __func__, retval); + goto out; + } + + /* Device already in use. */ + if (true == device->open_flag) { + retval = -EBUSY; + ioh_err(device, "In %s -> Device already opened(%d)", + __func__, retval); + goto out; + } + /* Device not in use. */ + + spin_lock(&(device->dev_lock)); + + iowrite32(ASSERT_RESET, device->base_address + IOH_VIDEO_IN_RESET); + + iowrite32(DE_ASSERT_RESET, device->base_address + IOH_VIDEO_IN_RESET); + + spin_unlock(&(device->dev_lock)); + + /* Allocating the DMA channel. */ + device->chan_dma = NULL; + ioh_request_dma(device); + if (!device->chan_dma) { + retval = -EINVAL; + ioh_err(device, "In %s -> ioh_request_dma failed(%d)", + __func__, retval); + goto out; + } + + /* Initializing the device variables and locks. */ +#ifdef IOH_VIDEO_IN_DMA_CONTIG + init_waitqueue_head(&(device->vsync_wait_queue)); + init_waitqueue_head(&(device->thread_sleep_queue)); + device->vsync_waiting_flag = false; +#else /* IOH_VIDEO_IN_DMA_CONTIG */ + init_waitqueue_head(&(device->read_wait_queue)); + init_waitqueue_head(&(device->thread_sleep_queue)); + + spin_lock_init(&(device->read_buffer_lock)); + device->read_buffer = NULL; + device->read_wait_flag = false; + + init_waitqueue_head(&(device->vsync_wait_queue)); + device->vsync_waiting_flag = false; +#endif /* IOH_VIDEO_IN_DMA_CONTIG */ + + /* Registering the interrupt handler. */ + retval = request_irq(device->irq, + ioh_video_in_interrupt, + IRQF_SHARED, IOH_VIN_DRV_NAME, + (void *)dev); + if (0 != retval) { + ioh_err(device, "In %s -> request_irq failed(%d)", + __func__, retval); + goto out_free_dma; + } + + device->thread_sleep_flag = true; + device->thread_exit_flag = false; + device->overflow_flag = false; + device->dma_flag = -1; + + /* Creating the video capturing thread. */ + if (NULL == + (kthread_run(ioh_video_in_thread_fn, (void *)dev, + IOH_VIDEOIN_THREAD_NAME))) { + retval = -EIO; + ioh_err(device, "In %s -> kthread_run failed(%d)", + __func__, retval); + goto out_free_irq; + } + + msleep(SLEEP_DELAY); + + /* Device hardware initialization. */ + retval = ioh_video_in_init(device); + if (IOH_VIDEOIN_SUCCESS != retval) { + ioh_err(device, "In %s -> ioh_video_in_init failed(%d)", + __func__, retval); + goto out_thread_exit; + } + + /* setting the open flag. */ + device->open_flag = true; + + retval = IOH_VIDEOIN_SUCCESS; + + ioh_dbg(device, "Function %s ended(%d)", __func__, retval); + + return retval; + + /* If open failed. */ +out_thread_exit: + /* Stopping the video capturing thread. */ + device->thread_exit_flag = true; + wake_up(&(device->thread_sleep_queue)); + +out_free_irq: + /* Un-registering the interrupt handler. */ + free_irq(device->irq, (void *)dev); + +out_free_dma: + /* Freeing the DMA channel. */ + ioh_free_dma(device); + +out: + ioh_dbg(device, "Function %s ended(%d)", __func__, retval); + + return retval; +} + +static int ioh_video_in_close(struct ioh_vin_dev *dev) +{ + struct BT656_device *device = dev->video_in_dev; + + /* Attaining the device specific structure. */ + + if (true == device->open_flag) { + + /* Stopping the kernel thread if running. */ + device->thread_exit_flag = true; + wake_up(&(device->thread_sleep_queue)); + + msleep(SLEEP_DELAY); + + /* De-initializing the device hardware. */ + ioh_video_in_exit(device); + + /* Un-registering the interrupt handler. */ + free_irq(device->irq, (void *)dev); + + /* Freeing the DMA channel. */ + ioh_free_dma(device); + +#ifdef IOH_VIDEO_IN_DMA_CONTIG +#else /* IOH_VIDEO_IN_DMA_CONTIG */ + /* Re-setting the read buffer pointer. */ + device->read_buffer = NULL; +#endif /* IOH_VIDEO_IN_DMA_CONTIG */ + + /* Re-setting the open flag variable. */ + device->open_flag = false; + } + + ioh_dbg(device, "Function %s ended", __func__); + + return IOH_VIDEOIN_SUCCESS; +} + +static long ioh_video_in_ioctl(struct file *p_file, void *priv, + int command, void *param) +{ + int retval = -EAGAIN; + struct ioh_vin_fh *fh = priv; + struct ioh_vin_dev *dev = fh->dev; + struct BT656_device *device = dev->video_in_dev; + /* Attaining the device specific structure. */ + + if (true == device->suspend_flag) { + ioh_err(device, "In %s -> The device is in suspend mode", + __func__); + goto out; + } + + /* if not suspend. */ + switch (command) { + /* For setting input format. */ + case IOH_VIDEO_SET_INPUT_FORMAT: + { + struct ioh_video_in_input_format format; + memcpy((void *)&format, (void *)param, sizeof(format)); + retval = ioh_video_in_set_input_format(device, format); + } + break; + + /* For getting input format. */ + case IOH_VIDEO_GET_INPUT_FORMAT: + { + struct ioh_video_in_input_format format; + format = ioh_video_in_get_input_format(device); + memcpy((void *)param, (void *)&format, sizeof(format)); + retval = IOH_VIDEOIN_SUCCESS; + } + break; + + /* For setting the output format. */ + case IOH_VIDEO_SET_OUTPUT_FORMAT: + { + struct ioh_video_in_output_format format; + memcpy((void *)&format, (void *)param, sizeof(format)); + retval = ioh_video_in_set_output_format(device, + format); + } + break; + + /* For getting the output format. */ + case IOH_VIDEO_GET_OUTPUT_FORMAT: + { + struct ioh_video_in_output_format format; + format = ioh_video_in_get_output_format(device); + memcpy((void *)param, (void *)&format, sizeof(format)); + retval = IOH_VIDEOIN_SUCCESS; + } + break; + + /* For setting the frame size. */ + case IOH_VIDEO_SET_SIZE: + { + struct ioh_video_in_frame_size frame_size; + memcpy((void *)&frame_size, (void *)param, + sizeof(frame_size)); + retval = ioh_video_in_set_size(device, frame_size); + } + break; + + /* For getting the frame size. */ + case IOH_VIDEO_GET_SIZE: + { + struct ioh_video_in_frame_size frame_size; + frame_size = ioh_video_in_get_size(device); + memcpy((void *)param, (void *)&frame_size, + sizeof(frame_size)); + retval = IOH_VIDEOIN_SUCCESS; + } + break; + + /* For setting the scan mode conversion method. */ + case IOH_VIDEO_SET_IP_TRANS: + { + enum ioh_video_in_scan_mode_method scan_mode; + memcpy((void *)&scan_mode, (void *)param, + sizeof(scan_mode)); + retval = ioh_video_in_set_ip_trans(device, scan_mode); + } + break; + + /* For getting the scan mode conversion method. */ + case IOH_VIDEO_GET_IP_TRANS: + { + enum ioh_video_in_scan_mode_method scan_mode; + scan_mode = ioh_video_in_get_ip_trans(device); + memcpy((void *)param, (void *)&scan_mode, + sizeof(scan_mode)); + retval = IOH_VIDEOIN_SUCCESS; + } + break; + + /* For setting the luminance level settings. */ + case IOH_VIDEO_SET_LUMINENCE_LEVEL: + { + struct ioh_video_in_luminance_settings + luminance_settings; + memcpy((void *)&luminance_settings, (void *)param, + sizeof(luminance_settings)); + retval = ioh_video_in_set_luminance_level(device, + luminance_settings); + } + break; + + /* For getting the luminance level settings. */ + case IOH_VIDEO_GET_LUMINENCE_LEVEL: + { + struct + ioh_video_in_luminance_settings luminance_settings; + luminance_settings = + ioh_video_in_get_luminance_level(device); + memcpy((void *)param, (void *)&luminance_settings, + sizeof(luminance_settings)); + retval = IOH_VIDEOIN_SUCCESS; + } + break; + + /* For setting the RGB gain settings. */ + case IOH_VIDEO_SET_RGB_GAIN: + { + struct ioh_video_in_rgb_gain_settings rgb_settings; + memcpy((void *)&rgb_settings, (void *)param, + sizeof(rgb_settings)); + retval = ioh_video_in_set_rgb_gain(device, + rgb_settings); + } + break; + + /* For getting the RGB gain settings. */ + case IOH_VIDEO_GET_RGB_GAIN: + { + struct ioh_video_in_rgb_gain_settings rgb_settings; + rgb_settings = ioh_video_in_get_rgb_gain(device); + memcpy((void *)param, (void *)&rgb_settings, + sizeof(rgb_settings)); + retval = IOH_VIDEOIN_SUCCESS; + } + break; + + /* For initiating the video capturing process. */ + case IOH_VIDEO_CAP_START: + retval = ioh_video_in_cap_start(device); + break; + + /* For stopping the video capturing process. */ + case IOH_VIDEO_CAP_STOP: + ioh_video_in_cap_stop(device); + retval = IOH_VIDEOIN_SUCCESS; + + break; + + /* For setting the blanking signal timing settings. */ + case IOH_VIDEO_SET_BLANK_TIM: + { + struct ioh_video_in_blank_tim_settings blank_tim; + memcpy((void *)&blank_tim, (void *)param, + sizeof(blank_tim)); + retval = ioh_video_in_set_blank_tim(device, blank_tim); + } + break; + + /* For getting the blanking signal timing settings. */ + case IOH_VIDEO_GET_BLANK_TIM: + { + struct ioh_video_in_blank_tim_settings blank_tim; + blank_tim = ioh_video_in_get_blank_tim(device); + memcpy((void *)param, (void *)&blank_tim, + sizeof(blank_tim)); + retval = IOH_VIDEOIN_SUCCESS; + } + break; + + /* For setting the blue background mode. */ + case IOH_VIDEO_SET_BB: + { + enum ioh_video_in_bb_mode bb_mode; + memcpy((void *)&bb_mode, (void *)param, + sizeof(bb_mode)); + retval = ioh_video_in_set_bb(device, bb_mode); + } + break; + + /* For getting the blue background mode. */ + case IOH_VIDEO_GET_BB: + { + enum ioh_video_in_bb_mode bb_mode; + bb_mode = ioh_video_in_get_bb(device); + memcpy((void *)param, (void *)&bb_mode, + sizeof(bb_mode)); + retval = IOH_VIDEOIN_SUCCESS; + } + break; + + /* For setting the color bar mode. */ + case IOH_VIDEO_SET_CB: + { + struct ioh_video_in_cb_settings cb_settings; + memcpy((void *)&cb_settings, (void *)param, + sizeof(cb_settings)); + retval = ioh_video_in_set_cb(device, cb_settings); + } + break; + + /* For getting the color bar mode. */ + case IOH_VIDEO_GET_CB: + { + struct ioh_video_in_cb_settings cb_settings; + cb_settings = ioh_video_in_get_cb(device); + memcpy((void *)param, (void *)&cb_settings, + sizeof(cb_settings)); + retval = IOH_VIDEOIN_SUCCESS; + } + break; + + /* For getting the buffer size. */ + case IOH_VIDEO_GET_BUFFER_SIZE: + { + unsigned long buffer_size; + buffer_size = ioh_video_in_get_buffer_size(device); + memcpy((void *)param, (void *)&buffer_size, + sizeof(buffer_size)); + retval = IOH_VIDEOIN_SUCCESS; + } + break; + + /* For getting the frame buffer info. */ + case IOH_VIDEO_GET_FRAME_BUFFERS: +#ifdef IOH_VIDEO_IN_DMA_CONTIG + ioh_err(device, "In %s -> Invalid ioctl command", __func__); + retval = -EINVAL; +#else /* IOH_VIDEO_IN_DMA_CONTIG */ + { + struct ioh_video_in_frame_buffers buffers; + buffers = ioh_video_in_get_frame_buffers(device); + memcpy((void *)param, (void *)&buffers, + sizeof(buffers)); + retval = IOH_VIDEOIN_SUCCESS; + } +#endif /* IOH_VIDEO_IN_DMA_CONTIG */ + break; + + /* For reading the frame buffer. */ + case IOH_VIDEO_READ_FRAME_BUFFER: +#ifdef IOH_VIDEO_IN_DMA_CONTIG + ioh_err(device, "In %s -> Invalid ioctl command", __func__); + retval = -EINVAL; +#else /* IOH_VIDEO_IN_DMA_CONTIG */ + { + struct ioh_video_in_frame_buffer buffer; + buffer = ioh_video_in_read_frame_buffer(device); + memcpy((void *)param, (void *)&buffer, sizeof(buffer)); + retval = IOH_VIDEOIN_SUCCESS; + } +#endif /* IOH_VIDEO_IN_DMA_CONTIG */ + break; + + default: + ioh_err(device, "In %s -> Invalid ioctl command", __func__); + retval = -EINVAL; + break; + + } + /* End of switch */ +out: + ioh_dbg(device, "Function %s ended(%d)", __func__, retval); + return retval; +} + +/* ---- PCI ---- */ +#ifdef IOH_VIDEO_IN_DMA_CONTIG +#else /* IOH_VIDEO_IN_DMA_CONTIG */ +static int __devinit +ioh_video_in_set_frame_buffer(struct BT656_device *device) +{ + struct ioh_video_in_frame_buffer_info info; + int retval; + + if (n_frame_buf < MIN_N_FRAME_BUF) + n_frame_buf = MIN_N_FRAME_BUF; + if (n_frame_buf > MAX_N_FRAME_BUF) + n_frame_buf = MAX_N_FRAME_BUF; + + info.buffer_num = n_frame_buf; + info.order = ALLOC_ORDER; + + retval = ioh_video_in_alloc_frame_buffer(device, info); + + if (retval != IOH_VIDEOIN_SUCCESS) { + ioh_err(device, "In %s -> " + "ioh_video_in_alloc_frame_buffer failed(%d)", + __func__, retval); + goto out; + } + +out: + ioh_dbg(device, "Function %s ended(%d)", __func__, retval); + return retval; +} +static int __devexit ioh_video_in_clr_frame_buffer(struct BT656_device *device) +{ + struct ioh_video_in_frame_buffer_info info; + int retval; + + info.buffer_num = n_frame_buf; + info.order = ALLOC_ORDER; + + retval = ioh_video_in_free_frame_buffer(device, info); + + if (retval != IOH_VIDEOIN_SUCCESS) { + ioh_err(device, "In %s -> " + "ioh_video_in_free_frame_buffer failed(%d)", + __func__, retval); + goto out; + } +out: + ioh_dbg(device, "Function %s ended(%d)", __func__, retval); + return retval; +} +#endif /* IOH_VIDEO_IN_DMA_CONTIG */ + +static int __devinit +ioh_video_in_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + int retval; + void __iomem *base_address = NULL; + u32 physical_address = 0; + struct BT656_device *device = NULL; + struct ioh_vin_dev *dev = NULL; + + /* Enabling the PCI device. */ + retval = pci_enable_device(pdev); + if (0 != retval) { + printk(KERN_ERR "ioh_video_in : In %s " + "pci_enable_device failed (%d)", + __func__, retval); + goto out; + } + + /* Setting the device as the PCI master. */ + pci_set_master(pdev); + + /* Obtaining the physical address for further DMA use. */ + physical_address = pci_resource_start(pdev, 1); + if (0 == physical_address) { + printk(KERN_ERR "ioh_video_in : In %s " + "Cannot obtain the physical address", + __func__); + retval = -ENOMEM; + goto out_pcidev; + } + + /* Remapping the entire device regions to the kernel space memory. */ + base_address = pci_iomap(pdev, 1, 0); + if (base_address == NULL) { + printk(KERN_ERR "ioh_video_in : In %s " + "pci_iomap failed(0x%08x)", + __func__, (u32) base_address); + retval = -ENOMEM; + goto out_pcidev; + } + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + printk(KERN_ERR "ioh_video_in : In %s " + "kzalloc(dev) failed", __func__); + retval = -ENOMEM; + goto out_iounmap; + } + + device = kzalloc(sizeof(*device), GFP_KERNEL); + if (!device) { + printk(KERN_ERR "ioh_video_in : In %s " + "kzalloc(device) failed", __func__); + retval = -ENOMEM; + goto out_free_dev; + } + + /* initialize locks */ + spin_lock_init(&dev->slock); + mutex_init(&dev->mutex); + + mutex_lock(&dev->mutex); + + dev->video_in_dev = device; + + /* Filling in the device specific data structure fields. */ + spin_lock_init(&device->dev_lock); + + device->base_address = base_address; + device->physical_address = physical_address; + + device->p_device = pdev; + device->irq = pdev->irq; + + device->suspend_flag = false; + device->open_flag = false; + device->frame_skip_num = 0; + + if (id->device == PCI_DEVICE_ID_IVI_VIDEOIN) + device->ioh_type = ML7213_IOH; + else + device->ioh_type = ML7223_IOH; + + /* Saving the device structure for further use. */ + pci_set_drvdata(pdev, dev); + + retval = ioh_vin_dev_initialize(dev); + if (0 != retval) { + printk(KERN_ERR "ioh_video_in : In %s " + "ioh_vin_dev_initialize failed(%d)", + __func__, retval); + mutex_unlock(&dev->mutex); + goto out_free_device; + } + +#ifdef IOH_VIDEO_IN_DMA_CONTIG +#else /* IOH_VIDEO_IN_DMA_CONTIG */ + retval = ioh_video_in_set_frame_buffer(device); + if (IOH_VIDEOIN_SUCCESS != retval) { + printk(KERN_ERR "ioh_video_in : In %s " + "iioh_video_in_set_frame_buffer failed(%d)", + __func__, retval); + mutex_unlock(&dev->mutex); + goto out_free_device; + } +#endif /* IOH_VIDEO_IN_DMA_CONTIG */ + + retval = IOH_VIDEOIN_SUCCESS; /* Probe Successful */ + + mutex_unlock(&dev->mutex); + + ioh_dbg(device, "Function %s ended(%d)", __func__, retval); + + return retval; + + /* Releasing all the registered components + if probe is not successful. */ +out_free_device: + /* Free the device memory */ + kfree(device); +out_free_dev: + kfree(dev); +out_iounmap: + /* Un-mapping the remapped memory regions. */ + pci_iounmap(pdev, base_address); +out_pcidev: + /* Disabling the PCI device. */ + pci_disable_device(pdev); +out: + printk(KERN_DEBUG "Function %s ended(%d)", __func__, retval); + return retval; +} + +static void __devexit ioh_video_in_pci_remove(struct pci_dev *pdev) +{ + struct ioh_vin_dev *dev; + struct BT656_device *device; + + /* Obtaining the associated device structure. */ + dev = (struct ioh_vin_dev *)pci_get_drvdata(pdev); + + mutex_lock(&dev->mutex); + + device = dev->video_in_dev; + + ioh_vin_remove(dev); + +#ifdef IOH_VIDEO_IN_DMA_CONTIG +#else /* IOH_VIDEO_IN_DMA_CONTIG */ + ioh_video_in_clr_frame_buffer(device); +#endif /* IOH_VIDEO_IN_DMA_CONTIG */ + + /* Un-mapping the remapped device address. */ + pci_iounmap(pdev, device->base_address); + + /* Disabling the PCI device. */ + pci_disable_device(pdev); + + /* Resetting the driver data. */ + pci_set_drvdata(pdev, NULL); + + mutex_unlock(&dev->mutex); + + kfree(dev); + + kfree(device); +} + +#ifdef CONFIG_PM +static int ioh_video_in_pci_suspend(struct pci_dev *pdev, pm_message_t state) +{ + int retval; + struct ioh_vin_dev *dev; + struct BT656_device *device; + + /* Obtaining the associated device structure. */ + dev = (struct ioh_vin_dev *)pci_get_drvdata(pdev); + + mutex_lock(&dev->mutex); + + device = dev->video_in_dev; + + /* Saving the current state */ + retval = pci_save_state(pdev); + + if (0 != retval) { + ioh_err(device, "In %s -> Function " + "pci_save_state failed(%d)", __func__, retval); + } else { + /* Setting flag for suspension. */ + device->suspend_flag = true; + + /* Disabling the interrupts if enabled. */ + if (true == device->open_flag) { + /* Saving the current interrupt settings. */ + device->video_settings.current_interrupt_settings = + device->intenb; + + /* Disabling all the interrupts. */ + spin_lock(&(device->dev_lock)); + device->intenb.drevsem = VSYNC_NOT_SYNC; + device->intenb.dmarenb = DISABLE; + device->intenb.ofintenb = DISABLE; + device->intenb.hsintenb = DISABLE; + device->intenb.vsintenb = DISABLE; + write_intenb(device); + + iowrite32(ASSERT_RESET, + device->base_address + IOH_VIDEO_IN_RESET); + + iowrite32(DE_ASSERT_RESET, + device->base_address + IOH_VIDEO_IN_RESET); + + spin_unlock(&(device->dev_lock)); + } + + /* Disabling the wake up */ + pci_enable_wake(pdev, PCI_D3hot, 0); + + /* Putting the device to a new power state. */ + pci_set_power_state(pdev, pci_choose_state(pdev, state)); + + /* Disabling the device. */ + pci_disable_device(pdev); + + retval = IOH_VIDEOIN_SUCCESS; + } + + mutex_unlock(&dev->mutex); + + ioh_dbg(device, "Function %s ended(%d)", __func__, retval); + return retval; +} + +static int ioh_video_in_pci_resume(struct pci_dev *pdev) +{ + int retval = 0; + struct ioh_vin_dev *dev; + struct BT656_device *device; + + /* Obtaining the associated device structure. */ + dev = (struct ioh_vin_dev *)pci_get_drvdata(pdev); + + mutex_lock(&dev->mutex); + + device = dev->video_in_dev; + + /* Setting the state to power on state. */ + pci_set_power_state(pdev, PCI_D0); + + /* Restoring the state. */ + pci_restore_state(pdev); + + /* Enabling the device. */ + retval = pci_enable_device(pdev); + if (0 != retval) { + ioh_err(device, "In %s -> Function " + "pci_enable_device failed(%d)", __func__, retval); + } else { + /* Disabling wake up feature. */ + pci_enable_wake(pdev, PCI_D3hot, 0); + + /* Setting the device as PCI bus master. */ + pci_set_master(pdev); + + /* If device is in use. */ + if (true == device->open_flag) { + spin_lock(&(device->dev_lock)); + + iowrite32(ASSERT_RESET, + device->base_address + IOH_VIDEO_IN_RESET); + + iowrite32(DE_ASSERT_RESET, + device->base_address + IOH_VIDEO_IN_RESET); + + spin_unlock(&(device->dev_lock)); + + (void)ioh_video_in_set_input_format(device, + device-> + video_settings. + current_input_format); + (void)ioh_video_in_set_output_format(device, + device-> + video_settings. + current_output_format); + (void)ioh_video_in_set_ip_trans(device, + device->video_settings. + current_scan_mode_method); + (void)ioh_video_in_set_luminance_level(device, + device-> + video_settings. + current_luminance_settings); + (void)ioh_video_in_set_rgb_gain(device, + device->video_settings. + current_rgb_gain_settings); + (void)ioh_video_in_set_blank_tim(device, + device->video_settings. + current_blank_tim_settings); + (void)ioh_video_in_set_bb(device, + device->video_settings. + current_bb_mode); + (void)ioh_video_in_set_cb(device, + device->video_settings. + current_cb_settings); + + /* Re-enabling the interrupt settings. */ + spin_lock(&(device->dev_lock)); + device->intenb = device->video_settings. + current_interrupt_settings; + write_intenb(device); + spin_unlock(&(device->dev_lock)); + } + + /* Updating the suspend flag. */ + device->suspend_flag = false; + + retval = IOH_VIDEOIN_SUCCESS; + } + + mutex_unlock(&dev->mutex); + + ioh_dbg(device, "Function %s ended(%d)", __func__, retval); + return retval; +} +#endif /* CONFIG_PM */ + +static struct pci_device_id ioh_video_pcidev_id[] __devinitdata = { + {PCI_DEVICE(PCI_VENDOR_ID_IOH, PCI_DEVICE_ID_IVI_VIDEOIN)}, + {PCI_DEVICE(PCI_VENDOR_ID_IOH, PCI_DEVICE_ID_MP_VIDEOIN)}, + {} +}; + +MODULE_DEVICE_TABLE(pci, ioh_video_pcidev_id); + +static struct pci_driver ioh_video_driver = { + .name = "ioh_video_in", + .id_table = ioh_video_pcidev_id, + .probe = ioh_video_in_pci_probe, + .remove = __devexit_p(ioh_video_in_pci_remove), +#ifdef CONFIG_PM + .suspend = ioh_video_in_pci_suspend, + .resume = ioh_video_in_pci_resume, +#endif +}; + +static int __init ioh_video_in_pci_init(void) +{ + int retval; + + retval = pci_register_driver(&ioh_video_driver); + + if (0 != retval) { + printk(KERN_ERR "ioh_video_in " + "pci_register_driver failed (%d).", retval); + } + + return retval; +} + +static void __exit ioh_video_in_pci_exit(void) +{ + pci_unregister_driver(&ioh_video_driver); +} + +module_init(ioh_video_in_pci_init); +module_exit(ioh_video_in_pci_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("IOH video-in PCI Driver"); + diff --git a/drivers/media/video/ioh_video_in_main.h b/drivers/media/video/ioh_video_in_main.h new file mode 100644 index 0000000..f907ece --- /dev/null +++ b/drivers/media/video/ioh_video_in_main.h @@ -0,0 +1,1058 @@ +/* + * Copyright (C) 2010 OKI SEMICONDUCTOR CO., LTD. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __IOH_VIDEO_IN_H__ +#define __IOH_VIDEO_IN_H__ + +/*! @defgroup VideoIn */ + +/*! @defgroup Global + @ingroup VideoIn */ +/*! @defgroup PCILayer + @ingroup VideoIn */ +/*! @defgroup InterfaceLayer + @ingroup VideoIn */ +/*! @defgroup HALLayer + @ingroup VideoIn */ +/*! @defgroup Utilities + @ingroup VideoIn */ + +/*! @defgroup PCILayerAPI + @ingroup PCILayer */ +/*! @defgroup PCILayerFacilitators + @ingroup PCILayer */ + +/*! @defgroup InterfaceLayerAPI + @ingroup InterfaceLayer */ +/*! @defgroup InterfaceLayerFacilitators + @ingroup InterfaceLayer */ + +/*! @defgroup HALLayerAPI + @ingroup HALLayer */ + +/*! @defgroup UtilitiesAPI + @ingroup Utilities */ + +/*! @defgroup UtilitiesFacilitators + @ingroup Utilities */ + +/* includes */ +#include + +/* enumerators */ +/*! @ingroup InterfaceLayer + @enum ioh_video_in_input_data_format + @brief Defines constants to denote the different supported + input format. + @remarks This enum defines unique constants to denote the + different input formats supported by the + BT656(VideoIn) device. These constants + can be used by the user to specify the input video + data format to + the driver while specifying the input settings. + + @note The constants holds meaningful when used in + combination with other data + for setting input format. + + @see + - ioh_video_in_set_input_format + - ioh_video_in_set_output_format + - ioh_video_in_input_format + */ +enum ioh_video_in_input_data_format { + /* Input format for Square Pixel frequency */ + NT_SQPX_ITU_R_BT_656_4_8BIT, /**< NTSC Square Pixel + ITU-BT656-4 8Bit format. */ + NT_SQPX_ITU_R_BT_656_4_10BIT, /**< NTSC Square Pixel + ITU-BT656-4 10Bit format. */ + NT_SQPX_YCBCR_422_8BIT, /**< NTSC Square Pixel + YCbCr 4:2:2 8Bit format. */ + NT_SQPX_YCBCR_422_10BIT, /**< NTSC Square Pixel + YCbCr 4:2:2 10Bit format. */ + + /* Input format for ITU-R BT.601 */ + NT_BT601_ITU_R_BT_656_4_8BIT, /**< NTSC ITU-R BT.601 + ITU-BT656-4 8Bit format. */ + NT_BT601_ITU_R_BT_656_4_10BIT, /**< NTSC ITU-R BT.601 + ITU-BT656-4 10Bit format. */ + NT_BT601_YCBCR_422_8BIT, /**< NTSC ITU-R BT.601 + YCbCr 4:2:2 8Bit format. */ + NT_BT601_YCBCR_422_10BIT, /**< NTSC ITU-R BT.601 + YCbCr 4:2:2 10Bit format. */ + + /* Input format for RAW. */ + NT_RAW_8BIT, /**< NTSC RAW 8Bit format. */ + NT_RAW_10BIT, /**< NTSC RAW 10Bit format. */ + NT_RAW_12BIT, /**< NTSC RAW 12Bit format. */ + + /* Invalid Input Format. */ + INVALID_INPUT_DATA_FORMAT /**< Invalid Input data format. */ +}; + +/*! @ingroup InterfaceLayer + @enum ioh_video_in_numerical_format + @brief Defines constants indicating the different supported + input/output numerical format. + @remarks This enum defines unique constants to denote the + different numerical + format of the video data supported by the + BT656(VideoIn) device. These + constants can be used by the user to specify the + numerical format of + the video while specifying the input and output video + settings. + @note These constants holds meaningful when used along with + other data. + + @see + - ioh_video_in_set_input_format + - ioh_video_in_set_output_format + - ioh_video_in_input_format + - ioh_video_in_output_format + */ +enum ioh_video_in_numerical_format { + OFFSET_BINARY_FORMAT, /**< Offset binary format. */ + COMPLEMENTARY_FORMAT_OF_2, /**< Complementary format of 2. */ + DONT_CARE_NUMERICAL_FORMAT, /**< Dont care. */ + INVALID_NUMERICAL_FORMAT /**< Invalid numerical format. */ +}; + +/*! @ingroup InterfaceLayer + @enum ioh_video_in_output_data_format + @brief Defines constants indicating the different supported + output formats. + @remarks This enum defines unique constants to denote the + different output video + data formats supported by the BT656(VideoIn) device. + These constants can + be used by the user to specify the output data format + while specifying the + output video settings. + @note The constants holds meaningful when used with other + data for setting output format. + + @see + - ioh_video_in_set_output_format + - ioh_video_in_output_format + */ +enum ioh_video_in_output_data_format { + YCBCR_422_8BIT, /**< YCbCr 4:2:2 8bits format. */ + YCBCR_422_10BIT, /**< YCbCr 4:2:2 10bits format. */ + YCBCR_444_8BIT, /**< YCbCr 4:4:4 8bits format. */ + YCBCR_444_10BIT, /**< YCbCr 4:4:4 10bits + fromat. */ + RGB888, /**< RGB888 format. */ + RGB666, /**< RGB666 format. */ + RGB565, /**< RGB565 format. */ + RAW_8BIT, /**< RAW 8bits format. */ + RAW_10BIT, /**< RAW 10bits format. */ + RAW_12BIT, /**< RAW 12bits format. */ + INVALID_OUTPUT_DATA_FORMAT /**< Invalid output format. */ +}; + +/*! @ingroup InterfaceLayer + @enum ioh_video_in_luminance_range + @brief Defines constants denoting the different supported + Luminance range. + @remarks This enum defines unique constants denoting the + luminance range + format format of the BT656(VideoIN) device. These + constants can + be used by the user to denote the luminance range + format while specifying the output format settings. + + @note The constants holds meaningful when used with other + data for setting output format. + + @see + - ioh_video_in_set_output_format + - ioh_video_in_output_format + */ +enum ioh_video_in_luminance_range { + BT601_LUMINANCE_RANGE = 0x00000000, /**< ITU BT.601 + luminance range. */ + EXTENDENDED_LUMINANCE_RANGE = 0x00000010, /**< Extended + luminance range. */ + DONT_CARE_LUMINANNCE_RANGE = 0x00000011, /**< Dont care + luminance range. */ + INVALID_LUMINANCE_RANGE = 0x000000FF /**< Invalid Luminance + range. */ +}; + +/*! @ingroup InterfaceLayer + @enum ioh_video_in_rgb_gain_RGBLEV + @brief Defines constants denoting the different supported RGB + Level. + @remarks This enum defines unique constants denoting the RGB + level format + supported by the BT656(VideoIn device). These + constants can be used + by the user to denote the RGB Level setting while + specifying the output video settings. + @note The constants holds meaningful when used with other + data for setting output format. + + @see + - ioh_video_in_set_output_format + - ioh_video_in_output_format + */ +enum ioh_video_in_rgb_gain_RGBLEV { + RGB_FULL_SCALE_MODE = 0x00000000, /**< Full scale mode + (0 - 1023). */ + RGB_BT601_MODE = 0x00000008, /**< ITU BT.601 mode + (64 - 940). */ + DONT_CARE_RGBLEV = 0x00000009, /**< Dont care RGB + Gain level. */ + INVALID_RGB_GAIN_LEVEL = 0x000000FF /**< Invalid RGB gain + level. */ +}; + +/*! @ingroup InterfaceLayer + @enum ioh_video_in_scan_mode_method + @brief Defines constants denoting the different supported + scan mode conversion methods. + @remarks This enum defines unique constants to denote the + different + interpolation methods supported by the BT656(VideoIn) + device. + These constants can be used by the user to specify the + scan mode conversion methods i.e. the format for + converting the Interlace + input data format to Progrssive output data format. + + @see + - ioh_video_in_set_ip_trans_mode + - ioh_video_in_get_ip_trans_mode + */ +enum ioh_video_in_scan_mode_method { + LINE_INTERPOLATION = 0x00000000, /**< Line + Interpolation Method. */ + LINE_DOUBLER = 0x00000040, /**< Line doubler method. + */ + INVALID_SCAN_MODE_METHOD = 0x000000FF /**< Invalid scan mode + method. */ +}; + +/*! @ingroup InterfaceLayer + @enum ioh_video_in_luminance_NOSIG + @brief Defines constants denoting the different supported + Luminance NOSIG setting mode. + @remarks This enum defines unique constants to denote the NOSIG + format supported by the BT656(VideoIn) device. These + constants can be used by the user to denote the NOSIG + settings while + specifying the luminance settings. + @note The constants holds meaningful when used with other + data + for setting luminance level. + + @see + - ioh_video_in_set_luminance_level + - ioh_video_in_luminance_settings + */ +enum ioh_video_in_luminance_NOSIG { + NOSIG_NORMAL_MODE = 0x00000000, /**< Noramal mode. */ + NOSIG_NOINMASTER_MODE = 0x00000080, /**< Image non input + master mode. */ + INVALID_LUMINANCE_NOSIG = 0x000000FF /**< Invalid + luminance NOSIG mode. */ +}; + +/*! @ingroup InterfaceLayer + @enum ioh_video_in_luminance_LUMLEV + @brief Defines constants denoting the different Luminance + level setting mode. + @remarks This enum defines unique constants to denote the + LUMLEV format supported by the BT656(VideoIn) device. + These constants can be used by the user to denote the + LUMLEV format while specifying the luminance settings. + @note The constants holds meaningful when used with other + data for setting the luminance level. + + @see + - ioh_video_in_set_luminance_level + - ioh_video_in_luminance_settings + */ +enum ioh_video_in_luminance_LUMLEV { + LUMLEV_78_PERCENT = 0x00000000, /**< 78.125% */ + LUMLEV_81_PERCENT, /**< 81.250% */ + LUMLEV_84_PERCENT, /**< 84.375% */ + LUMLEV_87_PERCENT, /**< 87.500% */ + LUMLEV_90_PERCENT, /**< 90.625% */ + LUMLEV_93_PERCENT, /**< 93.750% */ + LUMLEV_96_PERCENT, /**< 96.875% */ + LUMLEV_100_PERCENT, /**< 100.000% */ + LUMLEV_103_PERCENT, /**< 103.125% */ + LUMLEV_106_PERCENT, /**< 106.250% */ + LUMLEV_109_PERCENT, /**< 109.375% */ + LUMLEV_112_PERCENT, /**< 112.500% */ + LUMLEV_115_PERCENT, /**< 115.625% */ + LUMLEV_118_PERCENT, /**< 118.750% */ + LUMLEV_121_PERCENT, /**< 121.875% */ + LUMLEV_125_PERCENT, /**< 125.000% */ + INVALID_LUMINANCE_LUMLEV /**< Invalid. */ +}; + +/*! @ingroup InterfaceLayer + @enum ioh_video_in_blank_tim_CNTCTL + @brief Defines constants denoting the different supported + CNTCTL mode + settings for Blanking Timing Signal. + @remarks This enum defines unique constants to denote the + different + Blanking Signal Timing Control settings supported by + the BT656(VideoIn) device. These constants can be used + by the user while specifying the Blanking Timing Signal + settings. + @note The constants holds meaningful when use with other + data for setting the Blanking Timing Signal format. + + @see + - ioh_video_in_set_blank_tim + - ioh_video_in_blank_tim_settings + */ +enum ioh_video_in_blank_tim_CNTCTL { + CNTCTL_STANDARD_SIGNAL = 0x00000000, /**< Standard + signal. */ + CNTCTL_NON_STANDARD_SIGNAL = 0x00000080, /**< Non standard + signal. */ + INVALID_BLANK_TIM_CNTCTL = 0x000000FF /**< Invalid Blank + tim settings. */ +}; + +/*! @ingroup InterfaceLayer + @enum ioh_video_in_blank_tim_BLKADJ + @brief Defines constants for denoting the different supported + BLKADJ mode settings for Blanking Timing Signal. + @remarks This enum defines unique constants to denote the + different Blanking Signal Timing Adjustmemt settings + supported by the BT656(VideoIn) device. + These constants can be used by the + user while specifying the Blanking Timing Signal + settings. + @note The constants holds meaningful when use with other + data for setting the Blanking Timing Signal format. + + @see + - ioh_video_in_set_blank_tim + - ioh_video_in_blank_tim_settings + */ +enum ioh_video_in_blank_tim_BLKADJ { + BLKADJ_MINUS_8_PIXEL = 0x00000000, /**< -8 pixel. */ + BLKADJ_MINUS_7_PIXEL, /**< -7 pixel. */ + BLKADJ_MINUS_6_PIXEL, /**< -6 pixel. */ + BLKADJ_MINUS_5_PIXEL, /**< -5 pixel. */ + BLKADJ_MINUS_4_PIXEL, /**< -4 pixel. */ + BLKADJ_MINUS_3_PIXEL, /**< -3 pixel. */ + BLKADJ_MINUS_2_PIXEL, /**< -2 pixel. */ + BLKADJ_MINUS_1_PIXEL, /**< -1 pixel. */ + BLKADJ_0_PIXEL, /**< 0 pixel. */ + BLKADJ_PLUS_1_PIXEL, /**< +1 pixel. */ + BLKADJ_PLUS_2_PIXEL, /**< +2 pixel. */ + BLKADJ_PLUS_3_PIXEL, /**< +3 pixel. */ + BLKADJ_PLUS_4_PIXEL, /**< +4 pixel. */ + BLKADJ_PLUS_5_PIXEL, /**< +5 pixel. */ + BLKADJ_PLUS_6_PIXEL, /**< +6 pixel. */ + BLKADJ_PLUS_7_PIXEL, /**< +7 pixel. */ + INVALID_BLANK_TIM_BLKADJ /**< Invalid. */ +}; + +/*! @ingroup InterfaceLayer + @enum ioh_video_in_bb_mode + @brief Defines constants denoting the different supported + Blue background mode. + @remarks This enum defines unique constants to denote the + Blue Background On/Off settings. These constants + can be used by the user to enable/disable the + Blue background mode. + @note The constants holds meaningful when use with other + data for setting the Blue Background settings. + + @see + - ioh_video_in_set_bb + - ioh_video_in_get_bb + */ +enum ioh_video_in_bb_mode { + BB_OUTPUT_OFF = 0x00000000, /**< Blue background OFF. */ + BB_OUTPUT_ON = 0x00000040, /**< Blue background ON. */ + INVALID_BB_MODE = 0x000000FF /**< Invalid BB mode. */ +}; + +/*! @ingroup InterfaceLayer + @enum ioh_video_in_cb_mode + @brief Defines constants denoting the different supported + Color Bar mode. + @remarks This enum defines unique constants to denote the Color + bar On/Off settings. These constants can be used by the + user to enable/disable the Color Bar settings. + @note The constants holds meaningful when used with other + data for Color Bar settings. + + @see + - ioh_video_in_set_cb + - ioh_video_in_cb_settings + */ +enum ioh_video_in_cb_mode { + CB_OUTPUT_OFF = 0x00000000, /**< Color Bar Mode OFF. */ + CB_OUTPUT_ON = 0x00000080, /**< Color Bar Mode ON. */ + INVALID_CB_MODE = 0x000000FF /**< Invalid CB mode. */ +}; + +/*! @ingroup InterfaceLayer + @enum ioh_video_in_cb_OUTLEV + @brief Defines constants denoting the different supported + output level of the Color Bar. + @remarks This enum defines unique constants to denote the + Output Level format of the Color Bar settings + supported by the BT656(VideoIn) device. + These constants can be used by the user while + specifying the Color Bar settings. + @note The constants holds menaingful when used with other + data for Color Bar settings. + + @see + - ioh_video_in_set_cb + - ioh_video_in_cb_settings + */ +enum ioh_video_in_cb_OUTLEV { + CB_OUTLEV_25_PERCENT = 0x00000000, /**< 25% Color bar. */ + CB_OUTLEV_50_PERCENT, /**< 50% Color bar. */ + CB_OUTLEV_75_PERCENT, /**< 75% Color bar. */ + CB_OUTLEV_100_PERCENT, /**< 100% Color bar. */ + INVALID_CB_OUTLEV /**< Invalid. */ +}; + +/* structures */ +/*! @ingroup InterfaceLayer + @struct ioh_video_in_input_format + @brief The structure used to specify settings of a particular + input format. + @remarks This structure defines the fields used to set/get the + input format settings of the BT656(VideoIn) device. The + user has to fill the individual fields with the unique + constants denoting the respective settings and pass on + to the driver through the respective ioctl calls. + @note The fields specify enum constants which are used to + specify the input format. + + @see + - ioh_video_in_set_input_format + - ioh_video_in_get_input_format + */ +struct ioh_video_in_input_format { + /*Input format */ + enum ioh_video_in_input_data_format format; /**< The input + video data format. */ + + /*IN2S Settings */ + enum ioh_video_in_numerical_format numerical_format; + /**< The input + video numerical format. */ +}; + +/*! @ingroup InterfaceLayer + @struct ioh_video_in_output_format + @brief Structures used to specify the settings of a + particular output format. + @remarks This structure defines the fileds used to set/get the + output format settings of the BT656(VideoIn) device. + The user has to fill the individual fields with the + unique constants denoting the respective settings and + pass on to the driver through the respective ioctl + calls. + @note The fields are constants denoting specific information + about the output format. + + @see + - ioh_video_in_set_output_format + - ioh_video_in_get_output_format + */ +struct ioh_video_in_output_format { + /*Output data format */ + enum ioh_video_in_output_data_format format; + /**< The output video data format. */ + + /*OUT2S Settings */ + enum ioh_video_in_numerical_format numerical_format; + /**< The output video numerical format. */ + + /*SBON Settings */ + enum ioh_video_in_luminance_range luminance_range; + /**< The luminance range format. */ + + /*RGBLEV Settings */ + enum ioh_video_in_rgb_gain_RGBLEV rgb_gain_level; + /**< The RGB gain level format. */ +}; + +/*! @ingroup InterfaceLayer + @struct ioh_video_in_luminance_settings + @brief Structure used to specify the settings for Luminance + level. + @remarks This structure defines the fields used to set/get the + luminanace settings of the BT656(VideoIn) device. + The user has to fill the individual fields with the + unique constants denoting the respective + settings and pass on to the driver through the + respective ioctl calls. + @note The fields are enum constants denoting the different + settings for luminance level. + + @see + - ioh_video_in_set_luminance_level + - ioh_video_in_get_luminance_level + */ +struct ioh_video_in_luminance_settings { + enum ioh_video_in_luminance_NOSIG luminance_nosig; + /**< The NOSIG settings. */ + enum ioh_video_in_luminance_LUMLEV luminance_lumlev; + /**< The LUMLEV settings. */ +}; + +/*! @ingroup InterfaceLayer + @struct ioh_video_in_rgb_gain_settings + @brief Structure used to specify the RGB Gain level. + @remarks This structure defines the fields used to set/get the + RGB gain settings of the BT656(VideoIn) device. + The fields denotes the 8bit register values that has + to be filled in by the user and pass on to the driver + through the respective ioctl call for setting the RGB + gain settings. + @see + - ioh_video_in_set_rgb_gain + - ioh_video_in_get_rgb_gain + */ +struct ioh_video_in_rgb_gain_settings { + unsigned char r_gain; /**< R gain (Values should be + between 0-255). */ + unsigned char g_gain; /**< G gain (Values should be + between 0-255). */ + unsigned char b_gain; /**< B gain (Values should be + between 0-255). */ +}; + +/*! @ingroup InterfaceLayer + @struct ioh_video_in_blank_tim_settings + @brief Structure used to specify the Blanking Timing Signal + Settings. + @remarks This structure defines the fields used to set/get the + Blanking Timing signal settings of the BT656(VideoIn) + device. + These fields have to be set by the user with unique + constants denoting the respective settings and pass + on to the driver through the respective ioctl calls. + @note The fields are enum constants denoting the different + settings of the Blanking Timing Signal. + + @see + - ioh_video_in_set_blank_tim + - ioh_video_in_get_blank_tim + */ +struct ioh_video_in_blank_tim_settings { + enum ioh_video_in_blank_tim_CNTCTL blank_tim_cntctl; + /**< Blanking Timing Signal Control settings. */ + enum ioh_video_in_blank_tim_BLKADJ blank_tim_blkadj; + /**< Blanking Timing Signal Adjustment settings.*/ +}; + +/*! @ingroup InterfaceLayer + @struct ioh_video_in_cb_settings + @breif Structure used to specify the Color bar settings. + @remarks This structure defines the fields used to set/get the + Color Bar settings of the BT656(VideoIn) device. These + fields have to be set by the user with unique constants + denoting the respective settings and pass on to the + driver through the respective ioctl calls. + @note The fields are enum constants denoting the different + Color Bar formats. + + @see + - ioh_video_in_set_cb + - ioh_video_in_get_cb + */ +struct ioh_video_in_cb_settings { + enum ioh_video_in_cb_mode cb_mode; + /**< Color Bar ON/OFF mode. */ + enum ioh_video_in_cb_OUTLEV cb_outlev; + /**< Color Bar Otput level settings. */ +}; + +/*! @ingroup InterfaceLayer + @struct ioh_video_in_frame_size + @breif Structure used to specify the framew size settings. + @remarks This structure defines the fields used to set/get the + frame size settings of the BT656(VideoIn) device. + These fields have to be set by the user with + X and Y components of the frmae and pass on to the + driver through the respective ioctl calls. + @note The fields denote the X and Y components of the frame. + + @see + - ioh_video_in_set_cb + - ioh_video_in_get_cb + */ +struct ioh_video_in_frame_size { + unsigned int X_component; /**< The X_component of the frame. */ + unsigned int Y_component; /**< The Y_component of the frame. */ + unsigned int pitch_size; /**< Pitch byte size */ +}; + +/*! @ingroup InterfaceLayer + @struct ioh_video_in_frame_buffer + @brief The structure for holding the video frame data. +*/ +struct ioh_video_in_frame_buffer { + int index; /* Buffer index */ + unsigned int virt_addr; /* Frame Buffer virtaul address */ + unsigned int phy_addr; /* Frame Buffer Physical address */ + unsigned int data_size; /* data size */ +}; + +/*! @ingroup VideoIn + @def MAXIMUM_FRAME_BUFFERS + @brief Maximum frame buffers to be allocated. + */ +#define MAXIMUM_FRAME_BUFFERS (5) + +/*! @ingroup VideoIn + @struct ioh_video_in_frame_buffer_info + @brief The structure for holding the video frame information. +*/ +struct ioh_video_in_frame_buffer_info { + int buffer_num; /* Number of frame buffer */ + int order; /* Page number log2 N of the frame buffer */ +}; + +/*! @ingroup VideoIn + @struct ioh_video_in_frame_buffers + @brief The structure of some frame buffers. +*/ +struct ioh_video_in_frame_buffers { + struct ioh_video_in_frame_buffer frame_buffer[MAXIMUM_FRAME_BUFFERS]; +}; + +/*! @ingroup InterfaceLayer + @def VIDEO_IN_IOCTL_MAGIC + @brief Outlines the byte value used to define the differnt + ioctl commands. +*/ +#define VIDEO_IN_IOCTL_MAGIC 'V' +#define BASE BASE_VIDIOC_PRIVATE + +/*! @ingroup InterfaceLayer + @def IOH_VIDEO_SET_INPUT_FORMAT + @brief Outlines the value specifing the ioctl command for + setting input format. + @remarks This ioctl command is issued to set the input format + settings. The parameter expected for this is a user + level address which points to a variable of type + struct ioh_video_in_input_format and it contains + values specifying the input format to be set. + @see + - ioh_video_in_ioctl + */ +#define IOH_VIDEO_SET_INPUT_FORMAT (_IOW(VIDEO_IN_IOCTL_MAGIC,\ +BASE + 1, struct ioh_video_in_input_format)) + +/*! @ingroup InterfaceLayer + @def IOH_VIDEO_GET_INPUT_FORMAT + @brief Outlines the value specifing the ioctl command for + getting the current input format + @remarks This ioctl command is issued for getting the current + input format settings. + The expected parameter for this command is a user + level address which points to a variable of type struct + ioh_video_in_input_format, to which the current input + setting has to be updated. + @see + - ioh_video_in_ioctl + */ +#define IOH_VIDEO_GET_INPUT_FORMAT (_IOR(VIDEO_IN_IOCTL_MAGIC,\ +BASE + 2, struct ioh_video_in_input_format)) + +/*! @ingroup InterfaceLayer + @def IOH_VIDEO_SET_OUTPUT_FORMAT + @brief Outlines the value specifing the ioctl command for + setting output format. + @remarks This ioctl command is issued to set the output format + settings. The expected parameter is a user level + address which points to a variable of type + struct ioh_video_in_output_format and it contains + values specifying the required output format. + @see + - ioh_video_in_ioctl + */ +#define IOH_VIDEO_SET_OUTPUT_FORMAT (_IOW(VIDEO_IN_IOCTL_MAGIC,\ +BASE + 3, struct ioh_video_in_output_format)) + +/*! @ingroup InterfaceLayer + @def IOH_VIDEO_GET_OUTPUT_FORMAT + @brief Outlines the value specifing the ioctl command for + getting the current output format. + @remarks This ioctl command is issued for getting the current + output format settings. + The expected parameter is a user level address + pointing to a variable of type + struct ioh_video_in_output_format, to which the + current output setting has to + be updated. + @see + - ioh_video_in_ioctl + */ +#define IOH_VIDEO_GET_OUTPUT_FORMAT (_IOR(VIDEO_IN_IOCTL_MAGIC,\ +BASE + 4, struct ioh_video_in_output_format)) + +/*! @ingroup InterfaceLayer + @def IOH_VIDEO_SET_SIZE + @brief Outlines the value specifing the ioctl command for + setting the frame size. + @remarks This ioctl command is issued for setting the frame + size. The expected parameter + is a user level address pointing to a variable of type + struct ioh_video_in_frame_size + and it contains the frame size value that has to be + set. + @see + - ioh_video_in_ioctl + */ +#define IOH_VIDEO_SET_SIZE (_IOW(VIDEO_IN_IOCTL_MAGIC,\ +BASE + 5, struct ioh_video_in_frame_size)) + +/*! @ingroup InterfaceLayer + @def IOH_VIDEO_GET_SIZE + @brief Outlines the value specifing the ioctl command for + getting the current frame size. + @remarks This ioctl command is issued for getting the current + frame size. The expected + parameter is a user level address pointing to a + variable of type struct ioh_video_in_frame_size + to which the current frame size has to be updated. + @see + - ioh_video_in_ioctl + */ +#define IOH_VIDEO_GET_SIZE (_IOR(VIDEO_IN_IOCTL_MAGIC,\ +BASE + 6, struct ioh_video_in_frame_size)) + +/*! @ingroup InterfaceLayer + @def IOH_VIDEO_SET_IP_TRANS + @brief Outlines the value specifing the ioctl command for + setting the scan mode conversion method. + @remarks This ioctl command is issued for setting the scan mode + conversion method. The expected + parameter is a user level address that points to a + variable of type enum ioh_video_in_scan_mode_method, + and it contains a value specifying the scan mode + conversion method that has to be set. + @see + - ioh_video_in_ioctl + */ +#define IOH_VIDEO_SET_IP_TRANS (_IOW(VIDEO_IN_IOCTL_MAGIC,\ +BASE + 7, enum ioh_video_in_scan_mode_method)) + +/*! @ingroup InterfaceLayer + @def IOH_VIDEO_GET_IP_TRANS + @brief Outlines the value specifing the ioctl command for + getting the current scan mode conversion method. + @remarks This ioctl command is issued for getting the current + scan mode conversion method. The expected + parameter is a user level address that points to a + variable of type enum + ioh_video_in_scan_mode_method to which the current + scan mode conversion method setting + has to be updated. + @see + - ioh_video_in_ioctl + */ +#define IOH_VIDEO_GET_IP_TRANS (_IOR(VIDEO_IN_IOCTL_MAGIC,\ +BASE + 8, enum ioh_video_in_scan_mode_method)) + +/*! @ingroup InterfaceLayer + @def IOH_VIDEO_SET_LUMINENCE_LEVEL + @brief Outlines the value specifing the ioctl command for + setting the luminance level settings. + @remarks This ioctl command is issued for setting the luminance + level of the output video. The expected + parameter is a user level address that points to a + variable of type struct + ioh_video_in_luminance_settings, and it contains + values specifying the required luminance settings. + @see + - ioh_video_in_ioctl + */ +#define IOH_VIDEO_SET_LUMINENCE_LEVEL (_IOW(VIDEO_IN_IOCTL_MAGIC,\ +BASE + 9, struct ioh_video_in_luminance_settings)) + +/*! @ingroup InterfaceLayer + @def IOH_VIDEO_GET_LUMINENCE_LEVEL + @brief Outlines the value specifing the ioctl command for + getting the current luminance level settings. + @remarks This ioctl command is issued for getting the current + luminance settings. The expected parameter is a user + level address pointing to a variable of type + struct ioh_video_in_luminance_settings, + to which the settings has to be updated. + @see + - ioh_video_in_ioctl + */ +#define IOH_VIDEO_GET_LUMINENCE_LEVEL (_IOR(VIDEO_IN_IOCTL_MAGIC,\ +BASE + 10, struct ioh_video_in_luminance_settings)) + +/*! @ingroup InterfaceLayer + @def IOH_VIDEO_SET_RGB_GAIN + @brief Outlines the value specifing the ioctl command for + setting the RGB gain level. + @remarks This ioctl command is issued for setting the RGB gain + settings. The expected parameter + is a user level address which points to a variable of + type struct ioh_video_in_rgb_gain_settings + and it contains values specifying the required RGB Gain + settings. + @see + - ioh_video_in_ioctl + */ +#define IOH_VIDEO_SET_RGB_GAIN (_IOW(VIDEO_IN_IOCTL_MAGIC,\ +BASE + 11, struct ioh_video_in_rgb_gain_settings)) + +/*! @ingroup InterfaceLayer + @def IOH_VIDEO_GET_RGB_GAIN + @brief Outlines the value specifing the ioctl command for + getting the current luminance level setting. + @remarks This ioctl command is issued for getting the current + RGB Gain settings. The expected + parameter is a user level address that points to a + variable of type struct ioh_video_in_rgb_gain_settings, + to which the settings has to be updated. + @see + - ioh_video_in_ioctl + */ +#define IOH_VIDEO_GET_RGB_GAIN (_IOR(VIDEO_IN_IOCTL_MAGIC,\ +BASE + 12, struct ioh_video_in_rgb_gain_settings)) + +/*! @ingroup InterfaceLayer + @def IOH_VIDEO_CAP_START + @brief Outlines the value specifing the ioctl command for + initiating the video capture process. + @remarks This ioctl command is issued to start capturing the + video data. This command does not + expect any parameter. + @see + - ioh_video_in_ioctl + */ +#define IOH_VIDEO_CAP_START (_IO(VIDEO_IN_IOCTL_MAGIC,\ +BASE + 13)) + +/*! @ingroup InterfaceLayer + @def IOH_VIDEO_CAP_STOP + @brief Outlines the value specifing the ioctl command for + stopping the video capturing process. + @remarks This ioctl command is issued to stop capturing the + video data. This command does not expect any parameter. + @see + - ioh_video_in_ioctl + */ +#define IOH_VIDEO_CAP_STOP (_IO(VIDEO_IN_IOCTL_MAGIC,\ +BASE + 14)) + +/*! @ingroup InterfaceLayer + @def IOH_VIDEO_SET_BLANK_TIM + @brief Outlines the value specifing the ioctl command for + setting Blanking Timing Signal. + @remarks This ioctl command is issued for setting the Blanking + Signal timing. The expected parameter is a user level + address which points to a variable of type + struct ioh_video_in_blank_tim_settings and it contains + the values specifying the required settings. + @see + - ioh_video_in_ioctl + */ +#define IOH_VIDEO_SET_BLANK_TIM (_IOW(VIDEO_IN_IOCTL_MAGIC,\ +BASE + 15, struct ioh_video_in_blank_tim_settings)) + +/*! @ingroup InterfaceLayer + @def IOH_VIDEO_GET_BLANK_TIM + @brief Outlines the value specifing the ioctl command for + getting the current Blanking Timing Signal. + @remarks This ioctl command is issued for getting the current + Blanking Signal timing settings. + The expected parameter is a user level address which + points to a variable of type + struct ioh_video_in_blank_tim_settings, to which the + current settings has to be updated. + @see + - ioh_video_in_ioctl + */ +#define IOH_VIDEO_GET_BLANK_TIM (_IOR(VIDEO_IN_IOCTL_MAGIC,\ +BASE + 16, struct ioh_video_in_blank_tim_settings)) + +/*! @ingroup InterfaceLayer + @def IOH_VIDEO_SET_BB + @brief Outlines the value specifing the ioctl command for + setting Blue background mode. + @remarks This ioctl command is issued for setting the Blue + Background settings. The expected + parameter is a user level address which points to + variable of type enum + ioh_video_in_bb_mode + and it contains the value specifying the required + settings. + @see + - ioh_video_in_ioctl + */ +#define IOH_VIDEO_SET_BB (_IOW(VIDEO_IN_IOCTL_MAGIC,\ +BASE + 17, enum ioh_video_in_bb_mode)) + +/*! @ingroup InterfaceLayer + @def IOH_VIDEO_GET_BB + @brief Outlines the value specifing the ioctl command for + getting the current Blue background mode. + @remarks This ioctl command is issued for getting the current + Blue background settings. The + expected parameter is a user level address + which points to a variable of type + enum ioh_video_in_bb_mode, + to which the current settings has to be updated. + @see + - ioh_video_in_ioctl + */ +#define IOH_VIDEO_GET_BB (_IOR(VIDEO_IN_IOCTL_MAGIC,\ +BASE + 18, enum ioh_video_in_bb_mode)) + +/*! @ingroup InterfaceLayer + @def IOH_VIDEO_SET_CB + @brief Outlines the value specifing the ioctl command for + setting Color bar output level. + @remarks This ioctl command is issued for setting the Color Bar + settings. The expected parameter is a + user level address which points to a variable of type + struct ioh_video_in_cb_settings and it + contains values specifying the required settings. + @see + - ioh_video_in_ioctl + */ +#define IOH_VIDEO_SET_CB (_IOW(VIDEO_IN_IOCTL_MAGIC,\ +BASE + 19, struct ioh_video_in_cb_settings)) + +/*! @ingroup InterfaceLayer + @def IOH_VIDEO_GET_CB + @brief Outlines the value specifing the ioctl command for + getting the current Color bar output level. + @remarks This ioctl command is issued for getting the current + color bar settings. The expected + parameter is a user level address which points to a + variable of type struct + ioh_video_in_cb_settings, to which the current + settings has to be updated. + @see + - ioh_video_in_ioctl + */ +#define IOH_VIDEO_GET_CB (_IOR(VIDEO_IN_IOCTL_MAGIC,\ +BASE + 20, struct ioh_video_in_cb_settings)) + +/*! @ingroup InterfaceLayer + @def IOH_VIDEO_GET_BUFFER_SIZE + @brief Outlines the value specifing the ioctl command for + getting the buffer size. + @remarks This ioctl command is issued for getting the buffer + size. The expected parameter is a + user level address which points to a variable of type + unsigned long, to which the buffer + size has to be updated. + @see + - ioh_video_in_ioctl + */ +#define IOH_VIDEO_GET_BUFFER_SIZE (_IOR(VIDEO_IN_IOCTL_MAGIC,\ +BASE + 21, unsigned long)) + +#if 0 +/*! @ingroup InterfaceLayer + @def IOH_VIDEO_ALLOC_FRAME_BUFFER + @brief Outlines the value specifing the ioctl command for + allocate the frame buffers. + @remarks This ioctl command is issued to allocate the + frame buffers. + The expected parameter is a user level address which + points to a variable of type struct + ioh_video_in_frame_buffer_info. + @see + - ioh_video_in_ioctl + */ +#define IOH_VIDEO_ALLOC_FRAME_BUFFER (_IOW(VIDEO_IN_IOCTL_MAGIC,\ +BASE + 22, struct ioh_video_in_frame_buffer_info)) + +/*! @ingroup InterfaceLayer + @def IOH_VIDEO_FREE_FRAME_BUFFER + @brief Outlines the value specifing the ioctl command for + free the frame buffers. + @remarks This ioctl command is issued to free the + frame buffers. + The expected parameter is a user level address which + points to a variable of type struct + ioh_video_in_frame_buffer_info. + @see + - ioh_video_in_ioctl + */ +#define IOH_VIDEO_FREE_FRAME_BUFFER (_IOW(VIDEO_IN_IOCTL_MAGIC,\ +BASE + 23, struct ioh_video_in_frame_buffer_info)) +#endif + +/*! @ingroup InterfaceLayer + @def IOH_VIDEO_GET_FRAME_BUFFERS + @brief Outlines the value specifing the ioctl command for + read the information of the frame buffers. + @remarks This ioctl command is issued to get the information of + frame buffers. + The expected parameter is a user level address which + points to a variable of type struct + ioh_video_in_frame_buffer. + @see + - ioh_video_in_ioctl + */ +#define IOH_VIDEO_GET_FRAME_BUFFERS (_IOR(VIDEO_IN_IOCTL_MAGIC,\ +BASE + 24, struct ioh_video_in_frame_buffers)) + +/*! @ingroup InterfaceLayer + @def IOH_VIDEO_READ_FRAME_BUFFER + @brief Outlines the value specifing the ioctl command for + read the frame buffer. + @remarks This ioctl command is issued to get frame buffer. + The expected parameter is a user level address which + points to a variable of type struct + ioh_video_in_frame_buffer. + @see + - ioh_video_in_ioctl + */ +#define IOH_VIDEO_READ_FRAME_BUFFER (_IOR(VIDEO_IN_IOCTL_MAGIC,\ +BASE + 25, struct ioh_video_in_frame_buffer)) + + +/*! @ingroup VideoIn + @def IOH_VIDEOIN_SUCCESS + @brief Outlines the return value of the function on success. +*/ +#define IOH_VIDEOIN_SUCCESS (0) + +/*! @ingroup VideoIn + @def IOH_VIDEOIN_FAIL + @brief Outlines the return value of the function on failure. + */ +#define IOH_VIDEOIN_FAIL (-1) + + +#endif + + diff --git a/drivers/media/video/ioh_video_in_ml86v76651.c b/drivers/media/video/ioh_video_in_ml86v76651.c new file mode 100644 index 0000000..7231e32 --- /dev/null +++ b/drivers/media/video/ioh_video_in_ml86v76651.c @@ -0,0 +1,620 @@ +/* + * Copyright (C) 2010 OKI SEMICONDUCTOR CO., LTD. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_DESCRIPTION("IOH video-in driver for OKI SEMICONDUCTOR ML86V76651."); +MODULE_LICENSE("GPL"); + +static int debug; +module_param(debug, bool, 0644); +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + +/* + * Basic window sizes. These probably belong somewhere more globally + * useful. + */ +#define VGA_WIDTH 640 +#define VGA_HEIGHT 480 + +/* Registers */ +#define REG_SSEPL 0x37 /* Sync Separation Level Contrl */ + +#define REG_COM7 0x12 /* Control 7 */ +#define COM7_FMT_HDTV 0x00 +#define COM7_FMT_VGA 0x40 /* VGA format */ +#define COM7_FMT_CIF 0x20 /* CIF format */ +#define COM7_FMT_QVGA 0x10 /* QVGA format */ +#define COM7_FMT_QCIF 0x08 /* QCIF format */ +#define REG_COM8 0x13 /* Control 8 */ +#define COM8_AEC 0x01 /* Auto exposure enable */ + + +#define REG_PID 0x0a /* Product ID MSB */ +#define REG_VER 0x0b /* Product ID LSB */ + +#define REG_MIDH 0x1c /* Manuf. ID high */ +#define REG_MIDL 0x1d /* Manuf. ID low */ + + +#define REG_HREF 0x32 /* HREF pieces */ +#define REG_VREF 0x03 /* Pieces of GAIN, VSTART, VSTOP */ + +#define REG_AECHM 0xa1 /* AEC MSC 5bit */ +#define REG_AECH 0x10 /* AEC value */ + + +/* + * Information we maintain about a known sensor. + */ +struct ml86v76651_format_struct; /* coming later */ +struct ml86v76651_info { + struct v4l2_subdev sd; + struct ml86v76651_format_struct *fmt; /* Current format */ + unsigned char sat; /* Saturation value */ + int hue; /* Hue value */ +}; + +static inline struct ml86v76651_info *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct ml86v76651_info, sd); +} + + + +/* + * The default register settings. + */ + +struct regval_list { + unsigned char reg_num; + unsigned char value; +}; + +static struct regval_list ml86v76651_default_regs[] = { +#ifdef IOH_VIDEO_IN_ML86V76653 +#else + {0x71, 0x00}, /* for Device Workaround */ + {0x71, 0x80}, /* 32MHz SQ_PIXEL */ +#endif + {0x00, 0x02}, /* 32MHz SQ_PIXEL */ + {0x01, 0x00}, /* BT656 */ + {0x51, 0x80}, /* Normal out */ + {0x50, 0x89}, /* Normal out */ + {0x68, 0xe0}, /* analog control */ + {0x78, 0x22}, /* status1 is ODD/EVEN */ +#ifdef IOH_VIDEO_IN_ML86V76653 +#else + {0x6f, 0x80}, /* Others */ +#endif + {0xff, 0xff}, /* end */ +}; + + +static struct regval_list ml86v76651_fmt_yuv422[] = { +#ifdef IOH_VIDEO_IN_ML86V76653 +#else + { 0x71, 0x00 }, /* for Device Workaround */ + { 0x71, 0x80 }, /* 32MHz SQ_PIXEL */ +#endif + { 0x00, 0x02 }, /* 32MHz SQ_PIXEL */ + { 0x01, 0x00 }, /* BT656 */ + { 0x51, 0x80 }, /* Normal out */ + { 0x50, 0x89 }, /* Normal out */ + { 0x68, 0xe0 }, /* analog control */ + { 0x78, 0x22 }, /* status1 is ODD/EVEN */ +#ifdef IOH_VIDEO_IN_ML86V76653 +#else + { 0x6f, 0x80 }, /* Others */ +#endif + { 0xff, 0xff }, /* end */ +}; + + +/* + * Low-level register I/O. + */ + +#if 1 +static int ioh_video_in_read_value(struct i2c_client *client, u8 reg, u8 *val) +{ + u8 data = 0; + + client->flags = 0; + data = 0; + if (i2c_master_send(client, &data, 1) != 1) + goto err; + msleep(2); + + client->flags = 0; + data = reg; + if (i2c_master_send(client, &data, 1) != 1) + goto err; + msleep(2); + + if (i2c_master_recv(client, &data, 1) != 1) + goto err; + msleep(2); + + *val = data; + + v4l_dbg(1, debug, client, "Function %s A(0x%02X) --> 0x%02X end.", + __func__, reg, *val); + + return 0; + +err: + v4l_err(client, "Function %s A(0x%02X) 0x%02X read error failed.", + __func__, reg, *val); + + return -EINVAL; +} + +static int ioh_video_in_write_value(struct i2c_client *client, u8 reg, u8 val) +{ + u8 data = 0; + unsigned char data2[2] = { reg, val }; + + client->flags = 0; + data = 0; + if (i2c_master_send(client, &data, 1) != 1) + goto err; + msleep(2); + + if (i2c_master_send(client, data2, 2) != 2) + goto err; + msleep(2); + + v4l_dbg(1, debug, client, "Function %s A(0x%02X) <-- 0x%02X end.", + __func__, reg, val); + + return 0; + +err: + v4l_err(client, "Function %s A(0x%02X) <-- 0x%02X write error failed.", + __func__, reg, val); + + return -EINVAL; +} + +#endif + +static int ml86v76651_read(struct v4l2_subdev *sd, unsigned char reg, + unsigned char *value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret; + +#if 0 + ret = i2c_smbus_read_byte_data(client, reg); + if (ret >= 0) { + *value = (unsigned char)ret; + ret = 0; + } +#else + ret = ioh_video_in_read_value(client, reg, value); +#endif + return ret; +} +static int ml86v76651_write(struct v4l2_subdev *sd, unsigned char reg, + unsigned char value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); +#if 0 + int ret = i2c_smbus_write_byte_data(client, reg, value); +#else + int ret = ioh_video_in_write_value(client, reg, value); +#endif + + /* This sensor doesn't have reset register... */ + + return ret; +} + + +/* + * Write a list of register settings; ff/ff stops the process. + */ +static int ml86v76651_write_array(struct v4l2_subdev *sd, + struct regval_list *vals) +{ + while (vals->reg_num != 0xff || vals->value != 0xff) { + int ret = ml86v76651_write(sd, vals->reg_num, vals->value); + if (ret < 0) + return ret; + vals++; + } + return 0; +} + + +/* + * Stuff that knows about the sensor. + */ +static int ml86v76651_reset(struct v4l2_subdev *sd, u32 val) +{ + /* This sensor doesn't have reset register... */ + return 0; +} + + +static int ml86v76651_init(struct v4l2_subdev *sd, u32 val) +{ + return ml86v76651_write_array(sd, ml86v76651_default_regs); +} + + +static int ml86v76651_detect(struct v4l2_subdev *sd) +{ + int ret; + + ret = ml86v76651_init(sd, 0); + if (ret < 0) + return ret; + + /* This sensor doesn't have id register... */ + + return 0; +} + +static struct ml86v76651_format_struct { + enum v4l2_mbus_pixelcode mbus_code; + enum v4l2_colorspace colorspace; + struct regval_list *regs; +} ml86v76651_formats[] = { + { + .mbus_code = V4L2_MBUS_FMT_UYVY8_2X8, + .colorspace = V4L2_COLORSPACE_JPEG, + .regs = ml86v76651_fmt_yuv422, + }, +}; +#define N_ML86V76651_FMTS ARRAY_SIZE(ml86v76651_formats) + +static struct regval_list ml86v76651_vga_regs[] = { + { 0xff, 0xff }, +}; + + +static struct ml86v76651_win_size { + int width; + int height; + struct regval_list *regs; /* Regs to tweak */ +/* h/vref stuff */ +} ml86v76651_win_sizes[] = { + /* VGA */ + { + .width = VGA_WIDTH, + .height = VGA_HEIGHT, + .regs = ml86v76651_vga_regs, + }, +}; + +#define N_WIN_SIZES (ARRAY_SIZE(ml86v76651_win_sizes)) + +static int ml86v76651_try_fmt_internal(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt, + struct ml86v76651_format_struct **ret_fmt, + struct ml86v76651_win_size **ret_wsize) +{ + int index; + struct ml86v76651_win_size *wsize; + + for (index = 0; index < N_ML86V76651_FMTS; index++) + if (ml86v76651_formats[index].mbus_code == fmt->code) + break; + if (index >= N_ML86V76651_FMTS) { + /* default to first format */ + index = 0; + fmt->code = ml86v76651_formats[0].mbus_code; + } + if (ret_fmt != NULL) + *ret_fmt = ml86v76651_formats + index; + + fmt->field = V4L2_FIELD_NONE; + + for (wsize = ml86v76651_win_sizes; + wsize < ml86v76651_win_sizes + N_WIN_SIZES; wsize++) + if (fmt->width >= wsize->width && fmt->height >= wsize->height) + break; + if (wsize >= ml86v76651_win_sizes + N_WIN_SIZES) + wsize--; /* Take the smallest one */ + if (ret_wsize != NULL) + *ret_wsize = wsize; + /* + * Note the size we'll actually handle. + */ + fmt->width = wsize->width; + fmt->height = wsize->height; + fmt->colorspace = ml86v76651_formats[index].colorspace; + + return 0; +} + +static int ml86v76651_try_mbus_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt) +{ + return ml86v76651_try_fmt_internal(sd, fmt, NULL, NULL); +} + +/* + * Set a format. + */ +static int ml86v76651_s_mbus_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt) +{ + int ret; + struct ml86v76651_format_struct *ovfmt; + struct ml86v76651_win_size *wsize; + struct ml86v76651_info *info = to_state(sd); + + ret = ml86v76651_try_fmt_internal(sd, fmt, &ovfmt, &wsize); + if (ret) + return ret; + + /* Reset */ + ml86v76651_reset(sd, 0); + + /* + * Now write the rest of the array. + */ + ml86v76651_write_array(sd, ovfmt->regs); + ret = 0; + if (wsize->regs) + ret = ml86v76651_write_array(sd, wsize->regs); + info->fmt = ovfmt; + + return ret; +} + + +/* + * Code for dealing with controls. + */ + +static unsigned char ml86v76651_sm_to_abs(unsigned char v) +{ + if ((v & 0x40) == 0) + return 63 - (v & 0x3f); + return 127 - (v & 0x3f); +} + + +static unsigned char ml86v76651_abs_to_sm(unsigned char v) +{ + if (v > 63) + return ((63 - v) | 0x40) & 0x7f; + return (63 - v) & 0x7f; +} + +static int ml86v76651_s_brightness(struct v4l2_subdev *sd, int value) +{ + unsigned char v; + int ret; + + v = ml86v76651_abs_to_sm(value); + + ret = ml86v76651_write(sd, REG_SSEPL, v); + + return ret; +} + +static int ml86v76651_g_brightness(struct v4l2_subdev *sd, __s32 *value) +{ + unsigned char v = 0; + int ret; + + ret = ml86v76651_read(sd, REG_SSEPL, &v); + + *value = ml86v76651_sm_to_abs(v); + + return ret; +} + + + +static int ml86v76651_queryctrl(struct v4l2_subdev *sd, + struct v4l2_queryctrl *qc) +{ + /* Fill in min, max, step and default value for these controls. */ + switch (qc->id) { + case V4L2_CID_BRIGHTNESS: + return v4l2_ctrl_query_fill(qc, 0, 127, 1, 63); + case V4L2_CID_CONTRAST: + case V4L2_CID_VFLIP: + case V4L2_CID_HFLIP: + case V4L2_CID_SATURATION: + case V4L2_CID_HUE: + return -EINVAL; + } + return -EINVAL; +} + +static int ml86v76651_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + return ml86v76651_g_brightness(sd, &ctrl->value); + case V4L2_CID_CONTRAST: + case V4L2_CID_SATURATION: + case V4L2_CID_HUE: + case V4L2_CID_VFLIP: + case V4L2_CID_HFLIP: + return -EINVAL; + } + return -EINVAL; +} + +static int ml86v76651_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + return ml86v76651_s_brightness(sd, ctrl->value); + case V4L2_CID_CONTRAST: + case V4L2_CID_SATURATION: + case V4L2_CID_HUE: + case V4L2_CID_VFLIP: + case V4L2_CID_HFLIP: + return -EINVAL; + } + return -EINVAL; +} + +static int ml86v76651_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_OV7670, 0); +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int ml86v76651_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + unsigned char val = 0; + int ret; + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + ret = ml86v76651_read(sd, reg->reg & 0xff, &val); + reg->val = val; + reg->size = 1; + return ret; +} + +static int ml86v76651_s_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + ml86v76651_write(sd, reg->reg & 0xff, reg->val & 0xff); + return 0; +} +#endif + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops ml86v76651_core_ops = { + .g_chip_ident = ml86v76651_g_chip_ident, + .g_ctrl = ml86v76651_g_ctrl, + .s_ctrl = ml86v76651_s_ctrl, + .queryctrl = ml86v76651_queryctrl, + .reset = ml86v76651_reset, + .init = ml86v76651_init, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = ml86v76651_g_register, + .s_register = ml86v76651_s_register, +#endif +}; + +static const struct v4l2_subdev_video_ops ml86v76651_video_ops = { + .try_mbus_fmt = ml86v76651_try_mbus_fmt, + .s_mbus_fmt = ml86v76651_s_mbus_fmt, +}; + +static const struct v4l2_subdev_ops ml86v76651_ops = { + .core = &ml86v76651_core_ops, + .video = &ml86v76651_video_ops, +}; + +/* ----------------------------------------------------------------------- */ + +static int ml86v76651_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct v4l2_subdev *sd; + struct ml86v76651_info *info; + int ret; + + info = kzalloc(sizeof(struct ml86v76651_info), GFP_KERNEL); + if (info == NULL) + return -ENOMEM; + sd = &info->sd; + v4l2_i2c_subdev_init(sd, client, &ml86v76651_ops); + + /* Make sure it's an ml86v76651 */ + ret = ml86v76651_detect(sd); + if (ret) { + v4l_dbg(1, debug, client, + "chip found @ 0x%x (%s) is not an ml86v76651 chip.\n", + client->addr << 1, client->adapter->name); + kfree(info); + return ret; + } + v4l_info(client, "chip found @ 0x%02x (%s)\n", + client->addr << 1, client->adapter->name); + + info->fmt = &ml86v76651_formats[0]; + info->sat = 128; /* Review this */ + + return 0; +} + + +static int ml86v76651_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(to_state(sd)); + return 0; +} + +static const struct i2c_device_id ml86v76651_id[] = { + { "ioh_i2c", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ml86v76651_id); + +static struct i2c_driver ml86v76651_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "ioh_i2c", + }, + .probe = ml86v76651_probe, + .remove = ml86v76651_remove, + .id_table = ml86v76651_id, +}; + +static __init int init_ml86v76651(void) +{ + return i2c_add_driver(&ml86v76651_driver); +} + +static __exit void exit_ml86v76651(void) +{ + i2c_del_driver(&ml86v76651_driver); +} + +module_init(init_ml86v76651); +module_exit(exit_ml86v76651); + + diff --git a/drivers/media/video/ioh_video_in_ncm13j.c b/drivers/media/video/ioh_video_in_ncm13j.c new file mode 100644 index 0000000..8f38f7d --- /dev/null +++ b/drivers/media/video/ioh_video_in_ncm13j.c @@ -0,0 +1,584 @@ +/* + * Copyright (C) 2010 OKI SEMICONDUCTOR CO., LTD. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_DESCRIPTION("IOH video-in driver for NIPPON CEMI-CON NCM13J."); +MODULE_LICENSE("GPL"); + +static int debug; +module_param(debug, bool, 0644); +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + +/* + * Basic window sizes. These probably belong somewhere more globally + * useful. + */ +#define QVGA_WIDTH 320 +#define QVGA_HEIGHT 240 +#define VGA_WIDTH 640 +#define VGA_HEIGHT 480 +#define HDTV_WIDTH 1280 +#define HDTV_HEIGHT 720 +#define SXGA_WIDTH 1280 +#define SXGA_HEIGHT 1024 + +/* Registers */ + +#define REG_RESET 0x000d +#define ASSERT_RESET 0x0023 +#define DEASSERT_RESET 0x0008 + +#define REG_UNIQUE_ID 0x0000 /* Manuf. ID address */ +#define REG_UNIQUE_VAL 0x148c /* Manuf. ID value */ + +/* + * Information we maintain about a known sensor. + */ +struct ncm13j_format_struct; /* coming later */ +struct ncm13j_info { + struct v4l2_subdev sd; + struct ncm13j_format_struct *fmt; /* Current format */ + unsigned char sat; /* Saturation value */ + int hue; /* Hue value */ +}; + +static inline struct ncm13j_info *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct ncm13j_info, sd); +} + + + +/* + * The default register settings. + */ + +struct regval_list { + u16 reg_num; + u16 value; +}; + +static struct regval_list ncm13j_default_regs[] = { + { 0x0066, 0x1b01}, /* PLL M=27 N=1 */ + { 0x0067, 0x0503}, /* PLL P=3 */ + { 0x0065, 0xa000}, /* PLL power up */ + { 0x0065, 0x2000}, /* PLL enable */ + { 0xffff, 0xffff}, /* end */ +}; + + +static struct regval_list ncm13j_fmt_yuv422[] = { + { 0x013a, 0x0200}, /* Output Format Control A */ + { 0x019b, 0x0200}, /* Output Format Control B */ + { 0xffff, 0xffff}, /* end */ +}; + + + +/* + * Low-level register I/O. + */ + +static int ioh_video_in_read_value(struct i2c_client *client, u8 reg, u8 *val) +{ + u8 data = 0; + + client->flags = 0; + data = reg; + if (i2c_master_send(client, &data, 1) != 1) + goto err; + msleep(2); + + if (i2c_master_recv(client, &data, 1) != 1) + goto err; + msleep(2); + + *val = data; + + v4l_dbg(1, debug, client, "Function %s A(0x%02X) --> 0x%02X end.", + __func__, reg, *val); + + return 0; + +err: + v4l_err(client, "Function %s A(0x%02X) 0x%02X read error failed.", + __func__, reg, *val); + + return -EINVAL; +} + +static int ioh_video_in_write_value(struct i2c_client *client, u8 reg, u8 val) +{ + unsigned char data2[2] = { reg, val }; + + client->flags = 0; + + if (i2c_master_send(client, data2, 2) != 2) + goto err; + msleep(2); + + v4l_dbg(1, debug, client, "Function %s A(0x%02X) <-- 0x%02X end.", + __func__, reg, val); + + return 0; + +err: + v4l_err(client, "Function %s A(0x%02X) <-- 0x%02X write error failed.", + __func__, reg, val); + + return -EINVAL; +} + +static int ncm13j_read(struct v4l2_subdev *sd, u16 reg, u16 *value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret; + unsigned char reg8; + unsigned char val8, valh, vall; + + /* Page_h setting */ + reg8 = 0xf0; + val8 = 0x00; + ret = ioh_video_in_write_value(client, reg8, val8); + + /* Page_l setting */ + reg8 = 0xf1; + val8 = (0x0700 & reg) >> 8; + ret = ioh_video_in_write_value(client, reg8, val8); + + /* MSB8 Read */ + reg8 = (0x00ff & reg); + ret = ioh_video_in_read_value(client, reg8, &valh); + + /* LSB8 Read */ + reg8 = 0xf1; + ret = ioh_video_in_read_value(client, reg8, &vall); + + *value = ((0x00ff & valh) << 8) | (0x00ff & vall); + + return ret; +} +static int ncm13j_write(struct v4l2_subdev *sd, u16 reg, u16 value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret; + unsigned char reg8; + unsigned char val8; + + /* Page_h Write */ + reg8 = 0xf0; + val8 = 0x00; + ret = ioh_video_in_write_value(client, reg8, val8); + + /* Page_l Write */ + reg8 = 0xf1; + val8 = (0x0700 & reg) >> 8; + ret = ioh_video_in_write_value(client, reg8, val8); + + /* MSB8 Write */ + reg8 = (0x00ff & reg); + val8 = (0xff00 & value) >> 8; + ret = ioh_video_in_write_value(client, reg8, val8); + + /* LSB8 Write */ + reg8 = 0xf1; + val8 = (0x00ff & value); + ret = ioh_video_in_write_value(client, reg8, val8); + + return ret; +} + + +/* + * Write a list of register settings; ff/ff stops the process. + */ +static int ncm13j_write_array(struct v4l2_subdev *sd, struct regval_list *vals) +{ + while (vals->reg_num != 0xffff || vals->value != 0xffff) { + int ret = ncm13j_write(sd, vals->reg_num, vals->value); + if (ret < 0) + return ret; + vals++; + } + return 0; +} + + +/* + * Stuff that knows about the sensor. + */ +static int ncm13j_reset(struct v4l2_subdev *sd, u32 val) +{ + ncm13j_write(sd, REG_RESET, ASSERT_RESET); + msleep(1); + ncm13j_write(sd, REG_RESET, DEASSERT_RESET); + msleep(1); + return 0; +} + + +static int ncm13j_init(struct v4l2_subdev *sd, u32 val) +{ + return ncm13j_write_array(sd, ncm13j_default_regs); +} + + +static int ncm13j_detect(struct v4l2_subdev *sd) +{ + u16 v; + int ret; + + ret = ncm13j_reset(sd, 0); + if (ret < 0) + return ret; + ret = ncm13j_read(sd, REG_UNIQUE_ID, &v); + if (ret < 0) + return ret; + if (v != REG_UNIQUE_VAL) /* id. */ + return -ENODEV; + ret = ncm13j_init(sd, 0); + if (ret < 0) + return ret; + return 0; +} + +static struct ncm13j_format_struct { + enum v4l2_mbus_pixelcode mbus_code; + enum v4l2_colorspace colorspace; + struct regval_list *regs; +} ncm13j_formats[] = { + { + .mbus_code = V4L2_MBUS_FMT_UYVY8_2X8, + .colorspace = V4L2_COLORSPACE_JPEG, + .regs = ncm13j_fmt_yuv422, + }, +}; +#define N_NCM13J_FMTS ARRAY_SIZE(ncm13j_formats) + +static struct regval_list ncm13j_qvga_regs[] = { + { 0x01a7, QVGA_WIDTH}, /* Horizontal Output Size A = 320 */ + { 0x01aa, QVGA_HEIGHT}, /* Vertical Output Size A = 240 */ + /* { 0x01a6, QVGA_WIDTH}, Horizontal Zoom = 320 */ + /* { 0x01a9, QVGA_HEIGHT}, Vertical Zoom = 240 */ + { 0x01ae, 0x0c09}, /* Reducer Zoom Step Size */ + { 0x00c8, 0x0000}, /* Context A */ + { 0x02c8, 0x0000}, /* Context A */ + { 0xffff, 0xffff}, /* end */ +}; + +static struct regval_list ncm13j_vga_regs[] = { + { 0x01a7, VGA_WIDTH}, /* Horizontal Output Size A = 640 */ + { 0x01aa, VGA_HEIGHT}, /* Vertical Output Size A = 480 */ + /* { 0x01a6, VGA_WIDTH}, Horizontal Zoom = 640 */ + /* { 0x01a9, VGA_HEIGHT}, Vertical Zoom = 480 */ + { 0x01ae, 0x0c09}, /* Reducer Zoom Step Size */ + { 0x00c8, 0x0000}, /* Context A */ + { 0x02c8, 0x0000}, /* Context A */ + { 0xffff, 0xffff}, /* end */ +}; + +static struct regval_list ncm13j_hdtv_regs[] = { + { 0x01a1, HDTV_WIDTH}, /* Horizontal Output Size B = 1280 */ + { 0x01a4, HDTV_HEIGHT}, /* Vertical Output Size B = 720 */ + { 0x01a6, HDTV_WIDTH}, /* Horizontal Zoom = 1280 */ + { 0x01a9, HDTV_HEIGHT}, /* Vertical Zoom = 720 */ + { 0x01ae, 0x1009}, /* Reducer Zoom Step Size */ + { 0x00c8, 0x000b}, /* Context B */ + { 0x02c8, 0x070b}, /* Context B */ + { 0xffff, 0xffff}, /* end */ +}; + +static struct regval_list ncm13j_sxga_regs[] = { + { 0x01a1, SXGA_WIDTH}, /* Horizontal Output Size B = 1280 */ + { 0x01a4, SXGA_HEIGHT}, /* Vertical Output Size B = 1024 */ + { 0x01a6, SXGA_WIDTH}, /* Horizontal Zoom = 1280 */ + { 0x01a9, SXGA_HEIGHT}, /* Vertical Zoom = 1024 */ + { 0x01ae, 0x0a08}, /* Reducer Zoom Step Size */ + { 0x00c8, 0x000b}, /* Context B */ + { 0x02c8, 0x070b}, /* Context B */ + { 0xffff, 0xffff}, /* end */ +}; + +static struct ncm13j_win_size { + int width; + int height; + struct regval_list *regs; /* Regs to tweak */ +/* h/vref stuff */ +} ncm13j_win_sizes[] = { + /* SXGA */ + { + .width = SXGA_WIDTH, + .height = SXGA_HEIGHT, + .regs = ncm13j_sxga_regs, + }, + /* HDTV */ + { + .width = HDTV_WIDTH, + .height = HDTV_HEIGHT, + .regs = ncm13j_hdtv_regs, + }, + /* VGA */ + { + .width = VGA_WIDTH, + .height = VGA_HEIGHT, + .regs = ncm13j_vga_regs, + }, + /* QVGA */ + { + .width = QVGA_WIDTH, + .height = QVGA_HEIGHT, + .regs = ncm13j_qvga_regs, + }, +}; + +#define N_WIN_SIZES (ARRAY_SIZE(ncm13j_win_sizes)) + +static int ncm13j_try_fmt_internal(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt, + struct ncm13j_format_struct **ret_fmt, + struct ncm13j_win_size **ret_wsize) +{ + int index; + struct ncm13j_win_size *wsize; + + for (index = 0; index < N_NCM13J_FMTS; index++) + if (ncm13j_formats[index].mbus_code == fmt->code) + break; + if (index >= N_NCM13J_FMTS) { + /* default to first format */ + index = 0; + fmt->code = ncm13j_formats[0].mbus_code; + } + if (ret_fmt != NULL) + *ret_fmt = ncm13j_formats + index; + + fmt->field = V4L2_FIELD_NONE; + + for (wsize = ncm13j_win_sizes; + wsize < ncm13j_win_sizes + N_WIN_SIZES; wsize++) + if (fmt->width >= wsize->width && fmt->height >= wsize->height) + break; + if (wsize >= ncm13j_win_sizes + N_WIN_SIZES) + wsize--; /* Take the smallest one */ + if (ret_wsize != NULL) + *ret_wsize = wsize; + /* + * Note the size we'll actually handle. + */ + fmt->width = wsize->width; + fmt->height = wsize->height; + fmt->colorspace = ncm13j_formats[index].colorspace; + + return 0; +} + +static int ncm13j_try_mbus_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt) +{ + return ncm13j_try_fmt_internal(sd, fmt, NULL, NULL); +} + +/* + * Set a format. + */ +static int ncm13j_s_mbus_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt) +{ + int ret; + struct ncm13j_format_struct *ovfmt; + struct ncm13j_win_size *wsize; + struct ncm13j_info *info = to_state(sd); + + ret = ncm13j_try_fmt_internal(sd, fmt, &ovfmt, &wsize); + + if (ret) + return ret; + + /* Reset */ + ncm13j_reset(sd, 0); + + /* + * Now write the rest of the array. Also store start/stops + */ + ncm13j_write_array(sd, ovfmt->regs /* + 1*/); + ret = 0; + if (wsize->regs) + ret = ncm13j_write_array(sd, wsize->regs); + info->fmt = ovfmt; + + return ret; +} + + +/* + * Code for dealing with controls. + */ + +static int ncm13j_queryctrl(struct v4l2_subdev *sd, + struct v4l2_queryctrl *qc) +{ + /* Fill in min, max, step and default value for these controls. */ + switch (qc->id) { + case V4L2_CID_BRIGHTNESS: + case V4L2_CID_CONTRAST: + case V4L2_CID_VFLIP: + case V4L2_CID_HFLIP: + case V4L2_CID_SATURATION: + case V4L2_CID_HUE: + return -EINVAL; + } + return -EINVAL; +} + +static int ncm13j_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + case V4L2_CID_CONTRAST: + case V4L2_CID_VFLIP: + case V4L2_CID_HFLIP: + case V4L2_CID_SATURATION: + case V4L2_CID_HUE: + return -EINVAL; + } + return -EINVAL; +} + +static int ncm13j_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + case V4L2_CID_CONTRAST: + case V4L2_CID_VFLIP: + case V4L2_CID_HFLIP: + case V4L2_CID_SATURATION: + case V4L2_CID_HUE: + return -EINVAL; + } + return -EINVAL; +} + +static int ncm13j_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_OV7670, 0); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops ncm13j_core_ops = { + .g_chip_ident = ncm13j_g_chip_ident, + .g_ctrl = ncm13j_g_ctrl, + .s_ctrl = ncm13j_s_ctrl, + .queryctrl = ncm13j_queryctrl, + .reset = ncm13j_reset, + .init = ncm13j_init, +}; + +static const struct v4l2_subdev_video_ops ncm13j_video_ops = { + .try_mbus_fmt = ncm13j_try_mbus_fmt, + .s_mbus_fmt = ncm13j_s_mbus_fmt, +}; + +static const struct v4l2_subdev_ops ncm13j_ops = { + .core = &ncm13j_core_ops, + .video = &ncm13j_video_ops, +}; + +/* ----------------------------------------------------------------------- */ + +static int ncm13j_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct v4l2_subdev *sd; + struct ncm13j_info *info; + int ret; + + info = kzalloc(sizeof(struct ncm13j_info), GFP_KERNEL); + if (info == NULL) + return -ENOMEM; + sd = &info->sd; + v4l2_i2c_subdev_init(sd, client, &ncm13j_ops); + + /* Make sure it's an ncm13j */ + ret = ncm13j_detect(sd); + if (ret) { + v4l_dbg(1, debug, client, + "chip found @ 0x%x (%s) is not an ncm13j chip.\n", + client->addr << 1, client->adapter->name); + kfree(info); + return ret; + } + v4l_info(client, "chip found @ 0x%02x (%s)\n", + client->addr << 1, client->adapter->name); + + info->fmt = &ncm13j_formats[0]; + info->sat = 128; /* Review this */ + + return 0; +} + + +static int ncm13j_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(to_state(sd)); + return 0; +} + +static const struct i2c_device_id ncm13j_id[] = { + { "ioh_i2c", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ncm13j_id); + +static struct i2c_driver ncm13j_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "ioh_i2c", + }, + .probe = ncm13j_probe, + .remove = ncm13j_remove, + .id_table = ncm13j_id, +}; + +static __init int init_ncm13j(void) +{ + return i2c_add_driver(&ncm13j_driver); +} + +static __exit void exit_ncm13j(void) +{ + i2c_del_driver(&ncm13j_driver); +} + +module_init(init_ncm13j); +module_exit(exit_ncm13j); + + diff --git a/drivers/media/video/ioh_video_in_ov7620.c b/drivers/media/video/ioh_video_in_ov7620.c new file mode 100644 index 0000000..78802d5 --- /dev/null +++ b/drivers/media/video/ioh_video_in_ov7620.c @@ -0,0 +1,637 @@ +/* + * Copyright (C) 2010 OKI SEMICONDUCTOR CO., LTD. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_DESCRIPTION("IOH video-in driver for OmniVision ov7620 sensor."); +MODULE_LICENSE("GPL"); + +static int debug; +module_param(debug, bool, 0644); +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + +/* + * Basic window sizes. These probably belong somewhere more globally + * useful. + */ +#define VGA_WIDTH 640 +#define VGA_HEIGHT 480 +#define QVGA_WIDTH 320 +#define QVGA_HEIGHT 240 + +/* Registers */ +#define REG_BRIGHT 0x06 /* Brightness */ + +#define REG_COMJ 0x2d /* Common Control J */ +#define COMJ_4 0x10 /* Auto brightness enable */ + +#define REG_COMC 0x14 /* Common Control C */ +#define COMC_FMT_QVGA 0x20 /* OVGA digital output format selesction */ + +#define REG_COMA 0x12 /* Common Control A */ +#define COMA_RESET 0x80 /* Register reset */ + +#define REG_MIDH 0x1c /* Manuf. ID high */ +#define REG_MIDL 0x1d /* Manuf. ID low */ + +#define REG_HSTART 0x17 /* Horiz start high bits */ +#define REG_HSTOP 0x18 /* Horiz stop high bits */ +#define REG_VSTART 0x19 /* Vert start high bits */ +#define REG_VSTOP 0x1a /* Vert stop high bits */ + +/* + * Information we maintain about a known sensor. + */ +struct ov7620_format_struct; /* coming later */ +struct ov7620_info { + struct v4l2_subdev sd; + struct ov7620_format_struct *fmt; /* Current format */ + unsigned char sat; /* Saturation value */ + int hue; /* Hue value */ +}; + +static inline struct ov7620_info *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct ov7620_info, sd); +} + + + +/* + * The default register settings. + */ + +struct regval_list { + unsigned char reg_num; + unsigned char value; +}; + +static struct regval_list ov7620_default_regs[] = { + { REG_COMC, 0x04 }, + { 0x11, 0x40 }, + { 0x13, 0x31 }, + { 0x28, 0x20 }, /* Progressive */ + { 0x2d, 0x91 }, + { 0xff, 0xff }, /* end */ +}; + + +static struct regval_list ov7620_fmt_yuv422[] = { + { REG_COMC, 0x04 }, + { 0x11, 0x40 }, + { 0x13, 0x31 }, + { 0x28, 0x20 }, /* Progressive */ + { 0x2d, 0x91 }, + { 0xff, 0xff }, /* end */ +}; + + + +/* + * Low-level register I/O. + */ + +#if 1 +static int ioh_video_in_read_value(struct i2c_client *client, u8 reg, u8 *val) +{ + u8 data = 0; + + client->flags = 0; + data = 0; + if (i2c_master_send(client, &data, 1) != 1) + goto err; + msleep(2); + + client->flags = 0; + data = reg; + if (i2c_master_send(client, &data, 1) != 1) + goto err; + msleep(2); + + if (i2c_master_recv(client, &data, 1) != 1) + goto err; + msleep(2); + + *val = data; + + v4l_dbg(1, debug, client, "Function %s A(0x%02X) --> 0x%02X end.", + __func__, reg, *val); + + return 0; + +err: + v4l_err(client, "Function %s A(0x%02X) 0x%02X read error failed.", + __func__, reg, *val); + + return -EINVAL; +} + +static int ioh_video_in_write_value(struct i2c_client *client, u8 reg, u8 val) +{ + u8 data = 0; + unsigned char data2[2] = { reg, val }; + + client->flags = 0; + data = 0; + if (i2c_master_send(client, &data, 1) != 1) + goto err; + msleep(2); + + if (i2c_master_send(client, data2, 2) != 2) + goto err; + msleep(2); + + v4l_dbg(1, debug, client, "Function %s A(0x%02X) <-- 0x%02X end.", + __func__, reg, val); + + return 0; + +err: + v4l_err(client, "Function %s A(0x%02X) <-- 0x%02X write error failed.", + __func__, reg, val); + + return -EINVAL; +} + +#endif + +static int ov7620_read(struct v4l2_subdev *sd, unsigned char reg, + unsigned char *value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret; + +#if 0 + ret = i2c_smbus_read_byte_data(client, reg); + if (ret >= 0) { + *value = (unsigned char)ret; + ret = 0; + } +#else + ret = ioh_video_in_read_value(client, reg, value); +#endif + return ret; +} +static int ov7620_write(struct v4l2_subdev *sd, unsigned char reg, + unsigned char value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); +#if 0 + int ret = i2c_smbus_write_byte_data(client, reg, value); +#else + int ret = ioh_video_in_write_value(client, reg, value); +#endif + + if (reg == REG_COMA && (value & COMA_RESET)) + msleep(2); /* Wait for reset to run */ + + return ret; +} + + +/* + * Write a list of register settings; ff/ff stops the process. + */ +static int ov7620_write_array(struct v4l2_subdev *sd, struct regval_list *vals) +{ + while (vals->reg_num != 0xff || vals->value != 0xff) { + int ret = ov7620_write(sd, vals->reg_num, vals->value); + if (ret < 0) + return ret; + vals++; + } + return 0; +} + + +/* + * Stuff that knows about the sensor. + */ +static int ov7620_reset(struct v4l2_subdev *sd, u32 val) +{ + ov7620_write(sd, REG_COMA, COMA_RESET); + msleep(1); + return 0; +} + + +static int ov7620_init(struct v4l2_subdev *sd, u32 val) +{ + return ov7620_write_array(sd, ov7620_default_regs); +} + + +static int ov7620_detect(struct v4l2_subdev *sd) +{ + unsigned char v; + int ret; + + ret = ov7620_init(sd, 0); + if (ret < 0) + return ret; + ret = ov7620_read(sd, REG_MIDH, &v); + if (ret < 0) + return ret; + if (v != 0x7f) /* OV manuf. id. */ + return -ENODEV; + ret = ov7620_read(sd, REG_MIDL, &v); + if (ret < 0) + return ret; + if (v != 0xa2) + return -ENODEV; + return 0; +} + +static struct ov7620_format_struct { + enum v4l2_mbus_pixelcode mbus_code; + enum v4l2_colorspace colorspace; + struct regval_list *regs; +} ov7620_formats[] = { + { + .mbus_code = V4L2_MBUS_FMT_UYVY8_2X8, + .colorspace = V4L2_COLORSPACE_JPEG, + .regs = ov7620_fmt_yuv422, + }, +}; +#define N_OV7620_FMTS ARRAY_SIZE(ov7620_formats) + + +/* + * Then there is the issue of window sizes. Try to capture the info here. + */ + +static struct regval_list ov7620_vga_regs[] = { + { 0xff, 0xff }, +}; + +static struct regval_list ov7620_qvga_regs[] = { + { 0xff, 0xff }, +}; + +static struct ov7620_win_size { + int width; + int height; + unsigned char comc_bit; + int hstart; + int hstop; + int vstart; + int vstop; + struct regval_list *regs; /* Regs to tweak */ +/* h/vref stuff */ +} ov7620_win_sizes[] = { + /* VGA */ + { + .width = VGA_WIDTH, + .height = VGA_HEIGHT, + .comc_bit = 0, + .hstart = 0x2f, + .hstop = 0xcf, + .vstart = 0x06, + .vstop = 0xf5, + .regs = ov7620_vga_regs, + }, + /* QVGA */ + { + .width = QVGA_WIDTH, + .height = QVGA_HEIGHT, + .comc_bit = COMC_FMT_QVGA, + .hstart = 0x2f, + .hstop = 0xcf, + .vstart = 0x06, + .vstop = 0xf5, + .regs = ov7620_qvga_regs, + }, +}; + +#define N_WIN_SIZES (ARRAY_SIZE(ov7620_win_sizes)) + + +/* + * Store a set of start/stop values into the camera. + */ +static int ov7620_set_hw(struct v4l2_subdev *sd, int hstart, int hstop, + int vstart, int vstop) +{ + int ret; + + ret = ov7620_write(sd, REG_HSTART, hstart); + ret += ov7620_write(sd, REG_HSTOP, hstop); + + ret += ov7620_write(sd, REG_VSTART, vstart); + ret += ov7620_write(sd, REG_VSTOP, vstop); + return ret; +} + +static int ov7620_try_fmt_internal(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt, + struct ov7620_format_struct **ret_fmt, + struct ov7620_win_size **ret_wsize) +{ + int index; + struct ov7620_win_size *wsize; + + for (index = 0; index < N_OV7620_FMTS; index++) + if (ov7620_formats[index].mbus_code == fmt->code) + break; + if (index >= N_OV7620_FMTS) { + /* default to first format */ + index = 0; + fmt->code = ov7620_formats[0].mbus_code; + } + if (ret_fmt != NULL) + *ret_fmt = ov7620_formats + index; + + fmt->field = V4L2_FIELD_NONE; + + for (wsize = ov7620_win_sizes; + wsize < ov7620_win_sizes + N_WIN_SIZES; wsize++) + if (fmt->width >= wsize->width && fmt->height >= wsize->height) + break; + if (wsize >= ov7620_win_sizes + N_WIN_SIZES) + wsize--; /* Take the smallest one */ + if (ret_wsize != NULL) + *ret_wsize = wsize; + /* + * Note the size we'll actually handle. + */ + fmt->width = wsize->width; + fmt->height = wsize->height; + fmt->colorspace = ov7620_formats[index].colorspace; + + return 0; +} + +static int ov7620_try_mbus_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt) +{ + return ov7620_try_fmt_internal(sd, fmt, NULL, NULL); +} + +/* + * Set a format. + */ +static int ov7620_s_mbus_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt) +{ + int ret; + struct ov7620_format_struct *ovfmt; + struct ov7620_win_size *wsize; + struct ov7620_info *info = to_state(sd); + unsigned char comc; + + ret = ov7620_try_fmt_internal(sd, fmt, &ovfmt, &wsize); + if (ret) + return ret; + + /* Reset */ + ov7620_reset(sd, 0); + + comc = ovfmt->regs[0].value; + comc |= wsize->comc_bit; + ov7620_write(sd, REG_COMC, comc); + /* + * Now write the rest of the array. Also store start/stops + */ + ov7620_write_array(sd, ovfmt->regs + 1); + ov7620_set_hw(sd, wsize->hstart, wsize->hstop, wsize->vstart, + wsize->vstop); + ret = 0; + if (wsize->regs) + ret = ov7620_write_array(sd, wsize->regs); + info->fmt = ovfmt; + + return ret; +} + + +/* + * Code for dealing with controls. + */ + +static int ov7620_s_brightness(struct v4l2_subdev *sd, int value) +{ + unsigned char comj = 0; + int ret; + + ov7620_read(sd, REG_COMJ, &comj); + comj &= ~COMJ_4; + ov7620_write(sd, REG_COMJ, comj); + + ret = ov7620_write(sd, REG_BRIGHT, value); + return ret; +} + +static int ov7620_g_brightness(struct v4l2_subdev *sd, __s32 *value) +{ + unsigned char v = 0; + int ret = ov7620_read(sd, REG_BRIGHT, &v); + + return ret; +} + +static int ov7620_queryctrl(struct v4l2_subdev *sd, + struct v4l2_queryctrl *qc) +{ + /* Fill in min, max, step and default value for these controls. */ + switch (qc->id) { + case V4L2_CID_BRIGHTNESS: + return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128); + case V4L2_CID_CONTRAST: + case V4L2_CID_VFLIP: + case V4L2_CID_HFLIP: + case V4L2_CID_SATURATION: + case V4L2_CID_HUE: + return -EINVAL; + } + return -EINVAL; +} + +static int ov7620_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + return ov7620_g_brightness(sd, &ctrl->value); + case V4L2_CID_CONTRAST: + case V4L2_CID_VFLIP: + case V4L2_CID_HFLIP: + case V4L2_CID_SATURATION: + case V4L2_CID_HUE: + return -EINVAL; + } + return -EINVAL; +} + +static int ov7620_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + return ov7620_s_brightness(sd, ctrl->value); + case V4L2_CID_CONTRAST: + case V4L2_CID_VFLIP: + case V4L2_CID_HFLIP: + case V4L2_CID_SATURATION: + case V4L2_CID_HUE: + return -EINVAL; + } + return -EINVAL; +} + +static int ov7620_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_OV7670, 0); +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int ov7620_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + unsigned char val = 0; + int ret; + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + ret = ov7620_read(sd, reg->reg & 0xff, &val); + reg->val = val; + reg->size = 1; + return ret; +} + +static int ov7620_s_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + ov7620_write(sd, reg->reg & 0xff, reg->val & 0xff); + return 0; +} +#endif + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops ov7620_core_ops = { + .g_chip_ident = ov7620_g_chip_ident, + .g_ctrl = ov7620_g_ctrl, + .s_ctrl = ov7620_s_ctrl, + .queryctrl = ov7620_queryctrl, + .reset = ov7620_reset, + .init = ov7620_init, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = ov7620_g_register, + .s_register = ov7620_s_register, +#endif +}; + +static const struct v4l2_subdev_video_ops ov7620_video_ops = { + .try_mbus_fmt = ov7620_try_mbus_fmt, + .s_mbus_fmt = ov7620_s_mbus_fmt, +}; + +static const struct v4l2_subdev_ops ov7620_ops = { + .core = &ov7620_core_ops, + .video = &ov7620_video_ops, +}; + +/* ----------------------------------------------------------------------- */ + +static int ov7620_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct v4l2_subdev *sd; + struct ov7620_info *info; + int ret; + + info = kzalloc(sizeof(struct ov7620_info), GFP_KERNEL); + if (info == NULL) + return -ENOMEM; + sd = &info->sd; + v4l2_i2c_subdev_init(sd, client, &ov7620_ops); + + /* Make sure it's an ov7620 */ + ret = ov7620_detect(sd); + if (ret) { + v4l_dbg(1, debug, client, + "chip found @ 0x%x (%s) is not an ov7620 chip.\n", + client->addr << 1, client->adapter->name); + kfree(info); + return ret; + } + v4l_info(client, "chip found @ 0x%02x (%s)\n", + client->addr << 1, client->adapter->name); + + info->fmt = &ov7620_formats[0]; + info->sat = 128; /* Review this */ + + return 0; +} + + +static int ov7620_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(to_state(sd)); + return 0; +} + +static const struct i2c_device_id ov7620_id[] = { + { "ioh_i2c", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ov7620_id); + +static struct i2c_driver ov7620_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "ioh_i2c", + }, + .probe = ov7620_probe, + .remove = ov7620_remove, + .id_table = ov7620_id, +}; + +static __init int init_ov7620(void) +{ + return i2c_add_driver(&ov7620_driver); +} + +static __exit void exit_ov7620(void) +{ + i2c_del_driver(&ov7620_driver); +} + +module_init(init_ov7620); +module_exit(exit_ov7620); + + diff --git a/drivers/media/video/ioh_video_in_ov9653.c b/drivers/media/video/ioh_video_in_ov9653.c new file mode 100644 index 0000000..7a35e32 --- /dev/null +++ b/drivers/media/video/ioh_video_in_ov9653.c @@ -0,0 +1,818 @@ +/* + * Copyright (C) 2010 OKI SEMICONDUCTOR CO., LTD. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_DESCRIPTION("IOH video-in driver for OmniVision ov9653 sensor."); +MODULE_LICENSE("GPL"); + +static int debug; +module_param(debug, bool, 0644); +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + +/* + * Basic window sizes. These probably belong somewhere more globally + * useful. + */ +#define HDTV_WIDTH 1280 +#define HDTV_HEIGHT 720 + +/* Registers */ +#define REG_COM1 0x04 /* Control 1 */ + +#define REG_COM7 0x12 /* Control 7 */ +#define COM7_RESET 0x80 /* Register reset */ +#define COM7_FMT_HDTV 0x00 +#define COM7_FMT_VGA 0x40 /* VGA format */ +#define COM7_FMT_CIF 0x20 /* CIF format */ +#define COM7_FMT_QVGA 0x10 /* QVGA format */ +#define COM7_FMT_QCIF 0x08 /* QCIF format */ +#define REG_COM8 0x13 /* Control 8 */ +#define COM8_AEC 0x01 /* Auto exposure enable */ + + +#define REG_PID 0x0a /* Product ID MSB */ +#define REG_VER 0x0b /* Product ID LSB */ + +#define REG_MIDH 0x1c /* Manuf. ID high */ +#define REG_MIDL 0x1d /* Manuf. ID low */ + +#define REG_HSTART 0x17 /* Horiz start high bits */ +#define REG_HSTOP 0x18 /* Horiz stop high bits */ +#define REG_VSTART 0x19 /* Vert start high bits */ +#define REG_VSTOP 0x1a /* Vert stop high bits */ + +#define REG_HREF 0x32 /* HREF pieces */ +#define REG_VREF 0x03 /* Pieces of GAIN, VSTART, VSTOP */ + +#define REG_AECHM 0xa1 /* AEC MSC 5bit */ +#define REG_AECH 0x10 /* AEC value */ + + +/* + * Information we maintain about a known sensor. + */ +struct ov9653_format_struct; /* coming later */ +struct ov9653_info { + struct v4l2_subdev sd; + struct ov9653_format_struct *fmt; /* Current format */ + unsigned char sat; /* Saturation value */ + int hue; /* Hue value */ +}; + +static inline struct ov9653_info *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct ov9653_info, sd); +} + + + +/* + * The default register settings. + */ + +struct regval_list { + unsigned char reg_num; + unsigned char value; +}; + +static struct regval_list ov9653_default_regs[] = { + { REG_COM7, 0x00 }, + { 0x11, 0x80 }, + { 0x39, 0x43 }, + { 0x38, 0x12 }, + { 0x0e, 0x00 }, + { 0x13, 0xc7 }, + { 0x1e, 0x34 }, + { 0x01, 0x80 }, + { 0x02, 0x80 }, + { 0x00, 0x00 }, + { 0x10, 0xf0 }, + { 0x1b, 0x00 }, + { 0x16, 0x06 }, + { 0x33, 0x10 }, + { 0x34, 0xbf }, + { 0xa8, 0x81 }, + { 0x41, 0x10 }, + { 0x96, 0x04 }, + { 0x3d, 0x19 }, + { 0x3a, 0x01 }, + { 0x1b, 0x01 }, + { 0x8e, 0x00 }, + { 0x3c, 0x60 }, + { 0x8f, 0xcf }, + { 0x8b, 0x06 }, + { 0x35, 0x91 }, + { 0x94, 0x99 }, + { 0x95, 0x99 }, + { 0x40, 0xc1 }, + { 0x29, 0x2f }, + { 0x0f, 0x42 }, + { 0x3a, 0x01 }, + { 0xa5, 0x80 }, + { 0x41, 0x00 }, + { 0x13, 0xc5 }, + { 0x3d, 0x92 }, + { 0x69, 0x80 }, + { 0x5c, 0x96 }, + { 0x5d, 0x96 }, + { 0x5e, 0x10 }, + { 0x59, 0xeb }, + { 0x5a, 0x9c }, + { 0x5b, 0x55 }, + { 0x43, 0xf0 }, + { 0x44, 0x10 }, + { 0x45, 0x55 }, + { 0x46, 0x86 }, + { 0x47, 0x64 }, + { 0x48, 0x86 }, + { 0x5f, 0xf0 }, + { 0x60, 0x8c }, + { 0x61, 0x20 }, + { 0xa5, 0xd9 }, + { 0xa4, 0x74 }, + { 0x8d, 0x02 }, + { 0x13, 0xc7 }, + { 0x4f, 0x46 }, + { 0x50, 0x36 }, + { 0x51, 0x0f }, + { 0x52, 0x17 }, + { 0x53, 0x7f }, + { 0x54, 0x96 }, + { 0x41, 0x32 }, + { 0x8c, 0x23 }, + { 0x3d, 0x92 }, + { 0x3e, 0x02 }, + { 0xa9, 0x97 }, + { 0x3a, 0x00 }, + { 0x8f, 0xcf }, + { 0x90, 0x00 }, + { 0x91, 0x00 }, + { 0x9f, 0x00 }, + { 0xa0, 0x00 }, + { 0x3a, 0x0d }, + { 0x94, 0x88 }, + { 0x95, 0x88 }, + { 0x24, 0x68 }, + { 0x25, 0x5c }, + { 0x26, 0xc3 }, + { 0x3b, 0x19 }, + { 0x14, 0x2a }, + { 0x3f, 0xa6 }, + { 0x6a, 0x21 }, + { 0xff, 0xff }, /* end */ +}; + + +static struct regval_list ov9653_fmt_yuv422[] = { + { REG_COM7, 0x00 }, + { 0x11, 0x80 }, + { 0x39, 0x43 }, + { 0x38, 0x12 }, + { 0x0e, 0x00 }, + { 0x13, 0xc7 }, + { 0x1e, 0x34 }, + { 0x01, 0x80 }, + { 0x02, 0x80 }, + { 0x00, 0x00 }, + { 0x10, 0xf0 }, + { 0x1b, 0x00 }, + { 0x16, 0x06 }, + { 0x33, 0x10 }, + { 0x34, 0xbf }, + { 0xa8, 0x81 }, + { 0x41, 0x10 }, + { 0x96, 0x04 }, + { 0x3d, 0x19 }, + { 0x3a, 0x01 }, + { 0x1b, 0x01 }, + { 0x8e, 0x00 }, + { 0x3c, 0x60 }, + { 0x8f, 0xcf }, + { 0x8b, 0x06 }, + { 0x35, 0x91 }, + { 0x94, 0x99 }, + { 0x95, 0x99 }, + { 0x40, 0xc1 }, + { 0x29, 0x2f }, + { 0x0f, 0x42 }, + { 0x3a, 0x01 }, + { 0xa5, 0x80 }, + { 0x41, 0x00 }, + { 0x13, 0xc5 }, + { 0x3d, 0x92 }, + { 0x69, 0x80 }, + { 0x5c, 0x96 }, + { 0x5d, 0x96 }, + { 0x5e, 0x10 }, + { 0x59, 0xeb }, + { 0x5a, 0x9c }, + { 0x5b, 0x55 }, + { 0x43, 0xf0 }, + { 0x44, 0x10 }, + { 0x45, 0x55 }, + { 0x46, 0x86 }, + { 0x47, 0x64 }, + { 0x48, 0x86 }, + { 0x5f, 0xf0 }, + { 0x60, 0x8c }, + { 0x61, 0x20 }, + { 0xa5, 0xd9 }, + { 0xa4, 0x74 }, + { 0x8d, 0x02 }, + { 0x13, 0xc7 }, + { 0x4f, 0x46 }, + { 0x50, 0x36 }, + { 0x51, 0x0f }, + { 0x52, 0x17 }, + { 0x53, 0x7f }, + { 0x54, 0x96 }, + { 0x41, 0x32 }, + { 0x8c, 0x23 }, + { 0x3d, 0x92 }, + { 0x3e, 0x02 }, + { 0xa9, 0x97 }, + { 0x3a, 0x00 }, + { 0x8f, 0xcf }, + { 0x90, 0x00 }, + { 0x91, 0x00 }, + { 0x9f, 0x00 }, + { 0xa0, 0x00 }, + { 0x3a, 0x0d }, + { 0x94, 0x88 }, + { 0x95, 0x88 }, + { 0x24, 0x68 }, + { 0x25, 0x5c }, + { 0x26, 0xc3 }, + { 0x3b, 0x19 }, + { 0x14, 0x2a }, + { 0x3f, 0xa6 }, + { 0x6a, 0x21 }, + { 0xff, 0xff }, /* end */ +}; + + +/* + * Low-level register I/O. + */ + +#if 1 +static int ioh_video_in_read_value(struct i2c_client *client, u8 reg, u8 *val) +{ + u8 data = 0; + + client->flags = 0; + data = 0; + if (i2c_master_send(client, &data, 1) != 1) + goto err; + msleep(2); + + client->flags = 0; + data = reg; + if (i2c_master_send(client, &data, 1) != 1) + goto err; + msleep(2); + + if (i2c_master_recv(client, &data, 1) != 1) + goto err; + msleep(2); + + *val = data; + + v4l_dbg(1, debug, client, "Function %s A(0x%02X) --> 0x%02X end.", + __func__, reg, *val); + + return 0; + +err: + v4l_err(client, "Function %s A(0x%02X) 0x%02X read error failed.", + __func__, reg, *val); + + return -EINVAL; +} + +static int ioh_video_in_write_value(struct i2c_client *client, u8 reg, u8 val) +{ + u8 data = 0; + unsigned char data2[2] = { reg, val }; + + client->flags = 0; + data = 0; + if (i2c_master_send(client, &data, 1) != 1) + goto err; + msleep(2); + + if (i2c_master_send(client, data2, 2) != 2) + goto err; + msleep(2); + + v4l_dbg(1, debug, client, "Function %s A(0x%02X) <-- 0x%02X end.", + __func__, reg, val); + + return 0; + +err: + v4l_err(client, "Function %s A(0x%02X) <-- 0x%02X write error failed.", + __func__, reg, val); + + return -EINVAL; +} + +#endif + +static int ov9653_read(struct v4l2_subdev *sd, unsigned char reg, + unsigned char *value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret; + +#if 0 + ret = i2c_smbus_read_byte_data(client, reg); + if (ret >= 0) { + *value = (unsigned char)ret; + ret = 0; + } +#else + ret = ioh_video_in_read_value(client, reg, value); +#endif + return ret; +} +static int ov9653_write(struct v4l2_subdev *sd, unsigned char reg, + unsigned char value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); +#if 0 + int ret = i2c_smbus_write_byte_data(client, reg, value); +#else + int ret = ioh_video_in_write_value(client, reg, value); +#endif + + if (reg == REG_COM7 && (value & COM7_RESET)) + msleep(2); /* Wait for reset to run */ + + return ret; +} + + +/* + * Write a list of register settings; ff/ff stops the process. + */ +static int ov9653_write_array(struct v4l2_subdev *sd, struct regval_list *vals) +{ + while (vals->reg_num != 0xff || vals->value != 0xff) { + int ret = ov9653_write(sd, vals->reg_num, vals->value); + if (ret < 0) + return ret; + vals++; + } + return 0; +} + + +/* + * Stuff that knows about the sensor. + */ +static int ov9653_reset(struct v4l2_subdev *sd, u32 val) +{ + ov9653_write(sd, REG_COM7, COM7_RESET); + msleep(1); + return 0; +} + + +static int ov9653_init(struct v4l2_subdev *sd, u32 val) +{ + return ov9653_write_array(sd, ov9653_default_regs); +} + + +static int ov9653_detect(struct v4l2_subdev *sd) +{ + unsigned char v; + int ret; + + ret = ov9653_init(sd, 0); + if (ret < 0) + return ret; + ret = ov9653_read(sd, REG_MIDH, &v); + if (ret < 0) + return ret; + if (v != 0x7f) /* OV manuf. id. */ + return -ENODEV; + ret = ov9653_read(sd, REG_MIDL, &v); + if (ret < 0) + return ret; + if (v != 0xa2) + return -ENODEV; + + ret = ov9653_read(sd, REG_PID, &v); + if (ret < 0) + return ret; + if (v != 0x96) /* PID + VER = 0x96 / 0x52 */ + return -ENODEV; + ret = ov9653_read(sd, REG_VER, &v); + if (ret < 0) + return ret; + if (v != 0x52) /* PID + VER = 0x96 / 0x52 */ + return -ENODEV; + return 0; +} + +static struct ov9653_format_struct { + enum v4l2_mbus_pixelcode mbus_code; + enum v4l2_colorspace colorspace; + struct regval_list *regs; +} ov9653_formats[] = { + { + .mbus_code = V4L2_MBUS_FMT_UYVY8_2X8, + .colorspace = V4L2_COLORSPACE_JPEG, + .regs = ov9653_fmt_yuv422, + }, +}; +#define N_OV9653_FMTS ARRAY_SIZE(ov9653_formats) + +static struct regval_list ov9653_hdtv_regs[] = { + { 0xff, 0xff }, +}; + + +static struct ov9653_win_size { + int width; + int height; + unsigned char com7_bit; + int hstart; + int hstop; + int vstart; + int vstop; + struct regval_list *regs; /* Regs to tweak */ +/* h/vref stuff */ +} ov9653_win_sizes[] = { + /* HDTV */ + { + .width = HDTV_WIDTH, + .height = HDTV_HEIGHT, + .com7_bit = COM7_FMT_HDTV, + .hstart = 238, + .hstop = 1518, + .vstart = 130, + .vstop = 850, + .regs = ov9653_hdtv_regs, + }, +}; + +#define N_WIN_SIZES (ARRAY_SIZE(ov9653_win_sizes)) + + +/* + * Store a set of start/stop values into the camera. + */ +static int ov9653_set_hw(struct v4l2_subdev *sd, int hstart, int hstop, + int vstart, int vstop) +{ + int ret; + unsigned char v; + + ret = ov9653_write(sd, REG_HSTART, (hstart >> 3) & 0xff); + ret += ov9653_write(sd, REG_HSTOP, (hstop >> 3) & 0xff); + ret += ov9653_read(sd, REG_HREF, &v); + v = (v & 0xc0) | ((hstop & 0x7) << 3) | (hstart & 0x7); + msleep(10); + ret += ov9653_write(sd, REG_HREF, v); + + ret += ov9653_write(sd, REG_VSTART, (vstart >> 3) & 0xff); + ret += ov9653_write(sd, REG_VSTOP, (vstop >> 3) & 0xff); + ret += ov9653_read(sd, REG_VREF, &v); + v = (v & 0xc0) | ((vstop & 0x7) << 3) | (vstart & 0x7); + msleep(10); + ret += ov9653_write(sd, REG_VREF, v); + return ret; +} + +static int ov9653_try_fmt_internal(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt, + struct ov9653_format_struct **ret_fmt, + struct ov9653_win_size **ret_wsize) +{ + int index; + struct ov9653_win_size *wsize; + + for (index = 0; index < N_OV9653_FMTS; index++) + if (ov9653_formats[index].mbus_code == fmt->code) + break; + if (index >= N_OV9653_FMTS) { + /* default to first format */ + index = 0; + fmt->code = ov9653_formats[0].mbus_code; + } + if (ret_fmt != NULL) + *ret_fmt = ov9653_formats + index; + + fmt->field = V4L2_FIELD_NONE; + + for (wsize = ov9653_win_sizes; + wsize < ov9653_win_sizes + N_WIN_SIZES; wsize++) + if (fmt->width >= wsize->width && fmt->height >= wsize->height) + break; + if (wsize >= ov9653_win_sizes + N_WIN_SIZES) + wsize--; /* Take the smallest one */ + if (ret_wsize != NULL) + *ret_wsize = wsize; + /* + * Note the size we'll actually handle. + */ + fmt->width = wsize->width; + fmt->height = wsize->height; + fmt->colorspace = ov9653_formats[index].colorspace; + + return 0; +} + +static int ov9653_try_mbus_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt) +{ + return ov9653_try_fmt_internal(sd, fmt, NULL, NULL); +} + +/* + * Set a format. + */ +static int ov9653_s_mbus_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt) +{ + int ret; + struct ov9653_format_struct *ovfmt; + struct ov9653_win_size *wsize; + struct ov9653_info *info = to_state(sd); + unsigned char com7; + + ret = ov9653_try_fmt_internal(sd, fmt, &ovfmt, &wsize); + if (ret) + return ret; + + /* Reset */ + ov9653_reset(sd, 0); + + com7 = ovfmt->regs[0].value; + com7 |= wsize->com7_bit; + ov9653_write(sd, REG_COM7, com7); + /* + * Now write the rest of the array. Also store start/stops + */ + ov9653_write_array(sd, ovfmt->regs + 1); + ov9653_set_hw(sd, wsize->hstart, wsize->hstop, wsize->vstart, + wsize->vstop); + ret = 0; + if (wsize->regs) + ret = ov9653_write_array(sd, wsize->regs); + info->fmt = ovfmt; + + return ret; +} + + +/* + * Code for dealing with controls. + */ + +static int ov9653_s_brightness(struct v4l2_subdev *sd, int value) +{ + unsigned char com8 = 0, v; + int ret; + + ov9653_read(sd, REG_COM8, &com8); + com8 &= ~COM8_AEC; + ov9653_write(sd, REG_COM8, com8); + + ret = ov9653_write(sd, REG_AECH, (value >> 2) & 0xff); + ret += ov9653_write(sd, REG_AECHM, (value >> 10) & 0x3f); + ret += ov9653_read(sd, REG_COM1, &v); + v = (v & 0xfc) | (value & 0x03); + msleep(10); + ret += ov9653_write(sd, REG_COM1, v); + return ret; +} + +static int ov9653_g_brightness(struct v4l2_subdev *sd, __s32 *value) +{ + unsigned char v = 0; + int val = 0; + int ret; + + ret = ov9653_read(sd, REG_COM1, &v); + val = v & 0x03; + ret += ov9653_read(sd, REG_AECH, &v); + val |= ((v & 0xff) << 2); + ret += ov9653_read(sd, REG_AECHM, &v); + val |= ((v & 0x3f) << 10); + + *value = val; + return ret; +} + + + +static int ov9653_queryctrl(struct v4l2_subdev *sd, + struct v4l2_queryctrl *qc) +{ + /* Fill in min, max, step and default value for these controls. */ + switch (qc->id) { + case V4L2_CID_BRIGHTNESS: + return v4l2_ctrl_query_fill(qc, 0, 65535, 1, 256); + case V4L2_CID_CONTRAST: + case V4L2_CID_VFLIP: + case V4L2_CID_HFLIP: + case V4L2_CID_SATURATION: + case V4L2_CID_HUE: + return -EINVAL; + } + return -EINVAL; +} + +static int ov9653_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + return ov9653_g_brightness(sd, &ctrl->value); + case V4L2_CID_CONTRAST: + case V4L2_CID_SATURATION: + case V4L2_CID_HUE: + case V4L2_CID_VFLIP: + case V4L2_CID_HFLIP: + return -EINVAL; + } + return -EINVAL; +} + +static int ov9653_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + return ov9653_s_brightness(sd, ctrl->value); + case V4L2_CID_CONTRAST: + case V4L2_CID_SATURATION: + case V4L2_CID_HUE: + case V4L2_CID_VFLIP: + case V4L2_CID_HFLIP: + return -EINVAL; + } + return -EINVAL; +} + +static int ov9653_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_OV7670, 0); +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int ov9653_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + unsigned char val = 0; + int ret; + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + ret = ov9653_read(sd, reg->reg & 0xff, &val); + reg->val = val; + reg->size = 1; + return ret; +} + +static int ov9653_s_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + ov9653_write(sd, reg->reg & 0xff, reg->val & 0xff); + return 0; +} +#endif + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops ov9653_core_ops = { + .g_chip_ident = ov9653_g_chip_ident, + .g_ctrl = ov9653_g_ctrl, + .s_ctrl = ov9653_s_ctrl, + .queryctrl = ov9653_queryctrl, + .reset = ov9653_reset, + .init = ov9653_init, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = ov9653_g_register, + .s_register = ov9653_s_register, +#endif +}; + +static const struct v4l2_subdev_video_ops ov9653_video_ops = { + .try_mbus_fmt = ov9653_try_mbus_fmt, + .s_mbus_fmt = ov9653_s_mbus_fmt, +}; + +static const struct v4l2_subdev_ops ov9653_ops = { + .core = &ov9653_core_ops, + .video = &ov9653_video_ops, +}; + +/* ----------------------------------------------------------------------- */ + +static int ov9653_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct v4l2_subdev *sd; + struct ov9653_info *info; + int ret; + + info = kzalloc(sizeof(struct ov9653_info), GFP_KERNEL); + if (info == NULL) + return -ENOMEM; + sd = &info->sd; + v4l2_i2c_subdev_init(sd, client, &ov9653_ops); + + /* Make sure it's an ov9653 */ + ret = ov9653_detect(sd); + if (ret) { + v4l_dbg(1, debug, client, + "chip found @ 0x%x (%s) is not an ov9653 chip.\n", + client->addr << 1, client->adapter->name); + kfree(info); + return ret; + } + v4l_info(client, "chip found @ 0x%02x (%s)\n", + client->addr << 1, client->adapter->name); + + info->fmt = &ov9653_formats[0]; + info->sat = 128; /* Review this */ + + return 0; +} + + +static int ov9653_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(to_state(sd)); + return 0; +} + +static const struct i2c_device_id ov9653_id[] = { + { "ioh_i2c", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ov9653_id); + +static struct i2c_driver ov9653_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "ioh_i2c", + }, + .probe = ov9653_probe, + .remove = ov9653_remove, + .id_table = ov9653_id, +}; + +static __init int init_ov9653(void) +{ + return i2c_add_driver(&ov9653_driver); +} + +static __exit void exit_ov9653(void) +{ + i2c_del_driver(&ov9653_driver); +} + +module_init(init_ov9653); +module_exit(exit_ov9653); +