Re: [PATCH v2 06/11] v4l: Add support for omap4iss driver

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

 



Hi sergio,

On Wed, Nov 30, 2011 at 06:14:55PM -0600, Sergio Aguirre wrote:
> This adds a very simplistic driver to utilize the
> CSI2A interface inside the ISS subsystem in OMAP4,
> and dump the data to memory.
> 
> Tested so far on omap4430sdp w/ ES2.1 GP & w/ ES2.2 EMU.
> 
> Check newly added Documentation/video4linux/omap4_camera.txt
> for details.
> 
> Signed-off-by: Sergio Aguirre <saaguirre@xxxxxx>
> ---
>  Documentation/video4linux/omap4_camera.txt  |   60 ++
>  arch/arm/plat-omap/include/plat/omap4-iss.h |   42 +

Perhaps this could be moved to include/media/omap4iss.h?

>  drivers/media/video/Kconfig                 |   13 +
>  drivers/media/video/Makefile                |    1 +
>  drivers/media/video/omap4iss/Makefile       |    6 +
>  drivers/media/video/omap4iss/iss.c          | 1179 ++++++++++++++++++++++++
>  drivers/media/video/omap4iss/iss.h          |  133 +++
>  drivers/media/video/omap4iss/iss_csi2.c     | 1324 +++++++++++++++++++++++++++
>  drivers/media/video/omap4iss/iss_csi2.h     |  166 ++++
>  drivers/media/video/omap4iss/iss_csiphy.c   |  215 +++++
>  drivers/media/video/omap4iss/iss_csiphy.h   |   69 ++
>  drivers/media/video/omap4iss/iss_regs.h     |  238 +++++
>  drivers/media/video/omap4iss/iss_video.c    | 1192 ++++++++++++++++++++++++
>  drivers/media/video/omap4iss/iss_video.h    |  205 +++++
>  14 files changed, 4843 insertions(+), 0 deletions(-)
>  create mode 100644 Documentation/video4linux/omap4_camera.txt
>  create mode 100644 arch/arm/plat-omap/include/plat/omap4-iss.h
>  create mode 100644 drivers/media/video/omap4iss/Makefile
>  create mode 100644 drivers/media/video/omap4iss/iss.c
>  create mode 100644 drivers/media/video/omap4iss/iss.h
>  create mode 100644 drivers/media/video/omap4iss/iss_csi2.c
>  create mode 100644 drivers/media/video/omap4iss/iss_csi2.h
>  create mode 100644 drivers/media/video/omap4iss/iss_csiphy.c
>  create mode 100644 drivers/media/video/omap4iss/iss_csiphy.h
>  create mode 100644 drivers/media/video/omap4iss/iss_regs.h
>  create mode 100644 drivers/media/video/omap4iss/iss_video.c
>  create mode 100644 drivers/media/video/omap4iss/iss_video.h
> 
> diff --git a/Documentation/video4linux/omap4_camera.txt b/Documentation/video4linux/omap4_camera.txt
> new file mode 100644
> index 0000000..a60c80f
> --- /dev/null
> +++ b/Documentation/video4linux/omap4_camera.txt
> @@ -0,0 +1,60 @@
> +                              OMAP4 ISS Driver
> +                              ================
> +
> +Introduction
> +------------
> +
> +The OMAP44XX family of chips contains the Imaging SubSystem (a.k.a. ISS),
> +Which contains several components that can be categorized in 3 big groups:
> +
> +- Interfaces (2 Interfaces: CSI2-A & CSI2-B/CCP2)
> +- ISP (Image Signal Processor)
> +- SIMCOP (Still Image Coprocessor)
> +
> +For more information, please look in [1] for latest version of:
> +	"OMAP4430 Multimedia Device Silicon Revision 2.x"
> +
> +As of Revision L, the ISS is described in detail in section 8.
> +
> +This driver is supporting _only_ the CSI2-A interface for now.
> +
> +It makes use of the Media Controller framework [2], and inherited most of the
> +code from OMAP3 ISP driver (found under drivers/media/video/omap3isp/*), except
> +that it doesn't need an IOMMU now for ISS buffers memory mapping.
> +
> +Supports usage of MMAP buffers only (for now).
> +
> +IMPORTANT: It is recommended to have this patchset:
> +  Contiguous Memory Allocator (v15) [3].
> +
> +Tested platforms
> +----------------
> +
> +- OMAP4430SDP, w/ ES2.1 GP & SEVM4430-CAM-V1-0 (Contains IMX060 & OV5640, in
> +  which only the last one is supported, outputting YUV422 frames).
> +
> +- PandaBoard, Rev. A2, w/ OMAP4430 ES2.1 GP & OV adapter board, tested with
> +  following sensors:
> +  * OV5640
> +  * OV5650
> +
> +- Tested on mainline kernel:
> +
> +	http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=summary
> +
> +  Tag: v3.0 (commit 02f8c6aee8df3cdc935e9bdd4f2d020306035dbe)
> +
> +File list
> +---------
> +drivers/media/video/omap4iss/
> +
> +References
> +----------
> +
> +[1] http://focus.ti.com/general/docs/wtbu/wtbudocumentcenter.tsp?navigationId=12037&templateId=6123#62
> +[2] http://lwn.net/Articles/420485/
> +[3] http://lwn.net/Articles/455801/
> +--
> +Author: Sergio Aguirre <saaguirre@xxxxxx>
> +Copyright (C) 2011, Texas Instruments
> +
> diff --git a/arch/arm/plat-omap/include/plat/omap4-iss.h b/arch/arm/plat-omap/include/plat/omap4-iss.h
> new file mode 100644
> index 0000000..3a1c6b6
> --- /dev/null
> +++ b/arch/arm/plat-omap/include/plat/omap4-iss.h
> @@ -0,0 +1,42 @@
> +#ifndef ARCH_ARM_PLAT_OMAP4_ISS_H
> +#define ARCH_ARM_PLAT_OMAP4_ISS_H
> +
> +#include <linux/i2c.h>
> +
> +struct iss_device;
> +
> +enum iss_interface_type {
> +	ISS_INTERFACE_CSI2A_PHY1,
> +};
> +
> +/**
> + * struct iss_csi2_platform_data - CSI2 interface platform data
> + * @crc: Enable the cyclic redundancy check
> + * @vpclk_div: Video port output clock control
> + */
> +struct iss_csi2_platform_data {
> +	unsigned crc:1;
> +	unsigned vpclk_div:2;
> +};
> +
> +struct iss_subdev_i2c_board_info {
> +	struct i2c_board_info *board_info;
> +	int i2c_adapter_id;
> +};
> +
> +struct iss_v4l2_subdevs_group {
> +	struct iss_subdev_i2c_board_info *subdevs;
> +	enum iss_interface_type interface;
> +	union {
> +		struct iss_csi2_platform_data csi2;
> +	} bus; /* gcc < 4.6.0 chokes on anonymous union initializers */
> +};
> +
> +struct iss_platform_data {
> +	struct iss_v4l2_subdevs_group *subdevs;
> +	void (*set_constraints)(struct iss_device *iss, bool enable);
> +};

If you could describe constraints in the data rather than having the
callback, the driver could use the data and moving to DT would be easier.

> +extern int omap4_init_camera(struct iss_platform_data *pdata,
> +			     struct omap_board_data *bdata);
> +#endif
> diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
> index b303a3f..ae2a99d 100644
> --- a/drivers/media/video/Kconfig
> +++ b/drivers/media/video/Kconfig
> @@ -796,6 +796,19 @@ config VIDEO_OMAP3_DEBUG
>  	---help---
>  	  Enable debug messages on OMAP 3 camera controller driver.
>  
> +config VIDEO_OMAP4
> +	tristate "OMAP 4 Camera support (EXPERIMENTAL)"
> +	depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && ARCH_OMAP4 && EXPERIMENTAL
> +	select VIDEOBUF2_DMA_CONTIG
> +	---help---
> +	  Driver for an OMAP 4 ISS controller.
> +
> +config VIDEO_OMAP4_DEBUG
> +	bool "OMAP 4 Camera debug messages"
> +	depends on VIDEO_OMAP4
> +	---help---
> +	  Enable debug messages on OMAP 4 ISS controller driver.
> +
>  config SOC_CAMERA
>  	tristate "SoC camera support"
>  	depends on VIDEO_V4L2 && HAS_DMA && I2C
> diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
> index 117f9c4..f02a4c4 100644
> --- a/drivers/media/video/Makefile
> +++ b/drivers/media/video/Makefile
> @@ -140,6 +140,7 @@ obj-$(CONFIG_VIDEO_MMP_CAMERA) += marvell-ccic/
>  obj-$(CONFIG_VIDEO_VIA_CAMERA) += via-camera.o
>  
>  obj-$(CONFIG_VIDEO_OMAP3)	+= omap3isp/
> +obj-$(CONFIG_VIDEO_OMAP4)	+= omap4iss/
>  
>  obj-$(CONFIG_USB_ZR364XX)       += zr364xx.o
>  obj-$(CONFIG_USB_STKWEBCAM)     += stkwebcam.o
> diff --git a/drivers/media/video/omap4iss/Makefile b/drivers/media/video/omap4iss/Makefile
> new file mode 100644
> index 0000000..1d3b0a7
> --- /dev/null
> +++ b/drivers/media/video/omap4iss/Makefile
> @@ -0,0 +1,6 @@
> +# Makefile for OMAP4 ISS driver
> +
> +omap4-iss-objs += \
> +	iss.o iss_csi2.o iss_csiphy.o iss_video.o
> +
> +obj-$(CONFIG_VIDEO_OMAP4) += omap4-iss.o
> diff --git a/drivers/media/video/omap4iss/iss.c b/drivers/media/video/omap4iss/iss.c
> new file mode 100644
> index 0000000..255738b
> --- /dev/null
> +++ b/drivers/media/video/omap4iss/iss.c
> @@ -0,0 +1,1179 @@
> +/*
> + * V4L2 Driver for OMAP4 ISS
> + *
> + * Copyright (C) 2011, Texas Instruments
> + *
> + * Author: Sergio Aguirre <saaguirre@xxxxxx>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/i2c.h>
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +#include <linux/sched.h>
> +#include <linux/vmalloc.h>
> +
> +#include <media/v4l2-common.h>
> +#include <media/v4l2-device.h>
> +
> +#include "iss.h"
> +#include "iss_regs.h"
> +
> +static void iss_save_ctx(struct iss_device *iss);
> +
> +static void iss_restore_ctx(struct iss_device *iss);
> +
> +/* Structure for saving/restoring ISS module registers */
> +static struct iss_reg iss_reg_list[] = {
> +	{OMAP4_ISS_MEM_TOP, ISS_HL_SYSCONFIG, 0},
> +	{OMAP4_ISS_MEM_TOP, ISS_CTRL, 0},
> +	{OMAP4_ISS_MEM_TOP, ISS_CLKCTRL, 0},
> +	{0, ISS_TOK_TERM, 0}
> +};
> +
> +/*
> + * omap4iss_flush - Post pending L3 bus writes by doing a register readback
> + * @iss: OMAP4 ISS device
> + *
> + * In order to force posting of pending writes, we need to write and
> + * readback the same register, in this case the revision register.
> + *
> + * See this link for reference:
> + *   http://www.mail-archive.com/linux-omap@xxxxxxxxxxxxxxx/msg08149.html
> + */
> +void omap4iss_flush(struct iss_device *iss)
> +{
> +	writel(0, iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_REVISION);
> +	readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_REVISION);
> +}
> +
> +/*
> + * iss_enable_interrupts - Enable ISS interrupts.
> + * @iss: OMAP4 ISS device
> + */
> +static void iss_enable_interrupts(struct iss_device *iss)
> +{
> +	static const u32 irq = ISS_HL_IRQ_CSIA;
> +
> +	/* Enable HL interrupts */
> +	writel(irq, iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_IRQSTATUS_5);
> +	writel(irq, iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_IRQENABLE_5_SET);
> +}
> +
> +/*
> + * iss_disable_interrupts - Disable ISS interrupts.
> + * @iss: OMAP4 ISS device
> + */
> +static void iss_disable_interrupts(struct iss_device *iss)
> +{
> +	writel(-1, iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_IRQENABLE_5_CLR);
> +}
> +
> +static inline void iss_isr_dbg(struct iss_device *iss, u32 irqstatus)
> +{
> +	static const char *name[] = {
> +		"ISP_IRQ0",
> +		"ISP_IRQ1",
> +		"ISP_IRQ2",
> +		"ISP_IRQ3",
> +		"CSIA_IRQ",
> +		"CSIB_IRQ",
> +		"CCP2_IRQ0",
> +		"CCP2_IRQ1",
> +		"CCP2_IRQ2",
> +		"CCP2_IRQ3",
> +		"CBUFF_IRQ",
> +		"BTE_IRQ",
> +		"SIMCOP_IRQ0",
> +		"SIMCOP_IRQ1",
> +		"SIMCOP_IRQ2",
> +		"SIMCOP_IRQ3",
> +		"CCP2_IRQ8",
> +		"HS_VS_IRQ",
> +		"res18",
> +		"res19",
> +		"res20",
> +		"res21",
> +		"res22",
> +		"res23",
> +		"res24",
> +		"res25",
> +		"res26",
> +		"res27",
> +		"res28",
> +		"res29",
> +		"res30",
> +		"res31",
> +	};
> +	int i;
> +
> +	dev_dbg(iss->dev, "ISS IRQ: ");
> +
> +	for (i = 0; i < ARRAY_SIZE(name); i++) {
> +		if ((1 << i) & irqstatus)
> +			printk(KERN_CONT "%s ", name[i]);
> +	}
> +	printk(KERN_CONT "\n");

pr_cont might be prettier, but I guess it's mostly the matter of personal
preference.

> +}
> +
> +/*
> + * iss_isr - Interrupt Service Routine for ISS module.
> + * @irq: Not used currently.
> + * @_iss: Pointer to the OMAP4 ISS device
> + *
> + * Handles the corresponding callback if plugged in.
> + *
> + * Returns IRQ_HANDLED when IRQ was correctly handled, or IRQ_NONE when the
> + * IRQ wasn't handled.
> + */
> +static irqreturn_t iss_isr(int irq, void *_iss)
> +{
> +	struct iss_device *iss = _iss;
> +	u32 irqstatus;
> +
> +	irqstatus = readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_IRQSTATUS_5);
> +	writel(irqstatus, iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_IRQSTATUS_5);
> +
> +	if (irqstatus & ISS_HL_IRQ_CSIA)
> +		omap4iss_csi2_isr(&iss->csi2a);
> +
> +	omap4iss_flush(iss);
> +
> +#if defined(DEBUG) && defined(ISS_ISR_DEBUG)
> +	iss_isr_dbg(iss, irqstatus);
> +#endif
> +
> +	return IRQ_HANDLED;
> +}
> +
> +/* -----------------------------------------------------------------------------
> + * Pipeline power management
> + *
> + * Entities must be powered up when part of a pipeline that contains at least
> + * one open video device node.
> + *
> + * To achieve this use the entity use_count field to track the number of users.
> + * For entities corresponding to video device nodes the use_count field stores
> + * the users count of the node. For entities corresponding to subdevs the
> + * use_count field stores the total number of users of all video device nodes
> + * in the pipeline.
> + *
> + * The omap4iss_pipeline_pm_use() function must be called in the open() and
> + * close() handlers of video device nodes. It increments or decrements the use
> + * count of all subdev entities in the pipeline.
> + *
> + * To react to link management on powered pipelines, the link setup notification
> + * callback updates the use count of all entities in the source and sink sides
> + * of the link.
> + */
> +
> +/*
> + * iss_pipeline_pm_use_count - Count the number of users of a pipeline
> + * @entity: The entity
> + *
> + * Return the total number of users of all video device nodes in the pipeline.
> + */
> +static int iss_pipeline_pm_use_count(struct media_entity *entity)
> +{
> +	struct media_entity_graph graph;
> +	int use = 0;
> +
> +	media_entity_graph_walk_start(&graph, entity);
> +
> +	while ((entity = media_entity_graph_walk_next(&graph))) {
> +		if (media_entity_type(entity) == MEDIA_ENT_T_DEVNODE)
> +			use += entity->use_count;
> +	}
> +
> +	return use;
> +}
> +
> +/*
> + * iss_pipeline_pm_power_one - Apply power change to an entity
> + * @entity: The entity
> + * @change: Use count change
> + *
> + * Change the entity use count by @change. If the entity is a subdev update its
> + * power state by calling the core::s_power operation when the use count goes
> + * from 0 to != 0 or from != 0 to 0.
> + *
> + * Return 0 on success or a negative error code on failure.
> + */
> +static int iss_pipeline_pm_power_one(struct media_entity *entity, int change)
> +{
> +	struct v4l2_subdev *subdev;
> +
> +	subdev = media_entity_type(entity) == MEDIA_ENT_T_V4L2_SUBDEV
> +	       ? media_entity_to_v4l2_subdev(entity) : NULL;
> +
> +	if (entity->use_count == 0 && change > 0 && subdev != NULL) {
> +		int ret;
> +
> +		ret = v4l2_subdev_call(subdev, core, s_power, 1);
> +		if (ret < 0 && ret != -ENOIOCTLCMD)
> +			return ret;
> +	}
> +
> +	entity->use_count += change;
> +	WARN_ON(entity->use_count < 0);
> +
> +	if (entity->use_count == 0 && change < 0 && subdev != NULL)
> +		v4l2_subdev_call(subdev, core, s_power, 0);
> +
> +	return 0;
> +}
> +
> +/*
> + * iss_pipeline_pm_power - Apply power change to all entities in a pipeline
> + * @entity: The entity
> + * @change: Use count change
> + *
> + * Walk the pipeline to update the use count and the power state of all non-node
> + * entities.
> + *
> + * Return 0 on success or a negative error code on failure.
> + */
> +static int iss_pipeline_pm_power(struct media_entity *entity, int change)
> +{
> +	struct media_entity_graph graph;
> +	struct media_entity *first = entity;
> +	int ret = 0;
> +
> +	if (!change)
> +		return 0;
> +
> +	media_entity_graph_walk_start(&graph, entity);
> +
> +	while (!ret && (entity = media_entity_graph_walk_next(&graph)))
> +		if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE)
> +			ret = iss_pipeline_pm_power_one(entity, change);
> +
> +	if (!ret)
> +		return 0;
> +
> +	media_entity_graph_walk_start(&graph, first);
> +
> +	while ((first = media_entity_graph_walk_next(&graph))
> +	       && first != entity)
> +		if (media_entity_type(first) != MEDIA_ENT_T_DEVNODE)
> +			iss_pipeline_pm_power_one(first, -change);
> +
> +	return ret;
> +}
> +
> +/*
> + * omap4iss_pipeline_pm_use - Update the use count of an entity
> + * @entity: The entity
> + * @use: Use (1) or stop using (0) the entity
> + *
> + * Update the use count of all entities in the pipeline and power entities on or
> + * off accordingly.
> + *
> + * Return 0 on success or a negative error code on failure. Powering entities
> + * off is assumed to never fail. No failure can occur when the use parameter is
> + * set to 0.
> + */
> +int omap4iss_pipeline_pm_use(struct media_entity *entity, int use)
> +{
> +	int change = use ? 1 : -1;
> +	int ret;
> +
> +	mutex_lock(&entity->parent->graph_mutex);
> +
> +	/* Apply use count to node. */
> +	entity->use_count += change;
> +	WARN_ON(entity->use_count < 0);
> +
> +	/* Apply power change to connected non-nodes. */
> +	ret = iss_pipeline_pm_power(entity, change);
> +	if (ret < 0)
> +		entity->use_count -= change;
> +
> +	mutex_unlock(&entity->parent->graph_mutex);
> +
> +	return ret;
> +}
> +
> +/*
> + * iss_pipeline_link_notify - Link management notification callback
> + * @source: Pad at the start of the link
> + * @sink: Pad at the end of the link
> + * @flags: New link flags that will be applied
> + *
> + * React to link management on powered pipelines by updating the use count of
> + * all entities in the source and sink sides of the link. Entities are powered
> + * on or off accordingly.
> + *
> + * Return 0 on success or a negative error code on failure. Powering entities
> + * off is assumed to never fail. This function will not fail for disconnection
> + * events.
> + */
> +static int iss_pipeline_link_notify(struct media_pad *source,
> +				    struct media_pad *sink, u32 flags)
> +{
> +	int source_use = iss_pipeline_pm_use_count(source->entity);
> +	int sink_use = iss_pipeline_pm_use_count(sink->entity);
> +	int ret;
> +
> +	if (!(flags & MEDIA_LNK_FL_ENABLED)) {
> +		/* Powering off entities is assumed to never fail. */
> +		iss_pipeline_pm_power(source->entity, -sink_use);
> +		iss_pipeline_pm_power(sink->entity, -source_use);
> +		return 0;
> +	}
> +
> +	ret = iss_pipeline_pm_power(source->entity, sink_use);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = iss_pipeline_pm_power(sink->entity, source_use);
> +	if (ret < 0)
> +		iss_pipeline_pm_power(source->entity, -sink_use);
> +
> +	return ret;
> +}
> +
> +/* -----------------------------------------------------------------------------
> + * Pipeline stream management
> + */
> +
> +/*
> + * iss_pipeline_enable - Enable streaming on a pipeline
> + * @pipe: ISS pipeline
> + * @mode: Stream mode (single shot or continuous)
> + *
> + * Walk the entities chain starting at the pipeline output video node and start
> + * all modules in the chain in the given mode.
> + *
> + * Return 0 if successful, or the return value of the failed video::s_stream
> + * operation otherwise.
> + */
> +static int iss_pipeline_enable(struct iss_pipeline *pipe,
> +			       enum iss_pipeline_stream_state mode)
> +{
> +	struct media_entity *entity;
> +	struct media_pad *pad;
> +	struct v4l2_subdev *subdev;
> +	unsigned long flags;
> +	int ret = 0;
> +
> +	spin_lock_irqsave(&pipe->lock, flags);
> +	pipe->state &= ~(ISS_PIPELINE_IDLE_INPUT | ISS_PIPELINE_IDLE_OUTPUT);
> +	spin_unlock_irqrestore(&pipe->lock, flags);
> +
> +	pipe->do_propagation = false;
> +
> +	entity = &pipe->output->video.entity;
> +	while (1) {
> +		pad = &entity->pads[0];
> +		if (!(pad->flags & MEDIA_PAD_FL_SINK))
> +			break;
> +
> +		pad = media_entity_remote_source(pad);
> +		if (pad == NULL ||
> +		    media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
> +			break;
> +
> +		entity = pad->entity;
> +		subdev = media_entity_to_v4l2_subdev(entity);
> +
> +		ret = v4l2_subdev_call(subdev, video, s_stream, mode);
> +		if (ret < 0 && ret != -ENOIOCTLCMD)
> +			break;
> +	}
> +
> +	return ret;
> +}
> +
> +/*
> + * iss_pipeline_disable - Disable streaming on a pipeline
> + * @pipe: ISS pipeline
> + *
> + * Walk the entities chain starting at the pipeline output video node and stop
> + * all modules in the chain. Wait synchronously for the modules to be stopped if
> + * necessary.
> + */
> +static int iss_pipeline_disable(struct iss_pipeline *pipe)
> +{
> +	struct media_entity *entity;
> +	struct media_pad *pad;
> +	struct v4l2_subdev *subdev;
> +	int failure = 0;
> +
> +	entity = &pipe->output->video.entity;
> +	while (1) {
> +		pad = &entity->pads[0];
> +		if (!(pad->flags & MEDIA_PAD_FL_SINK))
> +			break;
> +
> +		pad = media_entity_remote_source(pad);
> +		if (pad == NULL ||
> +		    media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
> +			break;
> +
> +		entity = pad->entity;
> +		subdev = media_entity_to_v4l2_subdev(entity);
> +
> +		v4l2_subdev_call(subdev, video, s_stream, 0);
> +	}
> +
> +	return failure;
> +}
> +
> +/*
> + * omap4iss_pipeline_set_stream - Enable/disable streaming on a pipeline
> + * @pipe: ISS pipeline
> + * @state: Stream state (stopped, single shot or continuous)
> + *
> + * Set the pipeline to the given stream state. Pipelines can be started in
> + * single-shot or continuous mode.
> + *
> + * Return 0 if successful, or the return value of the failed video::s_stream
> + * operation otherwise. The pipeline state is not updated when the operation
> + * fails, except when stopping the pipeline.
> + */
> +int omap4iss_pipeline_set_stream(struct iss_pipeline *pipe,
> +				 enum iss_pipeline_stream_state state)
> +{
> +	int ret;
> +
> +	if (state == ISS_PIPELINE_STREAM_STOPPED)
> +		ret = iss_pipeline_disable(pipe);
> +	else
> +		ret = iss_pipeline_enable(pipe, state);
> +
> +	if (ret == 0 || state == ISS_PIPELINE_STREAM_STOPPED)
> +		pipe->stream_state = state;
> +
> +	return ret;
> +}
> +
> +/*
> + * iss_pipeline_is_last - Verify if entity has an enabled link to the output
> + *			  video node
> + * @me: ISS module's media entity
> + *
> + * Returns 1 if the entity has an enabled link to the output video node or 0
> + * otherwise. It's true only while pipeline can have no more than one output
> + * node.
> + */
> +static int iss_pipeline_is_last(struct media_entity *me)
> +{
> +	struct iss_pipeline *pipe;
> +	struct media_pad *pad;
> +
> +	if (!me->pipe)
> +		return 0;
> +	pipe = to_iss_pipeline(me);
> +	if (pipe->stream_state == ISS_PIPELINE_STREAM_STOPPED)
> +		return 0;
> +	pad = media_entity_remote_source(&pipe->output->pad);
> +	return pad->entity == me;
> +}
> +
> +static int iss_reset(struct iss_device *iss)
> +{
> +	unsigned long timeout = 0;
> +
> +	writel(readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_SYSCONFIG) |
> +		ISS_HL_SYSCONFIG_SOFTRESET,
> +		iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_SYSCONFIG);
> +
> +	while (readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_SYSCONFIG) &
> +			ISS_HL_SYSCONFIG_SOFTRESET) {
> +		if (timeout++ > 10000) {
> +			dev_alert(iss->dev, "cannot reset ISS\n");
> +			return -ETIMEDOUT;
> +		}
> +		udelay(1);
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * iss_save_context - Saves the values of the ISS module registers.
> + * @iss: OMAP4 ISS device
> + * @reg_list: Structure containing pairs of register address and value to
> + *            modify on OMAP.
> + */
> +static void
> +iss_save_context(struct iss_device *iss, struct iss_reg *reg_list)
> +{
> +	struct iss_reg *next = reg_list;
> +
> +	for (; next->reg != ISS_TOK_TERM; next++)
> +		next->val = readl(iss->regs[next->mmio_range] + next->reg);
> +}
> +
> +/*
> + * iss_restore_context - Restores the values of the ISS module registers.
> + * @iss: OMAP4 ISS device
> + * @reg_list: Structure containing pairs of register address and value to
> + *            modify on OMAP.
> + */
> +static void
> +iss_restore_context(struct iss_device *iss, struct iss_reg *reg_list)
> +{
> +	struct iss_reg *next = reg_list;
> +
> +	for (; next->reg != ISS_TOK_TERM; next++)
> +		writel(next->val, iss->regs[next->mmio_range] + next->reg);
> +}
> +
> +/*
> + * iss_save_ctx - Saves ISS context.
> + * @iss: OMAP4 ISS device
> + *
> + * Routine for saving the context of each module in the ISS.
> + */
> +static void iss_save_ctx(struct iss_device *iss)
> +{
> +	iss_save_context(iss, iss_reg_list);
> +}
> +
> +/*
> + * iss_restore_ctx - Restores ISS context.
> + * @iss: OMAP4 ISS device
> + *
> + * Routine for restoring the context of each module in the ISS.
> + */
> +static void iss_restore_ctx(struct iss_device *iss)
> +{
> +	iss_restore_context(iss, iss_reg_list);
> +}
> +
> +/*
> + * iss_module_sync_idle - Helper to sync module with its idle state
> + * @me: ISS submodule's media entity
> + * @wait: ISS submodule's wait queue for streamoff/interrupt synchronization
> + * @stopping: flag which tells module wants to stop
> + *
> + * This function checks if ISS submodule needs to wait for next interrupt. If
> + * yes, makes the caller to sleep while waiting for such event.
> + */
> +int omap4iss_module_sync_idle(struct media_entity *me, wait_queue_head_t *wait,
> +			      atomic_t *stopping)
> +{
> +	struct iss_pipeline *pipe = to_iss_pipeline(me);
> +
> +	if (pipe->stream_state == ISS_PIPELINE_STREAM_STOPPED ||
> +	    (pipe->stream_state == ISS_PIPELINE_STREAM_SINGLESHOT &&
> +	     !iss_pipeline_ready(pipe)))
> +		return 0;
> +
> +	/*
> +	 * atomic_set() doesn't include memory barrier on ARM platform for SMP
> +	 * scenario. We'll call it here to avoid race conditions.
> +	 */
> +	atomic_set(stopping, 1);
> +	smp_mb();

It was new to me atomic_t requires memory barriers. But shouldn't this be
wmb() instead?

> +
> +	/*
> +	 * If module is the last one, it's writing to memory. In this case,
> +	 * it's necessary to check if the module is already paused due to
> +	 * DMA queue underrun or if it has to wait for next interrupt to be
> +	 * idle.
> +	 * If it isn't the last one, the function won't sleep but *stopping
> +	 * will still be set to warn next submodule caller's interrupt the
> +	 * module wants to be idle.
> +	 */
> +	if (iss_pipeline_is_last(me)) {

You could return here if the function returns zero.

> +		struct iss_video *video = pipe->output;
> +		unsigned long flags;
> +
> +		spin_lock_irqsave(&video->qlock, flags);
> +		if (video->dmaqueue_flags & ISS_VIDEO_DMAQUEUE_UNDERRUN) {
> +			spin_unlock_irqrestore(&video->qlock, flags);
> +			atomic_set(stopping, 0);
> +			smp_mb();
> +			return 0;
> +		}
> +		spin_unlock_irqrestore(&video->qlock, flags);
> +		if (!wait_event_timeout(*wait, !atomic_read(stopping),
> +					msecs_to_jiffies(1000))) {
> +			atomic_set(stopping, 0);
> +			smp_mb();
> +			return -ETIMEDOUT;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * omap4iss_module_sync_is_stopped - Helper to verify if module was stopping
> + * @wait: ISS submodule's wait queue for streamoff/interrupt synchronization
> + * @stopping: flag which tells module wants to stop
> + *
> + * This function checks if ISS submodule was stopping. In case of yes, it
> + * notices the caller by setting stopping to 0 and waking up the wait queue.
> + * Returns 1 if it was stopping or 0 otherwise.
> + */
> +int omap4iss_module_sync_is_stopping(wait_queue_head_t *wait,
> +				     atomic_t *stopping)
> +{
> +	if (atomic_cmpxchg(stopping, 1, 0)) {
> +		wake_up(wait);
> +		return 1;
> +	}
> +
> +	return 0;
> +}
> +
> +/* --------------------------------------------------------------------------
> + * Clock management
> + */
> +
> +#define ISS_CLKCTRL_MASK	(ISS_CLKCTRL_CSI2_A)
> +
> +static int __iss_subclk_update(struct iss_device *iss)
> +{
> +	u32 clk = 0;
> +	int ret = 0, timeout = 1000;
> +
> +	if (iss->subclk_resources & OMAP4_ISS_SUBCLK_CSI2_A)
> +		clk |= ISS_CLKCTRL_CSI2_A;
> +
> +	writel((readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_CLKCTRL) &
> +		~ISS_CLKCTRL_MASK) | clk,
> +		iss->regs[OMAP4_ISS_MEM_TOP] + ISS_CLKCTRL);
> +
> +	/* Wait for HW assertion */
> +	while (timeout-- > 0) {
> +		udelay(1);
> +		if ((readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_CLKSTAT) &
> +		     ISS_CLKCTRL_MASK) == clk)
> +			break;
> +	}
> +
> +	if (!timeout)
> +		ret = -EBUSY;
> +
> +	return ret;
> +}
> +
> +int omap4iss_subclk_enable(struct iss_device *iss,
> +			    enum iss_subclk_resource res)
> +{
> +	iss->subclk_resources |= res;
> +
> +	return __iss_subclk_update(iss);
> +}
> +
> +int omap4iss_subclk_disable(struct iss_device *iss,
> +			     enum iss_subclk_resource res)
> +{
> +	iss->subclk_resources &= ~res;
> +
> +	return __iss_subclk_update(iss);
> +}
> +
> +/*
> + * iss_enable_clocks - Enable ISS clocks
> + * @iss: OMAP4 ISS device
> + *
> + * Return 0 if successful, or clk_enable return value if any of tthem fails.
> + */
> +static int iss_enable_clocks(struct iss_device *iss)
> +{
> +	int r;
> +
> +	r = clk_enable(iss->iss_fck);
> +	if (r) {
> +		dev_err(iss->dev, "clk_enable iss_fck failed\n");
> +		goto out_clk_enable_fck;
> +	}
> +
> +	r = clk_enable(iss->iss_ctrlclk);
> +	if (r) {
> +		dev_err(iss->dev, "clk_enable iss_ctrlclk failed\n");
> +		goto out_clk_enable_ctrlclk;
> +	}
> +	return 0;
> +
> +out_clk_enable_ctrlclk:
> +	clk_disable(iss->iss_fck);
> +out_clk_enable_fck:
> +	return r;
> +}
> +
> +/*
> + * iss_disable_clocks - Disable ISS clocks
> + * @iss: OMAP4 ISS device
> + */
> +static void iss_disable_clocks(struct iss_device *iss)
> +{
> +	clk_disable(iss->iss_ctrlclk);
> +	clk_disable(iss->iss_fck);
> +}
> +
> +static void iss_put_clocks(struct iss_device *iss)
> +{
> +	if (iss->iss_fck) {
> +		clk_put(iss->iss_fck);
> +		iss->iss_fck = NULL;
> +	}
> +
> +	if (iss->iss_ctrlclk) {
> +		clk_put(iss->iss_ctrlclk);
> +		iss->iss_ctrlclk = NULL;
> +	}
> +}
> +
> +static int iss_get_clocks(struct iss_device *iss)
> +{
> +	iss->iss_fck = clk_get(iss->dev, "iss_fck");
> +	if (IS_ERR(iss->iss_fck)) {
> +		dev_err(iss->dev, "Unable to get iss_fck clock info\n");
> +		iss_put_clocks(iss);
> +		return PTR_ERR(iss->iss_fck);
> +	}
> +
> +	iss->iss_ctrlclk = clk_get(iss->dev, "iss_ctrlclk");
> +	if (IS_ERR(iss->iss_ctrlclk)) {
> +		dev_err(iss->dev, "Unable to get iss_ctrlclk clock info\n");
> +		iss_put_clocks(iss);
> +		return PTR_ERR(iss->iss_fck);
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * omap4iss_get - Acquire the ISS resource.
> + *
> + * Initializes the clocks for the first acquire.
> + *
> + * Increment the reference count on the ISS. If the first reference is taken,
> + * enable clocks and power-up all submodules.
> + *
> + * Return a pointer to the ISS device structure, or NULL if an error occurred.
> + */
> +struct iss_device *omap4iss_get(struct iss_device *iss)
> +{
> +	struct iss_device *__iss = iss;
> +
> +	if (iss == NULL)
> +		return NULL;
> +
> +	mutex_lock(&iss->iss_mutex);
> +	if (iss->ref_count > 0)
> +		goto out;
> +
> +	if (iss_enable_clocks(iss) < 0) {
> +		__iss = NULL;
> +		goto out;
> +	}
> +
> +	/* We don't want to restore context before saving it! */
> +	if (iss->has_context)
> +		iss_restore_ctx(iss);
> +	else
> +		iss->has_context = 1;
> +
> +	iss_enable_interrupts(iss);
> +
> +out:
> +	if (__iss != NULL)
> +		iss->ref_count++;
> +	mutex_unlock(&iss->iss_mutex);
> +
> +	return __iss;
> +}
> +
> +/*
> + * omap4iss_put - Release the ISS
> + *
> + * Decrement the reference count on the ISS. If the last reference is released,
> + * power-down all submodules, disable clocks and free temporary buffers.
> + */
> +void omap4iss_put(struct iss_device *iss)
> +{
> +	if (iss == NULL)
> +		return;
> +
> +	mutex_lock(&iss->iss_mutex);
> +	BUG_ON(iss->ref_count == 0);
> +	if (--iss->ref_count == 0) {
> +		iss_disable_interrupts(iss);
> +		iss_save_ctx(iss);
> +		iss_disable_clocks(iss);
> +	}
> +	mutex_unlock(&iss->iss_mutex);
> +}
> +
> +static int iss_map_mem_resource(struct platform_device *pdev,
> +				struct iss_device *iss,
> +				enum iss_mem_resources res)
> +{
> +	struct resource *mem;
> +
> +	/* request the mem region for the camera registers */
> +
> +	mem = platform_get_resource(pdev, IORESOURCE_MEM, res);
> +	if (!mem) {
> +		dev_err(iss->dev, "no mem resource?\n");
> +		return -ENODEV;
> +	}
> +
> +	if (!request_mem_region(mem->start, resource_size(mem), pdev->name)) {
> +		dev_err(iss->dev,
> +			"cannot reserve camera register I/O region\n");
> +		return -ENODEV;
> +	}
> +	iss->res[res] = mem;
> +
> +	/* map the region */
> +	iss->regs[res] = ioremap_nocache(mem->start, resource_size(mem));
> +	if (!iss->regs[res]) {
> +		dev_err(iss->dev, "cannot map camera register I/O region\n");
> +		return -ENODEV;
> +	}
> +
> +	return 0;
> +}
> +
> +static void iss_unregister_entities(struct iss_device *iss)
> +{
> +	omap4iss_csi2_unregister_entities(&iss->csi2a);
> +
> +	v4l2_device_unregister(&iss->v4l2_dev);
> +	media_device_unregister(&iss->media_dev);
> +}
> +
> +/*
> + * iss_register_subdev_group - Register a group of subdevices
> + * @iss: OMAP4 ISS device
> + * @board_info: I2C subdevs board information array
> + *
> + * Register all I2C subdevices in the board_info array. The array must be
> + * terminated by a NULL entry, and the first entry must be the sensor.
> + *
> + * Return a pointer to the sensor media entity if it has been successfully
> + * registered, or NULL otherwise.
> + */
> +static struct v4l2_subdev *
> +iss_register_subdev_group(struct iss_device *iss,
> +		     struct iss_subdev_i2c_board_info *board_info)
> +{
> +	struct v4l2_subdev *sensor = NULL;
> +	unsigned int first;
> +
> +	if (board_info->board_info == NULL)
> +		return NULL;
> +
> +	for (first = 1; board_info->board_info; ++board_info, first = 0) {
> +		struct v4l2_subdev *subdev;
> +		struct i2c_adapter *adapter;
> +
> +		adapter = i2c_get_adapter(board_info->i2c_adapter_id);
> +		if (adapter == NULL) {
> +			printk(KERN_ERR "%s: Unable to get I2C adapter %d for "
> +				"device %s\n", __func__,
> +				board_info->i2c_adapter_id,
> +				board_info->board_info->type);
> +			continue;
> +		}
> +
> +		subdev = v4l2_i2c_new_subdev_board(&iss->v4l2_dev, adapter,
> +				board_info->board_info, NULL);
> +		if (subdev == NULL) {
> +			printk(KERN_ERR "%s: Unable to register subdev %s\n",
> +				__func__, board_info->board_info->type);
> +			continue;
> +		}
> +
> +		if (first)
> +			sensor = subdev;
> +	}
> +
> +	return sensor;
> +}
> +
> +static int iss_register_entities(struct iss_device *iss)
> +{
> +	struct iss_platform_data *pdata = iss->pdata;
> +	struct iss_v4l2_subdevs_group *subdevs;
> +	int ret;
> +
> +	iss->media_dev.dev = iss->dev;
> +	strlcpy(iss->media_dev.model, "TI OMAP4 ISS",
> +		sizeof(iss->media_dev.model));
> +	iss->media_dev.link_notify = iss_pipeline_link_notify;
> +	ret = media_device_register(&iss->media_dev);
> +	if (ret < 0) {
> +		printk(KERN_ERR "%s: Media device registration failed (%d)\n",
> +			__func__, ret);
> +		return ret;
> +	}
> +
> +	iss->v4l2_dev.mdev = &iss->media_dev;
> +	ret = v4l2_device_register(iss->dev, &iss->v4l2_dev);
> +	if (ret < 0) {
> +		printk(KERN_ERR "%s: V4L2 device registration failed (%d)\n",
> +			__func__, ret);
> +		goto done;
> +	}
> +
> +	/* Register internal entities */
> +	ret = omap4iss_csi2_register_entities(&iss->csi2a, &iss->v4l2_dev);
> +	if (ret < 0)
> +		goto done;
> +
> +	/* Register external entities */
> +	for (subdevs = pdata->subdevs; subdevs && subdevs->subdevs; ++subdevs) {
> +		struct v4l2_subdev *sensor;
> +		struct media_entity *input;
> +		unsigned int flags;
> +		unsigned int pad;
> +
> +		sensor = iss_register_subdev_group(iss, subdevs->subdevs);
> +		if (sensor == NULL)
> +			continue;
> +
> +		sensor->host_priv = subdevs;
> +
> +		/* Connect the sensor to the correct interface module.
> +		 * CSI2a receiver through CSIPHY1.
> +		 */
> +		switch (subdevs->interface) {
> +		case ISS_INTERFACE_CSI2A_PHY1:
> +			input = &iss->csi2a.subdev.entity;
> +			pad = CSI2_PAD_SINK;
> +			flags = MEDIA_LNK_FL_IMMUTABLE
> +			      | MEDIA_LNK_FL_ENABLED;
> +			break;
> +
> +		default:
> +			printk(KERN_ERR "%s: invalid interface type %u\n",
> +			       __func__, subdevs->interface);
> +			ret = -EINVAL;
> +			goto done;
> +		}
> +
> +		ret = media_entity_create_link(&sensor->entity, 0, input, pad,
> +					       flags);
> +		if (ret < 0)
> +			goto done;
> +	}
> +
> +	ret = v4l2_device_register_subdev_nodes(&iss->v4l2_dev);
> +
> +done:
> +	if (ret < 0)
> +		iss_unregister_entities(iss);
> +
> +	return ret;
> +}
> +
> +static void iss_cleanup_modules(struct iss_device *iss)
> +{
> +	omap4iss_csi2_cleanup(iss);
> +}
> +
> +static int iss_initialize_modules(struct iss_device *iss)
> +{
> +	int ret;
> +
> +	ret = omap4iss_csiphy_init(iss);
> +	if (ret < 0) {
> +		dev_err(iss->dev, "CSI PHY initialization failed\n");
> +		goto error_csiphy;
> +	}
> +
> +	ret = omap4iss_csi2_init(iss);
> +	if (ret < 0) {
> +		dev_err(iss->dev, "CSI2 initialization failed\n");
> +		goto error_csi2;
> +	}
> +
> +	return 0;
> +
> +error_csi2:
> +error_csiphy:
> +	return ret;
> +}
> +
> +static int iss_probe(struct platform_device *pdev)
> +{
> +	struct iss_platform_data *pdata = pdev->dev.platform_data;
> +	struct iss_device *iss;
> +	int i, ret;
> +
> +	if (pdata == NULL)
> +		return -EINVAL;
> +
> +	iss = kzalloc(sizeof(*iss), GFP_KERNEL);
> +	if (!iss) {
> +		dev_err(&pdev->dev, "Could not allocate memory\n");
> +		return -ENOMEM;
> +	}
> +
> +	mutex_init(&iss->iss_mutex);
> +
> +	iss->dev = &pdev->dev;
> +	iss->pdata = pdata;
> +	iss->ref_count = 0;
> +
> +	iss->raw_dmamask = DMA_BIT_MASK(32);
> +	iss->dev->dma_mask = &iss->raw_dmamask;
> +	iss->dev->coherent_dma_mask = DMA_BIT_MASK(32);
> +
> +	platform_set_drvdata(pdev, iss);
> +
> +	/* Clocks */
> +	ret = iss_map_mem_resource(pdev, iss, OMAP4_ISS_MEM_TOP);
> +	if (ret < 0)
> +		goto error;
> +
> +	ret = iss_get_clocks(iss);
> +	if (ret < 0)
> +		goto error;
> +
> +	if (omap4iss_get(iss) == NULL)
> +		goto error;
> +
> +	ret = iss_reset(iss);
> +	if (ret < 0)
> +		goto error_iss;
> +
> +	iss->revision = readl(iss->regs[OMAP4_ISS_MEM_TOP] + ISS_HL_REVISION);
> +	dev_info(iss->dev, "Revision %08x found\n", iss->revision);
> +
> +	for (i = 1; i < OMAP4_ISS_MEM_LAST; i++) {
> +		ret = iss_map_mem_resource(pdev, iss, i);
> +		if (ret)
> +			goto error_iss;
> +	}
> +
> +	/* Interrupt */
> +	iss->irq_num = platform_get_irq(pdev, 0);
> +	if (iss->irq_num <= 0) {
> +		dev_err(iss->dev, "No IRQ resource\n");
> +		ret = -ENODEV;
> +		goto error_iss;
> +	}
> +
> +	if (request_irq(iss->irq_num, iss_isr, IRQF_SHARED, "OMAP4 ISS", iss)) {
> +		dev_err(iss->dev, "Unable to request IRQ\n");
> +		ret = -EINVAL;
> +		goto error_iss;
> +	}
> +
> +	/* Entities */
> +	ret = iss_initialize_modules(iss);
> +	if (ret < 0)
> +		goto error_irq;
> +
> +	ret = iss_register_entities(iss);
> +	if (ret < 0)
> +		goto error_modules;
> +
> +	omap4iss_put(iss);
> +
> +	return 0;
> +
> +error_modules:
> +	iss_cleanup_modules(iss);
> +error_irq:
> +	free_irq(iss->irq_num, iss);
> +error_iss:
> +	omap4iss_put(iss);
> +error:
> +	iss_put_clocks(iss);
> +
> +	for (i = 0; i < OMAP4_ISS_MEM_LAST; i++) {
> +		if (iss->regs[i]) {
> +			iounmap(iss->regs[i]);
> +			iss->regs[i] = NULL;
> +		}
> +
> +		if (iss->res[i]) {
> +			release_mem_region(iss->res[i]->start,
> +					   resource_size(iss->res[i]));
> +			iss->res[i] = NULL;
> +		}
> +	}
> +	platform_set_drvdata(pdev, NULL);
> +	kfree(iss);
> +
> +	return ret;
> +}
> +
> +static int iss_remove(struct platform_device *pdev)
> +{
> +	struct iss_device *iss = platform_get_drvdata(pdev);
> +	int i;
> +
> +	iss_unregister_entities(iss);
> +	iss_cleanup_modules(iss);
> +
> +	free_irq(iss->irq_num, iss);
> +	iss_put_clocks(iss);
> +
> +	for (i = 0; i < OMAP4_ISS_MEM_LAST; i++) {
> +		if (iss->regs[i]) {
> +			iounmap(iss->regs[i]);
> +			iss->regs[i] = NULL;
> +		}
> +
> +		if (iss->res[i]) {
> +			release_mem_region(iss->res[i]->start,
> +					   resource_size(iss->res[i]));
> +			iss->res[i] = NULL;
> +		}
> +	}
> +
> +	kfree(iss);
> +
> +	return 0;
> +}
> +
> +static struct platform_device_id omap4iss_id_table[] = {
> +	{ "omap4iss", 0 },
> +	{ },
> +};
> +MODULE_DEVICE_TABLE(platform, omap4iss_id_table);
> +
> +static struct platform_driver iss_driver = {
> +	.probe		= iss_probe,
> +	.remove		= iss_remove,
> +	.id_table	= omap4iss_id_table,
> +	.driver = {
> +		.owner	= THIS_MODULE,
> +		.name	= "omap4iss",
> +	},
> +};
> +
> +static int __init iss_init(void)
> +{
> +	return platform_driver_register(&iss_driver);
> +}
> +
> +static void __exit iss_exit(void)
> +{
> +	platform_driver_unregister(&iss_driver);
> +}
> +
> +/*
> + * FIXME: Had to make it late_initcall. Strangely while being module_init,
> + * The I2C communication was failing in the sensor, because no XCLK was
> + * provided.
> + */
> +late_initcall(iss_init);
> +module_exit(iss_exit);
> +
> +MODULE_DESCRIPTION("TI OMAP4 ISS driver");
> +MODULE_AUTHOR("Sergio Aguirre <saaguirre@xxxxxx>");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/media/video/omap4iss/iss.h b/drivers/media/video/omap4iss/iss.h
> new file mode 100644
> index 0000000..8346c80
> --- /dev/null
> +++ b/drivers/media/video/omap4iss/iss.h
> @@ -0,0 +1,133 @@
> +/*
> + * iss.h
> + *
> + * Copyright (C) 2011 Texas Instruments.
> + *
> + * Author: Sergio Aguirre <saaguirre@xxxxxx>
> + */
> +
> +#ifndef _OMAP4_ISS_H_
> +#define _OMAP4_ISS_H_
> +
> +#include <media/v4l2-device.h>
> +#include <linux/device.h>
> +#include <linux/io.h>
> +#include <linux/platform_device.h>
> +#include <linux/wait.h>
> +
> +#include <plat/omap4-iss.h>
> +
> +#include "iss_regs.h"
> +#include "iss_csiphy.h"
> +#include "iss_csi2.h"
> +
> +#define ISS_TOK_TERM		0xFFFFFFFF	/*
> +						 * terminating token for ISS
> +						 * modules reg list
> +						 */
> +#define to_iss_device(ptr_module)				\
> +	container_of(ptr_module, struct iss_device, ptr_module)
> +#define to_device(ptr_module)						\
> +	(to_iss_device(ptr_module)->dev)
> +
> +enum iss_mem_resources {
> +	OMAP4_ISS_MEM_TOP,
> +	OMAP4_ISS_MEM_CSI2_A_REGS1,
> +	OMAP4_ISS_MEM_CAMERARX_CORE1,
> +	OMAP4_ISS_MEM_LAST,
> +};
> +
> +enum iss_subclk_resource {
> +	OMAP4_ISS_SUBCLK_SIMCOP		= (1 << 0),
> +	OMAP4_ISS_SUBCLK_ISP		= (1 << 1),
> +	OMAP4_ISS_SUBCLK_CSI2_A		= (1 << 2),
> +	OMAP4_ISS_SUBCLK_CSI2_B		= (1 << 3),
> +	OMAP4_ISS_SUBCLK_CCP2		= (1 << 4),
> +};
> +
> +/*
> + * struct iss_reg - Structure for ISS register values.
> + * @reg: 32-bit Register address.
> + * @val: 32-bit Register value.
> + */
> +struct iss_reg {
> +	enum iss_mem_resources mmio_range;
> +	u32 reg;
> +	u32 val;
> +};
> +
> +struct iss_platform_callback {
> +	int (*csiphy_config)(struct iss_csiphy *phy,
> +			     struct iss_csiphy_dphy_cfg *dphy,
> +			     struct iss_csiphy_lanes_cfg *lanes);
> +};
> +
> +struct iss_device {
> +	struct v4l2_device v4l2_dev;
> +	struct media_device media_dev;
> +	struct device *dev;
> +	u32 revision;
> +
> +	/* platform HW resources */
> +	struct iss_platform_data *pdata;
> +	unsigned int irq_num;
> +
> +	struct resource *res[OMAP4_ISS_MEM_LAST];
> +	void __iomem *regs[OMAP4_ISS_MEM_LAST];
> +
> +	u64 raw_dmamask;
> +
> +	struct mutex iss_mutex;	/* For handling ref_count field */
> +	int has_context;
> +	int ref_count;
> +
> +	struct clk *iss_fck;
> +	struct clk *iss_ctrlclk;
> +
> +	/* ISS modules */
> +	struct iss_csi2_device csi2a;
> +	struct iss_csiphy csiphy1;
> +
> +	unsigned int subclk_resources;
> +
> +	struct iss_platform_callback platform_cb;
> +};
> +
> +#define v4l2_dev_to_iss_device(dev) \
> +	container_of(dev, struct iss_device, v4l2_dev)
> +
> +int omap4iss_module_sync_idle(struct media_entity *me, wait_queue_head_t *wait,
> +			      atomic_t *stopping);
> +
> +int omap4iss_module_sync_is_stopping(wait_queue_head_t *wait,
> +				     atomic_t *stopping);
> +
> +int omap4iss_pipeline_set_stream(struct iss_pipeline *pipe,
> +				 enum iss_pipeline_stream_state state);
> +
> +struct iss_device *omap4iss_get(struct iss_device *iss);
> +void omap4iss_put(struct iss_device *iss);
> +int omap4iss_subclk_enable(struct iss_device *iss,
> +			   enum iss_subclk_resource res);
> +int omap4iss_subclk_disable(struct iss_device *iss,
> +			    enum iss_subclk_resource res);
> +
> +int omap4iss_pipeline_pm_use(struct media_entity *entity, int use);
> +
> +int omap4iss_register_entities(struct platform_device *pdev,
> +			       struct v4l2_device *v4l2_dev);
> +void omap4iss_unregister_entities(struct platform_device *pdev);
> +
> +static inline enum v4l2_buf_type
> +iss_pad_buffer_type(const struct v4l2_subdev *subdev, int pad)
> +{
> +	if (pad >= subdev->entity.num_pads)
> +		return 0;
> +
> +	if (subdev->entity.pads[pad].flags & MEDIA_PAD_FL_SINK)
> +		return V4L2_BUF_TYPE_VIDEO_OUTPUT;
> +	else
> +		return V4L2_BUF_TYPE_VIDEO_CAPTURE;
> +}
> +
> +#endif /* _OMAP4_ISS_H_ */
> diff --git a/drivers/media/video/omap4iss/iss_csi2.c b/drivers/media/video/omap4iss/iss_csi2.c
> new file mode 100644
> index 0000000..916d5ef
> --- /dev/null
> +++ b/drivers/media/video/omap4iss/iss_csi2.c
> @@ -0,0 +1,1324 @@
> +/*
> + * iss_csi2.c
> + *
> + * TI OMAP4 ISS - CSI PHY module
> + *
> + * Copyright (C) 2011 Texas Instruments, Inc.
> + *
> + * Contacts: Sergio Aguirre <saaguirre@xxxxxx>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
> + * 02110-1301 USA
> + */
> +#include <linux/delay.h>
> +#include <media/v4l2-common.h>
> +#include <linux/v4l2-mediabus.h>
> +#include <linux/mm.h>
> +
> +#include "iss.h"
> +#include "iss_regs.h"
> +#include "iss_csi2.h"
> +
> +/*
> + * csi2_if_enable - Enable CSI2 Receiver interface.
> + * @enable: enable flag
> + *
> + */
> +static void csi2_if_enable(struct iss_csi2_device *csi2, u8 enable)
> +{
> +	struct iss_csi2_ctrl_cfg *currctrl = &csi2->ctrl;
> +
> +	writel((readl(csi2->regs1 + CSI2_CTRL) & ~CSI2_CTRL_IF_EN) |
> +		(enable ? CSI2_CTRL_IF_EN : 0),
> +		csi2->regs1 + CSI2_CTRL);
> +
> +	currctrl->if_enable = enable;
> +}
> +
> +/*
> + * csi2_recv_config - CSI2 receiver module configuration.
> + * @currctrl: iss_csi2_ctrl_cfg structure
> + *
> + */
> +static void csi2_recv_config(struct iss_csi2_device *csi2,
> +			     struct iss_csi2_ctrl_cfg *currctrl)
> +{
> +	u32 reg;
> +
> +	reg = readl(csi2->regs1 + CSI2_CTRL);
> +
> +	if (currctrl->frame_mode)
> +		reg |= CSI2_CTRL_FRAME;
> +	else
> +		reg &= ~CSI2_CTRL_FRAME;
> +
> +	if (currctrl->vp_clk_enable)
> +		reg |= CSI2_CTRL_VP_CLK_EN;
> +	else
> +		reg &= ~CSI2_CTRL_VP_CLK_EN;
> +
> +	if (currctrl->vp_only_enable)
> +		reg |= CSI2_CTRL_VP_ONLY_EN;
> +	else
> +		reg &= ~CSI2_CTRL_VP_ONLY_EN;
> +
> +	reg &= ~CSI2_CTRL_VP_OUT_CTRL_MASK;
> +	reg |= currctrl->vp_out_ctrl << CSI2_CTRL_VP_OUT_CTRL_SHIFT;
> +
> +	if (currctrl->ecc_enable)
> +		reg |= CSI2_CTRL_ECC_EN;
> +	else
> +		reg &= ~CSI2_CTRL_ECC_EN;
> +
> +	/*
> +	 * Set MFlag assertion boundaries to:
> +	 * Low: 4/8 of FIFO size
> +	 * High: 6/8 of FIFO size
> +	 */
> +	reg &= ~(CSI2_CTRL_MFLAG_LEVH_MASK | CSI2_CTRL_MFLAG_LEVL_MASK);
> +	reg |= (2 << CSI2_CTRL_MFLAG_LEVH_SHIFT) |
> +	       (4 << CSI2_CTRL_MFLAG_LEVL_SHIFT);
> +
> +	/* Generation of 16x64-bit bursts (Recommended) */
> +	reg |= CSI2_CTRL_BURST_SIZE_EXPAND;
> +
> +	/* Do Non-Posted writes (Recommended) */
> +	reg |= CSI2_CTRL_NON_POSTED_WRITE;
> +
> +	/*
> +	 * Enforce Little endian for all formats, including:
> +	 * YUV4:2:2 8-bit and YUV4:2:0 Legacy
> +	 */
> +	reg |= CSI2_CTRL_ENDIANNESS;
> +
> +	writel(reg, csi2->regs1 + CSI2_CTRL);
> +}
> +
> +static const unsigned int csi2_input_fmts[] = {
> +	V4L2_MBUS_FMT_SGRBG10_1X10,
> +	V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8,
> +	V4L2_MBUS_FMT_SRGGB10_1X10,
> +	V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8,
> +	V4L2_MBUS_FMT_SBGGR10_1X10,
> +	V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8,
> +	V4L2_MBUS_FMT_SGBRG10_1X10,
> +	V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8,
> +	V4L2_MBUS_FMT_UYVY8_1X16,
> +	V4L2_MBUS_FMT_YUYV8_1X16,
> +};
> +
> +/* To set the format on the CSI2 requires a mapping function that takes
> + * the following inputs:
> + * - 3 different formats (at this time)
> + * - 2 destinations (mem, vp+mem) (vp only handled separately)
> + * - 2 decompression options (on, off)
> + * Output should be CSI2 frame format code
> + * Array indices as follows: [format][dest][decompr]
> + * Not all combinations are valid. 0 means invalid.
> + */
> +static const u16 __csi2_fmt_map[][2][2] = {
> +	/* RAW10 formats */
> +	{
> +		/* Output to memory */
> +		{
> +			/* No DPCM decompression */
> +			CSI2_PIX_FMT_RAW10_EXP16,
> +			/* DPCM decompression */
> +			0,
> +		},
> +		/* Output to both */
> +		{
> +			/* No DPCM decompression */
> +			CSI2_PIX_FMT_RAW10_EXP16_VP,
> +			/* DPCM decompression */
> +			0,
> +		},
> +	},
> +	/* RAW10 DPCM8 formats */
> +	{
> +		/* Output to memory */
> +		{
> +			/* No DPCM decompression */
> +			CSI2_USERDEF_8BIT_DATA1,
> +			/* DPCM decompression */
> +			CSI2_USERDEF_8BIT_DATA1_DPCM10,
> +		},
> +		/* Output to both */
> +		{
> +			/* No DPCM decompression */
> +			CSI2_PIX_FMT_RAW8_VP,
> +			/* DPCM decompression */
> +			CSI2_USERDEF_8BIT_DATA1_DPCM10_VP,
> +		},
> +	},
> +	/* YUV422 formats */
> +	{
> +		/* Output to memory */
> +		{
> +			/* No DPCM decompression */
> +			CSI2_PIX_FMT_YUV422_8BIT,
> +			/* DPCM decompression */
> +			0,
> +		},
> +		/* Output to both */
> +		{
> +			/* No DPCM decompression */
> +			CSI2_PIX_FMT_YUV422_8BIT_VP,
> +			/* DPCM decompression */
> +			0,
> +		},
> +	},
> +};
> +
> +/*
> + * csi2_ctx_map_format - Map CSI2 sink media bus format to CSI2 format ID
> + * @csi2: ISS CSI2 device
> + *
> + * Returns CSI2 physical format id
> + */
> +static u16 csi2_ctx_map_format(struct iss_csi2_device *csi2)
> +{
> +	const struct v4l2_mbus_framefmt *fmt = &csi2->formats[CSI2_PAD_SINK];
> +	int fmtidx, destidx;
> +
> +	switch (fmt->code) {
> +	case V4L2_MBUS_FMT_SGRBG10_1X10:
> +	case V4L2_MBUS_FMT_SRGGB10_1X10:
> +	case V4L2_MBUS_FMT_SBGGR10_1X10:
> +	case V4L2_MBUS_FMT_SGBRG10_1X10:
> +		fmtidx = 0;
> +		break;
> +	case V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8:
> +	case V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8:
> +	case V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8:
> +	case V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8:
> +		fmtidx = 1;
> +		break;
> +	case V4L2_MBUS_FMT_UYVY8_1X16:
> +	case V4L2_MBUS_FMT_YUYV8_1X16:
> +		fmtidx = 2;
> +		break;
> +	default:
> +		WARN(1, KERN_ERR "CSI2: pixel format %08x unsupported!\n",
> +		     fmt->code);
> +		return 0;
> +	}
> +
> +	if (!(csi2->output & CSI2_OUTPUT_IPIPEIF) &&
> +	    !(csi2->output & CSI2_OUTPUT_MEMORY)) {
> +		/* Neither output enabled is a valid combination */
> +		return CSI2_PIX_FMT_OTHERS;
> +	}
> +
> +	/* If we need to skip frames at the beginning of the stream disable the
> +	 * video port to avoid sending the skipped frames to the IPIPEIF.
> +	 */
> +	destidx = csi2->frame_skip ? 0 : !!(csi2->output & CSI2_OUTPUT_IPIPEIF);
> +
> +	return __csi2_fmt_map[fmtidx][destidx][csi2->dpcm_decompress];
> +}
> +
> +/*
> + * csi2_set_outaddr - Set memory address to save output image
> + * @csi2: Pointer to ISS CSI2a device.
> + * @addr: 32-bit memory address aligned on 32 byte boundary.
> + *
> + * Sets the memory address where the output will be saved.
> + *
> + * Returns 0 if successful, or -EINVAL if the address is not in the 32 byte
> + * boundary.
> + */
> +static void csi2_set_outaddr(struct iss_csi2_device *csi2, u32 addr)
> +{
> +	struct iss_csi2_ctx_cfg *ctx = &csi2->contexts[0];
> +
> +	ctx->ping_addr = addr;
> +	ctx->pong_addr = addr;
> +	writel(ctx->ping_addr,
> +	       csi2->regs1 + CSI2_CTX_PING_ADDR(ctx->ctxnum));
> +	writel(ctx->pong_addr,
> +	       csi2->regs1 + CSI2_CTX_PONG_ADDR(ctx->ctxnum));
> +}
> +
> +/*
> + * is_usr_def_mapping - Checks whether USER_DEF_MAPPING should
> + *			be enabled by CSI2.
> + * @format_id: mapped format id
> + *
> + */
> +static inline int is_usr_def_mapping(u32 format_id)
> +{
> +	return (format_id & 0x40) ? 1 : 0;
> +}
> +
> +/*
> + * csi2_ctx_enable - Enable specified CSI2 context
> + * @ctxnum: Context number, valid between 0 and 7 values.
> + * @enable: enable
> + *
> + */
> +static void csi2_ctx_enable(struct iss_csi2_device *csi2, u8 ctxnum, u8 enable)
> +{
> +	struct iss_csi2_ctx_cfg *ctx = &csi2->contexts[ctxnum];
> +	u32 reg;
> +
> +	reg = readl(csi2->regs1 + CSI2_CTX_CTRL1(ctxnum));
> +
> +	if (enable) {
> +		unsigned int skip = 0;
> +
> +		if (csi2->frame_skip)
> +			skip = csi2->frame_skip;
> +		else if (csi2->output & CSI2_OUTPUT_MEMORY)
> +			skip = 1;
> +
> +		reg &= ~CSI2_CTX_CTRL1_COUNT_MASK;
> +		reg |= CSI2_CTX_CTRL1_COUNT_UNLOCK
> +		    |  (skip << CSI2_CTX_CTRL1_COUNT_SHIFT)
> +		    |  CSI2_CTX_CTRL1_CTX_EN;
> +	} else {
> +		reg &= ~CSI2_CTX_CTRL1_CTX_EN;
> +	}
> +
> +	writel(reg, csi2->regs1 + CSI2_CTX_CTRL1(ctxnum));
> +	ctx->enabled = enable;
> +}
> +
> +/*
> + * csi2_ctx_config - CSI2 context configuration.
> + * @ctx: context configuration
> + *
> + */
> +static void csi2_ctx_config(struct iss_csi2_device *csi2,
> +			    struct iss_csi2_ctx_cfg *ctx)
> +{
> +	u32 reg;
> +
> +	/* Set up CSI2_CTx_CTRL1 */
> +	reg = readl(csi2->regs1 + CSI2_CTX_CTRL1(ctx->ctxnum));
> +
> +	if (ctx->eof_enabled)
> +		reg |= CSI2_CTX_CTRL1_EOF_EN;
> +	else
> +		reg &= ~CSI2_CTX_CTRL1_EOF_EN;
> +
> +	if (ctx->eol_enabled)
> +		reg |= CSI2_CTX_CTRL1_EOL_EN;
> +	else
> +		reg &= ~CSI2_CTX_CTRL1_EOL_EN;
> +
> +	if (ctx->checksum_enabled)
> +		reg |= CSI2_CTX_CTRL1_CS_EN;
> +	else
> +		reg &= ~CSI2_CTX_CTRL1_CS_EN;
> +
> +	writel(reg, csi2->regs1 + CSI2_CTX_CTRL1(ctx->ctxnum));
> +
> +	/* Set up CSI2_CTx_CTRL2 */
> +	reg = readl(csi2->regs1 + CSI2_CTX_CTRL2(ctx->ctxnum));
> +
> +	reg &= ~(CSI2_CTX_CTRL2_VIRTUAL_ID_MASK);
> +	reg |= ctx->virtual_id << CSI2_CTX_CTRL2_VIRTUAL_ID_SHIFT;
> +
> +	reg &= ~(CSI2_CTX_CTRL2_FORMAT_MASK);
> +	reg |= ctx->format_id << CSI2_CTX_CTRL2_FORMAT_SHIFT;
> +
> +	if (ctx->dpcm_decompress) {
> +		if (ctx->dpcm_predictor)
> +			reg |= CSI2_CTX_CTRL2_DPCM_PRED;
> +		else
> +			reg &= ~CSI2_CTX_CTRL2_DPCM_PRED;
> +	}
> +
> +	if (is_usr_def_mapping(ctx->format_id)) {
> +		reg &= ~CSI2_CTX_CTRL2_USER_DEF_MAP_MASK;
> +		reg |= 2 << CSI2_CTX_CTRL2_USER_DEF_MAP_SHIFT;
> +	}
> +
> +	writel(reg, csi2->regs1 + CSI2_CTX_CTRL2(ctx->ctxnum));
> +
> +	/* Set up CSI2_CTx_CTRL3 */
> +	reg = readl(csi2->regs1 + CSI2_CTX_CTRL3(ctx->ctxnum));
> +	reg &= ~(CSI2_CTX_CTRL3_ALPHA_MASK);
> +	reg |= (ctx->alpha << CSI2_CTX_CTRL3_ALPHA_SHIFT);
> +
> +	writel(reg, csi2->regs1 + CSI2_CTX_CTRL3(ctx->ctxnum));
> +
> +	/* Set up CSI2_CTx_DAT_OFST */
> +	reg = readl(csi2->regs1 + CSI2_CTX_DAT_OFST(ctx->ctxnum));
> +	reg &= ~CSI2_CTX_DAT_OFST_MASK;
> +	reg |= ctx->data_offset;
> +	writel(reg, csi2->regs1 + CSI2_CTX_DAT_OFST(ctx->ctxnum));
> +
> +	writel(ctx->ping_addr,
> +		       csi2->regs1 + CSI2_CTX_PING_ADDR(ctx->ctxnum));
> +
> +	writel(ctx->pong_addr,
> +		       csi2->regs1 + CSI2_CTX_PONG_ADDR(ctx->ctxnum));
> +}
> +
> +/*
> + * csi2_timing_config - CSI2 timing configuration.
> + * @timing: csi2_timing_cfg structure
> + */
> +static void csi2_timing_config(struct iss_csi2_device *csi2,
> +			       struct iss_csi2_timing_cfg *timing)
> +{
> +	u32 reg;
> +
> +	reg = readl(csi2->regs1 + CSI2_TIMING);
> +
> +	if (timing->force_rx_mode)
> +		reg |= CSI2_TIMING_FORCE_RX_MODE_IO1;
> +	else
> +		reg &= ~CSI2_TIMING_FORCE_RX_MODE_IO1;
> +
> +	if (timing->stop_state_16x)
> +		reg |= CSI2_TIMING_STOP_STATE_X16_IO1;
> +	else
> +		reg &= ~CSI2_TIMING_STOP_STATE_X16_IO1;
> +
> +	if (timing->stop_state_4x)
> +		reg |= CSI2_TIMING_STOP_STATE_X4_IO1;
> +	else
> +		reg &= ~CSI2_TIMING_STOP_STATE_X4_IO1;
> +
> +	reg &= ~CSI2_TIMING_STOP_STATE_COUNTER_IO1_MASK;
> +	reg |= timing->stop_state_counter <<
> +	       CSI2_TIMING_STOP_STATE_COUNTER_IO1_SHIFT;
> +
> +	writel(reg, csi2->regs1 + CSI2_TIMING);
> +}
> +
> +/*
> + * csi2_irq_ctx_set - Enables CSI2 Context IRQs.
> + * @enable: Enable/disable CSI2 Context interrupts
> + */
> +static void csi2_irq_ctx_set(struct iss_csi2_device *csi2, int enable)
> +{
> +	u32 reg = CSI2_CTX_IRQ_FE;
> +	int i;
> +
> +	if (csi2->use_fs_irq)
> +		reg |= CSI2_CTX_IRQ_FS;
> +
> +	for (i = 0; i < 8; i++) {
> +		writel(reg, csi2->regs1 + CSI2_CTX_IRQSTATUS(i));
> +		if (enable)
> +			writel(readl(csi2->regs1 + CSI2_CTX_IRQENABLE(i)) | reg,
> +				csi2->regs1 + CSI2_CTX_IRQENABLE(i));
> +		else
> +			writel(readl(csi2->regs1 + CSI2_CTX_IRQENABLE(i)) &
> +				~reg,
> +				csi2->regs1 + CSI2_CTX_IRQENABLE(i));
> +	}
> +}
> +
> +/*
> + * csi2_irq_complexio1_set - Enables CSI2 ComplexIO IRQs.
> + * @enable: Enable/disable CSI2 ComplexIO #1 interrupts
> + */
> +static void csi2_irq_complexio1_set(struct iss_csi2_device *csi2, int enable)
> +{
> +	u32 reg;
> +	reg = CSI2_COMPLEXIO_IRQ_STATEALLULPMEXIT |
> +		CSI2_COMPLEXIO_IRQ_STATEALLULPMENTER |
> +		CSI2_COMPLEXIO_IRQ_STATEULPM5 |
> +		CSI2_COMPLEXIO_IRQ_ERRCONTROL5 |
> +		CSI2_COMPLEXIO_IRQ_ERRESC5 |
> +		CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS5 |
> +		CSI2_COMPLEXIO_IRQ_ERRSOTHS5 |
> +		CSI2_COMPLEXIO_IRQ_STATEULPM4 |
> +		CSI2_COMPLEXIO_IRQ_ERRCONTROL4 |
> +		CSI2_COMPLEXIO_IRQ_ERRESC4 |
> +		CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS4 |
> +		CSI2_COMPLEXIO_IRQ_ERRSOTHS4 |
> +		CSI2_COMPLEXIO_IRQ_STATEULPM3 |
> +		CSI2_COMPLEXIO_IRQ_ERRCONTROL3 |
> +		CSI2_COMPLEXIO_IRQ_ERRESC3 |
> +		CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS3 |
> +		CSI2_COMPLEXIO_IRQ_ERRSOTHS3 |
> +		CSI2_COMPLEXIO_IRQ_STATEULPM2 |
> +		CSI2_COMPLEXIO_IRQ_ERRCONTROL2 |
> +		CSI2_COMPLEXIO_IRQ_ERRESC2 |
> +		CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS2 |
> +		CSI2_COMPLEXIO_IRQ_ERRSOTHS2 |
> +		CSI2_COMPLEXIO_IRQ_STATEULPM1 |
> +		CSI2_COMPLEXIO_IRQ_ERRCONTROL1 |
> +		CSI2_COMPLEXIO_IRQ_ERRESC1 |
> +		CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS1 |
> +		CSI2_COMPLEXIO_IRQ_ERRSOTHS1;
> +	writel(reg, csi2->regs1 + CSI2_COMPLEXIO_IRQSTATUS);
> +	if (enable)
> +		reg |= readl(csi2->regs1 + CSI2_COMPLEXIO_IRQENABLE);
> +	else
> +		reg = 0;
> +	writel(reg, csi2->regs1 + CSI2_COMPLEXIO_IRQENABLE);
> +}
> +
> +/*
> + * csi2_irq_status_set - Enables CSI2 Status IRQs.
> + * @enable: Enable/disable CSI2 Status interrupts
> + */
> +static void csi2_irq_status_set(struct iss_csi2_device *csi2, int enable)
> +{
> +	u32 reg;
> +	reg = CSI2_IRQ_OCP_ERR |
> +		CSI2_IRQ_SHORT_PACKET |
> +		CSI2_IRQ_ECC_CORRECTION |
> +		CSI2_IRQ_ECC_NO_CORRECTION |
> +		CSI2_IRQ_COMPLEXIO_ERR |
> +		CSI2_IRQ_FIFO_OVF |
> +		CSI2_IRQ_CONTEXT0;
> +	writel(reg, csi2->regs1 + CSI2_IRQSTATUS);
> +	if (enable)
> +		reg |= readl(csi2->regs1 + CSI2_IRQENABLE);
> +	else
> +		reg = 0;
> +
> +	writel(reg, csi2->regs1 + CSI2_IRQENABLE);
> +}
> +
> +/*
> + * omap4iss_csi2_reset - Resets the CSI2 module.
> + *
> + * Must be called with the phy lock held.
> + *
> + * Returns 0 if successful, or -EBUSY if power command didn't respond.
> + */
> +int omap4iss_csi2_reset(struct iss_csi2_device *csi2)
> +{
> +	u8 soft_reset_retries = 0;
> +	u32 reg;
> +	int i;
> +
> +	if (!csi2->available)
> +		return -ENODEV;
> +
> +	if (csi2->phy->phy_in_use)
> +		return -EBUSY;
> +
> +	writel(readl(csi2->regs1 + CSI2_SYSCONFIG) |
> +		CSI2_SYSCONFIG_SOFT_RESET,
> +		csi2->regs1 + CSI2_SYSCONFIG);
> +
> +	do {
> +		reg = readl(csi2->regs1 + CSI2_SYSSTATUS) &
> +				    CSI2_SYSSTATUS_RESET_DONE;
> +		if (reg == CSI2_SYSSTATUS_RESET_DONE)
> +			break;
> +		soft_reset_retries++;
> +		if (soft_reset_retries < 5)
> +			udelay(100);
> +	} while (soft_reset_retries < 5);
> +
> +	if (soft_reset_retries == 5) {
> +		printk(KERN_ERR "CSI2: Soft reset try count exceeded!\n");
> +		return -EBUSY;
> +	}
> +
> +	writel(readl(csi2->regs1 + CSI2_COMPLEXIO_CFG) |
> +		CSI2_COMPLEXIO_CFG_RESET_CTRL,
> +		csi2->regs1 + CSI2_COMPLEXIO_CFG);
> +
> +	i = 100;
> +	do {
> +		reg = readl(csi2->phy->phy_regs + REGISTER1)
> +		    & REGISTER1_RESET_DONE_CTRLCLK;
> +		if (reg == REGISTER1_RESET_DONE_CTRLCLK)
> +			break;
> +		udelay(100);
> +	} while (--i > 0);
> +
> +	if (i == 0) {
> +		printk(KERN_ERR
> +		       "CSI2: Reset for CSI2_96M_FCLK domain Failed!\n");
> +		return -EBUSY;
> +	}
> +
> +	writel((readl(csi2->regs1 + CSI2_SYSCONFIG) &
> +		~(CSI2_SYSCONFIG_MSTANDBY_MODE_MASK |
> +		  CSI2_SYSCONFIG_AUTO_IDLE)) |
> +		CSI2_SYSCONFIG_MSTANDBY_MODE_NO,
> +		csi2->regs1 + CSI2_SYSCONFIG);
> +
> +	return 0;
> +}
> +
> +static int csi2_configure(struct iss_csi2_device *csi2)
> +{
> +	const struct iss_v4l2_subdevs_group *pdata;
> +	struct iss_csi2_timing_cfg *timing = &csi2->timing[0];
> +	struct v4l2_subdev *sensor;
> +	struct media_pad *pad;
> +
> +	/*
> +	 * CSI2 fields that can be updated while the context has
> +	 * been enabled or the interface has been enabled are not
> +	 * updated dynamically currently. So we do not allow to
> +	 * reconfigure if either has been enabled
> +	 */
> +	if (csi2->contexts[0].enabled || csi2->ctrl.if_enable)
> +		return -EBUSY;
> +
> +	pad = media_entity_remote_source(&csi2->pads[CSI2_PAD_SINK]);
> +	sensor = media_entity_to_v4l2_subdev(pad->entity);
> +	pdata = sensor->host_priv;
> +
> +	csi2->frame_skip = 0;
> +	v4l2_subdev_call(sensor, sensor, g_skip_frames, &csi2->frame_skip);
> +
> +	csi2->ctrl.vp_out_ctrl = pdata->bus.csi2.vpclk_div;
> +	csi2->ctrl.frame_mode = ISS_CSI2_FRAME_IMMEDIATE;
> +	csi2->ctrl.ecc_enable = pdata->bus.csi2.crc;
> +
> +	timing->force_rx_mode = 1;
> +	timing->stop_state_16x = 1;
> +	timing->stop_state_4x = 1;
> +	timing->stop_state_counter = 0x1FF;
> +
> +	/*
> +	 * The CSI2 receiver can't do any format conversion except DPCM
> +	 * decompression, so every set_format call configures both pads
> +	 * and enables DPCM decompression as a special case:
> +	 */
> +	if (csi2->formats[CSI2_PAD_SINK].code !=
> +	    csi2->formats[CSI2_PAD_SOURCE].code)
> +		csi2->dpcm_decompress = true;
> +	else
> +		csi2->dpcm_decompress = false;
> +
> +	csi2->contexts[0].format_id = csi2_ctx_map_format(csi2);
> +
> +	if (csi2->video_out.bpl_padding == 0)
> +		csi2->contexts[0].data_offset = 0;
> +	else
> +		csi2->contexts[0].data_offset = csi2->video_out.bpl_value;
> +
> +	/*
> +	 * Enable end of frame and end of line signals generation for
> +	 * context 0. These signals are generated from CSI2 receiver to
> +	 * qualify the last pixel of a frame and the last pixel of a line.
> +	 * Without enabling the signals CSI2 receiver writes data to memory
> +	 * beyond buffer size and/or data line offset is not handled correctly.
> +	 */
> +	csi2->contexts[0].eof_enabled = 1;
> +	csi2->contexts[0].eol_enabled = 1;
> +
> +	csi2_irq_complexio1_set(csi2, 1);
> +	csi2_irq_ctx_set(csi2, 1);
> +	csi2_irq_status_set(csi2, 1);
> +
> +	/* Set configuration (timings, format and links) */
> +	csi2_timing_config(csi2, timing);
> +	csi2_recv_config(csi2, &csi2->ctrl);
> +	csi2_ctx_config(csi2, &csi2->contexts[0]);
> +
> +	return 0;
> +}
> +
> +/*
> + * csi2_print_status - Prints CSI2 debug information.
> + */
> +#define CSI2_PRINT_REGISTER(iss, regs, name)\
> +	dev_dbg(iss->dev, "###CSI2 " #name "=0x%08x\n", \
> +		readl(regs + CSI2_##name))
> +
> +static void csi2_print_status(struct iss_csi2_device *csi2)
> +{
> +	struct iss_device *iss = csi2->iss;
> +
> +	if (!csi2->available)
> +		return;
> +
> +	dev_dbg(iss->dev, "-------------CSI2 Register dump-------------\n");
> +
> +	CSI2_PRINT_REGISTER(iss, csi2->regs1, SYSCONFIG);
> +	CSI2_PRINT_REGISTER(iss, csi2->regs1, SYSSTATUS);
> +	CSI2_PRINT_REGISTER(iss, csi2->regs1, IRQENABLE);
> +	CSI2_PRINT_REGISTER(iss, csi2->regs1, IRQSTATUS);
> +	CSI2_PRINT_REGISTER(iss, csi2->regs1, CTRL);
> +	CSI2_PRINT_REGISTER(iss, csi2->regs1, DBG_H);
> +	CSI2_PRINT_REGISTER(iss, csi2->regs1, COMPLEXIO_CFG);
> +	CSI2_PRINT_REGISTER(iss, csi2->regs1, COMPLEXIO_IRQSTATUS);
> +	CSI2_PRINT_REGISTER(iss, csi2->regs1, SHORT_PACKET);
> +	CSI2_PRINT_REGISTER(iss, csi2->regs1, COMPLEXIO_IRQENABLE);
> +	CSI2_PRINT_REGISTER(iss, csi2->regs1, DBG_P);
> +	CSI2_PRINT_REGISTER(iss, csi2->regs1, TIMING);
> +	CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_CTRL1(0));
> +	CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_CTRL2(0));
> +	CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_DAT_OFST(0));
> +	CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_PING_ADDR(0));
> +	CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_PONG_ADDR(0));
> +	CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_IRQENABLE(0));
> +	CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_IRQSTATUS(0));
> +	CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_CTRL3(0));
> +
> +	dev_dbg(iss->dev, "--------------------------------------------\n");
> +}
> +
> +/* -----------------------------------------------------------------------------
> + * Interrupt handling
> + */
> +
> +/*
> + * csi2_isr_buffer - Does buffer handling at end-of-frame
> + * when writing to memory.
> + */
> +static void csi2_isr_buffer(struct iss_csi2_device *csi2)
> +{
> +	struct iss_buffer *buffer;
> +
> +	csi2_ctx_enable(csi2, 0, 0);
> +
> +	buffer = omap4iss_video_buffer_next(&csi2->video_out, 0);
> +
> +	/*
> +	 * Let video queue operation restart engine if there is an underrun
> +	 * condition.
> +	 */
> +	if (buffer == NULL)
> +		return;
> +
> +	csi2_set_outaddr(csi2, buffer->iss_addr);
> +	csi2_ctx_enable(csi2, 0, 1);
> +}
> +
> +static void csi2_isr_ctx(struct iss_csi2_device *csi2,
> +			 struct iss_csi2_ctx_cfg *ctx)
> +{
> +	unsigned int n = ctx->ctxnum;
> +	u32 status;
> +
> +	status = readl(csi2->regs1 + CSI2_CTX_IRQSTATUS(n));
> +	writel(status, csi2->regs1 + CSI2_CTX_IRQSTATUS(n));
> +
> +	/* Propagate frame number */
> +	if (status & CSI2_CTX_IRQ_FS) {
> +		struct iss_pipeline *pipe =
> +				     to_iss_pipeline(&csi2->subdev.entity);
> +		if (pipe->do_propagation)
> +			atomic_inc(&pipe->frame_number);
> +	}
> +
> +	if (!(status & CSI2_CTX_IRQ_FE))
> +		return;
> +
> +	/* Skip interrupts until we reach the frame skip count. The CSI2 will be
> +	 * automatically disabled, as the frame skip count has been programmed
> +	 * in the CSI2_CTx_CTRL1::COUNT field, so reenable it.
> +	 *
> +	 * It would have been nice to rely on the FRAME_NUMBER interrupt instead
> +	 * but it turned out that the interrupt is only generated when the CSI2
> +	 * writes to memory (the CSI2_CTx_CTRL1::COUNT field is decreased
> +	 * correctly and reaches 0 when data is forwarded to the video port only
> +	 * but no interrupt arrives). Maybe a CSI2 hardware bug.
> +	 */
> +	if (csi2->frame_skip) {
> +		csi2->frame_skip--;
> +		if (csi2->frame_skip == 0) {
> +			ctx->format_id = csi2_ctx_map_format(csi2);
> +			csi2_ctx_config(csi2, ctx);
> +			csi2_ctx_enable(csi2, n, 1);
> +		}
> +		return;
> +	}
> +
> +	if (csi2->output & CSI2_OUTPUT_MEMORY)
> +		csi2_isr_buffer(csi2);
> +}
> +
> +/*
> + * omap4iss_csi2_isr - CSI2 interrupt handling.
> + *
> + * Return -EIO on Transmission error
> + */
> +int omap4iss_csi2_isr(struct iss_csi2_device *csi2)
> +{
> +	u32 csi2_irqstatus, cpxio1_irqstatus;
> +	struct iss_device *iss = csi2->iss;
> +	int retval = 0;
> +
> +	if (!csi2->available)
> +		return -ENODEV;
> +
> +	csi2_irqstatus = readl(csi2->regs1 + CSI2_IRQSTATUS);
> +	writel(csi2_irqstatus, csi2->regs1 + CSI2_IRQSTATUS);
> +
> +	/* Failure Cases */
> +	if (csi2_irqstatus & CSI2_IRQ_COMPLEXIO_ERR) {
> +		cpxio1_irqstatus = readl(csi2->regs1 +
> +					 CSI2_COMPLEXIO_IRQSTATUS);
> +		writel(cpxio1_irqstatus,
> +			csi2->regs1 + CSI2_COMPLEXIO_IRQSTATUS);
> +		dev_dbg(iss->dev, "CSI2: ComplexIO Error IRQ "
> +			"%x\n", cpxio1_irqstatus);
> +		retval = -EIO;
> +	}
> +
> +	if (csi2_irqstatus & (CSI2_IRQ_OCP_ERR |
> +			      CSI2_IRQ_SHORT_PACKET |
> +			      CSI2_IRQ_ECC_NO_CORRECTION |
> +			      CSI2_IRQ_COMPLEXIO_ERR |
> +			      CSI2_IRQ_FIFO_OVF)) {
> +		dev_dbg(iss->dev, "CSI2 Err:"
> +			" OCP:%d,"
> +			" Short_pack:%d,"
> +			" ECC:%d,"
> +			" CPXIO:%d,"
> +			" FIFO_OVF:%d,"
> +			"\n",
> +			(csi2_irqstatus &
> +			 CSI2_IRQ_OCP_ERR) ? 1 : 0,
> +			(csi2_irqstatus &
> +			 CSI2_IRQ_SHORT_PACKET) ? 1 : 0,
> +			(csi2_irqstatus &
> +			 CSI2_IRQ_ECC_NO_CORRECTION) ? 1 : 0,
> +			(csi2_irqstatus &
> +			 CSI2_IRQ_COMPLEXIO_ERR) ? 1 : 0,
> +			(csi2_irqstatus &
> +			 CSI2_IRQ_FIFO_OVF) ? 1 : 0);
> +		retval = -EIO;
> +	}
> +
> +	if (omap4iss_module_sync_is_stopping(&csi2->wait, &csi2->stopping))
> +		return 0;
> +
> +	/* Successful cases */
> +	if (csi2_irqstatus & CSI2_IRQ_CONTEXT0)
> +		csi2_isr_ctx(csi2, &csi2->contexts[0]);
> +
> +	if (csi2_irqstatus & CSI2_IRQ_ECC_CORRECTION)
> +		dev_dbg(iss->dev, "CSI2: ECC correction done\n");
> +
> +	return retval;
> +}
> +
> +/* -----------------------------------------------------------------------------
> + * ISS video operations
> + */
> +
> +/*
> + * csi2_queue - Queues the first buffer when using memory output
> + * @video: The video node
> + * @buffer: buffer to queue
> + */
> +static int csi2_queue(struct iss_video *video, struct iss_buffer *buffer)
> +{
> +	struct iss_device *iss = video->iss;
> +	struct iss_csi2_device *csi2 = &iss->csi2a;
> +
> +	csi2_set_outaddr(csi2, buffer->iss_addr);
> +
> +	/*
> +	 * If streaming was enabled before there was a buffer queued
> +	 * or underrun happened in the ISR, the hardware was not enabled
> +	 * and DMA queue flag ISS_VIDEO_DMAQUEUE_UNDERRUN is still set.
> +	 * Enable it now.
> +	 */
> +	if (csi2->video_out.dmaqueue_flags & ISS_VIDEO_DMAQUEUE_UNDERRUN) {
> +		/* Enable / disable context 0 and IRQs */
> +		csi2_if_enable(csi2, 1);
> +		csi2_ctx_enable(csi2, 0, 1);
> +		iss_video_dmaqueue_flags_clr(&csi2->video_out);
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct iss_video_operations csi2_issvideo_ops = {
> +	.queue = csi2_queue,
> +};
> +
> +/* -----------------------------------------------------------------------------
> + * V4L2 subdev operations
> + */
> +
> +static struct v4l2_mbus_framefmt *
> +__csi2_get_format(struct iss_csi2_device *csi2, struct v4l2_subdev_fh *fh,
> +		  unsigned int pad, enum v4l2_subdev_format_whence which)
> +{
> +	if (which == V4L2_SUBDEV_FORMAT_TRY)
> +		return v4l2_subdev_get_try_format(fh, pad);
> +	else
> +		return &csi2->formats[pad];
> +}
> +
> +static void
> +csi2_try_format(struct iss_csi2_device *csi2, struct v4l2_subdev_fh *fh,
> +		unsigned int pad, struct v4l2_mbus_framefmt *fmt,
> +		enum v4l2_subdev_format_whence which)
> +{
> +	enum v4l2_mbus_pixelcode pixelcode;
> +	struct v4l2_mbus_framefmt *format;
> +	const struct iss_format_info *info;
> +	unsigned int i;
> +
> +	switch (pad) {
> +	case CSI2_PAD_SINK:
> +		/* Clamp the width and height to valid range (1-8191). */
> +		for (i = 0; i < ARRAY_SIZE(csi2_input_fmts); i++) {
> +			if (fmt->code == csi2_input_fmts[i])
> +				break;
> +		}
> +
> +		/* If not found, use SGRBG10 as default */
> +		if (i >= ARRAY_SIZE(csi2_input_fmts))
> +			fmt->code = V4L2_MBUS_FMT_SGRBG10_1X10;
> +
> +		fmt->width = clamp_t(u32, fmt->width, 1, 8191);
> +		fmt->height = clamp_t(u32, fmt->height, 1, 8191);
> +		break;
> +
> +	case CSI2_PAD_SOURCE:
> +		/* Source format same as sink format, except for DPCM
> +		 * compression.
> +		 */
> +		pixelcode = fmt->code;
> +		format = __csi2_get_format(csi2, fh, CSI2_PAD_SINK, which);
> +		memcpy(fmt, format, sizeof(*fmt));
> +
> +		/*
> +		 * Only Allow DPCM decompression, and check that the
> +		 * pattern is preserved
> +		 */
> +		info = omap4iss_video_format_info(fmt->code);
> +		if (info->uncompressed == pixelcode)
> +			fmt->code = pixelcode;
> +		break;
> +	}
> +
> +	/* RGB, non-interlaced */
> +	fmt->colorspace = V4L2_COLORSPACE_SRGB;
> +	fmt->field = V4L2_FIELD_NONE;
> +}
> +
> +/*
> + * csi2_enum_mbus_code - Handle pixel format enumeration
> + * @sd     : pointer to v4l2 subdev structure
> + * @fh     : V4L2 subdev file handle
> + * @code   : pointer to v4l2_subdev_mbus_code_enum structure
> + * return -EINVAL or zero on success
> + */
> +static int csi2_enum_mbus_code(struct v4l2_subdev *sd,
> +			       struct v4l2_subdev_fh *fh,
> +			       struct v4l2_subdev_mbus_code_enum *code)
> +{
> +	struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd);
> +	struct v4l2_mbus_framefmt *format;
> +	const struct iss_format_info *info;
> +
> +	if (code->pad == CSI2_PAD_SINK) {
> +		if (code->index >= ARRAY_SIZE(csi2_input_fmts))
> +			return -EINVAL;
> +
> +		code->code = csi2_input_fmts[code->index];
> +	} else {
> +		format = __csi2_get_format(csi2, fh, CSI2_PAD_SINK,
> +					   V4L2_SUBDEV_FORMAT_TRY);
> +		switch (code->index) {
> +		case 0:
> +			/* Passthrough sink pad code */
> +			code->code = format->code;
> +			break;
> +		case 1:
> +			/* Uncompressed code */
> +			info = omap4iss_video_format_info(format->code);
> +			if (info->uncompressed == format->code)
> +				return -EINVAL;
> +
> +			code->code = info->uncompressed;
> +			break;
> +		default:
> +			return -EINVAL;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int csi2_enum_frame_size(struct v4l2_subdev *sd,
> +				struct v4l2_subdev_fh *fh,
> +				struct v4l2_subdev_frame_size_enum *fse)
> +{
> +	struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd);
> +	struct v4l2_mbus_framefmt format;
> +
> +	if (fse->index != 0)
> +		return -EINVAL;
> +
> +	format.code = fse->code;
> +	format.width = 1;
> +	format.height = 1;
> +	csi2_try_format(csi2, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY);
> +	fse->min_width = format.width;
> +	fse->min_height = format.height;
> +
> +	if (format.code != fse->code)
> +		return -EINVAL;
> +
> +	format.code = fse->code;
> +	format.width = -1;
> +	format.height = -1;
> +	csi2_try_format(csi2, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY);
> +	fse->max_width = format.width;
> +	fse->max_height = format.height;
> +
> +	return 0;
> +}
> +
> +/*
> + * csi2_get_format - Handle get format by pads subdev method
> + * @sd : pointer to v4l2 subdev structure
> + * @fh : V4L2 subdev file handle
> + * @fmt: pointer to v4l2 subdev format structure
> + * return -EINVAL or zero on success
> + */
> +static int csi2_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
> +			   struct v4l2_subdev_format *fmt)
> +{
> +	struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd);
> +	struct v4l2_mbus_framefmt *format;
> +
> +	format = __csi2_get_format(csi2, fh, fmt->pad, fmt->which);
> +	if (format == NULL)
> +		return -EINVAL;
> +
> +	fmt->format = *format;
> +	return 0;
> +}
> +
> +/*
> + * csi2_set_format - Handle set format by pads subdev method
> + * @sd : pointer to v4l2 subdev structure
> + * @fh : V4L2 subdev file handle
> + * @fmt: pointer to v4l2 subdev format structure
> + * return -EINVAL or zero on success
> + */
> +static int csi2_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
> +			   struct v4l2_subdev_format *fmt)
> +{
> +	struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd);
> +	struct v4l2_mbus_framefmt *format;
> +
> +	format = __csi2_get_format(csi2, fh, fmt->pad, fmt->which);
> +	if (format == NULL)
> +		return -EINVAL;
> +
> +	csi2_try_format(csi2, fh, fmt->pad, &fmt->format, fmt->which);
> +	*format = fmt->format;
> +
> +	/* Propagate the format from sink to source */
> +	if (fmt->pad == CSI2_PAD_SINK) {
> +		format = __csi2_get_format(csi2, fh, CSI2_PAD_SOURCE,
> +					   fmt->which);
> +		*format = fmt->format;
> +		csi2_try_format(csi2, fh, CSI2_PAD_SOURCE, format, fmt->which);
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * csi2_init_formats - Initialize formats on all pads
> + * @sd: ISS CSI2 V4L2 subdevice
> + * @fh: V4L2 subdev file handle
> + *
> + * Initialize all pad formats with default values. If fh is not NULL, try
> + * formats are initialized on the file handle. Otherwise active formats are
> + * initialized on the device.
> + */
> +static int csi2_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
> +{
> +	struct v4l2_subdev_format format;
> +
> +	memset(&format, 0, sizeof(format));
> +	format.pad = CSI2_PAD_SINK;
> +	format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
> +	format.format.code = V4L2_MBUS_FMT_SGRBG10_1X10;
> +	format.format.width = 4096;
> +	format.format.height = 4096;
> +	csi2_set_format(sd, fh, &format);
> +
> +	return 0;
> +}
> +
> +/*
> + * csi2_set_stream - Enable/Disable streaming on the CSI2 module
> + * @sd: ISS CSI2 V4L2 subdevice
> + * @enable: ISS pipeline stream state
> + *
> + * Return 0 on success or a negative error code otherwise.
> + */
> +static int csi2_set_stream(struct v4l2_subdev *sd, int enable)
> +{
> +	struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd);
> +	struct iss_device *iss = csi2->iss;
> +	struct iss_pipeline *pipe = to_iss_pipeline(&csi2->subdev.entity);
> +	struct iss_video *video_out = &csi2->video_out;
> +
> +	if (csi2->state == ISS_PIPELINE_STREAM_STOPPED) {
> +		if (enable == ISS_PIPELINE_STREAM_STOPPED)
> +			return 0;
> +
> +		omap4iss_subclk_enable(iss, OMAP4_ISS_SUBCLK_CSI2_A);
> +	}
> +
> +	switch (enable) {
> +	case ISS_PIPELINE_STREAM_CONTINUOUS:
> +		if (omap4iss_csiphy_acquire(csi2->phy) < 0)
> +			return -ENODEV;
> +		csi2->use_fs_irq = pipe->do_propagation;
> +		csi2_configure(csi2);
> +		csi2_print_status(csi2);
> +
> +		/*
> +		 * When outputting to memory with no buffer available, let the
> +		 * buffer queue handler start the hardware. A DMA queue flag
> +		 * ISS_VIDEO_DMAQUEUE_QUEUED will be set as soon as there is
> +		 * a buffer available.
> +		 */
> +		if (csi2->output & CSI2_OUTPUT_MEMORY &&
> +		    !(video_out->dmaqueue_flags & ISS_VIDEO_DMAQUEUE_QUEUED))
> +			break;
> +		/* Enable context 0 and IRQs */
> +		atomic_set(&csi2->stopping, 0);
> +		csi2_ctx_enable(csi2, 0, 1);
> +		csi2_if_enable(csi2, 1);
> +		iss_video_dmaqueue_flags_clr(video_out);
> +		break;
> +
> +	case ISS_PIPELINE_STREAM_STOPPED:
> +		if (csi2->state == ISS_PIPELINE_STREAM_STOPPED)
> +			return 0;
> +		if (omap4iss_module_sync_idle(&sd->entity, &csi2->wait,
> +					      &csi2->stopping))
> +			dev_dbg(iss->dev, "%s: module stop timeout.\n",
> +				sd->name);
> +		csi2_ctx_enable(csi2, 0, 0);
> +		csi2_if_enable(csi2, 0);
> +		csi2_irq_ctx_set(csi2, 0);
> +		omap4iss_csiphy_release(csi2->phy);
> +		omap4iss_subclk_disable(iss, OMAP4_ISS_SUBCLK_CSI2_A);
> +		iss_video_dmaqueue_flags_clr(video_out);
> +		break;
> +	}
> +
> +	csi2->state = enable;
> +	return 0;
> +}
> +
> +/* subdev video operations */
> +static const struct v4l2_subdev_video_ops csi2_video_ops = {
> +	.s_stream = csi2_set_stream,
> +};
> +
> +/* subdev pad operations */
> +static const struct v4l2_subdev_pad_ops csi2_pad_ops = {
> +	.enum_mbus_code = csi2_enum_mbus_code,
> +	.enum_frame_size = csi2_enum_frame_size,
> +	.get_fmt = csi2_get_format,
> +	.set_fmt = csi2_set_format,
> +};
> +
> +/* subdev operations */
> +static const struct v4l2_subdev_ops csi2_ops = {
> +	.video = &csi2_video_ops,
> +	.pad = &csi2_pad_ops,
> +};
> +
> +/* subdev internal operations */
> +static const struct v4l2_subdev_internal_ops csi2_internal_ops = {
> +	.open = csi2_init_formats,
> +};
> +
> +/* -----------------------------------------------------------------------------
> + * Media entity operations
> + */
> +
> +/*
> + * csi2_link_setup - Setup CSI2 connections.
> + * @entity : Pointer to media entity structure
> + * @local  : Pointer to local pad array
> + * @remote : Pointer to remote pad array
> + * @flags  : Link flags
> + * return -EINVAL or zero on success
> + */
> +static int csi2_link_setup(struct media_entity *entity,
> +			   const struct media_pad *local,
> +			   const struct media_pad *remote, u32 flags)
> +{
> +	struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
> +	struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd);
> +	struct iss_csi2_ctrl_cfg *ctrl = &csi2->ctrl;
> +
> +	/*
> +	 * The ISS core doesn't support pipelines with multiple video outputs.
> +	 * Revisit this when it will be implemented, and return -EBUSY for now.
> +	 */
> +
> +	switch (local->index | media_entity_type(remote->entity)) {
> +	case CSI2_PAD_SOURCE | MEDIA_ENT_T_DEVNODE:
> +		if (flags & MEDIA_LNK_FL_ENABLED) {
> +			if (csi2->output & ~CSI2_OUTPUT_MEMORY)
> +				return -EBUSY;
> +			csi2->output |= CSI2_OUTPUT_MEMORY;
> +		} else {
> +			csi2->output &= ~CSI2_OUTPUT_MEMORY;
> +		}
> +		break;
> +
> +	case CSI2_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV:
> +		if (flags & MEDIA_LNK_FL_ENABLED) {
> +			if (csi2->output & ~CSI2_OUTPUT_IPIPEIF)
> +				return -EBUSY;
> +			csi2->output |= CSI2_OUTPUT_IPIPEIF;
> +		} else {
> +			csi2->output &= ~CSI2_OUTPUT_IPIPEIF;
> +		}
> +		break;
> +
> +	default:
> +		/* Link from camera to CSI2 is fixed... */
> +		return -EINVAL;
> +	}
> +
> +	ctrl->vp_only_enable =
> +		(csi2->output & CSI2_OUTPUT_MEMORY) ? false : true;
> +	ctrl->vp_clk_enable = !!(csi2->output & CSI2_OUTPUT_IPIPEIF);
> +
> +	return 0;
> +}
> +
> +/* media operations */
> +static const struct media_entity_operations csi2_media_ops = {
> +	.link_setup = csi2_link_setup,
> +};
> +
> +/*
> + * csi2_init_entities - Initialize subdev and media entity.
> + * @csi2: Pointer to csi2 structure.
> + * return -ENOMEM or zero on success
> + */
> +static int csi2_init_entities(struct iss_csi2_device *csi2)
> +{
> +	struct v4l2_subdev *sd = &csi2->subdev;
> +	struct media_pad *pads = csi2->pads;
> +	struct media_entity *me = &sd->entity;
> +	int ret;
> +
> +	v4l2_subdev_init(sd, &csi2_ops);
> +	sd->internal_ops = &csi2_internal_ops;
> +	strlcpy(sd->name, "OMAP4 ISS CSI2a", sizeof(sd->name));
> +
> +	sd->grp_id = 1 << 16;	/* group ID for iss subdevs */
> +	v4l2_set_subdevdata(sd, csi2);
> +	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> +
> +	pads[CSI2_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
> +	pads[CSI2_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
> +
> +	me->ops = &csi2_media_ops;
> +	ret = media_entity_init(me, CSI2_PADS_NUM, pads, 0);
> +	if (ret < 0)
> +		return ret;
> +
> +	csi2_init_formats(sd, NULL);
> +
> +	/* Video device node */
> +	csi2->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> +	csi2->video_out.ops = &csi2_issvideo_ops;
> +	csi2->video_out.bpl_alignment = 32;
> +	csi2->video_out.bpl_zero_padding = 1;
> +	csi2->video_out.bpl_max = 0x1ffe0;
> +	csi2->video_out.iss = csi2->iss;
> +	csi2->video_out.capture_mem = PAGE_ALIGN(4096 * 4096) * 3;
> +
> +	ret = omap4iss_video_init(&csi2->video_out, "CSI2a");
> +	if (ret < 0)
> +		return ret;
> +
> +	/* Connect the CSI2 subdev to the video node. */
> +	ret = media_entity_create_link(&csi2->subdev.entity, CSI2_PAD_SOURCE,
> +				       &csi2->video_out.video.entity, 0, 0);
> +	if (ret < 0)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +void omap4iss_csi2_unregister_entities(struct iss_csi2_device *csi2)
> +{
> +	media_entity_cleanup(&csi2->subdev.entity);
> +
> +	v4l2_device_unregister_subdev(&csi2->subdev);
> +	omap4iss_video_unregister(&csi2->video_out);
> +}
> +
> +int omap4iss_csi2_register_entities(struct iss_csi2_device *csi2,
> +				    struct v4l2_device *vdev)
> +{
> +	int ret;
> +
> +	/* Register the subdev and video nodes. */
> +	ret = v4l2_device_register_subdev(vdev, &csi2->subdev);
> +	if (ret < 0)
> +		goto error;
> +
> +	ret = omap4iss_video_register(&csi2->video_out, vdev);
> +	if (ret < 0)
> +		goto error;
> +
> +	return 0;
> +
> +error:
> +	omap4iss_csi2_unregister_entities(csi2);
> +	return ret;
> +}
> +
> +/* -----------------------------------------------------------------------------
> + * ISS CSI2 initialisation and cleanup
> + */
> +
> +/*
> + * omap4iss_csi2_cleanup - Routine for module driver cleanup
> + */
> +void omap4iss_csi2_cleanup(struct iss_device *iss)
> +{
> +}
> +
> +/*
> + * omap4iss_csi2_init - Routine for module driver init
> + */
> +int omap4iss_csi2_init(struct iss_device *iss)
> +{
> +	struct iss_csi2_device *csi2a = &iss->csi2a;
> +	int ret;
> +
> +	csi2a->iss = iss;
> +	csi2a->available = 1;
> +	csi2a->regs1 = iss->regs[OMAP4_ISS_MEM_CSI2_A_REGS1];
> +	csi2a->phy = &iss->csiphy1;
> +	csi2a->state = ISS_PIPELINE_STREAM_STOPPED;
> +	init_waitqueue_head(&csi2a->wait);
> +
> +	ret = csi2_init_entities(csi2a);
> +	if (ret < 0)
> +		goto fail;
> +
> +	return 0;
> +fail:
> +	omap4iss_csi2_cleanup(iss);
> +	return ret;
> +}
> diff --git a/drivers/media/video/omap4iss/iss_csi2.h b/drivers/media/video/omap4iss/iss_csi2.h
> new file mode 100644
> index 0000000..4fa94cf
> --- /dev/null
> +++ b/drivers/media/video/omap4iss/iss_csi2.h
> @@ -0,0 +1,166 @@
> +/*
> + * iss_csi2.h
> + *
> + * TI OMAP4 ISS - CSI2 module
> + *
> + * Copyright (C) 2011 Texas Instruments, Inc.
> + *
> + * Contacts: Sergio Aguirre <saaguirre@xxxxxx>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
> + * 02110-1301 USA
> + */
> +
> +#ifndef OMAP4_ISS_CSI2_H
> +#define OMAP4_ISS_CSI2_H
> +
> +#include <linux/types.h>
> +#include <linux/videodev2.h>
> +
> +#include "iss_video.h"
> +
> +struct iss_csiphy;
> +
> +/* This is not an exhaustive list */
> +enum iss_csi2_pix_formats {
> +	CSI2_PIX_FMT_OTHERS = 0,
> +	CSI2_PIX_FMT_YUV422_8BIT = 0x1e,
> +	CSI2_PIX_FMT_YUV422_8BIT_VP = 0x9e,
> +	CSI2_PIX_FMT_RAW10_EXP16 = 0xab,
> +	CSI2_PIX_FMT_RAW10_EXP16_VP = 0x12f,
> +	CSI2_PIX_FMT_RAW8 = 0x2a,
> +	CSI2_PIX_FMT_RAW8_DPCM10_EXP16 = 0x2aa,
> +	CSI2_PIX_FMT_RAW8_DPCM10_VP = 0x32a,
> +	CSI2_PIX_FMT_RAW8_VP = 0x12a,
> +	CSI2_USERDEF_8BIT_DATA1_DPCM10_VP = 0x340,
> +	CSI2_USERDEF_8BIT_DATA1_DPCM10 = 0x2c0,
> +	CSI2_USERDEF_8BIT_DATA1 = 0x40,
> +};
> +
> +enum iss_csi2_irqevents {
> +	OCP_ERR_IRQ = 0x4000,
> +	SHORT_PACKET_IRQ = 0x2000,
> +	ECC_CORRECTION_IRQ = 0x1000,
> +	ECC_NO_CORRECTION_IRQ = 0x800,
> +	COMPLEXIO2_ERR_IRQ = 0x400,
> +	COMPLEXIO1_ERR_IRQ = 0x200,
> +	FIFO_OVF_IRQ = 0x100,
> +	CONTEXT7 = 0x80,
> +	CONTEXT6 = 0x40,
> +	CONTEXT5 = 0x20,
> +	CONTEXT4 = 0x10,
> +	CONTEXT3 = 0x8,
> +	CONTEXT2 = 0x4,
> +	CONTEXT1 = 0x2,
> +	CONTEXT0 = 0x1,
> +};
> +
> +enum iss_csi2_ctx_irqevents {
> +	CTX_ECC_CORRECTION = 0x100,
> +	CTX_LINE_NUMBER = 0x80,
> +	CTX_FRAME_NUMBER = 0x40,
> +	CTX_CS = 0x20,
> +	CTX_LE = 0x8,
> +	CTX_LS = 0x4,
> +	CTX_FE = 0x2,
> +	CTX_FS = 0x1,
> +};
> +
> +enum iss_csi2_frame_mode {
> +	ISS_CSI2_FRAME_IMMEDIATE,
> +	ISS_CSI2_FRAME_AFTERFEC,
> +};
> +
> +#define ISS_CSI2_MAX_CTX_NUM	7
> +
> +struct iss_csi2_ctx_cfg {
> +	u8 ctxnum;		/* context number 0 - 7 */
> +	u8 dpcm_decompress;
> +
> +	/* Fields in CSI2_CTx_CTRL2 - locked by CSI2_CTx_CTRL1.CTX_EN */
> +	u8 virtual_id;
> +	u16 format_id;		/* as in CSI2_CTx_CTRL2[9:0] */
> +	u8 dpcm_predictor;	/* 1: simple, 0: advanced */
> +
> +	/* Fields in CSI2_CTx_CTRL1/3 - Shadowed */
> +	u16 alpha;
> +	u16 data_offset;
> +	u32 ping_addr;
> +	u32 pong_addr;
> +	u8 eof_enabled;
> +	u8 eol_enabled;
> +	u8 checksum_enabled;
> +	u8 enabled;
> +};
> +
> +struct iss_csi2_timing_cfg {
> +	u8 ionum;			/* IO1 or IO2 as in CSI2_TIMING */
> +	unsigned force_rx_mode:1;
> +	unsigned stop_state_16x:1;
> +	unsigned stop_state_4x:1;
> +	u16 stop_state_counter;
> +};
> +
> +struct iss_csi2_ctrl_cfg {
> +	bool vp_clk_enable;
> +	bool vp_only_enable;
> +	u8 vp_out_ctrl;
> +	enum iss_csi2_frame_mode frame_mode;
> +	bool ecc_enable;
> +	bool if_enable;
> +};
> +
> +#define CSI2_PAD_SINK		0
> +#define CSI2_PAD_SOURCE		1
> +#define CSI2_PADS_NUM		2
> +
> +#define CSI2_OUTPUT_IPIPEIF	(1 << 0)
> +#define CSI2_OUTPUT_MEMORY	(1 << 1)
> +
> +struct iss_csi2_device {
> +	struct v4l2_subdev subdev;
> +	struct media_pad pads[CSI2_PADS_NUM];
> +	struct v4l2_mbus_framefmt formats[CSI2_PADS_NUM];
> +
> +	struct iss_video video_out;
> +	struct iss_device *iss;
> +
> +	u8 available;		/* Is the IP present on the silicon? */
> +
> +	/* Pointer to register remaps into kernel space */
> +	void __iomem *regs1;
> +	void __iomem *regs2;
> +
> +	u32 output; /* output to IPIPEIF, memory or both? */
> +	bool dpcm_decompress;
> +	unsigned int frame_skip;
> +	bool use_fs_irq;
> +
> +	struct iss_csiphy *phy;
> +	struct iss_csi2_ctx_cfg contexts[ISS_CSI2_MAX_CTX_NUM + 1];
> +	struct iss_csi2_timing_cfg timing[2];
> +	struct iss_csi2_ctrl_cfg ctrl;
> +	enum iss_pipeline_stream_state state;
> +	wait_queue_head_t wait;
> +	atomic_t stopping;
> +};
> +
> +int omap4iss_csi2_isr(struct iss_csi2_device *csi2);
> +int omap4iss_csi2_reset(struct iss_csi2_device *csi2);
> +int omap4iss_csi2_init(struct iss_device *iss);
> +void omap4iss_csi2_cleanup(struct iss_device *iss);
> +void omap4iss_csi2_unregister_entities(struct iss_csi2_device *csi2);
> +int omap4iss_csi2_register_entities(struct iss_csi2_device *csi2,
> +				    struct v4l2_device *vdev);
> +#endif	/* OMAP4_ISS_CSI2_H */
> diff --git a/drivers/media/video/omap4iss/iss_csiphy.c b/drivers/media/video/omap4iss/iss_csiphy.c
> new file mode 100644
> index 0000000..9545622
> --- /dev/null
> +++ b/drivers/media/video/omap4iss/iss_csiphy.c
> @@ -0,0 +1,215 @@
> +/*
> + * iss_csiphy.c
> + *
> + * TI OMAP4 ISS - CSI PHY module
> + *
> + * Copyright (C) 2011 Texas Instruments, Inc.
> + *
> + * Contacts: Sergio Aguirre <saaguirre@xxxxxx>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
> + * 02110-1301 USA
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +
> +#include "iss.h"
> +#include "iss_regs.h"
> +#include "iss_csiphy.h"
> +
> +/*
> + * csiphy_lanes_config - Configuration of CSIPHY lanes.
> + *
> + * Updates HW configuration.
> + * Called with phy->mutex taken.
> + */
> +static void csiphy_lanes_config(struct iss_csiphy *phy)
> +{
> +	unsigned int i;
> +	u32 reg;
> +
> +	reg = readl(phy->cfg_regs + CSI2_COMPLEXIO_CFG);
> +
> +	for (i = 0; i < phy->num_data_lanes; i++) {
> +		reg &= ~(CSI2_COMPLEXIO_CFG_DATA_POL(i + 1) |
> +			 CSI2_COMPLEXIO_CFG_DATA_POSITION_MASK(i + 1));
> +		reg |= (phy->lanes.data[i].pol ?
> +			CSI2_COMPLEXIO_CFG_DATA_POL(i + 1) : 0);
> +		reg |= (phy->lanes.data[i].pos <<
> +			CSI2_COMPLEXIO_CFG_DATA_POSITION_SHIFT(i + 1));
> +	}
> +
> +	reg &= ~(CSI2_COMPLEXIO_CFG_CLOCK_POL |
> +		 CSI2_COMPLEXIO_CFG_CLOCK_POSITION_MASK);
> +	reg |= phy->lanes.clk.pol ? CSI2_COMPLEXIO_CFG_CLOCK_POL : 0;
> +	reg |= phy->lanes.clk.pos << CSI2_COMPLEXIO_CFG_CLOCK_POSITION_SHIFT;
> +
> +	writel(reg, phy->cfg_regs + CSI2_COMPLEXIO_CFG);
> +}
> +
> +/*
> + * csiphy_set_power
> + * @power: Power state to be set.
> + *
> + * Returns 0 if successful, or -EBUSY if the retry count is exceeded.
> + */
> +static int csiphy_set_power(struct iss_csiphy *phy, u32 power)
> +{
> +	u32 reg;
> +	u8 retry_count;
> +
> +	writel((readl(phy->cfg_regs + CSI2_COMPLEXIO_CFG) &
> +		~CSI2_COMPLEXIO_CFG_PWD_CMD_MASK) |
> +		power,
> +		phy->cfg_regs + CSI2_COMPLEXIO_CFG);
> +
> +	retry_count = 0;
> +	do {
> +		udelay(50);
> +		reg = readl(phy->cfg_regs + CSI2_COMPLEXIO_CFG) &
> +				CSI2_COMPLEXIO_CFG_PWD_STATUS_MASK;
> +
> +		if (reg != power >> 2)
> +			retry_count++;
> +
> +	} while ((reg != power >> 2) && (retry_count < 100));
> +
> +	if (retry_count == 100) {
> +		printk(KERN_ERR "CSI2 CIO set power failed!\n");
> +		return -EBUSY;
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * csiphy_dphy_config - Configure CSI2 D-PHY parameters.
> + *
> + * Called with phy->mutex taken.
> + */
> +static void csiphy_dphy_config(struct iss_csiphy *phy)
> +{
> +	u32 reg;
> +
> +	/* Set up REGISTER0 */
> +	reg = readl(phy->phy_regs + REGISTER0);
> +
> +	reg &= ~(REGISTER0_THS_TERM_MASK |
> +		 REGISTER0_THS_SETTLE_MASK);
> +	reg |= phy->dphy.ths_term << REGISTER0_THS_TERM_SHIFT;
> +	reg |= phy->dphy.ths_settle << REGISTER0_THS_SETTLE_SHIFT;
> +
> +	writel(reg, phy->phy_regs + REGISTER0);
> +
> +	/* Set up REGISTER1 */
> +	reg = readl(phy->phy_regs + REGISTER1);
> +
> +	reg &= ~(REGISTER1_TCLK_TERM_MASK |
> +		 REGISTER1_CTRLCLK_DIV_FACTOR_MASK |
> +		 REGISTER1_TCLK_SETTLE_MASK);
> +	reg |= phy->dphy.tclk_term << REGISTER1_TCLK_TERM_SHIFT;
> +	reg |= phy->dphy.tclk_miss << REGISTER1_CTRLCLK_DIV_FACTOR_SHIFT;
> +	reg |= phy->dphy.tclk_settle << REGISTER1_TCLK_SETTLE_SHIFT;
> +
> +	writel(reg, phy->phy_regs + REGISTER1);
> +}
> +
> +static int csiphy_config(struct iss_csiphy *phy,
> +			 struct iss_csiphy_dphy_cfg *dphy,
> +			 struct iss_csiphy_lanes_cfg *lanes)
> +{
> +	unsigned int used_lanes = 0;
> +	unsigned int i;
> +
> +	/* Clock and data lanes verification */
> +	for (i = 0; i < phy->num_data_lanes; i++) {
> +		if (lanes->data[i].pos == 0)
> +			continue;
> +
> +		if (lanes->data[i].pol > 1 || lanes->data[i].pos > 5)
> +			return -EINVAL;
> +
> +		if (used_lanes & (1 << lanes->data[i].pos))
> +			return -EINVAL;
> +
> +		used_lanes |= 1 << lanes->data[i].pos;
> +	}
> +
> +	if (lanes->clk.pol > 1 || lanes->clk.pos > 4)
> +		return -EINVAL;
> +
> +	if (lanes->clk.pos == 0 || used_lanes & (1 << lanes->clk.pos))
> +		return -EINVAL;
> +
> +	mutex_lock(&phy->mutex);
> +	phy->dphy = *dphy;
> +	phy->lanes = *lanes;
> +	mutex_unlock(&phy->mutex);
> +
> +	return 0;
> +}
> +
> +int omap4iss_csiphy_acquire(struct iss_csiphy *phy)
> +{
> +	int rval;
> +
> +	mutex_lock(&phy->mutex);
> +
> +	rval = omap4iss_csi2_reset(phy->csi2);
> +	if (rval)
> +		goto done;
> +
> +	csiphy_dphy_config(phy);
> +	csiphy_lanes_config(phy);
> +
> +	rval = csiphy_set_power(phy, CSI2_COMPLEXIO_CFG_PWD_CMD_ON);
> +	if (rval)
> +		goto done;
> +
> +	phy->phy_in_use = 1;
> +
> +done:
> +	mutex_unlock(&phy->mutex);
> +	return rval;
> +}
> +
> +void omap4iss_csiphy_release(struct iss_csiphy *phy)
> +{
> +	mutex_lock(&phy->mutex);
> +	if (phy->phy_in_use) {
> +		csiphy_set_power(phy, CSI2_COMPLEXIO_CFG_PWD_CMD_OFF);
> +		phy->phy_in_use = 0;
> +	}
> +	mutex_unlock(&phy->mutex);
> +}
> +
> +/*
> + * omap4iss_csiphy_init - Initialize the CSI PHY frontends
> + */
> +int omap4iss_csiphy_init(struct iss_device *iss)
> +{
> +	struct iss_csiphy *phy1 = &iss->csiphy1;
> +
> +	iss->platform_cb.csiphy_config = csiphy_config;
> +
> +	phy1->iss = iss;
> +	phy1->csi2 = &iss->csi2a;
> +	phy1->num_data_lanes = ISS_CSIPHY1_NUM_DATA_LANES;
> +	phy1->cfg_regs = iss->regs[OMAP4_ISS_MEM_CSI2_A_REGS1];
> +	phy1->phy_regs = iss->regs[OMAP4_ISS_MEM_CAMERARX_CORE1];
> +	mutex_init(&phy1->mutex);
> +
> +	return 0;
> +}
> diff --git a/drivers/media/video/omap4iss/iss_csiphy.h b/drivers/media/video/omap4iss/iss_csiphy.h
> new file mode 100644
> index 0000000..c513ba8
> --- /dev/null
> +++ b/drivers/media/video/omap4iss/iss_csiphy.h
> @@ -0,0 +1,69 @@
> +/*
> + * iss_csiphy.h
> + *
> + * TI OMAP4 ISS - CSI PHY module
> + *
> + * Copyright (C) 2011 Texas Instruments, Inc.
> + *
> + * Contacts: Sergio Aguirre <saaguirre@xxxxxx>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
> + * 02110-1301 USA
> + */
> +
> +#ifndef OMAP4_ISS_CSI_PHY_H
> +#define OMAP4_ISS_CSI_PHY_H
> +
> +struct iss_csi2_device;
> +
> +struct csiphy_lane {
> +	u8 pos;
> +	u8 pol;
> +};
> +
> +#define ISS_CSIPHY1_NUM_DATA_LANES	4
> +
> +struct iss_csiphy_lanes_cfg {
> +	struct csiphy_lane data[ISS_CSIPHY1_NUM_DATA_LANES];
> +	struct csiphy_lane clk;
> +};
> +
> +struct iss_csiphy_dphy_cfg {
> +	u8 ths_term;
> +	u8 ths_settle;
> +	u8 tclk_term;
> +	unsigned tclk_miss:1;
> +	u8 tclk_settle;
> +};
> +
> +struct iss_csiphy {
> +	struct iss_device *iss;
> +	struct mutex mutex;	/* serialize csiphy configuration */
> +	u8 phy_in_use;
> +	struct iss_csi2_device *csi2;
> +
> +	/* Pointer to register remaps into kernel space */
> +	void __iomem *cfg_regs;
> +	void __iomem *phy_regs;
> +
> +	u8 num_data_lanes;	/* number of CSI2 Data Lanes supported */
> +	struct iss_csiphy_lanes_cfg lanes;
> +	struct iss_csiphy_dphy_cfg dphy;
> +};
> +
> +int omap4iss_csiphy_acquire(struct iss_csiphy *phy);
> +void omap4iss_csiphy_release(struct iss_csiphy *phy);
> +int omap4iss_csiphy_init(struct iss_device *iss);
> +
> +#endif	/* OMAP4_ISS_CSI_PHY_H */
> diff --git a/drivers/media/video/omap4iss/iss_regs.h b/drivers/media/video/omap4iss/iss_regs.h
> new file mode 100644
> index 0000000..0bd70ac
> --- /dev/null
> +++ b/drivers/media/video/omap4iss/iss_regs.h
> @@ -0,0 +1,238 @@
> +/*
> + * iss_regs.h
> + *
> + * Copyright (C) 2011 Texas Instruments.
> + *
> + * Author: Sergio Aguirre <saaguirre@xxxxxx>
> + */
> +
> +#ifndef _OMAP4_ISS_REGS_H_
> +#define _OMAP4_ISS_REGS_H_
> +
> +/* ISS */
> +#define ISS_HL_REVISION					(0x0)
> +
> +#define ISS_HL_SYSCONFIG				(0x10)
> +#define ISS_HL_SYSCONFIG_IDLEMODE_SHIFT			2
> +#define ISS_HL_SYSCONFIG_IDLEMODE_FORCEIDLE		0x0
> +#define ISS_HL_SYSCONFIG_IDLEMODE_NOIDLE		0x1
> +#define ISS_HL_SYSCONFIG_IDLEMODE_SMARTIDLE		0x2
> +#define ISS_HL_SYSCONFIG_SOFTRESET			(1 << 0)
> +
> +#define ISS_HL_IRQSTATUS_5				(0x24 + (0x10 * 5))
> +#define ISS_HL_IRQENABLE_5_SET				(0x28 + (0x10 * 5))
> +#define ISS_HL_IRQENABLE_5_CLR				(0x2C + (0x10 * 5))
> +
> +#define ISS_HL_IRQ_BTE					(1 << 11)
> +#define ISS_HL_IRQ_CBUFF				(1 << 10)
> +#define ISS_HL_IRQ_CSIA					(1 << 4)
> +
> +#define ISS_CTRL					(0x80)
> +
> +#define ISS_CLKCTRL					(0x84)
> +#define ISS_CLKCTRL_VPORT2_CLK				(1 << 30)
> +#define ISS_CLKCTRL_VPORT1_CLK				(1 << 29)
> +#define ISS_CLKCTRL_VPORT0_CLK				(1 << 28)
> +#define ISS_CLKCTRL_CCP2				(1 << 4)
> +#define ISS_CLKCTRL_CSI2_B				(1 << 3)
> +#define ISS_CLKCTRL_CSI2_A				(1 << 2)
> +#define ISS_CLKCTRL_ISP					(1 << 1)
> +#define ISS_CLKCTRL_SIMCOP				(1 << 0)
> +
> +#define ISS_CLKSTAT					(0x88)
> +#define ISS_CLKSTAT_VPORT2_CLK				(1 << 30)
> +#define ISS_CLKSTAT_VPORT1_CLK				(1 << 29)
> +#define ISS_CLKSTAT_VPORT0_CLK				(1 << 28)
> +#define ISS_CLKSTAT_CCP2				(1 << 4)
> +#define ISS_CLKSTAT_CSI2_B				(1 << 3)
> +#define ISS_CLKSTAT_CSI2_A				(1 << 2)
> +#define ISS_CLKSTAT_ISP					(1 << 1)
> +#define ISS_CLKSTAT_SIMCOP				(1 << 0)
> +
> +#define ISS_PM_STATUS					(0x8C)
> +#define ISS_PM_STATUS_CBUFF_PM_MASK			(3 << 12)
> +#define ISS_PM_STATUS_BTE_PM_MASK			(3 << 10)
> +#define ISS_PM_STATUS_SIMCOP_PM_MASK			(3 << 8)
> +#define ISS_PM_STATUS_ISP_PM_MASK			(3 << 6)
> +#define ISS_PM_STATUS_CCP2_PM_MASK			(3 << 4)
> +#define ISS_PM_STATUS_CSI2_B_PM_MASK			(3 << 2)
> +#define ISS_PM_STATUS_CSI2_A_PM_MASK			(3 << 0)
> +
> +#define REGISTER0					(0x0)
> +#define REGISTER0_HSCLOCKCONFIG				(1 << 24)
> +#define REGISTER0_THS_TERM_MASK				(0xFF << 8)
> +#define REGISTER0_THS_TERM_SHIFT			8
> +#define REGISTER0_THS_SETTLE_MASK			(0xFF << 0)
> +#define REGISTER0_THS_SETTLE_SHIFT			0
> +
> +#define REGISTER1					(0x4)
> +#define REGISTER1_RESET_DONE_CTRLCLK			(1 << 29)
> +#define REGISTER1_CLOCK_MISS_DETECTOR_STATUS		(1 << 25)
> +#define REGISTER1_TCLK_TERM_MASK			(0x3F << 18)
> +#define REGISTER1_TCLK_TERM_SHIFT			18
> +#define REGISTER1_DPHY_HS_SYNC_PATTERN_MASK		(0xFF << 10)
> +#define REGISTER1_CTRLCLK_DIV_FACTOR_MASK		(0x3 << 8)
> +#define REGISTER1_CTRLCLK_DIV_FACTOR_SHIFT		8
> +#define REGISTER1_TCLK_SETTLE_MASK			(0xFF << 0)
> +#define REGISTER1_TCLK_SETTLE_SHIFT			0
> +
> +#define REGISTER2					(0x8)
> +
> +#define CSI2_SYSCONFIG					(0x10)
> +#define CSI2_SYSCONFIG_MSTANDBY_MODE_MASK		(3 << 12)
> +#define CSI2_SYSCONFIG_MSTANDBY_MODE_FORCE		(0 << 12)
> +#define CSI2_SYSCONFIG_MSTANDBY_MODE_NO			(1 << 12)
> +#define CSI2_SYSCONFIG_MSTANDBY_MODE_SMART		(2 << 12)
> +#define CSI2_SYSCONFIG_SOFT_RESET			(1 << 1)
> +#define CSI2_SYSCONFIG_AUTO_IDLE			(1 << 0)
> +
> +#define CSI2_SYSSTATUS					(0x14)
> +#define CSI2_SYSSTATUS_RESET_DONE			(1 << 0)
> +
> +#define CSI2_IRQSTATUS					(0x18)
> +#define CSI2_IRQENABLE					(0x1C)
> +
> +/* Shared bits across CSI2_IRQENABLE and IRQSTATUS */
> +
> +#define CSI2_IRQ_OCP_ERR				(1 << 14)
> +#define CSI2_IRQ_SHORT_PACKET				(1 << 13)
> +#define CSI2_IRQ_ECC_CORRECTION				(1 << 12)
> +#define CSI2_IRQ_ECC_NO_CORRECTION			(1 << 11)
> +#define CSI2_IRQ_COMPLEXIO_ERR				(1 << 9)
> +#define CSI2_IRQ_FIFO_OVF				(1 << 8)
> +#define CSI2_IRQ_CONTEXT0				(1 << 0)
> +
> +#define CSI2_CTRL					(0x40)
> +#define CSI2_CTRL_MFLAG_LEVH_MASK			(7 << 20)
> +#define CSI2_CTRL_MFLAG_LEVH_SHIFT			20
> +#define CSI2_CTRL_MFLAG_LEVL_MASK			(7 << 17)
> +#define CSI2_CTRL_MFLAG_LEVL_SHIFT			17
> +#define CSI2_CTRL_BURST_SIZE_EXPAND			(1 << 16)
> +#define CSI2_CTRL_VP_CLK_EN				(1 << 15)
> +#define CSI2_CTRL_NON_POSTED_WRITE			(1 << 13)
> +#define CSI2_CTRL_VP_ONLY_EN				(1 << 11)
> +#define CSI2_CTRL_VP_OUT_CTRL_MASK			(3 << 8)
> +#define CSI2_CTRL_VP_OUT_CTRL_SHIFT			8
> +#define CSI2_CTRL_DBG_EN				(1 << 7)
> +#define CSI2_CTRL_BURST_SIZE_MASK			(3 << 5)
> +#define CSI2_CTRL_ENDIANNESS				(1 << 4)
> +#define CSI2_CTRL_FRAME					(1 << 3)
> +#define CSI2_CTRL_ECC_EN				(1 << 2)
> +#define CSI2_CTRL_IF_EN					(1 << 0)
> +
> +#define CSI2_DBG_H					(0x44)
> +
> +#define CSI2_COMPLEXIO_CFG				(0x50)
> +#define CSI2_COMPLEXIO_CFG_RESET_CTRL			(1 << 30)
> +#define CSI2_COMPLEXIO_CFG_RESET_DONE			(1 << 29)
> +#define CSI2_COMPLEXIO_CFG_PWD_CMD_MASK			(3 << 27)
> +#define CSI2_COMPLEXIO_CFG_PWD_CMD_OFF			(0 << 27)
> +#define CSI2_COMPLEXIO_CFG_PWD_CMD_ON			(1 << 27)
> +#define CSI2_COMPLEXIO_CFG_PWD_CMD_ULP			(2 << 27)
> +#define CSI2_COMPLEXIO_CFG_PWD_STATUS_MASK		(3 << 25)
> +#define CSI2_COMPLEXIO_CFG_PWD_STATUS_OFF		(0 << 25)
> +#define CSI2_COMPLEXIO_CFG_PWD_STATUS_ON		(1 << 25)
> +#define CSI2_COMPLEXIO_CFG_PWD_STATUS_ULP		(2 << 25)
> +#define CSI2_COMPLEXIO_CFG_PWR_AUTO			(1 << 24)
> +#define CSI2_COMPLEXIO_CFG_DATA_POL(i)			(1 << (((i) * 4) + 3))
> +#define CSI2_COMPLEXIO_CFG_DATA_POSITION_MASK(i)	(7 << ((i) * 4))
> +#define CSI2_COMPLEXIO_CFG_DATA_POSITION_SHIFT(i)	((i) * 4)
> +#define CSI2_COMPLEXIO_CFG_CLOCK_POL			(1 << 3)
> +#define CSI2_COMPLEXIO_CFG_CLOCK_POSITION_MASK		(7 << 0)
> +#define CSI2_COMPLEXIO_CFG_CLOCK_POSITION_SHIFT		0
> +
> +#define CSI2_COMPLEXIO_IRQSTATUS			(0x54)
> +
> +#define CSI2_SHORT_PACKET				(0x5C)
> +
> +#define CSI2_COMPLEXIO_IRQENABLE			(0x60)
> +
> +/* Shared bits across CSI2_COMPLEXIO_IRQENABLE and IRQSTATUS */
> +#define CSI2_COMPLEXIO_IRQ_STATEALLULPMEXIT		(1 << 26)
> +#define CSI2_COMPLEXIO_IRQ_STATEALLULPMENTER		(1 << 25)
> +#define CSI2_COMPLEXIO_IRQ_STATEULPM5			(1 << 24)
> +#define CSI2_COMPLEXIO_IRQ_STATEULPM4			(1 << 23)
> +#define CSI2_COMPLEXIO_IRQ_STATEULPM3			(1 << 22)
> +#define CSI2_COMPLEXIO_IRQ_STATEULPM2			(1 << 21)
> +#define CSI2_COMPLEXIO_IRQ_STATEULPM1			(1 << 20)
> +#define CSI2_COMPLEXIO_IRQ_ERRCONTROL5			(1 << 19)
> +#define CSI2_COMPLEXIO_IRQ_ERRCONTROL4			(1 << 18)
> +#define CSI2_COMPLEXIO_IRQ_ERRCONTROL3			(1 << 17)
> +#define CSI2_COMPLEXIO_IRQ_ERRCONTROL2			(1 << 16)
> +#define CSI2_COMPLEXIO_IRQ_ERRCONTROL1			(1 << 15)
> +#define CSI2_COMPLEXIO_IRQ_ERRESC5			(1 << 14)
> +#define CSI2_COMPLEXIO_IRQ_ERRESC4			(1 << 13)
> +#define CSI2_COMPLEXIO_IRQ_ERRESC3			(1 << 12)
> +#define CSI2_COMPLEXIO_IRQ_ERRESC2			(1 << 11)
> +#define CSI2_COMPLEXIO_IRQ_ERRESC1			(1 << 10)
> +#define CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS5		(1 << 9)
> +#define CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS4		(1 << 8)
> +#define CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS3		(1 << 7)
> +#define CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS2		(1 << 6)
> +#define CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS1		(1 << 5)
> +#define CSI2_COMPLEXIO_IRQ_ERRSOTHS5			(1 << 4)
> +#define CSI2_COMPLEXIO_IRQ_ERRSOTHS4			(1 << 3)
> +#define CSI2_COMPLEXIO_IRQ_ERRSOTHS3			(1 << 2)
> +#define CSI2_COMPLEXIO_IRQ_ERRSOTHS2			(1 << 1)
> +#define CSI2_COMPLEXIO_IRQ_ERRSOTHS1			(1 << 0)
> +
> +#define CSI2_DBG_P					(0x68)
> +
> +#define CSI2_TIMING					(0x6C)
> +#define CSI2_TIMING_FORCE_RX_MODE_IO1			(1 << 15)
> +#define CSI2_TIMING_STOP_STATE_X16_IO1			(1 << 14)
> +#define CSI2_TIMING_STOP_STATE_X4_IO1			(1 << 13)
> +#define CSI2_TIMING_STOP_STATE_COUNTER_IO1_MASK		(0x1FFF << 0)
> +#define CSI2_TIMING_STOP_STATE_COUNTER_IO1_SHIFT	0
> +
> +#define CSI2_CTX_CTRL1(i)				(0x70 + (0x20 * i))
> +#define CSI2_CTX_CTRL1_GENERIC				(1 << 30)
> +#define CSI2_CTX_CTRL1_TRANSCODE			(0xF << 24)
> +#define CSI2_CTX_CTRL1_FEC_NUMBER_MASK			(0xFF << 16)
> +#define CSI2_CTX_CTRL1_COUNT_MASK			(0xFF << 8)
> +#define CSI2_CTX_CTRL1_COUNT_SHIFT			8
> +#define CSI2_CTX_CTRL1_EOF_EN				(1 << 7)
> +#define CSI2_CTX_CTRL1_EOL_EN				(1 << 6)
> +#define CSI2_CTX_CTRL1_CS_EN				(1 << 5)
> +#define CSI2_CTX_CTRL1_COUNT_UNLOCK			(1 << 4)
> +#define CSI2_CTX_CTRL1_PING_PONG			(1 << 3)
> +#define CSI2_CTX_CTRL1_CTX_EN				(1 << 0)
> +
> +#define CSI2_CTX_CTRL2(i)				(0x74 + (0x20 * i))
> +#define CSI2_CTX_CTRL2_USER_DEF_MAP_SHIFT		13
> +#define CSI2_CTX_CTRL2_USER_DEF_MAP_MASK		\
> +		(0x3 << CSI2_CTX_CTRL2_USER_DEF_MAP_SHIFT)
> +#define CSI2_CTX_CTRL2_VIRTUAL_ID_MASK			(3 << 11)
> +#define CSI2_CTX_CTRL2_VIRTUAL_ID_SHIFT			11
> +#define CSI2_CTX_CTRL2_DPCM_PRED			(1 << 10)
> +#define CSI2_CTX_CTRL2_FORMAT_MASK			(0x3FF << 0)
> +#define CSI2_CTX_CTRL2_FORMAT_SHIFT			0
> +
> +#define CSI2_CTX_DAT_OFST(i)				(0x78 + (0x20 * i))
> +#define CSI2_CTX_DAT_OFST_MASK				(0xFFF << 5)
> +
> +#define CSI2_CTX_PING_ADDR(i)				(0x7C + (0x20 * i))
> +#define CSI2_CTX_PING_ADDR_MASK				0xFFFFFFE0
> +
> +#define CSI2_CTX_PONG_ADDR(i)				(0x80 + (0x20 * i))
> +#define CSI2_CTX_PONG_ADDR_MASK				CSI2_CTX_PING_ADDR_MASK
> +
> +#define CSI2_CTX_IRQENABLE(i)				(0x84 + (0x20 * i))
> +#define CSI2_CTX_IRQSTATUS(i)				(0x88 + (0x20 * i))
> +
> +#define CSI2_CTX_CTRL3(i)				(0x8C + (0x20 * i))
> +#define CSI2_CTX_CTRL3_ALPHA_SHIFT			5
> +#define CSI2_CTX_CTRL3_ALPHA_MASK			\
> +		(0x3fff << CSI2_CTX_CTRL3_ALPHA_SHIFT)
> +
> +/* Shared bits across CSI2_CTX_IRQENABLE and IRQSTATUS */
> +#define CSI2_CTX_IRQ_ECC_CORRECTION			(1 << 8)
> +#define CSI2_CTX_IRQ_LINE_NUMBER			(1 << 7)
> +#define CSI2_CTX_IRQ_FRAME_NUMBER			(1 << 6)
> +#define CSI2_CTX_IRQ_CS					(1 << 5)
> +#define CSI2_CTX_IRQ_LE					(1 << 3)
> +#define CSI2_CTX_IRQ_LS					(1 << 2)
> +#define CSI2_CTX_IRQ_FE					(1 << 1)
> +#define CSI2_CTX_IRQ_FS					(1 << 0)
> +
> +#endif /* _OMAP4_CAMERA_REGS_H_ */
> diff --git a/drivers/media/video/omap4iss/iss_video.c b/drivers/media/video/omap4iss/iss_video.c
> new file mode 100644
> index 0000000..3248711
> --- /dev/null
> +++ b/drivers/media/video/omap4iss/iss_video.c
> @@ -0,0 +1,1192 @@
> +/*
> + * iss_video.c
> + *
> + * TI OMAP4 ISS - Generic video node
> + *
> + * Copyright (C) 2011 Texas Instruments, Inc.
> + *
> + * Contacts: Sergio Aguirre <saaguirre@xxxxxx>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
> + * 02110-1301 USA
> + */
> +
> +#include <asm/cacheflush.h>
> +#include <linux/clk.h>
> +#include <linux/mm.h>
> +#include <linux/pagemap.h>
> +#include <linux/sched.h>
> +#include <linux/slab.h>
> +#include <linux/vmalloc.h>
> +#include <linux/module.h>
> +#include <media/v4l2-dev.h>
> +#include <media/v4l2-ioctl.h>
> +#include <plat/omap-pm.h>
> +
> +#include "iss_video.h"
> +#include "iss.h"
> +
> +
> +/* -----------------------------------------------------------------------------
> + * Helper functions
> + */
> +
> +static struct iss_format_info formats[] = {
> +	{ V4L2_MBUS_FMT_Y8_1X8, V4L2_MBUS_FMT_Y8_1X8,
> +	  V4L2_MBUS_FMT_Y8_1X8, V4L2_MBUS_FMT_Y8_1X8,
> +	  V4L2_PIX_FMT_GREY, 8, },
> +	{ V4L2_MBUS_FMT_Y10_1X10, V4L2_MBUS_FMT_Y10_1X10,
> +	  V4L2_MBUS_FMT_Y10_1X10, V4L2_MBUS_FMT_Y8_1X8,
> +	  V4L2_PIX_FMT_Y10, 10, },
> +	{ V4L2_MBUS_FMT_Y12_1X12, V4L2_MBUS_FMT_Y10_1X10,
> +	  V4L2_MBUS_FMT_Y12_1X12, V4L2_MBUS_FMT_Y8_1X8,
> +	  V4L2_PIX_FMT_Y12, 12, },
> +	{ V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_MBUS_FMT_SBGGR8_1X8,
> +	  V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_MBUS_FMT_SBGGR8_1X8,
> +	  V4L2_PIX_FMT_SBGGR8, 8, },
> +	{ V4L2_MBUS_FMT_SGBRG8_1X8, V4L2_MBUS_FMT_SGBRG8_1X8,
> +	  V4L2_MBUS_FMT_SGBRG8_1X8, V4L2_MBUS_FMT_SGBRG8_1X8,
> +	  V4L2_PIX_FMT_SGBRG8, 8, },
> +	{ V4L2_MBUS_FMT_SGRBG8_1X8, V4L2_MBUS_FMT_SGRBG8_1X8,
> +	  V4L2_MBUS_FMT_SGRBG8_1X8, V4L2_MBUS_FMT_SGRBG8_1X8,
> +	  V4L2_PIX_FMT_SGRBG8, 8, },
> +	{ V4L2_MBUS_FMT_SRGGB8_1X8, V4L2_MBUS_FMT_SRGGB8_1X8,
> +	  V4L2_MBUS_FMT_SRGGB8_1X8, V4L2_MBUS_FMT_SRGGB8_1X8,
> +	  V4L2_PIX_FMT_SRGGB8, 8, },
> +	{ V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8,
> +	  V4L2_MBUS_FMT_SGRBG10_1X10, 0,
> +	  V4L2_PIX_FMT_SGRBG10DPCM8, 8, },
> +	{ V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_MBUS_FMT_SBGGR10_1X10,
> +	  V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_MBUS_FMT_SBGGR8_1X8,
> +	  V4L2_PIX_FMT_SBGGR10, 10, },
> +	{ V4L2_MBUS_FMT_SGBRG10_1X10, V4L2_MBUS_FMT_SGBRG10_1X10,
> +	  V4L2_MBUS_FMT_SGBRG10_1X10, V4L2_MBUS_FMT_SGBRG8_1X8,
> +	  V4L2_PIX_FMT_SGBRG10, 10, },
> +	{ V4L2_MBUS_FMT_SGRBG10_1X10, V4L2_MBUS_FMT_SGRBG10_1X10,
> +	  V4L2_MBUS_FMT_SGRBG10_1X10, V4L2_MBUS_FMT_SGRBG8_1X8,
> +	  V4L2_PIX_FMT_SGRBG10, 10, },
> +	{ V4L2_MBUS_FMT_SRGGB10_1X10, V4L2_MBUS_FMT_SRGGB10_1X10,
> +	  V4L2_MBUS_FMT_SRGGB10_1X10, V4L2_MBUS_FMT_SRGGB8_1X8,
> +	  V4L2_PIX_FMT_SRGGB10, 10, },
> +	{ V4L2_MBUS_FMT_SBGGR12_1X12, V4L2_MBUS_FMT_SBGGR10_1X10,
> +	  V4L2_MBUS_FMT_SBGGR12_1X12, V4L2_MBUS_FMT_SBGGR8_1X8,
> +	  V4L2_PIX_FMT_SBGGR12, 12, },
> +	{ V4L2_MBUS_FMT_SGBRG12_1X12, V4L2_MBUS_FMT_SGBRG10_1X10,
> +	  V4L2_MBUS_FMT_SGBRG12_1X12, V4L2_MBUS_FMT_SGBRG8_1X8,
> +	  V4L2_PIX_FMT_SGBRG12, 12, },
> +	{ V4L2_MBUS_FMT_SGRBG12_1X12, V4L2_MBUS_FMT_SGRBG10_1X10,
> +	  V4L2_MBUS_FMT_SGRBG12_1X12, V4L2_MBUS_FMT_SGRBG8_1X8,
> +	  V4L2_PIX_FMT_SGRBG12, 12, },
> +	{ V4L2_MBUS_FMT_SRGGB12_1X12, V4L2_MBUS_FMT_SRGGB10_1X10,
> +	  V4L2_MBUS_FMT_SRGGB12_1X12, V4L2_MBUS_FMT_SRGGB8_1X8,
> +	  V4L2_PIX_FMT_SRGGB12, 12, },
> +	{ V4L2_MBUS_FMT_UYVY8_1X16, V4L2_MBUS_FMT_UYVY8_1X16,
> +	  V4L2_MBUS_FMT_UYVY8_1X16, 0,
> +	  V4L2_PIX_FMT_UYVY, 16, },
> +	{ V4L2_MBUS_FMT_YUYV8_1X16, V4L2_MBUS_FMT_YUYV8_1X16,
> +	  V4L2_MBUS_FMT_YUYV8_1X16, 0,
> +	  V4L2_PIX_FMT_YUYV, 16, },
> +};
> +
> +const struct iss_format_info *
> +omap4iss_video_format_info(enum v4l2_mbus_pixelcode code)
> +{
> +	unsigned int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(formats); ++i) {
> +		if (formats[i].code == code)
> +			return &formats[i];
> +	}
> +
> +	return NULL;
> +}
> +
> +/*
> + * iss_video_mbus_to_pix - Convert v4l2_mbus_framefmt to v4l2_pix_format
> + * @video: ISS video instance
> + * @mbus: v4l2_mbus_framefmt format (input)
> + * @pix: v4l2_pix_format format (output)
> + *
> + * Fill the output pix structure with information from the input mbus format.
> + * The bytesperline and sizeimage fields are computed from the requested bytes
> + * per line value in the pix format and information from the video instance.
> + *
> + * Return the number of padding bytes at end of line.
> + */
> +static unsigned int iss_video_mbus_to_pix(const struct iss_video *video,
> +					  const struct v4l2_mbus_framefmt *mbus,
> +					  struct v4l2_pix_format *pix)
> +{
> +	unsigned int bpl = pix->bytesperline;
> +	unsigned int min_bpl;
> +	unsigned int i;
> +
> +	memset(pix, 0, sizeof(*pix));
> +	pix->width = mbus->width;
> +	pix->height = mbus->height;
> +
> +	for (i = 0; i < ARRAY_SIZE(formats); ++i) {
> +		if (formats[i].code == mbus->code)
> +			break;
> +	}
> +
> +	if (WARN_ON(i == ARRAY_SIZE(formats)))
> +		return 0;
> +
> +	min_bpl = pix->width * ALIGN(formats[i].bpp, 8) / 8;
> +
> +	/* Clamp the requested bytes per line value. If the maximum bytes per
> +	 * line value is zero, the module doesn't support user configurable line
> +	 * sizes. Override the requested value with the minimum in that case.
> +	 */
> +	if (video->bpl_max)
> +		bpl = clamp(bpl, min_bpl, video->bpl_max);
> +	else
> +		bpl = min_bpl;
> +
> +	if (!video->bpl_zero_padding || bpl != min_bpl)
> +		bpl = ALIGN(bpl, video->bpl_alignment);
> +
> +	pix->pixelformat = formats[i].pixelformat;
> +	pix->bytesperline = bpl;
> +	pix->sizeimage = pix->bytesperline * pix->height;
> +	pix->colorspace = mbus->colorspace;
> +	pix->field = mbus->field;
> +
> +	return bpl - min_bpl;
> +}
> +
> +static void iss_video_pix_to_mbus(const struct v4l2_pix_format *pix,
> +				  struct v4l2_mbus_framefmt *mbus)
> +{
> +	unsigned int i;
> +
> +	memset(mbus, 0, sizeof(*mbus));
> +	mbus->width = pix->width;
> +	mbus->height = pix->height;
> +
> +	for (i = 0; i < ARRAY_SIZE(formats); ++i) {
> +		if (formats[i].pixelformat == pix->pixelformat)
> +			break;
> +	}
> +
> +	if (WARN_ON(i == ARRAY_SIZE(formats)))
> +		return;
> +
> +	mbus->code = formats[i].code;
> +	mbus->colorspace = pix->colorspace;
> +	mbus->field = pix->field;
> +}
> +
> +static struct v4l2_subdev *
> +iss_video_remote_subdev(struct iss_video *video, u32 *pad)
> +{
> +	struct media_pad *remote;
> +
> +	remote = media_entity_remote_source(&video->pad);
> +
> +	if (remote == NULL ||
> +	    media_entity_type(remote->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
> +		return NULL;
> +
> +	if (pad)
> +		*pad = remote->index;
> +
> +	return media_entity_to_v4l2_subdev(remote->entity);
> +}
> +
> +/* Return a pointer to the ISS video instance at the far end of the pipeline. */
> +static struct iss_video *
> +iss_video_far_end(struct iss_video *video)
> +{
> +	struct media_entity_graph graph;
> +	struct media_entity *entity = &video->video.entity;
> +	struct media_device *mdev = entity->parent;
> +	struct iss_video *far_end = NULL;
> +
> +	mutex_lock(&mdev->graph_mutex);
> +	media_entity_graph_walk_start(&graph, entity);
> +
> +	while ((entity = media_entity_graph_walk_next(&graph))) {
> +		if (entity == &video->video.entity)
> +			continue;
> +
> +		if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE)
> +			continue;
> +
> +		far_end = to_iss_video(media_entity_to_video_device(entity));
> +		if (far_end->type != video->type)
> +			break;
> +
> +		far_end = NULL;
> +	}
> +
> +	mutex_unlock(&mdev->graph_mutex);
> +	return far_end;
> +}
> +
> +/*
> + * Validate a pipeline by checking both ends of all links for format
> + * discrepancies.
> + *
> + * Compute the minimum time per frame value as the maximum of time per frame
> + * limits reported by every block in the pipeline.
> + *
> + * Return 0 if all formats match, or -EPIPE if at least one link is found with
> + * different formats on its two ends.
> + */
> +static int iss_video_validate_pipeline(struct iss_pipeline *pipe)
> +{
> +	struct v4l2_subdev_format fmt_source;
> +	struct v4l2_subdev_format fmt_sink;
> +	struct media_pad *pad;
> +	struct v4l2_subdev *subdev;
> +	int ret;
> +
> +	subdev = iss_video_remote_subdev(pipe->output, NULL);
> +	if (subdev == NULL)
> +		return -EPIPE;
> +
> +	while (1) {
> +		/* Retrieve the sink format */
> +		pad = &subdev->entity.pads[0];
> +		if (!(pad->flags & MEDIA_PAD_FL_SINK))
> +			break;
> +
> +		fmt_sink.pad = pad->index;
> +		fmt_sink.which = V4L2_SUBDEV_FORMAT_ACTIVE;
> +		ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt_sink);
> +		if (ret < 0 && ret != -ENOIOCTLCMD)
> +			return -EPIPE;
> +
> +		/* Retrieve the source format */
> +		pad = media_entity_remote_source(pad);
> +		if (pad == NULL ||
> +		    media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
> +			break;
> +
> +		subdev = media_entity_to_v4l2_subdev(pad->entity);
> +
> +		fmt_source.pad = pad->index;
> +		fmt_source.which = V4L2_SUBDEV_FORMAT_ACTIVE;
> +		ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt_source);
> +		if (ret < 0 && ret != -ENOIOCTLCMD)
> +			return -EPIPE;
> +
> +		/* Check if the two ends match */
> +		if (fmt_source.format.width != fmt_sink.format.width ||
> +		    fmt_source.format.height != fmt_sink.format.height)
> +			return -EPIPE;
> +
> +		if (fmt_source.format.code != fmt_sink.format.code)
> +			return -EPIPE;
> +	}
> +
> +	return 0;
> +}
> +
> +static int
> +__iss_video_get_format(struct iss_video *video, struct v4l2_format *format)
> +{
> +	struct v4l2_subdev_format fmt;
> +	struct v4l2_subdev *subdev;
> +	u32 pad;
> +	int ret;
> +
> +	subdev = iss_video_remote_subdev(video, &pad);
> +	if (subdev == NULL)
> +		return -EINVAL;
> +
> +	mutex_lock(&video->mutex);
> +
> +	fmt.pad = pad;
> +	fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
> +	ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt);
> +	if (ret == -ENOIOCTLCMD)
> +		ret = -EINVAL;
> +
> +	mutex_unlock(&video->mutex);
> +
> +	if (ret)
> +		return ret;
> +
> +	format->type = video->type;
> +	return iss_video_mbus_to_pix(video, &fmt.format, &format->fmt.pix);
> +}
> +
> +static int
> +iss_video_check_format(struct iss_video *video, struct iss_video_fh *vfh)
> +{
> +	struct v4l2_format format;
> +	int ret;
> +
> +	memcpy(&format, &vfh->format, sizeof(format));
> +	ret = __iss_video_get_format(video, &format);
> +	if (ret < 0)
> +		return ret;
> +
> +	if (vfh->format.fmt.pix.pixelformat != format.fmt.pix.pixelformat ||
> +	    vfh->format.fmt.pix.height != format.fmt.pix.height ||
> +	    vfh->format.fmt.pix.width != format.fmt.pix.width ||
> +	    vfh->format.fmt.pix.bytesperline != format.fmt.pix.bytesperline ||
> +	    vfh->format.fmt.pix.sizeimage != format.fmt.pix.sizeimage)
> +		return -EINVAL;
> +
> +	return ret;
> +}
> +
> +/* -----------------------------------------------------------------------------
> + * Video queue operations
> + */
> +
> +static int iss_video_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
> +				 unsigned int *count, unsigned int *num_planes,
> +				 unsigned int sizes[], void *alloc_ctxs[])
> +{
> +	struct iss_video_fh *vfh = vb2_get_drv_priv(vq);
> +	struct iss_video *video = vfh->video;
> +
> +	/* Revisit multi-planar support for NV12 */
> +	*num_planes = 1;
> +
> +	sizes[0] = vfh->format.fmt.pix.sizeimage;
> +	if (sizes[0] == 0)
> +		return -EINVAL;
> +
> +	alloc_ctxs[0] = video->alloc_ctx;
> +
> +	*count = min(*count, (unsigned int)(video->capture_mem / PAGE_ALIGN(sizes[0])));
> +
> +	return 0;
> +}
> +
> +static void iss_video_buf_cleanup(struct vb2_buffer *vb)
> +{
> +	struct iss_buffer *buffer = container_of(vb, struct iss_buffer, vb);
> +
> +	if (buffer->iss_addr)
> +		buffer->iss_addr = 0;
> +}
> +
> +static int iss_video_buf_prepare(struct vb2_buffer *vb)
> +{
> +	struct iss_video_fh *vfh = vb2_get_drv_priv(vb->vb2_queue);
> +	struct iss_buffer *buffer = container_of(vb, struct iss_buffer, vb);
> +	struct iss_video *video = vfh->video;
> +	unsigned long size = vfh->format.fmt.pix.sizeimage;
> +	dma_addr_t addr;
> +
> +	if (vb2_plane_size(vb, 0) < size)
> +		return -ENOBUFS;
> +
> +	addr = vb2_dma_contig_plane_dma_addr(vb, 0);
> +	if (!IS_ALIGNED(addr, 32)) {
> +		dev_dbg(video->iss->dev, "Buffer address must be "
> +			"aligned to 32 bytes boundary.\n");
> +		return -EINVAL;
> +	}
> +
> +	vb2_set_plane_payload(vb, 0, size);
> +	buffer->iss_addr = addr;
> +	return 0;
> +}
> +
> +static void iss_video_buf_queue(struct vb2_buffer *vb)
> +{
> +	struct iss_video_fh *vfh = vb2_get_drv_priv(vb->vb2_queue);
> +	struct iss_video *video = vfh->video;
> +	struct iss_buffer *buffer = container_of(vb, struct iss_buffer, vb);
> +	struct iss_pipeline *pipe = to_iss_pipeline(&video->video.entity);
> +	unsigned int empty;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&video->qlock, flags);
> +	empty = list_empty(&video->dmaqueue);
> +	list_add_tail(&buffer->list, &video->dmaqueue);
> +	spin_unlock_irqrestore(&video->qlock, flags);
> +
> +	if (empty) {
> +		enum iss_pipeline_state state;
> +		unsigned int start;
> +
> +		if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
> +			state = ISS_PIPELINE_QUEUE_OUTPUT;
> +		else
> +			state = ISS_PIPELINE_QUEUE_INPUT;
> +
> +		spin_lock_irqsave(&pipe->lock, flags);
> +		pipe->state |= state;
> +		video->ops->queue(video, buffer);
> +		video->dmaqueue_flags |= ISS_VIDEO_DMAQUEUE_QUEUED;
> +
> +		start = iss_pipeline_ready(pipe);
> +		if (start)
> +			pipe->state |= ISS_PIPELINE_STREAM;
> +		spin_unlock_irqrestore(&pipe->lock, flags);
> +
> +		if (start)
> +			omap4iss_pipeline_set_stream(pipe,
> +						ISS_PIPELINE_STREAM_SINGLESHOT);
> +	}
> +}
> +
> +static struct vb2_ops iss_video_vb2ops = {
> +	.queue_setup	= iss_video_queue_setup,
> +	.buf_prepare	= iss_video_buf_prepare,
> +	.buf_queue	= iss_video_buf_queue,
> +	.buf_cleanup	= iss_video_buf_cleanup,
> +};
> +
> +/*
> + * omap4iss_video_buffer_next - Complete the current buffer and return the next
> + * @video: ISS video object
> + * @error: Whether an error occurred during capture
> + *
> + * Remove the current video buffer from the DMA queue and fill its timestamp,
> + * field count and state fields before waking up its completion handler.
> + *
> + * The buffer state is set to VIDEOBUF_DONE if no error occurred (@error is 0)
> + * or VIDEOBUF_ERROR otherwise (@error is non-zero).
> + *
> + * The DMA queue is expected to contain at least one buffer.
> + *
> + * Return a pointer to the next buffer in the DMA queue, or NULL if the queue is
> + * empty.
> + */
> +struct iss_buffer *omap4iss_video_buffer_next(struct iss_video *video,
> +					      unsigned int error)
> +{
> +	struct iss_pipeline *pipe = to_iss_pipeline(&video->video.entity);
> +	enum iss_pipeline_state state;
> +	struct iss_buffer *buf;
> +	unsigned long flags;
> +	struct timespec ts;
> +
> +	spin_lock_irqsave(&video->qlock, flags);
> +	if (WARN_ON(list_empty(&video->dmaqueue))) {
> +		spin_unlock_irqrestore(&video->qlock, flags);
> +		return NULL;
> +	}
> +
> +	buf = list_first_entry(&video->dmaqueue, struct iss_buffer,
> +			       list);
> +	list_del(&buf->list);
> +	spin_unlock_irqrestore(&video->qlock, flags);
> +
> +	ktime_get_ts(&ts);
> +	buf->vb.v4l2_buf.timestamp.tv_sec = ts.tv_sec;
> +	buf->vb.v4l2_buf.timestamp.tv_usec = ts.tv_nsec / NSEC_PER_USEC;
> +
> +	/* Do frame number propagation only if this is the output video node.
> +	 * Frame number either comes from the CSI receivers or it gets
> +	 * incremented here if H3A is not active.
> +	 * Note: There is no guarantee that the output buffer will finish
> +	 * first, so the input number might lag behind by 1 in some cases.
> +	 */
> +	if (video == pipe->output && !pipe->do_propagation)
> +		buf->vb.v4l2_buf.sequence = atomic_inc_return(&pipe->frame_number);
> +	else
> +		buf->vb.v4l2_buf.sequence = atomic_read(&pipe->frame_number);
> +
> +	vb2_buffer_done(&buf->vb, error < 0 ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
> +
> +	spin_lock_irqsave(&video->qlock, flags);
> +	if (list_empty(&video->dmaqueue)) {
> +		spin_unlock_irqrestore(&video->qlock, flags);
> +		if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
> +			state = ISS_PIPELINE_QUEUE_OUTPUT
> +			      | ISS_PIPELINE_STREAM;
> +		else
> +			state = ISS_PIPELINE_QUEUE_INPUT
> +			      | ISS_PIPELINE_STREAM;
> +
> +		spin_lock_irqsave(&pipe->lock, flags);
> +		pipe->state &= ~state;
> +		if (video->pipe.stream_state == ISS_PIPELINE_STREAM_CONTINUOUS)
> +			video->dmaqueue_flags |= ISS_VIDEO_DMAQUEUE_UNDERRUN;
> +		spin_unlock_irqrestore(&pipe->lock, flags);
> +		return NULL;
> +	}
> +
> +	if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && pipe->input != NULL) {
> +		spin_lock_irqsave(&pipe->lock, flags);
> +		pipe->state &= ~ISS_PIPELINE_STREAM;
> +		spin_unlock_irqrestore(&pipe->lock, flags);
> +	}
> +
> +	buf = list_first_entry(&video->dmaqueue, struct iss_buffer,
> +			       list);
> +	spin_unlock_irqrestore(&video->qlock, flags);
> +	buf->vb.state = VB2_BUF_STATE_ACTIVE;
> +	return buf;
> +}
> +
> +/* -----------------------------------------------------------------------------
> + * V4L2 ioctls
> + */
> +
> +static int
> +iss_video_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
> +{
> +	struct iss_video *video = video_drvdata(file);
> +
> +	strlcpy(cap->driver, ISS_VIDEO_DRIVER_NAME, sizeof(cap->driver));
> +	strlcpy(cap->card, video->video.name, sizeof(cap->card));
> +	strlcpy(cap->bus_info, "media", sizeof(cap->bus_info));
> +	cap->version = ISS_VIDEO_DRIVER_VERSION;
> +
> +	if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
> +		cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
> +	else
> +		cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
> +
> +	return 0;
> +}
> +
> +static int
> +iss_video_get_format(struct file *file, void *fh, struct v4l2_format *format)
> +{
> +	struct iss_video_fh *vfh = to_iss_video_fh(fh);
> +	struct iss_video *video = video_drvdata(file);
> +
> +	if (format->type != video->type)
> +		return -EINVAL;
> +
> +	mutex_lock(&video->mutex);
> +	*format = vfh->format;
> +	mutex_unlock(&video->mutex);
> +
> +	return 0;
> +}
> +
> +static int
> +iss_video_set_format(struct file *file, void *fh, struct v4l2_format *format)
> +{
> +	struct iss_video_fh *vfh = to_iss_video_fh(fh);
> +	struct iss_video *video = video_drvdata(file);
> +	struct v4l2_mbus_framefmt fmt;
> +
> +	if (format->type != video->type)
> +		return -EINVAL;
> +
> +	mutex_lock(&video->mutex);
> +
> +	/* Fill the bytesperline and sizeimage fields by converting to media bus
> +	 * format and back to pixel format.
> +	 */
> +	iss_video_pix_to_mbus(&format->fmt.pix, &fmt);
> +	iss_video_mbus_to_pix(video, &fmt, &format->fmt.pix);
> +
> +	vfh->format = *format;
> +
> +	mutex_unlock(&video->mutex);
> +	return 0;
> +}
> +
> +static int
> +iss_video_try_format(struct file *file, void *fh, struct v4l2_format *format)
> +{
> +	struct iss_video *video = video_drvdata(file);
> +	struct v4l2_subdev_format fmt;
> +	struct v4l2_subdev *subdev;
> +	u32 pad;
> +	int ret;
> +
> +	if (format->type != video->type)
> +		return -EINVAL;
> +
> +	subdev = iss_video_remote_subdev(video, &pad);
> +	if (subdev == NULL)
> +		return -EINVAL;
> +
> +	iss_video_pix_to_mbus(&format->fmt.pix, &fmt.format);
> +
> +	fmt.pad = pad;
> +	fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
> +	ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt);
> +	if (ret)
> +		return ret == -ENOIOCTLCMD ? -EINVAL : ret;
> +
> +	iss_video_mbus_to_pix(video, &fmt.format, &format->fmt.pix);
> +	return 0;
> +}
> +
> +static int
> +iss_video_cropcap(struct file *file, void *fh, struct v4l2_cropcap *cropcap)
> +{
> +	struct iss_video *video = video_drvdata(file);
> +	struct v4l2_subdev *subdev;
> +	int ret;
> +
> +	subdev = iss_video_remote_subdev(video, NULL);
> +	if (subdev == NULL)
> +		return -EINVAL;
> +
> +	mutex_lock(&video->mutex);
> +	ret = v4l2_subdev_call(subdev, video, cropcap, cropcap);
> +	mutex_unlock(&video->mutex);
> +
> +	return ret == -ENOIOCTLCMD ? -EINVAL : ret;
> +}
> +
> +static int
> +iss_video_get_crop(struct file *file, void *fh, struct v4l2_crop *crop)
> +{
> +	struct iss_video *video = video_drvdata(file);
> +	struct v4l2_subdev_format format;
> +	struct v4l2_subdev *subdev;
> +	u32 pad;
> +	int ret;
> +
> +	subdev = iss_video_remote_subdev(video, &pad);
> +	if (subdev == NULL)
> +		return -EINVAL;
> +
> +	/* Try the get crop operation first and fallback to get format if not
> +	 * implemented.
> +	 */
> +	ret = v4l2_subdev_call(subdev, video, g_crop, crop);
> +	if (ret != -ENOIOCTLCMD)
> +		return ret;
> +
> +	format.pad = pad;
> +	format.which = V4L2_SUBDEV_FORMAT_ACTIVE;
> +	ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &format);
> +	if (ret < 0)
> +		return ret == -ENOIOCTLCMD ? -EINVAL : ret;
> +
> +	crop->c.left = 0;
> +	crop->c.top = 0;
> +	crop->c.width = format.format.width;
> +	crop->c.height = format.format.height;
> +
> +	return 0;
> +}
> +
> +static int
> +iss_video_set_crop(struct file *file, void *fh, struct v4l2_crop *crop)
> +{
> +	struct iss_video *video = video_drvdata(file);
> +	struct v4l2_subdev *subdev;
> +	int ret;
> +
> +	subdev = iss_video_remote_subdev(video, NULL);
> +	if (subdev == NULL)
> +		return -EINVAL;
> +
> +	mutex_lock(&video->mutex);
> +	ret = v4l2_subdev_call(subdev, video, s_crop, crop);
> +	mutex_unlock(&video->mutex);
> +
> +	return ret == -ENOIOCTLCMD ? -EINVAL : ret;
> +}
> +
> +static int
> +iss_video_get_param(struct file *file, void *fh, struct v4l2_streamparm *a)
> +{
> +	struct iss_video_fh *vfh = to_iss_video_fh(fh);
> +	struct iss_video *video = video_drvdata(file);
> +
> +	if (video->type != V4L2_BUF_TYPE_VIDEO_OUTPUT ||
> +	    video->type != a->type)
> +		return -EINVAL;
> +
> +	memset(a, 0, sizeof(*a));
> +	a->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
> +	a->parm.output.capability = V4L2_CAP_TIMEPERFRAME;
> +	a->parm.output.timeperframe = vfh->timeperframe;
> +
> +	return 0;
> +}
> +
> +static int
> +iss_video_set_param(struct file *file, void *fh, struct v4l2_streamparm *a)
> +{
> +	struct iss_video_fh *vfh = to_iss_video_fh(fh);
> +	struct iss_video *video = video_drvdata(file);
> +
> +	if (video->type != V4L2_BUF_TYPE_VIDEO_OUTPUT ||
> +	    video->type != a->type)
> +		return -EINVAL;
> +
> +	if (a->parm.output.timeperframe.denominator == 0)
> +		a->parm.output.timeperframe.denominator = 1;
> +
> +	vfh->timeperframe = a->parm.output.timeperframe;
> +
> +	return 0;
> +}
> +
> +static int
> +iss_video_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *rb)
> +{
> +	struct iss_video_fh *vfh = to_iss_video_fh(fh);
> +
> +	return vb2_reqbufs(&vfh->queue, rb);
> +}
> +
> +static int
> +iss_video_querybuf(struct file *file, void *fh, struct v4l2_buffer *b)
> +{
> +	struct iss_video_fh *vfh = to_iss_video_fh(fh);
> +
> +	return vb2_querybuf(&vfh->queue, b);
> +}
> +
> +static int
> +iss_video_qbuf(struct file *file, void *fh, struct v4l2_buffer *b)
> +{
> +	struct iss_video_fh *vfh = to_iss_video_fh(fh);
> +
> +	return vb2_qbuf(&vfh->queue, b);
> +}
> +
> +static int
> +iss_video_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b)
> +{
> +	struct iss_video_fh *vfh = to_iss_video_fh(fh);
> +
> +	return vb2_dqbuf(&vfh->queue, b, file->f_flags & O_NONBLOCK);
> +}
> +
> +/*
> + * Stream management
> + *
> + * Every ISS pipeline has a single input and a single output. The input can be
> + * either a sensor or a video node. The output is always a video node.
> + *
> + * As every pipeline has an output video node, the ISS video objects at the
> + * pipeline output stores the pipeline state. It tracks the streaming state of
> + * both the input and output, as well as the availability of buffers.
> + *
> + * In sensor-to-memory mode, frames are always available at the pipeline input.
> + * Starting the sensor usually requires I2C transfers and must be done in
> + * interruptible context. The pipeline is started and stopped synchronously
> + * to the stream on/off commands. All modules in the pipeline will get their
> + * subdev set stream handler called. The module at the end of the pipeline must
> + * delay starting the hardware until buffers are available at its output.
> + *
> + * In memory-to-memory mode, starting/stopping the stream requires
> + * synchronization between the input and output. ISS modules can't be stopped
> + * in the middle of a frame, and at least some of the modules seem to become
> + * busy as soon as they're started, even if they don't receive a frame start
> + * event. For that reason frames need to be processed in single-shot mode. The
> + * driver needs to wait until a frame is completely processed and written to
> + * memory before restarting the pipeline for the next frame. Pipelined
> + * processing might be possible but requires more testing.
> + *
> + * Stream start must be delayed until buffers are available at both the input
> + * and output. The pipeline must be started in the videobuf queue callback with
> + * the buffers queue spinlock held. The modules subdev set stream operation must
> + * not sleep.
> + */
> +static int
> +iss_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type)
> +{
> +	struct iss_video_fh *vfh = to_iss_video_fh(fh);
> +	struct iss_video *video = video_drvdata(file);
> +	enum iss_pipeline_state state;
> +	struct iss_pipeline *pipe;
> +	struct iss_video *far_end;
> +	unsigned long flags;
> +	int ret;
> +
> +	if (type != video->type)
> +		return -EINVAL;
> +
> +	mutex_lock(&video->stream_lock);
> +
> +	if (video->streaming) {
> +		mutex_unlock(&video->stream_lock);
> +		return -EBUSY;
> +	}
> +
> +	/* Start streaming on the pipeline. No link touching an entity in the
> +	 * pipeline can be activated or deactivated once streaming is started.
> +	 */
> +	pipe = video->video.entity.pipe
> +	     ? to_iss_pipeline(&video->video.entity) : &video->pipe;
> +	media_entity_pipeline_start(&video->video.entity, &pipe->pipe);
> +
> +	/* Verify that the currently configured format matches the output of
> +	 * the connected subdev.
> +	 */
> +	ret = iss_video_check_format(video, vfh);
> +	if (ret < 0)
> +		goto error;
> +
> +	video->bpl_padding = ret;
> +	video->bpl_value = vfh->format.fmt.pix.bytesperline;
> +
> +	/* Find the ISS video node connected at the far end of the pipeline and
> +	 * update the pipeline.
> +	 */
> +	far_end = iss_video_far_end(video);
> +
> +	if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
> +		state = ISS_PIPELINE_STREAM_OUTPUT | ISS_PIPELINE_IDLE_OUTPUT;
> +		pipe->input = far_end;
> +		pipe->output = video;
> +	} else {
> +		if (far_end == NULL) {
> +			ret = -EPIPE;
> +			goto error;
> +		}
> +
> +		state = ISS_PIPELINE_STREAM_INPUT | ISS_PIPELINE_IDLE_INPUT;
> +		pipe->input = video;
> +		pipe->output = far_end;
> +	}
> +
> +	if (video->iss->pdata->set_constraints)
> +		video->iss->pdata->set_constraints(video->iss, true);
> +
> +	/* Validate the pipeline and update its state. */
> +	ret = iss_video_validate_pipeline(pipe);
> +	if (ret < 0)
> +		goto error;
> +
> +	spin_lock_irqsave(&pipe->lock, flags);
> +	pipe->state &= ~ISS_PIPELINE_STREAM;
> +	pipe->state |= state;
> +	spin_unlock_irqrestore(&pipe->lock, flags);
> +
> +	/* Set the maximum time per frame as the value requested by userspace.
> +	 * This is a soft limit that can be overridden if the hardware doesn't
> +	 * support the request limit.
> +	 */
> +	if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
> +		pipe->max_timeperframe = vfh->timeperframe;
> +
> +	video->queue = &vfh->queue;
> +	INIT_LIST_HEAD(&video->dmaqueue);
> +	spin_lock_init(&video->qlock);
> +	atomic_set(&pipe->frame_number, -1);
> +
> +	ret = vb2_streamon(&vfh->queue, type);
> +	if (ret < 0)
> +		goto error;
> +
> +	/* In sensor-to-memory mode, the stream can be started synchronously
> +	 * to the stream on command. In memory-to-memory mode, it will be
> +	 * started when buffers are queued on both the input and output.
> +	 */
> +	if (pipe->input == NULL) {
> +		unsigned long flags;
> +		ret = omap4iss_pipeline_set_stream(pipe,
> +					      ISS_PIPELINE_STREAM_CONTINUOUS);
> +		if (ret < 0)
> +			goto error;
> +		spin_lock_irqsave(&video->qlock, flags);
> +		if (list_empty(&video->dmaqueue))
> +			video->dmaqueue_flags |= ISS_VIDEO_DMAQUEUE_UNDERRUN;
> +		spin_unlock_irqrestore(&video->qlock, flags);
> +	}
> +
> +error:
> +	if (ret < 0) {
> +		vb2_streamoff(&vfh->queue, type);
> +		if (video->iss->pdata->set_constraints)
> +			video->iss->pdata->set_constraints(video->iss, false);
> +		media_entity_pipeline_stop(&video->video.entity);
> +		video->queue = NULL;
> +	}
> +
> +	if (!ret)
> +		video->streaming = 1;
> +
> +	mutex_unlock(&video->stream_lock);
> +	return ret;
> +}
> +
> +static int
> +iss_video_streamoff(struct file *file, void *fh, enum v4l2_buf_type type)
> +{
> +	struct iss_video_fh *vfh = to_iss_video_fh(fh);
> +	struct iss_video *video = video_drvdata(file);
> +	struct iss_pipeline *pipe = to_iss_pipeline(&video->video.entity);
> +	enum iss_pipeline_state state;
> +	unsigned long flags;
> +
> +	if (type != video->type)
> +		return -EINVAL;
> +
> +	mutex_lock(&video->stream_lock);
> +
> +	if (!vb2_is_streaming(&vfh->queue))
> +		goto done;
> +
> +	/* Update the pipeline state. */
> +	if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
> +		state = ISS_PIPELINE_STREAM_OUTPUT
> +		      | ISS_PIPELINE_QUEUE_OUTPUT;
> +	else
> +		state = ISS_PIPELINE_STREAM_INPUT
> +		      | ISS_PIPELINE_QUEUE_INPUT;
> +
> +	spin_lock_irqsave(&pipe->lock, flags);
> +	pipe->state &= ~state;
> +	spin_unlock_irqrestore(&pipe->lock, flags);
> +
> +	/* Stop the stream. */
> +	omap4iss_pipeline_set_stream(pipe, ISS_PIPELINE_STREAM_STOPPED);
> +	vb2_streamoff(&vfh->queue, type);
> +	video->queue = NULL;
> +	video->streaming = 0;
> +
> +	if (video->iss->pdata->set_constraints)
> +		video->iss->pdata->set_constraints(video->iss, false);
> +	media_entity_pipeline_stop(&video->video.entity);
> +
> +done:
> +	mutex_unlock(&video->stream_lock);
> +	return 0;
> +}
> +
> +static int
> +iss_video_enum_input(struct file *file, void *fh, struct v4l2_input *input)
> +{
> +	if (input->index > 0)
> +		return -EINVAL;
> +
> +	strlcpy(input->name, "camera", sizeof(input->name));
> +	input->type = V4L2_INPUT_TYPE_CAMERA;
> +
> +	return 0;
> +}
> +
> +static int
> +iss_video_g_input(struct file *file, void *fh, unsigned int *input)
> +{
> +	*input = 0;
> +
> +	return 0;
> +}
> +
> +static int
> +iss_video_s_input(struct file *file, void *fh, unsigned int input)
> +{
> +	return input == 0 ? 0 : -EINVAL;
> +}
> +
> +static const struct v4l2_ioctl_ops iss_video_ioctl_ops = {
> +	.vidioc_querycap		= iss_video_querycap,
> +	.vidioc_g_fmt_vid_cap		= iss_video_get_format,
> +	.vidioc_s_fmt_vid_cap		= iss_video_set_format,
> +	.vidioc_try_fmt_vid_cap		= iss_video_try_format,
> +	.vidioc_g_fmt_vid_out		= iss_video_get_format,
> +	.vidioc_s_fmt_vid_out		= iss_video_set_format,
> +	.vidioc_try_fmt_vid_out		= iss_video_try_format,
> +	.vidioc_cropcap			= iss_video_cropcap,
> +	.vidioc_g_crop			= iss_video_get_crop,
> +	.vidioc_s_crop			= iss_video_set_crop,
> +	.vidioc_g_parm			= iss_video_get_param,
> +	.vidioc_s_parm			= iss_video_set_param,
> +	.vidioc_reqbufs			= iss_video_reqbufs,
> +	.vidioc_querybuf		= iss_video_querybuf,
> +	.vidioc_qbuf			= iss_video_qbuf,
> +	.vidioc_dqbuf			= iss_video_dqbuf,
> +	.vidioc_streamon		= iss_video_streamon,
> +	.vidioc_streamoff		= iss_video_streamoff,
> +	.vidioc_enum_input		= iss_video_enum_input,
> +	.vidioc_g_input			= iss_video_g_input,
> +	.vidioc_s_input			= iss_video_s_input,
> +};
> +
> +/* -----------------------------------------------------------------------------
> + * V4L2 file operations
> + */
> +
> +static int iss_video_open(struct file *file)
> +{
> +	struct iss_video *video = video_drvdata(file);
> +	struct iss_video_fh *handle;
> +	struct vb2_queue *q;
> +	int ret = 0;
> +
> +	handle = kzalloc(sizeof(*handle), GFP_KERNEL);
> +	if (handle == NULL)
> +		return -ENOMEM;
> +
> +	v4l2_fh_init(&handle->vfh, &video->video);
> +	v4l2_fh_add(&handle->vfh);
> +
> +	/* If this is the first user, initialise the pipeline. */
> +	if (omap4iss_get(video->iss) == NULL) {
> +		ret = -EBUSY;
> +		goto done;
> +	}
> +
> +	ret = omap4iss_pipeline_pm_use(&video->video.entity, 1);
> +	if (ret < 0) {
> +		omap4iss_put(video->iss);
> +		goto done;
> +	}
> +
> +	video->alloc_ctx = vb2_dma_contig_init_ctx(video->iss->dev);
> +	if (IS_ERR(video->alloc_ctx)) {
> +		ret = PTR_ERR(video->alloc_ctx);
> +		omap4iss_put(video->iss);
> +		goto done;
> +	}
> +
> +	q = &handle->queue;
> +
> +	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> +	q->io_modes = VB2_MMAP;
> +	q->drv_priv = handle;
> +	q->ops = &iss_video_vb2ops;
> +	q->mem_ops = &vb2_dma_contig_memops;
> +	q->buf_struct_size = sizeof(struct iss_buffer);
> +
> +	ret = vb2_queue_init(q);
> +	if (ret) {
> +		omap4iss_put(video->iss);
> +		goto done;
> +	}
> +
> +	memset(&handle->format, 0, sizeof(handle->format));
> +	handle->format.type = video->type;
> +	handle->timeperframe.denominator = 1;
> +
> +	handle->video = video;
> +	file->private_data = &handle->vfh;
> +
> +done:
> +	if (ret < 0) {
> +		v4l2_fh_del(&handle->vfh);
> +		kfree(handle);
> +	}
> +
> +	return ret;
> +}
> +
> +static int iss_video_release(struct file *file)
> +{
> +	struct iss_video *video = video_drvdata(file);
> +	struct v4l2_fh *vfh = file->private_data;
> +	struct iss_video_fh *handle = to_iss_video_fh(vfh);
> +
> +	/* Disable streaming and free the buffers queue resources. */
> +	iss_video_streamoff(file, vfh, video->type);
> +
> +	omap4iss_pipeline_pm_use(&video->video.entity, 0);
> +
> +	/* Release the file handle. */
> +	v4l2_fh_del(vfh);
> +	kfree(handle);
> +	file->private_data = NULL;
> +
> +	omap4iss_put(video->iss);
> +
> +	return 0;
> +}
> +
> +static unsigned int iss_video_poll(struct file *file, poll_table *wait)
> +{
> +	struct iss_video_fh *vfh = to_iss_video_fh(file->private_data);
> +
> +	return vb2_poll(&vfh->queue, file, wait);
> +}
> +
> +static int iss_video_mmap(struct file *file, struct vm_area_struct *vma)
> +{
> +	struct iss_video_fh *vfh = to_iss_video_fh(file->private_data);
> +
> +	return vb2_mmap(&vfh->queue, vma);;
> +}
> +
> +static struct v4l2_file_operations iss_video_fops = {
> +	.owner = THIS_MODULE,
> +	.unlocked_ioctl = video_ioctl2,
> +	.open = iss_video_open,
> +	.release = iss_video_release,
> +	.poll = iss_video_poll,
> +	.mmap = iss_video_mmap,
> +};
> +
> +/* -----------------------------------------------------------------------------
> + * ISS video core
> + */
> +
> +static const struct iss_video_operations iss_video_dummy_ops = {
> +};
> +
> +int omap4iss_video_init(struct iss_video *video, const char *name)
> +{
> +	const char *direction;
> +	int ret;
> +
> +	switch (video->type) {
> +	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
> +		direction = "output";
> +		video->pad.flags = MEDIA_PAD_FL_SINK;
> +		break;
> +	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
> +		direction = "input";
> +		video->pad.flags = MEDIA_PAD_FL_SOURCE;
> +		break;
> +
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	ret = media_entity_init(&video->video.entity, 1, &video->pad, 0);
> +	if (ret < 0)
> +		return ret;
> +
> +	mutex_init(&video->mutex);
> +	atomic_set(&video->active, 0);
> +
> +	spin_lock_init(&video->pipe.lock);
> +	mutex_init(&video->stream_lock);
> +
> +	/* Initialize the video device. */
> +	if (video->ops == NULL)
> +		video->ops = &iss_video_dummy_ops;
> +
> +	video->video.fops = &iss_video_fops;
> +	snprintf(video->video.name, sizeof(video->video.name),
> +		 "OMAP4 ISS %s %s", name, direction);
> +	video->video.vfl_type = VFL_TYPE_GRABBER;
> +	video->video.release = video_device_release_empty;
> +	video->video.ioctl_ops = &iss_video_ioctl_ops;
> +	video->pipe.stream_state = ISS_PIPELINE_STREAM_STOPPED;
> +
> +	video_set_drvdata(&video->video, video);
> +
> +	return 0;
> +}
> +
> +int omap4iss_video_register(struct iss_video *video, struct v4l2_device *vdev)
> +{
> +	int ret;
> +
> +	video->video.v4l2_dev = vdev;
> +
> +	ret = video_register_device(&video->video, VFL_TYPE_GRABBER, -1);
> +	if (ret < 0)
> +		printk(KERN_ERR "%s: could not register video device (%d)\n",
> +			__func__, ret);
> +
> +	return ret;
> +}
> +
> +void omap4iss_video_unregister(struct iss_video *video)
> +{
> +	if (video_is_registered(&video->video)) {
> +		media_entity_cleanup(&video->video.entity);
> +		video_unregister_device(&video->video);
> +	}
> +}
> diff --git a/drivers/media/video/omap4iss/iss_video.h b/drivers/media/video/omap4iss/iss_video.h
> new file mode 100644
> index 0000000..fc123b0
> --- /dev/null
> +++ b/drivers/media/video/omap4iss/iss_video.h
> @@ -0,0 +1,205 @@
> +/*
> + * iss_video.h
> + *
> + * TI OMAP4 ISS - Generic video node
> + *
> + * Copyright (C) 2011 Texas Instruments, Inc.
> + *
> + * Contacts: Sergio Aguirre <saaguirre@xxxxxx>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
> + * 02110-1301 USA
> + */
> +
> +#ifndef OMAP4_ISS_VIDEO_H
> +#define OMAP4_ISS_VIDEO_H
> +
> +#include <linux/v4l2-mediabus.h>
> +#include <linux/version.h>
> +#include <media/media-entity.h>
> +#include <media/v4l2-dev.h>
> +#include <media/v4l2-fh.h>
> +#include <media/videobuf2-core.h>
> +#include <media/videobuf2-dma-contig.h>
> +
> +#define ISS_VIDEO_DRIVER_NAME		"issvideo"
> +#define ISS_VIDEO_DRIVER_VERSION	KERNEL_VERSION(0, 0, 1)
> +
> +struct iss_device;
> +struct iss_video;
> +struct v4l2_mbus_framefmt;
> +struct v4l2_pix_format;
> +
> +/*
> + * struct iss_format_info - ISS media bus format information
> + * @code: V4L2 media bus format code
> + * @truncated: V4L2 media bus format code for the same format truncated to 10
> + *	bits. Identical to @code if the format is 10 bits wide or less.
> + * @uncompressed: V4L2 media bus format code for the corresponding uncompressed
> + *	format. Identical to @code if the format is not DPCM compressed.
> + * @flavor: V4L2 media bus format code for the same pixel layout but
> + *	shifted to be 8 bits per pixel. =0 if format is not shiftable.
> + * @pixelformat: V4L2 pixel format FCC identifier
> + * @bpp: Bits per pixel
> + */
> +struct iss_format_info {
> +	enum v4l2_mbus_pixelcode code;
> +	enum v4l2_mbus_pixelcode truncated;
> +	enum v4l2_mbus_pixelcode uncompressed;
> +	enum v4l2_mbus_pixelcode flavor;
> +	u32 pixelformat;
> +	unsigned int bpp;
> +};
> +
> +enum iss_pipeline_stream_state {
> +	ISS_PIPELINE_STREAM_STOPPED = 0,
> +	ISS_PIPELINE_STREAM_CONTINUOUS = 1,
> +	ISS_PIPELINE_STREAM_SINGLESHOT = 2,
> +};
> +
> +enum iss_pipeline_state {
> +	/* The stream has been started on the input video node. */
> +	ISS_PIPELINE_STREAM_INPUT = 1,
> +	/* The stream has been started on the output video node. */
> +	ISS_PIPELINE_STREAM_OUTPUT = 2,
> +	/* At least one buffer is queued on the input video node. */
> +	ISS_PIPELINE_QUEUE_INPUT = 4,
> +	/* At least one buffer is queued on the output video node. */
> +	ISS_PIPELINE_QUEUE_OUTPUT = 8,
> +	/* The input entity is idle, ready to be started. */
> +	ISS_PIPELINE_IDLE_INPUT = 16,
> +	/* The output entity is idle, ready to be started. */
> +	ISS_PIPELINE_IDLE_OUTPUT = 32,
> +	/* The pipeline is currently streaming. */
> +	ISS_PIPELINE_STREAM = 64,
> +};
> +
> +struct iss_pipeline {
> +	struct media_pipeline pipe;
> +	spinlock_t lock;		/* Pipeline state and queue flags */
> +	unsigned int state;
> +	enum iss_pipeline_stream_state stream_state;
> +	struct iss_video *input;
> +	struct iss_video *output;
> +	atomic_t frame_number;
> +	bool do_propagation; /* of frame number */
> +	struct v4l2_fract max_timeperframe;
> +};
> +
> +#define to_iss_pipeline(__e) \
> +	container_of((__e)->pipe, struct iss_pipeline, pipe)
> +
> +static inline int iss_pipeline_ready(struct iss_pipeline *pipe)
> +{
> +	return pipe->state == (ISS_PIPELINE_STREAM_INPUT |
> +			       ISS_PIPELINE_STREAM_OUTPUT |
> +			       ISS_PIPELINE_QUEUE_INPUT |
> +			       ISS_PIPELINE_QUEUE_OUTPUT |
> +			       ISS_PIPELINE_IDLE_INPUT |
> +			       ISS_PIPELINE_IDLE_OUTPUT);
> +}
> +
> +/*
> + * struct iss_buffer - ISS buffer
> + * @buffer: ISS video buffer
> + * @iss_addr: Physical address of the buffer.
> + */
> +struct iss_buffer {
> +	/* common v4l buffer stuff -- must be first */
> +	struct vb2_buffer	vb;
> +	struct list_head	list;
> +	dma_addr_t iss_addr;
> +};
> +
> +#define to_iss_buffer(buf)	container_of(buf, struct iss_buffer, buffer)
> +
> +enum iss_video_dmaqueue_flags {
> +	/* Set if DMA queue becomes empty when ISS_PIPELINE_STREAM_CONTINUOUS */
> +	ISS_VIDEO_DMAQUEUE_UNDERRUN = (1 << 0),
> +	/* Set when queuing buffer to an empty DMA queue */
> +	ISS_VIDEO_DMAQUEUE_QUEUED = (1 << 1),
> +};
> +
> +#define iss_video_dmaqueue_flags_clr(video)	\
> +			({ (video)->dmaqueue_flags = 0; })
> +
> +/*
> + * struct iss_video_operations - ISS video operations
> + * @queue:	Resume streaming when a buffer is queued. Called on VIDIOC_QBUF
> + *		if there was no buffer previously queued.
> + */
> +struct iss_video_operations {
> +	int(*queue)(struct iss_video *video, struct iss_buffer *buffer);
> +};
> +
> +struct iss_video {
> +	struct video_device video;
> +	enum v4l2_buf_type type;
> +	struct media_pad pad;
> +
> +	struct mutex mutex;		/* format and crop settings */
> +	atomic_t active;
> +
> +	struct iss_device *iss;
> +
> +	unsigned int capture_mem;
> +	unsigned int bpl_alignment;	/* alignment value */
> +	unsigned int bpl_zero_padding;	/* whether the alignment is optional */
> +	unsigned int bpl_max;		/* maximum bytes per line value */
> +	unsigned int bpl_value;		/* bytes per line value */
> +	unsigned int bpl_padding;	/* padding at end of line */
> +
> +	/* Entity video node streaming */
> +	unsigned int streaming:1;
> +
> +	/* Pipeline state */
> +	struct iss_pipeline pipe;
> +	struct mutex stream_lock;	/* pipeline and stream states */
> +
> +	/* Video buffers queue */
> +	struct vb2_queue *queue;
> +	spinlock_t qlock;	/* Spinlock for dmaqueue */
> +	struct list_head dmaqueue;
> +	enum iss_video_dmaqueue_flags dmaqueue_flags;
> +	struct vb2_alloc_ctx *alloc_ctx;
> +
> +	const struct iss_video_operations *ops;
> +};
> +
> +#define to_iss_video(vdev)	container_of(vdev, struct iss_video, video)
> +
> +struct iss_video_fh {
> +	struct v4l2_fh vfh;
> +	struct iss_video *video;
> +	struct vb2_queue queue;
> +	struct v4l2_format format;
> +	struct v4l2_fract timeperframe;
> +};
> +
> +#define to_iss_video_fh(fh)	container_of(fh, struct iss_video_fh, vfh)
> +#define iss_video_queue_to_iss_video_fh(q) \
> +				container_of(q, struct iss_video_fh, queue)
> +
> +int omap4iss_video_init(struct iss_video *video, const char *name);
> +int omap4iss_video_register(struct iss_video *video,
> +			    struct v4l2_device *vdev);
> +void omap4iss_video_unregister(struct iss_video *video);
> +struct iss_buffer *omap4iss_video_buffer_next(struct iss_video *video,
> +					      unsigned int error);
> +struct media_pad *omap4iss_video_remote_pad(struct iss_video *video);
> +
> +const struct iss_format_info *
> +omap4iss_video_format_info(enum v4l2_mbus_pixelcode code);
> +
> +#endif /* OMAP4_ISS_VIDEO_H */
> -- 
> 1.7.7.4
> 

-- 
Sakari Ailus
e-mail: sakari.ailus@xxxxxx	jabber/XMPP/Gmail: sailus@xxxxxxxxxxxxxx
--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux