Re: [PATCH 1/9] omap3isp: Add ISP main driver and register definitions

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Tue, 2009-03-03 at 12:06 +0200, Sakari Ailus wrote:
> TODO:
> 
> - Release resoures in isp_probe() if something fails.
> 
> - Implement a sensible generic interface so that the ISP can offer a
>   v4l2_subdev (like the v4l2-int-device slaves) interface towards the
>   camera driver.
> 
> - Handle CSI1 and CSI2 error cases (currently unhandled?).
> 
> - Fix H3A / HIST interrupt enabling / disabling.
> 
> - Clean up the private ioctls.
> 
> Signed-off-by: Sakari Ailus <sakari.ailus@xxxxxxxxxxxxxxxxxxxxxxxxxx>
> ---
>  drivers/media/video/Makefile     |    2 +
>  drivers/media/video/isp/Makefile |   12 +
>  drivers/media/video/isp/isp.c    | 2418 ++++++++++++++++++++++++++++++++++++++
>  drivers/media/video/isp/isp.h    |  318 +++++
>  drivers/media/video/isp/ispreg.h | 1673 ++++++++++++++++++++++++++
>  5 files changed, 4423 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/media/video/isp/Makefile
>  create mode 100644 drivers/media/video/isp/isp.c
>  create mode 100644 drivers/media/video/isp/isp.h
>  create mode 100644 drivers/media/video/isp/ispreg.h
> 
> diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
> index 72f6d03..e654270 100644
> --- a/drivers/media/video/Makefile
> +++ b/drivers/media/video/Makefile
> @@ -106,6 +106,8 @@ obj-$(CONFIG_VIDEO_CX2341X) += cx2341x.o
>  obj-$(CONFIG_VIDEO_CAFE_CCIC) += cafe_ccic.o
>  obj-$(CONFIG_VIDEO_OV7670) 	+= ov7670.o
>  
> +obj-y				+= isp/
> +
>  obj-$(CONFIG_VIDEO_TCM825X) += tcm825x.o
>  
>  obj-$(CONFIG_USB_DABUSB)        += dabusb.o
> diff --git a/drivers/media/video/isp/Makefile b/drivers/media/video/isp/Makefile
> new file mode 100644
> index 0000000..f14d617
> --- /dev/null
> +++ b/drivers/media/video/isp/Makefile
> @@ -0,0 +1,12 @@
> +# Makefile for OMAP3 ISP driver
> +
> +ifdef CONFIG_ARCH_OMAP3410
> +isp-mod-objs += \
> +	isp.o ispccdc.o
> +else
> +isp-mod-objs += \
> +	isp.o ispccdc.o ispmmu.o \
> +	isppreview.o ispresizer.o isph3a.o isphist.o isp_af.o ispcsi2.o
> +endif
> +
> +obj-$(CONFIG_VIDEO_OMAP3) += isp-mod.o
> diff --git a/drivers/media/video/isp/isp.c b/drivers/media/video/isp/isp.c
> new file mode 100644
> index 0000000..12a545c
> --- /dev/null
> +++ b/drivers/media/video/isp/isp.c
> @@ -0,0 +1,2418 @@
> +/*
> + * isp.c
> + *
> + * Driver Library for ISP Control module in TI's OMAP3 Camera ISP
> + * ISP interface and IRQ related APIs are defined here.
> + *
> + * Copyright (C) 2009 Texas Instruments.
> + * Copyright (C) 2009 Nokia.
> + *
> + * Contributors:
> + * 	Sameer Venkatraman <sameerv@xxxxxx>
> + * 	Mohit Jalori <mjalori@xxxxxx>
> + * 	Sergio Aguirre <saaguirre@xxxxxx>
> + * 	Sakari Ailus <sakari.ailus@xxxxxxxxx>
> + * 	Tuukka Toivonen <tuukka.o.toivonen@xxxxxxxxx>
> + *	Toni Leinonen <toni.leinonen@xxxxxxxxx>
> + *
> + * This package is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
> + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
> + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
> + */
> +
> +#include <asm/cacheflush.h>
> +
> +#include <linux/delay.h>
> +#include <linux/interrupt.h>
> +#include <linux/clk.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/vmalloc.h>
> +#include <linux/platform_device.h>
> +
> +#include "isp.h"
> +#include "ispmmu.h"
> +#include "ispreg.h"
> +#include "ispccdc.h"
> +#include "isph3a.h"
> +#include "isphist.h"
> +#include "isp_af.h"
> +#include "isppreview.h"
> +#include "ispresizer.h"
> +#include "ispcsi2.h"
> +
> +static struct isp_device *omap3isp;
> +
> +static int isp_try_size(struct v4l2_pix_format *pix_input,
> +					struct v4l2_pix_format *pix_output);
> +
> +static void isp_save_ctx(void);
> +
> +static void isp_restore_ctx(void);
> +
> +static void isp_buf_init(void);
> +
> +/* List of image formats supported via OMAP ISP */
> +const static struct v4l2_fmtdesc isp_formats[] = {
> +	{
> +		.description = "UYVY, packed",
> +		.pixelformat = V4L2_PIX_FMT_UYVY,
> +	},
> +	{
> +		.description = "YUYV (YUV 4:2:2), packed",
> +		.pixelformat = V4L2_PIX_FMT_YUYV,
> +	},
> +	{
> +		.description = "Bayer10 (GrR/BGb)",
> +		.pixelformat = V4L2_PIX_FMT_SGRBG10,
> +	},
> +};
> +
> +/* ISP Crop capabilities */
> +static struct v4l2_rect ispcroprect;
> +static struct v4l2_rect cur_rect;
> +
> +/**
> + * struct vcontrol - Video control structure.
> + * @qc: V4L2 Query control structure.
> + * @current_value: Current value of the control.
> + */
> +static struct vcontrol {
> +	struct v4l2_queryctrl qc;
> +	int current_value;
> +} video_control[] = {
> +	{
> +		{
> +			.id = V4L2_CID_BRIGHTNESS,
> +			.type = V4L2_CTRL_TYPE_INTEGER,
> +			.name = "Brightness",
> +			.minimum = ISPPRV_BRIGHT_LOW,
> +			.maximum = ISPPRV_BRIGHT_HIGH,
> +			.step = ISPPRV_BRIGHT_STEP,
> +			.default_value = ISPPRV_BRIGHT_DEF,
> +		},
> +		.current_value = ISPPRV_BRIGHT_DEF,
> +	},
> +	{
> +		{
> +			.id = V4L2_CID_CONTRAST,
> +			.type = V4L2_CTRL_TYPE_INTEGER,
> +			.name = "Contrast",
> +			.minimum = ISPPRV_CONTRAST_LOW,
> +			.maximum = ISPPRV_CONTRAST_HIGH,
> +			.step = ISPPRV_CONTRAST_STEP,
> +			.default_value = ISPPRV_CONTRAST_DEF,
> +		},
> +		.current_value = ISPPRV_CONTRAST_DEF,
> +	},
> +	{
> +		{
> +			.id = V4L2_CID_COLORFX,
> +			.type = V4L2_CTRL_TYPE_MENU,
> +			.name = "Color Effects",
> +			.minimum = V4L2_COLORFX_NONE,
> +			.maximum = V4L2_COLORFX_SEPIA,
> +			.step = 1,
> +			.default_value = V4L2_COLORFX_NONE,
> +		},
> +		.current_value = V4L2_COLORFX_NONE,
> +	}
> +};
> +
> +static struct v4l2_querymenu video_menu[] = {
> +	{
> +		.id = V4L2_CID_COLORFX,
> +		.index = 0,
> +		.name = "None",
> +	},
> +	{
> +		.id = V4L2_CID_COLORFX,
> +		.index = 1,
> +		.name = "B&W",
> +	},
> +	{
> +		.id = V4L2_CID_COLORFX,
> +		.index = 2,
> +		.name = "Sepia",
> +	},
> +};
> +
> +struct isp_buf {
> +	dma_addr_t isp_addr;
> +	void (*complete)(struct videobuf_buffer *vb, void *priv);
> +	struct videobuf_buffer *vb;
> +	void *priv;
> +	u32 vb_state;
> +};
> +
> +#define ISP_BUFS_IS_FULL(bufs) \
> +	(((bufs)->queue + 1) % NUM_BUFS == (bufs)->done)
> +#define ISP_BUFS_IS_EMPTY(bufs)		((bufs)->queue == (bufs)->done)
> +#define ISP_BUFS_IS_LAST(bufs) \
> +	((bufs)->queue == ((bufs)->done + 1) % NUM_BUFS)
> +#define ISP_BUFS_QUEUED(bufs) \
> +	((((bufs)->done - (bufs)->queue + NUM_BUFS)) % NUM_BUFS)
> +#define ISP_BUF_DONE(bufs)		((bufs)->buf + (bufs)->done)
> +#define ISP_BUF_NEXT_DONE(bufs)	\
> +	((bufs)->buf + ((bufs)->done + 1) % NUM_BUFS)
> +#define ISP_BUF_QUEUE(bufs)		((bufs)->buf + (bufs)->queue)
> +#define ISP_BUF_MARK_DONE(bufs) \
> +	(bufs)->done = ((bufs)->done + 1) % NUM_BUFS;
> +#define ISP_BUF_MARK_QUEUED(bufs) \
> +	(bufs)->queue = ((bufs)->queue + 1) % NUM_BUFS;
> +
> +struct isp_bufs {
> +	dma_addr_t isp_addr_capture[VIDEO_MAX_FRAME];
> +	spinlock_t lock;	/* For handling current buffer */
> +	/* queue full: (ispsg.queue + 1) % NUM_BUFS == ispsg.done
> +	   queue empty: ispsg.queue == ispsg.done */
> +	struct isp_buf buf[NUM_BUFS];
> +	/* Next slot to queue a buffer. */
> +	int queue;
> +	/* Buffer that is being processed. */
> +	int done;
> +	/* Wait for this many hs_vs before anything else. */
> +	int wait_hs_vs;
> +};
> +
> +/**
> + * struct ispirq - Structure for containing callbacks to be called in ISP ISR.
> + * @isp_callbk: Array which stores callback functions, indexed by the type of
> + *              callback (8 possible types).
> + * @isp_callbk_arg1: Pointer to array containing pointers to the first argument
> + *                   to be passed to the requested callback function.
> + * @isp_callbk_arg2: Pointer to array containing pointers to the second
> + *                   argument to be passed to the requested callback function.
> + *
> + * This structure is used to contain all the callback functions related for
> + * each callback type (CBK_CCDC_VD0, CBK_CCDC_VD1, CBK_PREV_DONE,
> + * CBK_RESZ_DONE, CBK_MMU_ERR, CBK_H3A_AWB_DONE, CBK_HIST_DONE, CBK_HS_VS,
> + * CBK_LSC_ISR).
> + */
> +struct isp_irq {
> +	isp_callback_t isp_callbk[CBK_END];
> +	isp_vbq_callback_ptr isp_callbk_arg1[CBK_END];
> +	void *isp_callbk_arg2[CBK_END];
> +};
> +
> +/**
> + * struct ispmodule - Structure for storing ISP sub-module information.
> + * @isp_pipeline: Bit mask for submodules enabled within the ISP.
> + * @applyCrop: Flag to do a crop operation when video buffer queue ISR is done
> + * @pix: Structure containing the format and layout of the output image.
> + * @ccdc_input_width: ISP CCDC module input image width.
> + * @ccdc_input_height: ISP CCDC module input image height.
> + * @ccdc_output_width: ISP CCDC module output image width.
> + * @ccdc_output_height: ISP CCDC module output image height.
> + * @preview_input_width: ISP Preview module input image width.
> + * @preview_input_height: ISP Preview module input image height.
> + * @preview_output_width: ISP Preview module output image width.
> + * @preview_output_height: ISP Preview module output image height.
> + * @resizer_input_width: ISP Resizer module input image width.
> + * @resizer_input_height: ISP Resizer module input image height.
> + * @resizer_output_width: ISP Resizer module output image width.
> + * @resizer_output_height: ISP Resizer module output image height.
> + */
> +struct isp_module {
> +	unsigned int isp_pipeline;
> +	int applyCrop;
> +	struct v4l2_pix_format pix;
> +	unsigned int ccdc_input_width;
> +	unsigned int ccdc_input_height;
> +	unsigned int ccdc_output_width;
> +	unsigned int ccdc_output_height;
> +	unsigned int preview_input_width;
> +	unsigned int preview_input_height;
> +	unsigned int preview_output_width;
> +	unsigned int preview_output_height;
> +	unsigned int resizer_input_width;
> +	unsigned int resizer_input_height;
> +	unsigned int resizer_output_width;
> +	unsigned int resizer_output_height;
> +};
> +
> +#define RAW_CAPTURE(isp) \
> +	(!((isp)->module.isp_pipeline & OMAP_ISP_PREVIEW))
> +
> +/**
> + * struct isp - Structure for storing ISP Control module information
> + * @lock: Spinlock to sync between isr and processes.
> + * @isp_mutex: Semaphore used to get access to the ISP.
> + * @ref_count: Reference counter.
> + * @cam_ick: Pointer to ISP Interface clock.
> + * @cam_fck: Pointer to ISP Functional clock.
> + *
> + * This structure is used to store the OMAP ISP Control Information.
> + */
> +static struct isp {
> +	spinlock_t lock;	/* For handling registered ISP callbacks */
> +	struct mutex isp_mutex;	/* For handling ref_count field */
> +	int ref_count;
> +	struct clk *cam_ick;
> +	struct clk *cam_mclk;
> +	struct clk *csi2_fck;
> +	struct isp_interface_config *config;
> +	dma_addr_t tmp_buf;
> +	size_t tmp_buf_size;
> +	unsigned long tmp_buf_offset;
> +	 struct isp_bufs bufs;
> +	 struct isp_irq irq;
> +	 struct isp_module module;
> +} isp_obj;
> +
> +/* Structure for saving/restoring ISP module registers */
> +static struct isp_reg isp_reg_list[] = {
> +	{OMAP3_ISP_IOMEM_MAIN, ISP_SYSCONFIG, 0},
> +	{OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_GRESET_LENGTH, 0},
> +	{OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_PSTRB_REPLAY, 0},
> +	{OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, 0},
> +	{OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL, 0},
> +	{OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_FRAME, 0},
> +	{OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_PSTRB_DELAY, 0},
> +	{OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_STRB_DELAY, 0},
> +	{OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_SHUT_DELAY, 0},
> +	{OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_PSTRB_LENGTH, 0},
> +	{OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_STRB_LENGTH, 0},
> +	{OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_SHUT_LENGTH, 0},
> +	{OMAP3_ISP_IOMEM_CBUFF, ISP_CBUFF_SYSCONFIG, 0},
> +	{OMAP3_ISP_IOMEM_CBUFF, ISP_CBUFF_IRQENABLE, 0},
> +	{OMAP3_ISP_IOMEM_CBUFF, ISP_CBUFF0_CTRL, 0},
> +	{OMAP3_ISP_IOMEM_CBUFF, ISP_CBUFF1_CTRL, 0},
> +	{OMAP3_ISP_IOMEM_CBUFF, ISP_CBUFF0_START, 0},
> +	{OMAP3_ISP_IOMEM_CBUFF, ISP_CBUFF1_START, 0},
> +	{OMAP3_ISP_IOMEM_CBUFF, ISP_CBUFF0_END, 0},
> +	{OMAP3_ISP_IOMEM_CBUFF, ISP_CBUFF1_END, 0},
> +	{OMAP3_ISP_IOMEM_CBUFF, ISP_CBUFF0_WINDOWSIZE, 0},
> +	{OMAP3_ISP_IOMEM_CBUFF, ISP_CBUFF1_WINDOWSIZE, 0},
> +	{OMAP3_ISP_IOMEM_CBUFF, ISP_CBUFF0_THRESHOLD, 0},
> +	{OMAP3_ISP_IOMEM_CBUFF, ISP_CBUFF1_THRESHOLD, 0},
> +	{0, ISP_TOK_TERM, 0}
> +};
> +
> +u32 isp_reg_readl(enum isp_mem_resources isp_mmio_range, u32 reg_offset)
> +{
> +	return __raw_readl(omap3isp->mmio_base[isp_mmio_range] + reg_offset);
> +}
> +EXPORT_SYMBOL(isp_reg_readl);
> +
> +void isp_reg_writel(u32 reg_value, enum isp_mem_resources isp_mmio_range,
> +								u32 reg_offset)
> +{
> +	__raw_writel(reg_value,
> +			omap3isp->mmio_base[isp_mmio_range] + reg_offset);
> +}
> +EXPORT_SYMBOL(isp_reg_writel);
> +
> +/*
> + *
> + * V4L2 Handling
> + *
> + */
> +
> +/**
> + * find_vctrl - Returns the index of the ctrl array of the requested ctrl ID.
> + * @id: Requested control ID.
> + *
> + * Returns 0 if successful, -EINVAL if not found, or -EDOM if its out of
> + * domain.
> + **/
> +static int find_vctrl(int id)
> +{
> +	int i;
> +
> +	if (id < V4L2_CID_BASE)
> +		return -EDOM;
> +
> +	for (i = (ARRAY_SIZE(video_control) - 1); i >= 0; i--)
> +		if (video_control[i].qc.id == id)
> +			break;
> +
> +	if (i < 0)
> +		i = -EINVAL;
> +
> +	return i;
> +}
> +
> +static int find_next_vctrl(int id)
> +{
> +	int i;
> +	u32 best = (u32)-1;
> +
> +	for (i = 0; i < ARRAY_SIZE(video_control); i++) {
> +		if (video_control[i].qc.id > id &&
> +						(best == (u32)-1 ||
> +						video_control[i].qc.id <
> +						video_control[best].qc.id)) {
> +			best = i;
> +		}
> +	}
> +
> +	if (best == (u32)-1)
> +		return -EINVAL;
> +
> +	return best;
> +}
> +
> +/**
> + * find_vmenu - Returns index of the menu array of the requested ctrl option.
> + * @id: Requested control ID.
> + * @index: Requested menu option index.
> + *
> + * Returns 0 if successful, -EINVAL if not found, or -EDOM if its out of
> + * domain.
> + **/
> +static int find_vmenu(int id, int index)
> +{
> +	int i;
> +
> +	if (id < V4L2_CID_BASE)
> +		return -EDOM;
> +
> +	for (i = (ARRAY_SIZE(video_menu) - 1); i >= 0; i--) {
> +		if ((video_menu[i].id != id) || (video_menu[i].index != index))
> +			continue;
> +		return i;
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +/**
> + * isp_release_resources - Free ISP submodules
> + **/
> +static void isp_release_resources(void)
> +{
> +	if (isp_obj.module.isp_pipeline & OMAP_ISP_CCDC)
> +		ispccdc_free();
> +
> +	if (isp_obj.module.isp_pipeline & OMAP_ISP_PREVIEW)
> +		isppreview_free();
> +
> +	if (isp_obj.module.isp_pipeline & OMAP_ISP_RESIZER)
> +		ispresizer_free();
> +	return;
> +}
> +
> +static int isp_wait(int (*busy)(void), int wait_for_busy, int max_wait)
> +{
> +	int wait = 0;
> +
> +	if (max_wait == 0)
> +		max_wait = 10000; /* 10 ms */
> +
> +	while ((wait_for_busy && !busy())
> +	       || (!wait_for_busy && busy())) {
> +		rmb();
> +		udelay(1);
> +		wait++;
> +		if (wait > max_wait) {
> +			printk(KERN_ALERT "%s: wait is too much\n", __func__);
> +			return -EBUSY;
> +		}
> +	}
> +	DPRINTK_ISPCTRL(KERN_ALERT "%s: wait %d\n", __func__, wait);
> +
> +	return 0;
> +}
> +
> +static int ispccdc_sbl_wait_idle(int max_wait)
> +{
> +	return isp_wait(ispccdc_sbl_busy, 0, max_wait);
> +}
> +
> +static void isp_enable_interrupts(int is_raw)
> +{
> +	isp_reg_writel(-1, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS);
> +	isp_reg_or(OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE,
> +		    IRQ0ENABLE_CCDC_LSC_PREF_ERR_IRQ |
> +		    IRQ0ENABLE_HS_VS_IRQ |
> +		    IRQ0ENABLE_CCDC_VD0_IRQ |
> +		    IRQ0ENABLE_CCDC_VD1_IRQ);
> +
> +	if (is_raw)
> +		return;
> +
> +	isp_reg_or(OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE,
> +		    IRQ0ENABLE_PRV_DONE_IRQ |
> +		    IRQ0ENABLE_RSZ_DONE_IRQ);
> +
> +	return;
> +}
> +
> +static void isp_disable_interrupts(void)
> +{
> +	isp_reg_and(OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE,
> +		    ~(IRQ0ENABLE_CCDC_LSC_PREF_ERR_IRQ |
> +			IRQ0ENABLE_HS_VS_IRQ |
> +			IRQ0ENABLE_CCDC_VD0_IRQ |
> +			IRQ0ENABLE_CCDC_VD1_IRQ |
> +			IRQ0ENABLE_PRV_DONE_IRQ |
> +			IRQ0ENABLE_RSZ_DONE_IRQ));
> +}
> +
> +/**
> + * isp_set_callback - Sets the callback for the ISP module done events.
> + * @type: Type of the event for which callback is requested.
> + * @callback: Method to be called as callback in the ISR context.
> + * @arg1: First argument to be passed when callback is called in ISR.
> + * @arg2: Second argument to be passed when callback is called in ISR.
> + *
> + * This function sets a callback function for a done event in the ISP
> + * module, and enables the corresponding interrupt.
> + **/
> +int isp_set_callback(enum isp_callback_type type, isp_callback_t callback,
> +						isp_vbq_callback_ptr arg1,
> +						void *arg2)
> +{
> +	unsigned long irqflags = 0;
> +
> +	if (callback == NULL) {
> +		DPRINTK_ISPCTRL("ISP_ERR : Null Callback\n");
> +		return -EINVAL;
> +	}
> +
> +	spin_lock_irqsave(&isp_obj.lock, irqflags);
> +	isp_obj.irq.isp_callbk[type] = callback;
> +	isp_obj.irq.isp_callbk_arg1[type] = arg1;
> +	isp_obj.irq.isp_callbk_arg2[type] = arg2;
> +	spin_unlock_irqrestore(&isp_obj.lock, irqflags);
> +
> +	switch (type) {
> +	case CBK_H3A_AWB_DONE:
> +		isp_reg_writel(IRQ0ENABLE_H3A_AWB_DONE_IRQ,
> +					OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS);
> +		isp_reg_or(OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE,
> +						IRQ0ENABLE_H3A_AWB_DONE_IRQ);
> +		break;
> +	case CBK_H3A_AF_DONE:
> +		isp_reg_writel(IRQ0ENABLE_H3A_AF_DONE_IRQ,
> +					OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS);
> +		isp_reg_or(OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE,
> +						IRQ0ENABLE_H3A_AF_DONE_IRQ);
> +		break;
> +	case CBK_HIST_DONE:
> +		isp_reg_writel(IRQ0ENABLE_HIST_DONE_IRQ,
> +					OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS);
> +		isp_reg_or(OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE,
> +						IRQ0ENABLE_HIST_DONE_IRQ);
> +		break;
> +	case CBK_PREV_DONE:
> +		isp_reg_writel(IRQ0ENABLE_PRV_DONE_IRQ,
> +					OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS);
> +		isp_reg_or(OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE,
> +						IRQ0ENABLE_PRV_DONE_IRQ);
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(isp_set_callback);
> +
> +/**
> + * isp_unset_callback - Clears the callback for the ISP module done events.
> + * @type: Type of the event for which callback to be cleared.
> + *
> + * This function clears a callback function for a done event in the ISP
> + * module, and disables the corresponding interrupt.
> + **/
> +int isp_unset_callback(enum isp_callback_type type)
> +{
> +	unsigned long irqflags = 0;
> +
> +	spin_lock_irqsave(&isp_obj.lock, irqflags);
> +	isp_obj.irq.isp_callbk[type] = NULL;
> +	isp_obj.irq.isp_callbk_arg1[type] = NULL;
> +	isp_obj.irq.isp_callbk_arg2[type] = NULL;
> +	spin_unlock_irqrestore(&isp_obj.lock, irqflags);
> +
> +	switch (type) {
> +	case CBK_H3A_AWB_DONE:
> +		isp_reg_and(OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE,
> +						~IRQ0ENABLE_H3A_AWB_DONE_IRQ);
> +		break;
> +	case CBK_H3A_AF_DONE:
> +		isp_reg_and(OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE,
> +						~IRQ0ENABLE_H3A_AF_DONE_IRQ);
> +		break;
> +	case CBK_HIST_DONE:
> +		isp_reg_and(OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE,
> +						~IRQ0ENABLE_HIST_DONE_IRQ);
> +		break;
> +	case CBK_CSIA:
> +		isp_csi2_irq_set(0);
> +		break;
> +	case CBK_CSIB:
> +		isp_reg_writel(IRQ0ENABLE_CSIB_IRQ, OMAP3_ISP_IOMEM_MAIN,
> +							ISP_IRQ0STATUS);
> +		isp_reg_or(OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE,
> +						IRQ0ENABLE_CSIB_IRQ);
> +		break;
> +	case CBK_PREV_DONE:
> +		isp_reg_and(OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE,
> +						~IRQ0ENABLE_PRV_DONE_IRQ);
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(isp_unset_callback);
> +
> +/**
> + * isp_set_xclk - Configures the specified cam_xclk to the desired frequency.
> + * @xclk: Desired frequency of the clock in Hz.
> + * @xclksel: XCLK to configure (0 = A, 1 = B).
> + *
> + * Configures the specified MCLK divisor in the ISP timing control register
> + * (TCTRL_CTRL) to generate the desired xclk clock value.
> + *
> + * Divisor = CM_CAM_MCLK_HZ / xclk
> + *
> + * Returns the final frequency that is actually being generated
> + **/
> +u32 isp_set_xclk(u32 xclk, u8 xclksel)
> +{
> +	u32 divisor;
> +	u32 currentxclk;
> +
> +	if (xclk >= CM_CAM_MCLK_HZ) {
> +		divisor = ISPTCTRL_CTRL_DIV_BYPASS;
> +		currentxclk = CM_CAM_MCLK_HZ;
> +	} else if (xclk >= 2) {
> +		divisor = CM_CAM_MCLK_HZ / xclk;
> +		if (divisor >= ISPTCTRL_CTRL_DIV_BYPASS)
> +			divisor = ISPTCTRL_CTRL_DIV_BYPASS - 1;
> +		currentxclk = CM_CAM_MCLK_HZ / divisor;
> +	} else {
> +		divisor = xclk;
> +		currentxclk = 0;
> +	}
> +
> +	switch (xclksel) {
> +	case 0:
> +		isp_reg_and_or(OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL,
> +					~ISPTCTRL_CTRL_DIVA_MASK,
> +					divisor << ISPTCTRL_CTRL_DIVA_SHIFT);
> +		DPRINTK_ISPCTRL("isp_set_xclk(): cam_xclka set to %d Hz\n",
> +								currentxclk);
> +		break;
> +	case 1:
> +		isp_reg_and_or(OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL,
> +					~ISPTCTRL_CTRL_DIVB_MASK,
> +					divisor << ISPTCTRL_CTRL_DIVB_SHIFT);
> +		DPRINTK_ISPCTRL("isp_set_xclk(): cam_xclkb set to %d Hz\n",
> +								currentxclk);
> +		break;
> +	default:
> +		DPRINTK_ISPCTRL("ISP_ERR: isp_set_xclk(): Invalid requested "
> +						"xclk. Must be 0 (A) or 1 (B)."
> +						"\n");
> +		return -EINVAL;
> +	}
> +
> +	return currentxclk;
> +}
> +EXPORT_SYMBOL(isp_set_xclk);
> +
> +/**
> + * isp_power_settings - Sysconfig settings, for Power Management.
> + * @isp_sysconfig: Structure containing the power settings for ISP to configure
> + *
> + * Sets the power settings for the ISP, and SBL bus.
> + **/
> +static void isp_power_settings(int idle)
> +{
> +	if (idle) {
> +		isp_reg_writel(ISP_SYSCONFIG_AUTOIDLE |
> +				(ISP_SYSCONFIG_MIDLEMODE_SMARTSTANDBY <<
> +				ISP_SYSCONFIG_MIDLEMODE_SHIFT),
> +				OMAP3_ISP_IOMEM_MAIN,
> +				ISP_SYSCONFIG);
> +		if (omap_rev() == OMAP3430_REV_ES1_0) {
> +			isp_reg_writel(ISPCSI1_AUTOIDLE |
> +					(ISPCSI1_MIDLEMODE_SMARTSTANDBY <<
> +					ISPCSI1_MIDLEMODE_SHIFT),
> +					OMAP3_ISP_IOMEM_CSI2A,
> +					ISP_CSIA_SYSCONFIG);
> +			isp_reg_writel(ISPCSI1_AUTOIDLE |
> +					(ISPCSI1_MIDLEMODE_SMARTSTANDBY <<
> +					ISPCSI1_MIDLEMODE_SHIFT),
> +					OMAP3_ISP_IOMEM_CCP2,
> +					ISP_CSIB_SYSCONFIG);
> +		}
> +		isp_reg_writel(ISPCTRL_SBL_AUTOIDLE, OMAP3_ISP_IOMEM_MAIN,
> +								ISP_CTRL);
> +
> +	} else {
> +		isp_reg_writel(ISP_SYSCONFIG_AUTOIDLE |
> +				(ISP_SYSCONFIG_MIDLEMODE_FORCESTANDBY <<
> +				ISP_SYSCONFIG_MIDLEMODE_SHIFT),
> +				OMAP3_ISP_IOMEM_MAIN,
> +				ISP_SYSCONFIG);
> +		if (omap_rev() == OMAP3430_REV_ES1_0) {
> +			isp_reg_writel(ISPCSI1_AUTOIDLE |
> +					(ISPCSI1_MIDLEMODE_FORCESTANDBY <<
> +					ISPCSI1_MIDLEMODE_SHIFT),
> +					OMAP3_ISP_IOMEM_CSI2A,
> +					ISP_CSIA_SYSCONFIG);
> +
> +			isp_reg_writel(ISPCSI1_AUTOIDLE |
> +					(ISPCSI1_MIDLEMODE_FORCESTANDBY <<
> +					ISPCSI1_MIDLEMODE_SHIFT),
> +					OMAP3_ISP_IOMEM_CCP2,
> +					ISP_CSIB_SYSCONFIG);
> +		}
> +
> +		isp_reg_writel(ISPCTRL_SBL_AUTOIDLE, OMAP3_ISP_IOMEM_MAIN,
> +								ISP_CTRL);
> +	}
> +}
> +
> +#define BIT_SET(var, shift, mask, val)		\
> +	do {					\
> +		var = (var & ~(mask << shift))	\
> +			| (val << shift);	\
> +	} while (0)
> +
> +static int isp_init_csi(struct isp_interface_config *config)
> +{
> +	u32 i = 0, val, reg;
> +	int format;
> +
> +	switch (config->u.csi.format) {
> +	case V4L2_PIX_FMT_SGRBG10:
> +		format = 0x16;		/* RAW10+VP */
> +		break;
> +	case V4L2_PIX_FMT_SGRBG10DPCM8:
> +		format = 0x12;		/* RAW8+DPCM10+VP */
> +		break;
> +	default:
> +		printk(KERN_ERR "isp_init_csi: bad csi format\n");
> +		return -EINVAL;
> +	}
> +
> +	/* Reset the CSI and wait for reset to complete */
> +	isp_reg_writel(isp_reg_readl(OMAP3_ISP_IOMEM_CCP2, ISPCSI1_SYSCONFIG) |
> +							BIT(1),
> +							OMAP3_ISP_IOMEM_CCP2,
> +							ISPCSI1_SYSCONFIG);
> +	while (!(isp_reg_readl(OMAP3_ISP_IOMEM_CCP2, ISPCSI1_SYSSTATUS) &
> +								BIT(0))) {
> +		udelay(10);
> +		if (i++ > 10)
> +			break;
> +	}
> +	if (!(isp_reg_readl(OMAP3_ISP_IOMEM_CCP2, ISPCSI1_SYSSTATUS) &
> +								BIT(0))) {
> +		printk(KERN_WARNING
> +			"omap3_isp: timeout waiting for csi reset\n");
> +	}
> +
> +	/* ISPCSI1_CTRL */
> +	val = isp_reg_readl(OMAP3_ISP_IOMEM_CCP2, ISPCSI1_CTRL);
> +	val &= ~BIT(11);	/* Enable VP only off ->
> +				extract embedded data to interconnect */
> +	BIT_SET(val, 8, 0x3, config->u.csi.vpclk);	/* Video port clock */
> +/*	val |= BIT(3);	*/	/* Wait for FEC before disabling interface */
> +	val |= BIT(2);		/* I/O cell output is parallel
> +				(no effect, but errata says should be enabled
> +				for class 1/2) */
> +	val |= BIT(12);		/* VP clock polarity to falling edge
> +				(needed or bad picture!) */
> +
> +	/* Data/strobe physical layer */
> +	BIT_SET(val, 1, 1, config->u.csi.signalling);
> +	BIT_SET(val, 10, 1, config->u.csi.strobe_clock_inv);
> +	val |= BIT(4);		/* Magic bit to enable CSI1 and strobe mode */
> +	isp_reg_writel(val, OMAP3_ISP_IOMEM_CCP2, ISPCSI1_CTRL);
> +
> +	/* ISPCSI1_LCx_CTRL logical channel #0 */
> +	reg = ISPCSI1_LCx_CTRL(0);	/* reg = ISPCSI1_CTRL1; */
> +	val = isp_reg_readl(OMAP3_ISP_IOMEM_CCP2, reg);
> +	/* Format = RAW10+VP or RAW8+DPCM10+VP*/
> +	BIT_SET(val, 3, 0x1f, format);
> +	/* Enable setting of frame regions of interest */
> +	BIT_SET(val, 1, 1, 1);
> +	BIT_SET(val, 2, 1, config->u.csi.crc);
> +	isp_reg_writel(val, OMAP3_ISP_IOMEM_CCP2, reg);
> +
> +	/* ISPCSI1_DAT_START for logical channel #0 */
> +	reg = ISPCSI1_LCx_DAT_START(0);		/* reg = ISPCSI1_DAT_START; */
> +	val = isp_reg_readl(OMAP3_ISP_IOMEM_CCP2, reg);
> +	BIT_SET(val, 16, 0xfff, config->u.csi.data_start);
> +	isp_reg_writel(val, OMAP3_ISP_IOMEM_CCP2, reg);
> +
> +	/* ISPCSI1_DAT_SIZE for logical channel #0 */
> +	reg = ISPCSI1_LCx_DAT_SIZE(0);		/* reg = ISPCSI1_DAT_SIZE; */
> +	val = isp_reg_readl(OMAP3_ISP_IOMEM_CCP2, reg);
> +	BIT_SET(val, 16, 0xfff, config->u.csi.data_size);
> +	isp_reg_writel(val, OMAP3_ISP_IOMEM_CCP2, reg);
> +
> +	/* Clear status bits for logical channel #0 */
> +	isp_reg_writel(0xFFF & ~BIT(6), OMAP3_ISP_IOMEM_CCP2,
> +						ISPCSI1_LC01_IRQSTATUS);
> +
> +	/* Enable CSI1 */
> +	val = isp_reg_readl(OMAP3_ISP_IOMEM_CCP2, ISPCSI1_CTRL);
> +	val |=  BIT(0) | BIT(4);
> +	isp_reg_writel(val, OMAP3_ISP_IOMEM_CCP2, ISPCSI1_CTRL);
> +
> +	if (!(isp_reg_readl(OMAP3_ISP_IOMEM_CCP2, ISPCSI1_CTRL) & BIT(4))) {
> +		printk(KERN_WARNING "OMAP3 CSI1 bus not available\n");
> +		if (config->u.csi.signalling)	/* Strobe mode requires CSI1 */
> +			return -EIO;
> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * isp_configure_interface - Configures ISP Control I/F related parameters.
> + * @config: Pointer to structure containing the desired configuration for the
> + * 	ISP.
> + *
> + * Configures ISP control register (ISP_CTRL) with the values specified inside
> + * the config structure. Controls:
> + * - Selection of parallel or serial input to the preview hardware.
> + * - Data lane shifter.
> + * - Pixel clock polarity.
> + * - 8 to 16-bit bridge at the input of CCDC module.
> + * - HS or VS synchronization signal detection
> + **/
> +int isp_configure_interface(struct isp_interface_config *config)
> +{
> +	u32 ispctrl_val = isp_reg_readl(OMAP3_ISP_IOMEM_MAIN, ISP_CTRL);
> +	int r;
> +
> +	isp_obj.config = config;
> +
> +	ispctrl_val &= ISPCTRL_SHIFT_MASK;
> +	ispctrl_val |= (config->dataline_shift << ISPCTRL_SHIFT_SHIFT);
> +	ispctrl_val &= ~ISPCTRL_PAR_CLK_POL_INV;
> +
> +	ispctrl_val &= (ISPCTRL_PAR_SER_CLK_SEL_MASK);
> +
> +	isp_buf_init();
> +
> +	switch (config->ccdc_par_ser) {
> +	case ISP_PARLL:
> +		ispctrl_val |= ISPCTRL_PAR_SER_CLK_SEL_PARALLEL;
> +		ispctrl_val |= (config->u.par.par_clk_pol
> +						<< ISPCTRL_PAR_CLK_POL_SHIFT);
> +		ispctrl_val &= ~ISPCTRL_PAR_BRIDGE_BENDIAN;
> +		ispctrl_val |= (config->u.par.par_bridge
> +						<< ISPCTRL_PAR_BRIDGE_SHIFT);
> +		break;
> +	case ISP_CSIA:
> +		ispctrl_val |= ISPCTRL_PAR_SER_CLK_SEL_CSIA;
> +		ispctrl_val &= ~ISPCTRL_PAR_BRIDGE_BENDIAN;
> +
> +		isp_csi2_ctx_config_format(0, config->u.csi.format);
> +		isp_csi2_ctx_update(0, false);
> +
> +		if (config->u.csi.crc)
> +			isp_csi2_ctrl_config_ecc_enable(true);
> +
> +		isp_csi2_ctrl_config_vp_out_ctrl(config->u.csi.vpclk);
> +		isp_csi2_ctrl_config_vp_only_enable(true);
> +		isp_csi2_ctrl_config_vp_clk_enable(true);
> +		isp_csi2_ctrl_update(false);
> +
> +		isp_csi2_irq_complexio1_set(1);
> +		isp_csi2_irq_status_set(1);
> +		isp_csi2_irq_set(1);
> +
> +		isp_csi2_enable(1);
> +		mdelay(3);
> +		break;
> +	case ISP_CSIB:
> +		ispctrl_val |= ISPCTRL_PAR_SER_CLK_SEL_CSIB;
> +		r = isp_init_csi(config);
> +		if (r)
> +			return r;
> +		break;
> +	case ISP_NONE:
> +		return 0;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	ispctrl_val &= ~(ISPCTRL_SYNC_DETECT_VSRISE);
> +	ispctrl_val |= (config->hsvs_syncdetect);
> +
> +	isp_reg_writel(ispctrl_val, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL);
> +
> +	/* Set sensor specific fields in CCDC and Previewer module.*/
> +	isppreview_set_skip(config->prev_sph, config->prev_slv);
> +	ispccdc_set_wenlog(config->wenlog);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(isp_configure_interface);
> +
> +static int isp_buf_process(struct isp_bufs *bufs);
> +
> +/**
> + * omap34xx_isp_isr - Interrupt Service Routine for Camera ISP module.
> + * @irq: Not used currently.
> + * @ispirq_disp: Pointer to the object that is passed while request_irq is
> + *               called. This is the isp_obj.irq object containing info on the
> + *               callback.
> + *
> + * Handles the corresponding callback if plugged in.
> + *
> + * Returns IRQ_HANDLED when IRQ was correctly handled, or IRQ_NONE when the
> + * IRQ wasn't handled.
> + **/
> +static irqreturn_t omap34xx_isp_isr(int irq, void *_isp)
> +{
> +	struct isp *isp = _isp;
> +	struct isp_irq *irqdis = &isp->irq;
> +	struct isp_bufs *bufs = &isp->bufs;
> +	unsigned long flags;
> +	u32 irqstatus = 0;
> +	unsigned long irqflags = 0;
> +	int wait_hs_vs = 0;
> +
> +	irqstatus = isp_reg_readl(OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS);
> +	isp_reg_writel(irqstatus, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS);
> +
> +	spin_lock_irqsave(&bufs->lock, flags);
> +	wait_hs_vs = bufs->wait_hs_vs;
> +	if (irqstatus & HS_VS && bufs->wait_hs_vs)
> +		bufs->wait_hs_vs--;
> +	spin_unlock_irqrestore(&bufs->lock, flags);
> +
> +	spin_lock_irqsave(&isp_obj.lock, irqflags);
> +	/*
> +	 * We need to wait for the first HS_VS interrupt from CCDC.
> +	 * Otherwise our frame (and everything else) might be bad.
> +	 */
> +	if (wait_hs_vs)
> +		goto out_ignore_buff;
> +
> +	if ((irqstatus & CCDC_VD0) == CCDC_VD0) {
> +		if (RAW_CAPTURE(&isp_obj))
> +			isp_buf_process(bufs);
> +	}
> +
> +	if ((irqstatus & PREV_DONE) == PREV_DONE) {
> +		if (irqdis->isp_callbk[CBK_PREV_DONE])
> +			irqdis->isp_callbk[CBK_PREV_DONE](PREV_DONE,
> +				irqdis->isp_callbk_arg1[CBK_PREV_DONE],
> +				irqdis->isp_callbk_arg2[CBK_PREV_DONE]);
> +		else if (!RAW_CAPTURE(&isp_obj) && !ispresizer_busy()) {
> +			if (isp_obj.module.applyCrop) {
> +				ispresizer_applycrop();
> +				if (!ispresizer_busy())
> +					isp_obj.module.applyCrop = 0;
> +			}
> +			if (!isppreview_busy()) {
> +				ispresizer_enable(1);
> +				if (isppreview_busy()) {
> +					/* FIXME: locking! */
> +					ISP_BUF_DONE(bufs)->vb_state =
> +						VIDEOBUF_ERROR;
> +					printk(KERN_ERR "%s: can't stop"
> +					       " preview\n", __func__);
> +				}
> +			}
> +			if (!isppreview_busy())
> +				isppreview_config_shadow_registers();
> +			if (!isppreview_busy())
> +				isph3a_update_wb();
> +		}
> +	}
> +
> +	if ((irqstatus & RESZ_DONE) == RESZ_DONE) {
> +		if (!RAW_CAPTURE(&isp_obj)) {
> +			if (!ispresizer_busy())
> +				ispresizer_config_shadow_registers();
> +			isp_buf_process(bufs);
> +		}
> +	}
> +
> +	if ((irqstatus & H3A_AWB_DONE) == H3A_AWB_DONE) {
> +		if (irqdis->isp_callbk[CBK_H3A_AWB_DONE])
> +			irqdis->isp_callbk[CBK_H3A_AWB_DONE](H3A_AWB_DONE,
> +				irqdis->isp_callbk_arg1[CBK_H3A_AWB_DONE],
> +				irqdis->isp_callbk_arg2[CBK_H3A_AWB_DONE]);
> +	}
> +
> +	if ((irqstatus & HIST_DONE) == HIST_DONE) {
> +		if (irqdis->isp_callbk[CBK_HIST_DONE])
> +			irqdis->isp_callbk[CBK_HIST_DONE](HIST_DONE,
> +				irqdis->isp_callbk_arg1[CBK_HIST_DONE],
> +				irqdis->isp_callbk_arg2[CBK_HIST_DONE]);
> +	}
> +
> +	if ((irqstatus & H3A_AF_DONE) == H3A_AF_DONE) {
> +		if (irqdis->isp_callbk[CBK_H3A_AF_DONE])
> +			irqdis->isp_callbk[CBK_H3A_AF_DONE](H3A_AF_DONE,
> +				irqdis->isp_callbk_arg1[CBK_H3A_AF_DONE],
> +				irqdis->isp_callbk_arg2[CBK_H3A_AF_DONE]);
> +	}
> +
> +
> +out_ignore_buff:
> +	if (irqstatus & LSC_PRE_ERR) {
> +		struct isp_buf *buf = ISP_BUF_DONE(bufs);
> +		ispccdc_enable_lsc(0);
> +		ispccdc_enable_lsc(1);
> +		/* Mark buffer faulty. */
> +		buf->vb_state = VIDEOBUF_ERROR;
> +		printk(KERN_ERR "%s: lsc prefetch error\n", __func__);
> +	}
> +
> +	if ((irqstatus & CSIA) == CSIA) {
> +		struct isp_buf *buf = ISP_BUF_DONE(bufs);
> +		isp_csi2_isr();
> +		buf->vb_state = VIDEOBUF_ERROR;
> +	}
> +
> +	if (irqstatus & IRQ0STATUS_CSIB_IRQ) {
> +		u32 ispcsi1_irqstatus;
> +
> +		ispcsi1_irqstatus = isp_reg_readl(OMAP3_ISP_IOMEM_CCP2,
> +						ISPCSI1_LC01_IRQSTATUS);
> +		DPRINTK_ISPCTRL("%x\n", ispcsi1_irqstatus);
> +	}
> +
> +	if (irqdis->isp_callbk[CBK_CATCHALL]) {
> +		irqdis->isp_callbk[CBK_CATCHALL](irqstatus,
> +			irqdis->isp_callbk_arg1[CBK_CATCHALL],
> +			irqdis->isp_callbk_arg2[CBK_CATCHALL]);
> +	}
> +
> +	spin_unlock_irqrestore(&isp_obj.lock, irqflags);
> +
> +#if 1
> +	{
> +		static const struct {
> +			int num;
> +			char *name;
> +		} bits[] = {
> +			{ 31, "HS_VS_IRQ" },
> +			{ 30, "SEC_ERR_IRQ" },
> +			{ 29, "OCP_ERR_IRQ" },
> +			{ 28, "MMU_ERR_IRQ" },
> +			{ 27, "res27" },
> +			{ 26, "res26" },
> +			{ 25, "OVF_IRQ" },
> +			{ 24, "RSZ_DONE_IRQ" },
> +			{ 23, "res23" },
> +			{ 22, "res22" },
> +			{ 21, "CBUFF_IRQ" },
> +			{ 20, "PRV_DONE_IRQ" },
> +			{ 19, "CCDC_LSC_PREFETCH_ERROR" },
> +			{ 18, "CCDC_LSC_PREFETCH_COMPLETED" },
> +			{ 17, "CCDC_LSC_DONE" },
> +			{ 16, "HIST_DONE_IRQ" },
> +			{ 15, "res15" },
> +			{ 14, "res14" },
> +			{ 13, "H3A_AWB_DONE_IRQ" },
> +			{ 12, "H3A_AF_DONE_IRQ" },
> +			{ 11, "CCDC_ERR_IRQ" },
> +			{ 10, "CCDC_VD2_IRQ" },
> +			{  9, "CCDC_VD1_IRQ" },
> +			{  8, "CCDC_VD0_IRQ" },
> +			{  7, "res7" },
> +			{  6, "res6" },
> +			{  5, "res5" },
> +			{  4, "CSIB_IRQ" },
> +			{  3, "CSIB_LCM_IRQ" },
> +			{  2, "res2" },
> +			{  1, "res1" },
> +			{  0, "CSIA_IRQ" },
> +		};
> +		int i;
> +		for (i = 0; i < ARRAY_SIZE(bits); i++) {
> +			if ((1 << bits[i].num) & irqstatus)
> +				DPRINTK_ISPCTRL("%s ", bits[i].name);
> +		}
> +		DPRINTK_ISPCTRL("\n");
> +	}
> +#endif
> +
> +	return IRQ_HANDLED;
> +}
> +
> +/* Device name, needed for resource tracking layer */
> +struct device_driver camera_drv = {
> +	.name = "camera"
> +};
> +
> +struct device camera_dev = {
> +	.driver = &camera_drv,
> +};
> +
> +/**
> + *  isp_tmp_buf_free - To free allocated 10MB memory
> + *
> + **/
> +static void isp_tmp_buf_free(void)
> +{
> +	if (isp_obj.tmp_buf) {
> +		ispmmu_vfree(isp_obj.tmp_buf);
> +		isp_obj.tmp_buf = 0;
> +		isp_obj.tmp_buf_size = 0;
> +	}
> +}
> +
> +/**
> + *  isp_tmp_buf_alloc - To allocate a 10MB memory
> + *
> + **/
> +static u32 isp_tmp_buf_alloc(size_t size)
> +{
> +	isp_tmp_buf_free();
> +
> +	printk(KERN_INFO "%s: allocating %d bytes\n", __func__, size);
> +
> +	isp_obj.tmp_buf = ispmmu_vmalloc(size);
> +	if (IS_ERR((void *)isp_obj.tmp_buf)) {
> +		printk(KERN_ERR "ispmmu_vmap mapping failed ");
> +		return -ENOMEM;
> +	}
> +	isp_obj.tmp_buf_size = size;
> +
> +	isppreview_set_outaddr(isp_obj.tmp_buf);
> +	ispresizer_set_inaddr(isp_obj.tmp_buf);
> +
> +	return 0;
> +}
> +
> +/**
> + * isp_start - Starts ISP submodule
> + *
> + * Start the needed isp components assuming these components
> + * are configured correctly.
> + **/
> +void isp_start(void)
> +{
> +	if ((isp_obj.module.isp_pipeline & OMAP_ISP_PREVIEW) &&
> +						is_isppreview_enabled())
> +		isppreview_enable(1);
> +
> +	return;
> +}
> +EXPORT_SYMBOL(isp_start);
> +
> +#define ISP_STATISTICS_BUSY				\
> +	()
> +#define ISP_STOP_TIMEOUT	msecs_to_jiffies(1000)
> +/**
> + * isp_stop - Stops isp submodules
> + **/
> +void isp_stop()
> +{
> +	unsigned long timeout = jiffies + ISP_STOP_TIMEOUT;
> +	int reset = 0;
> +
> +	isp_disable_interrupts();
> +
> +	/*
> +	 * We need to stop all the modules after CCDC first or they'll
> +	 * never stop since they may not get a full frame from CCDC.
> +	 */
> +	isp_af_enable(0);
> +	isph3a_aewb_enable(0);
> +	isp_hist_enable(0);
> +	isppreview_enable(0);
> +	ispresizer_enable(0);
> +
> +	timeout = jiffies + ISP_STOP_TIMEOUT;
> +	while (isp_af_busy()
> +	       || isph3a_aewb_busy()
> +	       || isp_hist_busy()
> +	       || isppreview_busy()
> +	       || ispresizer_busy()) {
> +		if (time_after(jiffies, timeout)) {
> +			printk(KERN_ERR "%s: can't stop non-ccdc modules\n",
> +			       __func__);
> +			reset = 1;
> +			break;
> +		}
> +		msleep(1);
> +	}
> +
> +	/* Let's stop CCDC now. */
> +	ispccdc_enable_lsc(0);
> +	ispccdc_enable(0);
> +
> +	timeout = jiffies + ISP_STOP_TIMEOUT;
> +	while (ispccdc_busy()) {
> +		if (time_after(jiffies, timeout)) {
> +			printk(KERN_ERR "%s: can't stop ccdc\n", __func__);
> +			reset = 1;
> +			break;
> +		}
> +		msleep(1);
> +	}
> +
> +	isp_buf_init();
> +
> +	if (!reset)
> +		return;
> +
> +	isp_save_ctx();
> +	isp_reg_writel(isp_reg_readl(OMAP3_ISP_IOMEM_MAIN, ISP_SYSCONFIG)
> +		       | ISP_SYSCONFIG_SOFTRESET,
> +		       OMAP3_ISP_IOMEM_MAIN, ISP_SYSCONFIG);
> +	timeout = 0;
> +	while (!(isp_reg_readl(OMAP3_ISP_IOMEM_MAIN, ISP_SYSSTATUS) & 0x1)) {
> +		if (timeout++ > 10000) {
> +			printk(KERN_ALERT "%s: cannot reset ISP\n", __func__);
> +			break;
> +		}
> +		udelay(1);
> +	}
> +	isp_restore_ctx();
> +}
> +EXPORT_SYMBOL(isp_stop);
> +
> +static void isp_set_buf(struct isp_buf *buf)
> +{
> +	if ((isp_obj.module.isp_pipeline & OMAP_ISP_RESIZER) &&
> +						is_ispresizer_enabled())
> +		ispresizer_set_outaddr(buf->isp_addr);
> +	else if (isp_obj.module.isp_pipeline & OMAP_ISP_CCDC)
> +		ispccdc_set_outaddr(buf->isp_addr);
> +
> +}
> +
> +/**
> + * isp_calc_pipeline - Sets pipeline depending of input and output pixel format
> + * @pix_input: Pointer to V4L2 pixel format structure for input image.
> + * @pix_output: Pointer to V4L2 pixel format structure for output image.
> + **/
> +static u32 isp_calc_pipeline(struct v4l2_pix_format *pix_input,
> +			     struct v4l2_pix_format *pix_output)
> +{
> +	isp_release_resources();
> +	if ((pix_input->pixelformat == V4L2_PIX_FMT_SGRBG10
> +	     || pix_input->pixelformat == V4L2_PIX_FMT_SGRBG10DPCM8)
> +	    && pix_output->pixelformat != V4L2_PIX_FMT_SGRBG10) {
> +		isp_obj.module.isp_pipeline = OMAP_ISP_CCDC | OMAP_ISP_PREVIEW |
> +							OMAP_ISP_RESIZER;
> +		ispccdc_request();
> +		isppreview_request();
> +		ispresizer_request();
> +		ispccdc_config_datapath(CCDC_RAW, CCDC_OTHERS_VP);
> +		isppreview_config_datapath(PRV_RAW_CCDC, PREVIEW_MEM);
> +		ispresizer_config_datapath(RSZ_MEM_YUV);
> +	} else {
> +		isp_obj.module.isp_pipeline = OMAP_ISP_CCDC;
> +		ispccdc_request();
> +		if (pix_input->pixelformat == V4L2_PIX_FMT_SGRBG10
> +		    || pix_input->pixelformat == V4L2_PIX_FMT_SGRBG10DPCM8)
> +			ispccdc_config_datapath(CCDC_RAW, CCDC_OTHERS_VP_MEM);
> +		else
> +			ispccdc_config_datapath(CCDC_YUV_SYNC,
> +							CCDC_OTHERS_MEM);
> +	}
> +	return 0;
> +}
> +
> +/**
> + * isp_config_pipeline - Configures the image size and ycpos for ISP submodules
> + * @pix_input: Pointer to V4L2 pixel format structure for input image.
> + * @pix_output: Pointer to V4L2 pixel format structure for output image.
> + *
> + * The configuration of ycpos depends on the output pixel format for both the
> + * Preview and Resizer submodules.
> + **/
> +static void isp_config_pipeline(struct v4l2_pix_format *pix_input,
> +				struct v4l2_pix_format *pix_output)
> +{
> +	ispccdc_config_size(isp_obj.module.ccdc_input_width,
> +			isp_obj.module.ccdc_input_height,
> +			isp_obj.module.ccdc_output_width,
> +			isp_obj.module.ccdc_output_height);
> +
> +	if (isp_obj.module.isp_pipeline & OMAP_ISP_PREVIEW) {
> +		isppreview_config_size(isp_obj.module.preview_input_width,
> +			isp_obj.module.preview_input_height,
> +			isp_obj.module.preview_output_width,
> +			isp_obj.module.preview_output_height);
> +	}
> +
> +	if (isp_obj.module.isp_pipeline & OMAP_ISP_RESIZER) {
> +		ispresizer_config_size(isp_obj.module.resizer_input_width,
> +					isp_obj.module.resizer_input_height,
> +					isp_obj.module.resizer_output_width,
> +					isp_obj.module.resizer_output_height);
> +	}
> +
> +	if (pix_output->pixelformat == V4L2_PIX_FMT_UYVY) {
> +		isppreview_config_ycpos(YCPOS_YCrYCb);
> +		if (is_ispresizer_enabled())
> +			ispresizer_config_ycpos(0);
> +	} else {
> +		isppreview_config_ycpos(YCPOS_CrYCbY);
> +		if (is_ispresizer_enabled())
> +			ispresizer_config_ycpos(1);
> +	}
> +
> +	return;
> +}
> +
> +static void isp_buf_init(void)
> +{
> +	struct isp_bufs *bufs = &isp_obj.bufs;
> +	int sg;
> +
> +	bufs->queue = 0;
> +	bufs->done = 0;
> +	bufs->wait_hs_vs = isp_obj.config->wait_hs_vs;
> +	for (sg = 0; sg < NUM_BUFS; sg++) {
> +		bufs->buf[sg].complete = NULL;
> +		bufs->buf[sg].vb = NULL;
> +		bufs->buf[sg].priv = NULL;
> +	}
> +}
> +
> +/**
> + * isp_vbq_sync - Walks the pages table and flushes the cache for
> + *                each page.
> + **/
> +static int isp_vbq_sync(struct videobuf_buffer *vb, int when)
> +{
> +	flush_cache_all();
> +
> +	return 0;
> +}
> +
> +static int isp_buf_process(struct isp_bufs *bufs)
> +{
> +	struct isp_buf *buf = NULL;
> +	unsigned long flags;
> +	int last;
> +
> +	spin_lock_irqsave(&bufs->lock, flags);
> +
> +	if (ISP_BUFS_IS_EMPTY(bufs))
> +		goto out;
> +
> +	if (RAW_CAPTURE(&isp_obj) && ispccdc_sbl_wait_idle(1000)) {
> +		printk(KERN_ERR "ccdc %d won't become idle!\n",
> +		       RAW_CAPTURE(&isp_obj));
> +		goto out;
> +	}
> +
> +	/* We had at least one buffer in queue. */
> +	buf = ISP_BUF_DONE(bufs);
> +	last = ISP_BUFS_IS_LAST(bufs);
> +
> +	if (!last) {
> +		/* Set new buffer address. */
> +		isp_set_buf(ISP_BUF_NEXT_DONE(bufs));
> +	} else {
> +		/* Tell ISP not to write any of our buffers. */
> +		isp_disable_interrupts();
> +		if (RAW_CAPTURE(&isp_obj))
> +			ispccdc_enable(0);
> +		else
> +			ispresizer_enable(0);
> +		/*
> +		 * We must wait for the HS_VS since before that the
> +		 * CCDC may trigger interrupts even if it's not
> +		 * receiving a frame.
> +		 */
> +		bufs->wait_hs_vs = isp_obj.config->wait_hs_vs;
> +	}
> +	if ((RAW_CAPTURE(&isp_obj) && ispccdc_busy())
> +	    || (!RAW_CAPTURE(&isp_obj) && ispresizer_busy())) {
> +		/*
> +		 * Next buffer available: for the transfer to succeed, the
> +		 * CCDC (RAW capture) or resizer (YUV capture) must be idle
> +		 * for the duration of transfer setup. Bad things happen
> +		 * otherwise!
> +		 *
> +		 * Next buffer not available: if we fail to stop the
> +		 * ISP the buffer is probably going to be bad.
> +		 */
> +		/* Mark this buffer faulty. */
> +		buf->vb_state = VIDEOBUF_ERROR;
> +		/* Mark next faulty, too, in case we have one. */
> +		if (!last) {
> +			ISP_BUF_NEXT_DONE(bufs)->vb_state =
> +				VIDEOBUF_ERROR;
> +			printk(KERN_ALERT "OUCH!!!\n");
> +		} else {
> +			printk(KERN_ALERT "Ouch!\n");
> +		}
> +	}
> +
> +	/* Mark the current buffer as done. */
> +	ISP_BUF_MARK_DONE(bufs);
> +
> +	DPRINTK_ISPCTRL(KERN_ALERT "%s: finish %d mmu %p\n", __func__,
> +	       (bufs->done - 1 + NUM_BUFS) % NUM_BUFS,
> +	       (bufs->buf+((bufs->done - 1 + NUM_BUFS) % NUM_BUFS))->isp_addr);
> +
> +out:
> +	spin_unlock_irqrestore(&bufs->lock, flags);
> +
> +	if (buf != NULL) {
> +		/*
> +		 * We want to dequeue a buffer from the video buffer
> +		 * queue. Let's do it!
> +		 */
> +		isp_vbq_sync(buf->vb, DMA_FROM_DEVICE);
> +		buf->vb->state = buf->vb_state;
> +		buf->complete(buf->vb, buf->priv);
> +	}
> +
> +	return 0;
> +}
> +
> +int isp_buf_queue(struct videobuf_buffer *vb,
> +		  void (*complete)(struct videobuf_buffer *vb, void *priv),
> +		  void *priv)
> +{
> +	unsigned long flags;
> +	struct isp_buf *buf;
> +	struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
> +	const struct scatterlist *sglist = dma->sglist;
> +	struct isp_bufs *bufs = &isp_obj.bufs;
> +	int sglen = dma->sglen;
> +
> +	BUG_ON(sglen < 0 || !sglist);
> +
> +	isp_vbq_sync(vb, DMA_TO_DEVICE);
> +
> +	spin_lock_irqsave(&bufs->lock, flags);
> +
> +	BUG_ON(ISP_BUFS_IS_FULL(bufs));
> +
> +	buf = ISP_BUF_QUEUE(bufs);
> +
> +	buf->isp_addr = bufs->isp_addr_capture[vb->i];
> +	buf->complete = complete;
> +	buf->vb = vb;
> +	buf->priv = priv;
> +	buf->vb_state = VIDEOBUF_DONE;
> +
> +	if (ISP_BUFS_IS_EMPTY(bufs)) {
> +		isp_enable_interrupts(RAW_CAPTURE(&isp_obj));
> +		isp_set_buf(buf);
> +		ispccdc_enable(1);
> +		isp_start();
> +	}
> +
> +	ISP_BUF_MARK_QUEUED(bufs);
> +
> +	spin_unlock_irqrestore(&bufs->lock, flags);
> +
> +	DPRINTK_ISPCTRL(KERN_ALERT "%s: queue %d vb %d, mmu %p\n", __func__,
> +	       (bufs->queue - 1 + NUM_BUFS) % NUM_BUFS, vb->i,
> +	       buf->isp_addr);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(isp_buf_queue);
> +
> +int isp_vbq_setup(struct videobuf_queue *vbq, unsigned int *cnt,
> +		  unsigned int *size)
> +{
> +	int rval = 0;
> +	size_t tmp_size = PAGE_ALIGN(isp_obj.module.preview_output_width
> +				     * isp_obj.module.preview_output_height
> +				     * ISP_BYTES_PER_PIXEL);
> +
> +	if (isp_obj.module.isp_pipeline & OMAP_ISP_PREVIEW
> +	    && isp_obj.tmp_buf_size < tmp_size)
> +		rval = isp_tmp_buf_alloc(tmp_size);
> +
> +	return rval;
> +}
> +EXPORT_SYMBOL(isp_vbq_setup);
> +
> +/**
> + * isp_vbq_prepare - Videobuffer queue prepare.
> + * @vbq: Pointer to videobuf_queue structure.
> + * @vb: Pointer to videobuf_buffer structure.
> + * @field: Requested Field order for the videobuffer.
> + *
> + * Returns 0 if successful, or -EIO if the ispmmu was unable to map a
> + * scatter-gather linked list data space.
> + **/
> +int isp_vbq_prepare(struct videobuf_queue *vbq, struct videobuf_buffer *vb,
> +							enum v4l2_field field)
> +{
> +	unsigned int isp_addr;
> +	struct videobuf_dmabuf *vdma;
> +	struct isp_bufs *bufs = &isp_obj.bufs;
> +
> +	int err = 0;
> +
> +	vdma = videobuf_to_dma(vb);
> +
> +	isp_addr = ispmmu_vmap(vdma->sglist, vdma->sglen);
> +
> +	if (IS_ERR_VALUE(isp_addr))
> +		err = -EIO;
> +	else
> +		bufs->isp_addr_capture[vb->i] = isp_addr;
> +
> +	return err;
> +}
> +EXPORT_SYMBOL(isp_vbq_prepare);
> +
> +/**
> + * isp_vbq_release - Videobuffer queue release.
> + * @vbq: Pointer to videobuf_queue structure.
> + * @vb: Pointer to videobuf_buffer structure.
> + **/
> +void isp_vbq_release(struct videobuf_queue *vbq, struct videobuf_buffer *vb)
> +{
> +	struct isp_bufs *bufs = &isp_obj.bufs;
> +
> +	ispmmu_vunmap(bufs->isp_addr_capture[vb->i]);
> +	bufs->isp_addr_capture[vb->i] = (dma_addr_t)NULL;
> +	return;
> +}
> +EXPORT_SYMBOL(isp_vbq_release);
> +
> +/**
> + * isp_queryctrl - Query V4L2 control from existing controls in ISP.
> + * @a: Pointer to v4l2_queryctrl structure. It only needs the id field filled.
> + *
> + * Returns 0 if successful, or -EINVAL if not found in ISP.
> + **/
> +int isp_queryctrl(struct v4l2_queryctrl *a)
> +{
> +	int i;
> +
> +	if (a->id & V4L2_CTRL_FLAG_NEXT_CTRL) {
> +		a->id &= ~V4L2_CTRL_FLAG_NEXT_CTRL;
> +		i = find_next_vctrl(a->id);
> +	} else {
> +		i = find_vctrl(a->id);
> +	}
> +
> +	if (i < 0)
> +		return -EINVAL;
> +
> +	*a = video_control[i].qc;
> +	return 0;
> +}
> +EXPORT_SYMBOL(isp_queryctrl);
> +
> +/**
> + * isp_queryctrl - Query V4L2 control from existing controls in ISP.
> + * @a: Pointer to v4l2_queryctrl structure. It only needs the id field filled.
> + *
> + * Returns 0 if successful, or -EINVAL if not found in ISP.
> + **/
> +int isp_querymenu(struct v4l2_querymenu *a)
> +{
> +	int i;
> +
> +	i = find_vmenu(a->id, a->index);
> +
> +	if (i < 0)
> +		return -EINVAL;
> +
> +	*a = video_menu[i];
> +	return 0;
> +}
> +EXPORT_SYMBOL(isp_querymenu);
> +
> +/**
> + * isp_g_ctrl - Gets value of the desired V4L2 control.
> + * @a: V4L2 control to read actual value from.
> + *
> + * Return 0 if successful, or -EINVAL if chosen control is not found.
> + **/
> +int isp_g_ctrl(struct v4l2_control *a)
> +{
> +	u8 current_value;
> +	int rval = 0;
> +
> +	if (!isp_obj.ref_count)
> +		return -EINVAL;
> +
> +	switch (a->id) {
> +	case V4L2_CID_BRIGHTNESS:
> +		isppreview_query_brightness(&current_value);
> +		a->value = current_value / ISPPRV_BRIGHT_UNITS;
> +		break;
> +	case V4L2_CID_CONTRAST:
> +		isppreview_query_contrast(&current_value);
> +		a->value = current_value / ISPPRV_CONTRAST_UNITS;
> +		break;
> +	case V4L2_CID_COLORFX:
> +		isppreview_get_color(&current_value);
> +		a->value = current_value;
> +		break;
> +	default:
> +		rval = -EINVAL;
> +		break;
> +	}
> +
> +	return rval;
> +}
> +EXPORT_SYMBOL(isp_g_ctrl);
> +
> +/**
> + * isp_s_ctrl - Sets value of the desired V4L2 control.
> + * @a: V4L2 control to read actual value from.
> + *
> + * Return 0 if successful, -EINVAL if chosen control is not found or value
> + * is out of bounds, -EFAULT if copy_from_user or copy_to_user operation fails
> + * from camera abstraction layer related controls or the transfered user space
> + * pointer via the value field is not set properly.
> + **/
> +int isp_s_ctrl(struct v4l2_control *a)
> +{
> +	int rval = 0;
> +	u8 new_value = a->value;
> +
> +	if (!isp_obj.ref_count)
> +		return -EINVAL;
> +
> +	switch (a->id) {
> +	case V4L2_CID_BRIGHTNESS:
> +		if (new_value > ISPPRV_BRIGHT_HIGH)
> +			rval = -EINVAL;
> +		else
> +			isppreview_update_brightness(&new_value);
> +		break;
> +	case V4L2_CID_CONTRAST:
> +		if (new_value > ISPPRV_CONTRAST_HIGH)
> +			rval = -EINVAL;
> +		else
> +			isppreview_update_contrast(&new_value);
> +		break;
> +	case V4L2_CID_COLORFX:
> +		if (new_value > V4L2_COLORFX_SEPIA)
> +			rval = -EINVAL;
> +		else
> +			isppreview_set_color(&new_value);
> +		break;
> +	default:
> +		rval = -EINVAL;
> +		break;
> +	}
> +
> +	return rval;
> +}
> +EXPORT_SYMBOL(isp_s_ctrl);
> +
> +/**
> + * isp_handle_private - Handle all private ioctls for isp module.
> + * @cmd: ioctl cmd value
> + * @arg: ioctl arg value
> + *
> + * Return 0 if successful, -EINVAL if chosen cmd value is not handled or value
> + * is out of bounds, -EFAULT if ioctl arg value is not valid.
> + * Function simply routes the input ioctl cmd id to the appropriate handler in
> + * the isp module.
> + **/
> +int isp_handle_private(int cmd, void *arg)
> +{
> +	int rval = 0;
> +
> +	if (!isp_obj.ref_count)
> +		return -EINVAL;
> +
> +	switch (cmd) {
> +	case VIDIOC_PRIVATE_ISP_CCDC_CFG:
> +		rval = omap34xx_isp_ccdc_config(arg);
> +		break;
> +	case VIDIOC_PRIVATE_ISP_PRV_CFG:
> +		rval = omap34xx_isp_preview_config(arg);
> +		break;
> +	case VIDIOC_PRIVATE_ISP_AEWB_CFG: {
> +		struct isph3a_aewb_config *params;
> +		params = (struct isph3a_aewb_config *)arg;
> +		rval = isph3a_aewb_configure(params);
> +		}
> +		break;
> +	case VIDIOC_PRIVATE_ISP_AEWB_REQ: {
> +		struct isph3a_aewb_data *data;
> +		data = (struct isph3a_aewb_data *)arg;
> +		rval = isph3a_aewb_request_statistics(data);
> +		}
> +		break;
> +	case VIDIOC_PRIVATE_ISP_HIST_CFG: {
> +		struct isp_hist_config *params;
> +		params = (struct isp_hist_config *)arg;
> +		rval = isp_hist_configure(params);
> +		}
> +		break;
> +	case VIDIOC_PRIVATE_ISP_HIST_REQ: {
> +		struct isp_hist_data *data;
> +		data = (struct isp_hist_data *)arg;
> +		rval = isp_hist_request_statistics(data);
> +		}
> +		break;
> +	case VIDIOC_PRIVATE_ISP_AF_CFG: {
> +		struct af_configuration *params;
> +		params = (struct af_configuration *)arg;
> +		rval = isp_af_configure(params);
> +		}
> +		break;
> +	case VIDIOC_PRIVATE_ISP_AF_REQ: {
> +		struct isp_af_data *data;
> +		data = (struct isp_af_data *)arg;
> +		rval = isp_af_request_statistics(data);
> +		}
> +		break;
> +	default:
> +		rval = -EINVAL;
> +		break;
> +	}
> +	return rval;
> +}
> +EXPORT_SYMBOL(isp_handle_private);
> +
> +/**
> + * isp_enum_fmt_cap - Gets more information of chosen format index and type
> + * @f: Pointer to structure containing index and type of format to read from.
> + *
> + * Returns 0 if successful, or -EINVAL if format index or format type is
> + * invalid.
> + **/
> +int isp_enum_fmt_cap(struct v4l2_fmtdesc *f)
> +{
> +	int index = f->index;
> +	enum v4l2_buf_type type = f->type;
> +	int rval = -EINVAL;
> +
> +	if (index >= NUM_ISP_CAPTURE_FORMATS)
> +		goto err;
> +
> +	memset(f, 0, sizeof(*f));
> +	f->index = index;
> +	f->type = type;
> +
> +	switch (f->type) {
> +	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
> +		rval = 0;
> +		break;
> +	default:
> +		goto err;
> +	}
> +
> +	f->flags = isp_formats[index].flags;
> +	strncpy(f->description, isp_formats[index].description,
> +						sizeof(f->description));
> +	f->pixelformat = isp_formats[index].pixelformat;
> +err:
> +	return rval;
> +}
> +EXPORT_SYMBOL(isp_enum_fmt_cap);
> +
> +/**
> + * isp_g_fmt_cap - Gets current output image format.
> + * @f: Pointer to V4L2 format structure to be filled with current output format
> + **/
> +void isp_g_fmt_cap(struct v4l2_pix_format *pix)
> +{
> +	*pix = isp_obj.module.pix;
> +	return;
> +}
> +EXPORT_SYMBOL(isp_g_fmt_cap);
> +
> +/**
> + * isp_s_fmt_cap - Sets I/O formats and crop and configures pipeline in ISP
> + * @f: Pointer to V4L2 format structure to be filled with current output format
> + *
> + * Returns 0 if successful, or return value of either isp_try_size or
> + * isp_try_fmt if there is an error.
> + **/
> +int isp_s_fmt_cap(struct v4l2_pix_format *pix_input,
> +					struct v4l2_pix_format *pix_output)
> +{
> +	int crop_scaling_w = 0, crop_scaling_h = 0;
> +	int rval = 0;
> +
> +	if (!isp_obj.ref_count)
> +		return -EINVAL;
> +
> +	rval = isp_calc_pipeline(pix_input, pix_output);
> +	if (rval)
> +		goto out;
> +
> +	rval = isp_try_size(pix_input, pix_output);
> +	if (rval)
> +		goto out;
> +
> +	rval = isp_try_fmt(pix_input, pix_output);
> +	if (rval)
> +		goto out;
> +
> +	if (ispcroprect.width != pix_output->width) {
> +		crop_scaling_w = 1;
> +		ispcroprect.left = 0;
> +		ispcroprect.width = pix_output->width;
> +	}
> +
> +	if (ispcroprect.height != pix_output->height) {
> +		crop_scaling_h = 1;
> +		ispcroprect.top = 0;
> +		ispcroprect.height = pix_output->height;
> +	}
> +
> +	isp_config_pipeline(pix_input, pix_output);
> +
> +	if ((isp_obj.module.isp_pipeline & OMAP_ISP_RESIZER) &&
> +	    (crop_scaling_h || crop_scaling_w))
> +		isp_config_crop(pix_output);
> +
> +out:
> +	return rval;
> +}
> +EXPORT_SYMBOL(isp_s_fmt_cap);
> +
> +/**
> + * isp_config_crop - Configures crop parameters in isp resizer.
> + * @croppix: Pointer to V4L2 pixel format structure containing crop parameters
> + **/
> +void isp_config_crop(struct v4l2_pix_format *croppix)
> +{
> +	u8 crop_scaling_w;
> +	u8 crop_scaling_h;
> +	unsigned long org_left, num_pix, new_top;
> +
> +	struct v4l2_pix_format *pix = croppix;
> +
> +	crop_scaling_w = (isp_obj.module.preview_output_width * 10) /
> +								pix->width;
> +	crop_scaling_h = (isp_obj.module.preview_output_height * 10) /
> +								pix->height;
> +
> +	cur_rect.left = (ispcroprect.left * crop_scaling_w) / 10;
> +	cur_rect.top = (ispcroprect.top * crop_scaling_h) / 10;
> +	cur_rect.width = (ispcroprect.width * crop_scaling_w) / 10;
> +	cur_rect.height = (ispcroprect.height * crop_scaling_h) / 10;
> +
> +	org_left = cur_rect.left;
> +	while (((int)cur_rect.left & 0xFFFFFFF0) != (int)cur_rect.left)
> +		(int)cur_rect.left--;
> +
> +	num_pix = org_left - cur_rect.left;
> +	new_top = (int)(num_pix * 3) / 4;
> +	cur_rect.top = cur_rect.top - new_top;
> +	cur_rect.height = (2 * new_top) + cur_rect.height;
> +
> +	cur_rect.width = cur_rect.width + (2 * num_pix);
> +	while (((int)cur_rect.width & 0xFFFFFFF0) != (int)cur_rect.width)
> +		(int)cur_rect.width--;
> +
> +	isp_obj.tmp_buf_offset = ((cur_rect.left * 2) +
> +		((isp_obj.module.preview_output_width) * 2 * cur_rect.top));
> +
> +	ispresizer_trycrop(cur_rect.left, cur_rect.top, cur_rect.width,
> +					cur_rect.height,
> +					isp_obj.module.resizer_output_width,
> +					isp_obj.module.resizer_output_height);
> +
> +	return;
> +}
> +EXPORT_SYMBOL(isp_config_crop);
> +
> +/**
> + * isp_g_crop - Gets crop rectangle size and position.
> + * @a: Pointer to V4L2 crop structure to be filled.
> + *
> + * Always returns 0.
> + **/
> +int isp_g_crop(struct v4l2_crop *a)
> +{
> +	struct v4l2_crop *crop = a;
> +
> +	crop->c = ispcroprect;
> +	return 0;
> +}
> +EXPORT_SYMBOL(isp_g_crop);
> +
> +/**
> + * isp_s_crop - Sets crop rectangle size and position and queues crop operation
> + * @a: Pointer to V4L2 crop structure with desired parameters.
> + * @pix: Pointer to V4L2 pixel format structure with desired parameters.
> + *
> + * Returns 0 if successful, or -EINVAL if crop parameters are out of bounds.
> + **/
> +int isp_s_crop(struct v4l2_crop *a, struct v4l2_pix_format *pix)
> +{
> +	struct v4l2_crop *crop = a;
> +	int rval = 0;
> +
> +	if (!isp_obj.ref_count)
> +		return -EINVAL;
> +
> +	if (crop->c.left < 0)
> +		crop->c.left = 0;
> +	if (crop->c.width < 0)
> +		crop->c.width = 0;
> +	if (crop->c.top < 0)
> +		crop->c.top = 0;
> +	if (crop->c.height < 0)
> +		crop->c.height = 0;
> +
> +	if (crop->c.left >= pix->width)
> +		crop->c.left = pix->width - 1;
> +	if (crop->c.top >= pix->height)
> +		crop->c.top = pix->height - 1;
> +
> +	if (crop->c.left + crop->c.width > pix->width)
> +		crop->c.width = pix->width - crop->c.left;
> +	if (crop->c.top + crop->c.height > pix->height)
> +		crop->c.height = pix->height - crop->c.top;
> +
> +	ispcroprect.left = crop->c.left;
> +	ispcroprect.top = crop->c.top;
> +	ispcroprect.width = crop->c.width;
> +	ispcroprect.height = crop->c.height;
> +
> +	isp_config_crop(pix);
> +
> +	isp_obj.module.applyCrop = 1;
> +
> +	return rval;
> +}
> +EXPORT_SYMBOL(isp_s_crop);
> +
> +/**
> + * isp_try_fmt_cap - Tries desired input/output image formats
> + * @pix_input: Pointer to V4L2 pixel format structure for input image.
> + * @pix_output: Pointer to V4L2 pixel format structure for output image.
> + *
> + * Returns 0 if successful, or return value of either isp_try_size or
> + * isp_try_fmt if there is an error.
> + **/
> +int isp_try_fmt_cap(struct v4l2_pix_format *pix_input,
> +					struct v4l2_pix_format *pix_output)
> +{
> +	int rval = 0;
> +
> +	rval = isp_calc_pipeline(pix_input, pix_output);
> +	if (rval)
> +		goto out;
> +
> +	rval = isp_try_size(pix_input, pix_output);
> +	if (rval)
> +		goto out;
> +
> +	rval = isp_try_fmt(pix_input, pix_output);
> +	if (rval)
> +		goto out;
> +
> +out:
> +	return rval;
> +}
> +EXPORT_SYMBOL(isp_try_fmt_cap);
> +
> +/**
> + * isp_try_size - Tries size configuration for I/O images of each ISP submodule
> + * @pix_input: Pointer to V4L2 pixel format structure for input image.
> + * @pix_output: Pointer to V4L2 pixel format structure for output image.
> + *
> + * Returns 0 if successful, or return value of ispccdc_try_size,
> + * isppreview_try_size, or ispresizer_try_size (depending on the pipeline
> + * configuration) if there is an error.
> + **/
> +static int isp_try_size(struct v4l2_pix_format *pix_input,
> +			struct v4l2_pix_format *pix_output)
> +{
> +	int rval = 0;
> +
> +	if ((pix_output->width <= ISPRSZ_MIN_OUTPUT) ||
> +				(pix_output->height <= ISPRSZ_MIN_OUTPUT))
> +		return -EINVAL;
> +
> +	if ((pix_output->width >= ISPRSZ_MAX_OUTPUT) ||
> +				(pix_output->height > ISPRSZ_MAX_OUTPUT))
> +		return -EINVAL;
> +
> +	isp_obj.module.ccdc_input_width = pix_input->width;
> +	isp_obj.module.ccdc_input_height = pix_input->height;
> +	isp_obj.module.resizer_output_width = pix_output->width;
> +	isp_obj.module.resizer_output_height = pix_output->height;
> +
> +	if (isp_obj.module.isp_pipeline & OMAP_ISP_CCDC) {
> +		rval = ispccdc_try_size(isp_obj.module.ccdc_input_width,
> +					isp_obj.module.ccdc_input_height,
> +					&isp_obj.module.ccdc_output_width,
> +					&isp_obj.module.ccdc_output_height);
> +		if (rval) {
> +			printk(KERN_ERR "ISP_ERR: The dimensions %dx%d are not"
> +					" supported\n", pix_input->width,
> +					pix_input->height);
> +			return rval;
> +		}
> +		pix_output->width = isp_obj.module.ccdc_output_width;
> +		pix_output->height = isp_obj.module.ccdc_output_height;
> +	}
> +
> +	if (isp_obj.module.isp_pipeline & OMAP_ISP_PREVIEW) {
> +		isp_obj.module.preview_input_width =
> +					isp_obj.module.ccdc_output_width;
> +		isp_obj.module.preview_input_height =
> +					isp_obj.module.ccdc_output_height;
> +		rval = isppreview_try_size(isp_obj.module.preview_input_width,
> +					isp_obj.module.preview_input_height,
> +					&isp_obj.module.preview_output_width,
> +					&isp_obj.module.preview_output_height);
> +		if (rval) {
> +			printk(KERN_ERR "ISP_ERR: The dimensions %dx%d are not"
> +					" supported\n", pix_input->width,
> +					pix_input->height);
> +			return rval;
> +		}
> +		pix_output->width = isp_obj.module.preview_output_width;
> +		pix_output->height = isp_obj.module.preview_output_height;
> +	}
> +
> +	if (isp_obj.module.isp_pipeline & OMAP_ISP_RESIZER) {
> +		isp_obj.module.resizer_input_width =
> +					isp_obj.module.preview_output_width;
> +		isp_obj.module.resizer_input_height =
> +					isp_obj.module.preview_output_height;
> +		rval = ispresizer_try_size(&isp_obj.module.resizer_input_width,
> +					&isp_obj.module.resizer_input_height,
> +					&isp_obj.module.resizer_output_width,
> +					&isp_obj.module.resizer_output_height);
> +		if (rval) {
> +			printk(KERN_ERR "ISP_ERR: The dimensions %dx%d are not"
> +					" supported\n", pix_input->width,
> +					pix_input->height);
> +			return rval;
> +		}
> +		pix_output->width = isp_obj.module.resizer_output_width;
> +		pix_output->height = isp_obj.module.resizer_output_height;
> +	}
> +
> +	return rval;
> +}
> +
> +/**
> + * isp_try_fmt - Validates input/output format parameters.
> + * @pix_input: Pointer to V4L2 pixel format structure for input image.
> + * @pix_output: Pointer to V4L2 pixel format structure for output image.
> + *
> + * Always returns 0.
> + **/
> +int isp_try_fmt(struct v4l2_pix_format *pix_input,
> +					struct v4l2_pix_format *pix_output)
> +{
> +	int ifmt;
> +
> +	for (ifmt = 0; ifmt < NUM_ISP_CAPTURE_FORMATS; ifmt++) {
> +		if (pix_output->pixelformat == isp_formats[ifmt].pixelformat)
> +			break;
> +	}
> +	if (ifmt == NUM_ISP_CAPTURE_FORMATS)
> +		ifmt = 1;
> +	pix_output->pixelformat = isp_formats[ifmt].pixelformat;
> +	pix_output->field = V4L2_FIELD_NONE;
> +	pix_output->bytesperline = pix_output->width * ISP_BYTES_PER_PIXEL;
> +	pix_output->sizeimage =
> +		PAGE_ALIGN(pix_output->bytesperline * pix_output->height);
> +	pix_output->priv = 0;
> +	switch (pix_output->pixelformat) {
> +	case V4L2_PIX_FMT_YUYV:
> +	case V4L2_PIX_FMT_UYVY:
> +		pix_output->colorspace = V4L2_COLORSPACE_JPEG;
> +		break;
> +	default:
> +		pix_output->colorspace = V4L2_COLORSPACE_SRGB;
> +	}
> +
> +	isp_obj.module.pix.pixelformat = pix_output->pixelformat;
> +	isp_obj.module.pix.width = pix_output->width;
> +	isp_obj.module.pix.height = pix_output->height;
> +	isp_obj.module.pix.field = pix_output->field;
> +	isp_obj.module.pix.bytesperline = pix_output->bytesperline;
> +	isp_obj.module.pix.sizeimage = pix_output->sizeimage;
> +	isp_obj.module.pix.priv = pix_output->priv;
> +	isp_obj.module.pix.colorspace = pix_output->colorspace;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(isp_try_fmt);
> +
> +/**
> + * isp_save_ctx - Saves ISP, CCDC, HIST, H3A, PREV, RESZ & MMU context.
> + *
> + * Routine for saving the context of each module in the ISP.
> + * CCDC, HIST, H3A, PREV, RESZ and MMU.
> + **/
> +static void isp_save_ctx(void)
> +{
> +	isp_save_context(isp_reg_list);
> +	ispccdc_save_context();
> +	ispmmu_save_context();
> +	isphist_save_context();
> +	isph3a_save_context();
> +	isppreview_save_context();
> +	ispresizer_save_context();
> +}
> +
> +/**
> + * isp_restore_ctx - Restores ISP, CCDC, HIST, H3A, PREV, RESZ & MMU context.
> + *
> + * Routine for restoring the context of each module in the ISP.
> + * CCDC, HIST, H3A, PREV, RESZ and MMU.
> + **/
> +static void isp_restore_ctx(void)
> +{
> +	isp_restore_context(isp_reg_list);
> +	ispccdc_restore_context();
> +	ispmmu_restore_context();
> +	isphist_restore_context();
> +	isph3a_restore_context();
> +	isppreview_restore_context();
> +	ispresizer_restore_context();
> +}
> +
> +/**
> + * isp_get - Adquires the ISP resource.
> + *
> + * Initializes the clocks for the first acquire.
> + **/
> +int isp_get(void)
> +{
> +	static int has_context = 0;
> +	int ret_err = 0;
> +
> +	if (omap3isp == NULL)
> +		return -EBUSY;
> +
> +	DPRINTK_ISPCTRL("isp_get: old %d\n", isp_obj.ref_count);
> +	mutex_lock(&(isp_obj.isp_mutex));
> +	if (isp_obj.ref_count == 0) {
> +		ret_err = clk_enable(isp_obj.cam_ick);
> +		if (ret_err) {
> +			DPRINTK_ISPCTRL("ISP_ERR: clk_en for ick failed\n");
> +			goto out_clk_enable_ick;
> +		}
> +		ret_err = clk_enable(isp_obj.cam_mclk);
> +		if (ret_err) {
> +			DPRINTK_ISPCTRL("ISP_ERR: clk_en for mclk failed\n");
> +			goto out_clk_enable_mclk;
> +		}
> +		ret_err = clk_enable(isp_obj.csi2_fck);
> +		if (ret_err) {
> +			DPRINTK_ISPCTRL("ISP_ERR: clk_en for csi2_fclk"
> +								" failed\n");
> +			goto out_clk_enable_csi2_fclk;
> +		}
> +
> +		/* We don't want to restore context before saving it! */
> +		if (has_context)
> +			isp_restore_ctx();
> +		else
> +			has_context = 1;
> +	} else {
> +		mutex_unlock(&isp_obj.isp_mutex);
> +		return -EBUSY;
> +	}
> +	isp_obj.ref_count++;
> +
> +	mutex_unlock(&(isp_obj.isp_mutex));
> +
> +	DPRINTK_ISPCTRL("isp_get: new %d\n", isp_obj.ref_count);
> +	return isp_obj.ref_count;
> +
> +out_clk_enable_csi2_fclk:
> +	clk_disable(isp_obj.cam_mclk);
> +out_clk_enable_mclk:
> +	clk_disable(isp_obj.cam_ick);
> +out_clk_enable_ick:
> +
> +	mutex_unlock(&(isp_obj.isp_mutex));
> +
> +	return ret_err;
> +}
> +EXPORT_SYMBOL(isp_get);
> +
> +/**
> + * isp_put - Releases the ISP resource.
> + *
> + * Releases the clocks also for the last release.
> + **/
> +int isp_put(void)
> +{
> +	if (omap3isp == NULL)
> +		return -EBUSY;
> +
> +	DPRINTK_ISPCTRL("isp_put: old %d\n", isp_obj.ref_count);
> +	mutex_lock(&(isp_obj.isp_mutex));
> +	if (isp_obj.ref_count) {
> +		if (--isp_obj.ref_count == 0) {
> +			isp_save_ctx();
> +			isp_tmp_buf_free();
> +			isp_release_resources();
> +			isp_obj.module.isp_pipeline = 0;
> +			clk_disable(isp_obj.cam_ick);
> +			clk_disable(isp_obj.cam_mclk);
> +			clk_disable(isp_obj.csi2_fck);
> +			memset(&ispcroprect, 0, sizeof(ispcroprect));
> +			memset(&cur_rect, 0, sizeof(cur_rect));
> +		}
> +	}
> +	mutex_unlock(&(isp_obj.isp_mutex));
> +	DPRINTK_ISPCTRL("isp_put: new %d\n", isp_obj.ref_count);
> +	return isp_obj.ref_count;
> +}
> +EXPORT_SYMBOL(isp_put);
> +
> +/**
> + * isp_save_context - Saves the values of the ISP module registers.
> + * @reg_list: Structure containing pairs of register address and value to
> + *            modify on OMAP.
> + **/
> +void isp_save_context(struct isp_reg *reg_list)
> +{
> +	struct isp_reg *next = reg_list;
> +
> +	for (; next->reg != ISP_TOK_TERM; next++)
> +		next->val = isp_reg_readl(next->mmio_range, next->reg);
> +}
> +EXPORT_SYMBOL(isp_save_context);
> +
> +/**
> + * isp_restore_context - Restores the values of the ISP module registers.
> + * @reg_list: Structure containing pairs of register address and value to
> + *            modify on OMAP.
> + **/
> +void isp_restore_context(struct isp_reg *reg_list)
> +{
> +	struct isp_reg *next = reg_list;
> +
> +	for (; next->reg != ISP_TOK_TERM; next++)
> +		isp_reg_writel(next->val, next->mmio_range, next->reg);
> +}
> +EXPORT_SYMBOL(isp_restore_context);
> +
> +static int isp_remove(struct platform_device *pdev)
> +{
> +	struct isp_device *isp = platform_get_drvdata(pdev);
> +	int i;
> +
> +	isp_csi2_cleanup();
> +	isp_af_exit();
> +	isp_resizer_cleanup();
> +	isp_preview_cleanup();
> +	ispmmu_cleanup();
> +	isph3a_aewb_cleanup();
> +	isp_hist_cleanup();
> +	isp_ccdc_cleanup();
> +
> +	if (!isp)
> +		return 0;
> +
> +	clk_put(isp_obj.cam_ick);
> +	clk_put(isp_obj.cam_mclk);
> +	clk_put(isp_obj.csi2_fck);
> +
> +	free_irq(isp->irq, &isp_obj);
> +
> +	for (i = 0; i <= OMAP3_ISP_IOMEM_CSI2PHY; i++) {
> +		if (isp->mmio_base[i]) {
> +			iounmap((void *)isp->mmio_base[i]);
> +			isp->mmio_base[i] = 0;
> +		}
> +
> +		if (isp->mmio_base_phys[i]) {
> +			release_mem_region(isp->mmio_base_phys[i],
> +						isp->mmio_size[i]);
> +			isp->mmio_base_phys[i] = 0;
> +		}
> +	}
> +
> +	omap3isp = NULL;
> +
> +	kfree(isp);
> +
> +	return 0;
> +}
> +
> +static int isp_probe(struct platform_device *pdev)
> +{
> +	struct isp_device *isp;
> +	int ret_err = 0;
> +	int i;
> +
> +	isp = kzalloc(sizeof(*isp), GFP_KERNEL);
> +	if (!isp) {
> +		dev_err(&pdev->dev, "could not allocate memory\n");
> +		return -ENODEV;

return -ENOMEM; ?

> +	}
> +
> +	platform_set_drvdata(pdev, isp);
> +
> +	isp->dev = &pdev->dev;
> +
> +	for (i = 0; i <= OMAP3_ISP_IOMEM_CSI2PHY; i++) {
> +		struct resource *mem;
> +		/* request the mem region for the camera registers */
> +		mem = platform_get_resource(pdev, IORESOURCE_MEM, i);
> +		if (!mem) {
> +			dev_err(isp->dev, "no mem resource?\n");
> +			return -ENODEV;

Maybe ENODEV is not apropriate here too..

> +		}
> +
> +		if (!request_mem_region(mem->start, (mem->end - mem->start) + 1,
> +					pdev->name)) {
> +			dev_err(isp->dev,
> +				"cannot reserve camera register I/O region\n");
> +			return -ENODEV;
> +


<snip>

-- 
Best regards, Klimov Alexey

--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Arm (vger)]     [ARM Kernel]     [ARM MSM]     [Linux Tegra]     [Linux WPAN Networking]     [Linux Wireless Networking]     [Maemo Users]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite Trails]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux