RE: [PATCH] [media] v4l: soc-camera: add enum-frame-size ioctl

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

 



Hi,

(a general request: could you please configure your mailer to wrap Lines at somewhere around 70 characters?)
No matter how I try, it seems that it still fail at wrap to 70 with my
Outlook. :-( !!!SORRY for un-convenience!!!

I update the patch and test ok with our driver. Please refer to
attachment, patches and our driver source code. I have test
pxa955_cam_enum_fsizes() and default_enum_fsizes(), both works ok in
Android Gingerbread camera application.


-Qing

-----Original Message-----
From: Qing Xu [mailto:qingx@xxxxxxxxxxx]
Sent: 2011年1月20日 13:15
To: g.liakhovetski@xxxxxx
Cc: linux-media@xxxxxxxxxxxxxxx; Qing Xu
Subject: [PATCH] [media] v4l: soc-camera: add enum-frame-size ioctl

add vidioc_enum_framesizes implementation, follow default_g_parm()
and g_mbus_fmt() method

Signed-off-by: Qing Xu <qingx@xxxxxxxxxxx>
---
 drivers/media/video/soc_camera.c |   36 ++++++++++++++++++++++++++++++++++++
 include/media/soc_camera.h       |    1 +
 include/media/v4l2-subdev.h      |    2 ++
 3 files changed, 39 insertions(+), 0 deletions(-)

diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c
index 052bd6d..c89010a 100644
--- a/drivers/media/video/soc_camera.c
+++ b/drivers/media/video/soc_camera.c
@@ -145,6 +145,15 @@ static int soc_camera_s_std(struct file *file, void *priv, v4l2_std_id *a)
        return v4l2_subdev_call(sd, core, s_std, *a);
 }

+static int soc_camera_enum_fsizes(struct file *file, void *fh,
+                                        struct v4l2_frmsizeenum *fsize)
+{
+       struct soc_camera_device *icd = file->private_data;
+       struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+
+       return ici->ops->enum_fsizes(icd, fsize);
+}
+
 static int soc_camera_reqbufs(struct file *file, void *priv,
                              struct v4l2_requestbuffers *p)
 {
@@ -1160,6 +1169,30 @@ static int default_s_parm(struct soc_camera_device *icd,
        return v4l2_subdev_call(sd, video, s_parm, parm);
 }

+static int default_enum_fsizes(struct soc_camera_device *icd,
+                         struct v4l2_frmsizeenum *fsize)
+{
+       int ret;
+       struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+       const struct soc_camera_format_xlate *xlate;
+       __u32 pixfmt = fsize->pixel_format;
+       struct v4l2_frmsizeenum *fsize_mbus = fsize;
+
+       xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
+       if (!xlate)
+               return -EINVAL;
+       /* map xlate-code to pixel_format, sensor only handle xlate-code*/
+       fsize_mbus->pixel_format = xlate->code;
+
+       ret = v4l2_subdev_call(sd, video, enum_mbus_fsizes, fsize_mbus);
+       if (ret < 0)
+               return ret;
+
+       fsize->pixel_format = pixfmt;
+
+       return 0;
+}
+
 static void soc_camera_device_init(struct device *dev, void *pdata)
 {
        dev->platform_data      = pdata;
@@ -1195,6 +1228,8 @@ int soc_camera_host_register(struct soc_camera_host *ici)
                ici->ops->set_parm = default_s_parm;
        if (!ici->ops->get_parm)
                ici->ops->get_parm = default_g_parm;
+       if (!ici->ops->enum_fsizes)
+               ici->ops->enum_fsizes = default_enum_fsizes;

        mutex_lock(&list_lock);
        list_for_each_entry(ix, &hosts, list) {
@@ -1302,6 +1337,7 @@ static const struct v4l2_ioctl_ops soc_camera_ioctl_ops = {
        .vidioc_g_input          = soc_camera_g_input,
        .vidioc_s_input          = soc_camera_s_input,
        .vidioc_s_std            = soc_camera_s_std,
+       .vidioc_enum_framesizes  = soc_camera_enum_fsizes,
        .vidioc_reqbufs          = soc_camera_reqbufs,
        .vidioc_try_fmt_vid_cap  = soc_camera_try_fmt_vid_cap,
        .vidioc_querybuf         = soc_camera_querybuf,
diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h
index 86e3631..6e4800c 100644
--- a/include/media/soc_camera.h
+++ b/include/media/soc_camera.h
@@ -85,6 +85,7 @@ struct soc_camera_host_ops {
        int (*set_ctrl)(struct soc_camera_device *, struct v4l2_control *);
        int (*get_parm)(struct soc_camera_device *, struct v4l2_streamparm *);
        int (*set_parm)(struct soc_camera_device *, struct v4l2_streamparm *);
+       int (*enum_fsizes)(struct soc_camera_device *, struct v4l2_frmsizeenum *);
        unsigned int (*poll)(struct file *, poll_table *);
        const struct v4l2_queryctrl *controls;
        int num_controls;
diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h
index b0316a7..0d482c9 100644
--- a/include/media/v4l2-subdev.h
+++ b/include/media/v4l2-subdev.h
@@ -275,6 +275,8 @@ struct v4l2_subdev_video_ops {
                        struct v4l2_dv_timings *timings);
        int (*enum_mbus_fmt)(struct v4l2_subdev *sd, unsigned int index,
                             enum v4l2_mbus_pixelcode *code);
+       int (*enum_mbus_fsizes)(struct v4l2_subdev *sd,
+                            struct v4l2_frmsizeenum *fsize);
        int (*g_mbus_fmt)(struct v4l2_subdev *sd,
                          struct v4l2_mbus_framefmt *fmt);
        int (*try_mbus_fmt)(struct v4l2_subdev *sd,
--
1.6.3.3

Attachment: 0001-camera-soc-camera-add-enum-frame-sizes.patch
Description: 0001-camera-soc-camera-add-enum-frame-sizes.patch

Attachment: 0002-ARM-pxa-soc-camera-add-enum-frame-sizes.patch
Description: 0002-ARM-pxa-soc-camera-add-enum-frame-sizes.patch

/*-------------------------------------------------------

* Driver for OmniVision CMOS Image Sensor
*
* Copyright (C) 2010, Marvell International Ltd.
*				Qing Xu <qingx@xxxxxxxxxxx>
*
* Based on linux/drivers/media/video/mt9m001.c
*
* 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.

-------------------------------------------------------*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/videodev2.h>
#include <linux/i2c.h>

#include "ov5642.h"

#include <media/v4l2-common.h>
#include <media/v4l2-chip-ident.h>
#include <media/soc_camera.h>

/*
* flash mode:
* 00 Continous flash, flash on when on req flash off when off req.
* 01 One flash, the flash strobe is opened for a period of time specified
* by high 3 bytes.
* 10 periodic flash, the flash is on periodiclly the period is specified
* by high 3 bytes.
* 11 pulse flash, flash one time for a very short pulse, width of the
* pulse is specified by bit
*/
struct flash_struct {
	unsigned char	mode;
	unsigned char	brightness;
	unsigned int 	duration;
};

/* ov5642 has only one fixed colorspace per pixelcode */
struct ov5642_datafmt {
	enum v4l2_mbus_pixelcode	code;
	enum v4l2_colorspace		colorspace;
};

struct ov5642 {
	struct v4l2_subdev subdev;
	int model;	/* V4L2_IDENT_OV5642* codes from v4l2-chip-ident.h */
	struct v4l2_rect rect;
	u32 pixfmt;
	const struct ov5642_datafmt *curfmt;
	const struct ov5642_datafmt *fmts;
	int num_fmts;

	struct flash_struct flash;	/* Flash strobe related info */
	struct regval_list *regs_fmt;
	struct regval_list *regs_size;
};

static const struct ov5642_datafmt ov5642_colour_fmts[] = {
	{V4L2_MBUS_FMT_YUYV8_2X8_BE, V4L2_COLORSPACE_JPEG},
	{V4L2_MBUS_FMT_JPEG_1X8, V4L2_COLORSPACE_JPEG},
	{V4L2_MBUS_FMT_RGB565_2X8_LE, V4L2_COLORSPACE_SRGB}
};

/*
* Store information about the video data format.  The color matrix
* is deeply tied into the format, so keep the relevant values here.
* The magic matrix nubmers come from OmniVision.
*/
static struct ov5642_format_struct {
	enum v4l2_mbus_pixelcode	code;
	struct regval_list	*regs;
} ov5642_fmts[] = {
	{
		.code = V4L2_MBUS_FMT_YUYV8_2X8_LE,
		.regs = ov5642_fmt_yuv422,
	},{
		.code = V4L2_MBUS_FMT_YVYU8_2X8_LE,
		.regs = ov5642_fmt_yuv422,
	},{
		.code = V4L2_MBUS_FMT_YUYV8_2X8_BE,
		.regs = ov5642_fmt_yuv422,
	},{
		.code = V4L2_MBUS_FMT_YVYU8_2X8_BE,
		.regs = ov5642_fmt_yuv422,
	},{
		.code = V4L2_MBUS_FMT_JPEG_1X8,
		.regs = ov5642_fmt_jpg,
	},{
		.code = V4L2_MBUS_FMT_RGB565_2X8_LE,
		.regs = ov5642_fmt_rgb565,
	},
};

static struct ov5642_win_size {
	int	width;
	int	height;
	struct regval_list *regs;
} ov5642_sizes[] = {
	{
		.width = 320,
		.height = 240,
		.regs = ov5642_res_qvga,/* QVGA */
	}, {
		.width = 480,
		.height = 320,
		.regs = ov5642_res_half_vga,/* half VGA */
	}, {
		.width = 640,
		.height = 480,
		.regs = ov5642_res_vga,/* VGA */
	}, {
		.width = 800,
		.height = 480,
		.regs = ov5642_res_wvga,/* WVGA */
	}, {
		.width = 2592,
		.height = 1944,
		.regs = ov5642_res_5M,/*5M 2592x1944*/
	}, {
		.width = 1920,
		.height = 1080,
		.regs = ov5642_res_1080P,/*1080p 1920x1080 */
	}, {
		.width = 1280,
		.height = 720,
		.regs = ov5642_res_720P,/* 1280x720 */
	},
};

static struct ov5642_win_size ov5642_sizes_jpeg[] = {
	{
		.width = 320,
		.height = 240,
		.regs = ov5642_res_qvga,/* QVGA */
	}, {
		.width = 640,
		.height = 480,
		.regs = ov5642_res_vga,	/* VGA */
	}, {
		.width = 2592,
		.height = 1944,
		.regs = ov5642_res_5M,/*5M 2592x1944*/
	},
};

#define N_OV5642_FMTS ARRAY_SIZE(ov5642_fmts)
#define N_OV5642_SIZES ARRAY_SIZE(ov5642_sizes)
#define N_OV5642_SIZES_JPEG ARRAY_SIZE(ov5642_sizes_jpeg)
#define JPEG_BUF_SIZE (1024*1024)

enum flash_arg_format {
	FLASH_ARG_TIME_OFF	= 0,
	FLASH_ARG_MODE_0	= 0,
	FLASH_ARG_MODE_1,
	FLASH_ARG_MODE_2,
	FLASH_ARG_MODE_3,
	FLASH_ARG_MODE_EVER	= FLASH_ARG_MODE_0, /* Always on till send off  */
	FLASH_ARG_MODE_TIMED	= FLASH_ARG_MODE_1, /* On for some time */
	FLASH_ARG_MODE_REPEAT	= FLASH_ARG_MODE_2, /* Repeatedlly on */
	FLASH_ARG_MODE_BLINK	= FLASH_ARG_MODE_3, /* On for some jeffies defined by H/W */
	FLASH_ARG_MODE_MAX,	/* Equal or greater mode considered invalid */
	FLASH_ARG_LUMI_OFF	= 0, /* Flash off */
	FLASH_ARG_LUMI_FULL,
	FLASH_ARG_LUMI_DIM1,	/* One fifth luminance*/
	FLASH_ARG_LUMI_DIM2,	/* One third luminance, not supported in H/W configuration */
	FLASH_ARG_LUMI_MAX,	/* Equal or greater luminance considered invalid */
};

static const struct v4l2_queryctrl ov5642_controls[] = {
	{
		.id = V4L2_CID_FOCUS_AUTO,
		.type = V4L2_CTRL_TYPE_BOOLEAN,
		.name = "auto focus",
		.minimum = 0,
		.maximum = 1,
		.step = 1,
		.default_value = 0,
	},{
		.id = V4L2_CID_AUTO_WHITE_BALANCE,
		.type = V4L2_CTRL_TYPE_BOOLEAN,
		.name = "auto white balance",
		.minimum = 0,
		.maximum = 1,
		.step = 1,
		.default_value = 0,
	},{
		.id = V4L2_CID_EXPOSURE_AUTO,
		.type = V4L2_CTRL_TYPE_BOOLEAN,
		.name = "auto exposure",
		.minimum = 0,
		.maximum = 1,
		.step = 1,
		.default_value = 0,
	},{
		.id = V4L2_CID_FLASH_DURATION,
		.type = V4L2_CTRL_TYPE_INTEGER,
		.name = "flash duration",
		.minimum = 0,
		.maximum = 60000,
		.step = 1,
		.default_value = 1000,
	},{
		.id = V4L2_CID_FLASH_MODE,
		.type = V4L2_CTRL_TYPE_INTEGER,
		.name = "flash mode",
		.minimum = FLASH_ARG_MODE_0,
		.maximum = FLASH_ARG_MODE_MAX - 1,
		.step = 1,
		.default_value = 0,
	},{
		.id = V4L2_CID_FLASH_LUMINANCE,
		.type = V4L2_CTRL_TYPE_INTEGER,
		.name = "flash brightness",
		.minimum = FLASH_ARG_LUMI_OFF,
		.maximum = FLASH_ARG_LUMI_MAX - 1,
		.step = 1,
		.default_value = 0,
	}
};

#define setbit(reg, mask) \
do{ \
	unsigned char val; \
	ov5642_read(client, reg, &val); \
	val |= (mask); \
	ov5642_write(client, reg, val); \
}while (0)

#define clrbit(reg, mask) \
do{ \
	unsigned char val; \
	ov5642_read(client, reg, &val);	\
	val &= (~(mask));	\
	ov5642_write(client, reg, val);	\
}while (0)

static struct ov5642 *to_ov5642(const struct i2c_client *client)
{
	return container_of(i2c_get_clientdata(client), struct ov5642, subdev);
}


/*issue that OV sensor must write then read. 5642 register is 16bit!!!*/
static int ov5642_read(struct i2c_client *c, u16 reg,
		unsigned char *value)
{
	u8 data;
	u8 address[2];
	int ret = 0;
	address[0] = reg>>8;
	address[1] = reg;

	ret = (int)i2c_smbus_write_byte_data(c,address[0],address[1]);
	if (ret)
		goto exit;
	data = i2c_smbus_read_byte(c);
	*value = data;
exit:
	return ret;
}

static int ov5642_write(struct i2c_client *c, u16 reg,
		unsigned char value)
{
	u8 data[3];
	data[0] = reg>>8;
	data[1] = reg;
	data[2]=  value;
	i2c_master_send(c, data, 3);
	if (reg == REG_SYS && (value & SYS_RESET))
		msleep(2);  /* Wait for reset to run */
	return 0;
}

/* Write a list of register settings; ff/ff stops the process.*/
static int ov5642_write_array(struct i2c_client *c, struct regval_list *vals)
{
	while (vals->reg_num != 0xffff || vals->value != 0xff) {
		int ret = ov5642_write(c, vals->reg_num, vals->value);
		if (ret < 0)
			return ret;
		vals++;
	}
	return 0;
}

static int ov5642_detect(struct i2c_client *client)
{
	unsigned char v = 0;
	int ret = 0;

	ret = ov5642_read(client, REG_PIDH, &v);

	printk(KERN_NOTICE "cam: ov5642_detect 0x%x\n",v);
	if (ret < 0)
		return ret;
	if (v != 0x56)
		return -ENODEV;

	ret = ov5642_read(client, REG_PIDL, &v);

	printk(KERN_NOTICE "cam: ov5642_detect 0x%x\n",v);
	if (ret < 0)
		return ret;
	if (v != 0x42)
		return -ENODEV;

	return 0;
}


static int ov5642_get_awb(struct i2c_client *client, int *value)
{
	int ret;
	unsigned char v = 0;

	ret = ov5642_read(client, REG_AWB_EN, &v);
	*value = ((v & AWB_DIS) != AWB_DIS);
	return ret;
}

static int ov5642_set_awb(struct i2c_client *client, int value)
{
	unsigned char v = 0;
	int ret;

	ret = ov5642_read(client, REG_AWB_EN, &v);
	if (!value)
		v = AWB_DIS;
	else
		v = 0x00;
	ret = ov5642_write(client, REG_AWB_EN, v);
	return ret;
}

static int ov5642_get_ae(struct i2c_client *client, int *value)
{
	int ret;
	unsigned char v = 0;

	ret = ov5642_read(client, REG_AE_EN, &v);
	*value = ((v & AE_EN) == AE_EN);
	return ret;
}

static int ov5642_set_ae(struct i2c_client *client, int value)
{
	unsigned char v = 0;
	int ret;

	ret = ov5642_read(client, REG_AE_EN, &v);
	if (value)
		v |= AE_EN;
	else
		v &= ~AE_EN;
	ret += ov5642_write(client, REG_AE_EN, v);
	return ret;
}

static int ov5642_get_af(struct i2c_client *client, int *value)
{
	int ret;
	unsigned char v = 0;

	ret = ov5642_read(client, REG_AF_EN, &v);
	*value = (v & AF_EN) == AF_EN;
	return ret;
}

static int ov5642_set_af(struct i2c_client *client, int value)
{
	unsigned char v = 0;
	int ret;

	ret = ov5642_read(client, REG_AF_EN, &v);
	if (value)
		v |= AF_EN;
	else
		v &= ~AF_EN;
	ret += ov5642_write(client, REG_AF_EN, v);
	return ret;
}

static int ov5642_get_flash_lum(struct i2c_client *client, int *lum)
{
	struct ov5642 *ov5642 = to_ov5642(client);
	*lum = ov5642->flash.brightness;
	return *lum;
}

static int ov5642_set_flash_lum(struct i2c_client *client, int lum)
{
	struct ov5642 *ov5642 = to_ov5642(client);
	int ret = 0;

	if ((lum < 0) || (lum > FLASH_ARG_LUMI_MAX))
		return -EINVAL;

	if (lum == 0) {
		ov5642->flash.brightness = FLASH_ARG_LUMI_OFF;
		/* Request turn OFF the flash, so ignore flash mode */
		clrbit(REG_SYS_RESET, 0x08);	/* clear reset*/
		setbit(REG_CLK_ENABLE, 0x08);	/* enable clock*/
		setbit(REG_PAD_OUTPUT, 0x20);	/* pad output enable*/
		setbit(REG_FREX_MODE, 0x02);	/* FREX mode*/
		/* Strobe control */
		ret = ov5642_write(client, REG_STRB_CTRL, 0x03);
		/*
		* Write again to make sure strobe request end is received
		* after other bits are saved in the reg, use LED3 mode off
		*/
		ret |= ov5642_write(client, REG_STRB_CTRL, 0x03);
		return ret;
	}

	ov5642->flash.brightness = lum;
	clrbit(REG_SYS_RESET, 0x08);	/* clear reset*/
	setbit(REG_CLK_ENABLE, 0x08);	/* enable clock*/
	setbit(REG_PAD_OUTPUT, 0x02);	/* pad output enable*/
	setbit(REG_FREX_MODE, 0x02);	/* FREX mode*/

	switch (ov5642->flash.mode)
	{
		case FLASH_ARG_MODE_TIMED:
			if (ov5642->flash.duration) {
				/* Strobe control */
				ret = ov5642_write(client, REG_STRB_CTRL, 0x03);
				/* Send strobe request */
				ret |= ov5642_write(client, REG_STRB_CTRL, 0x83);
				msleep(ov5642->flash.duration);
				ret |= ov5642_write(client, REG_STRB_CTRL, 0x03);
				ov5642->flash.brightness = FLASH_ARG_LUMI_OFF;
				return ret;
			}
			/* NO break here, if duration is 0, leave it ON */
		case FLASH_ARG_MODE_EVER:
			/* Strobe control */
			ret = ov5642_write(client, REG_STRB_CTRL, 0x03);
			/* Send strobe request */
			ret |= ov5642_write(client, REG_STRB_CTRL, 0x83);
			return ret;
		case FLASH_ARG_MODE_REPEAT:
			/* Strobe control */
			ret = ov5642_write(client, REG_STRB_CTRL, 0x02);
			/* Send strobe request */
			ret |= ov5642_write(client, REG_STRB_CTRL, 0x82);
			return ret;
		case FLASH_ARG_MODE_BLINK:
			/* Use ov5642 xenon flash mode */
			return ret;
	}
	return 0;
}

static int ov5642_get_flash_time(struct i2c_client *client, int *time)
{
	struct ov5642 *ov5642 = to_ov5642(client);
	*time = ov5642->flash.duration;
	return *time;
}


static int ov5642_set_flash_time(struct i2c_client *client, int time)
{
	int ret = 0;
	struct ov5642 *ov5642 = to_ov5642(client);
	if (time < 0) {
		printk(KERN_ERR "cam: Invalid flash duration: %d\n", time);
		return -EINVAL;
	}
	ov5642->flash.duration = time;
	if (time == 0)
		ov5642->flash.mode = FLASH_ARG_MODE_EVER;
	else
		ov5642->flash.mode = FLASH_ARG_MODE_TIMED;
	/*
	* If the flash is on, the duration change will affect the
	* behavoir of flash immediatelly
	*/
	if (ov5642->flash.brightness != FLASH_ARG_LUMI_OFF)
		ret = ov5642_set_flash_lum(client, ov5642->flash.brightness);
	return ret;
}

static int ov5642_get_flash_mode(struct i2c_client *client, int *mode)
{
	struct ov5642 *ov5642 = to_ov5642(client);
	*mode = ov5642->flash.mode;
	return *mode;
}

static int ov5642_set_flash_mode(struct i2c_client *client, int mode)
{
	struct ov5642 *ov5642 = to_ov5642(client);

	if (mode >= FLASH_ARG_MODE_MAX)
		return -EINVAL;
	ov5642->flash.mode = mode;
	return 0;
}

static int ov5642_g_chip_ident(struct v4l2_subdev *sd,
				struct v4l2_dbg_chip_ident *id)
{
	struct i2c_client *client = sd->priv;
	struct ov5642 *ov5642 = to_ov5642(client);
	id->ident	= ov5642->model;
	id->revision	= 0;

	return 0;
}

static int ov5642_g_register(struct v4l2_subdev *sd,
			      struct v4l2_dbg_register *reg)
{
	struct i2c_client *client = sd->priv;
	return ov5642_read(client, (u16)reg->reg, (unsigned char *)&(reg->val));
}

static int ov5642_s_register(struct v4l2_subdev *sd,
			      struct v4l2_dbg_register *reg)
{
	struct i2c_client *client = sd->priv;
	return ov5642_write(client, (u16)reg->reg, (unsigned char)reg->val);
}

static int ov5642_s_stream(struct v4l2_subdev *sd, int enable)
{
	unsigned char val;
	struct i2c_client *client = sd->priv;
	if (enable) {
		ov5642_read(client, 0x3008, &val);
		val &= ~0x40;
		ov5642_write(client, 0x3008, val);
	} else {
		/* stop after one frame */
		ov5642_read(client, 0x4201, &val);
		val |= 0x01;
		ov5642_write(client, 0x4201, val);
	}
	return 0;
}
static int ov5642_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
			    enum v4l2_mbus_pixelcode *code)
{
	struct i2c_client *client = sd->priv;
	struct ov5642 *ov5642 = to_ov5642(client);

	if (index >= ov5642->num_fmts)
		return -EINVAL;

	*code = ov5642->fmts[index].code;
	return 0;
}

static int ov5642_try_fmt(struct v4l2_subdev *sd,
			   struct v4l2_mbus_framefmt *mf)
{
	int i;
	struct i2c_client *client = sd->priv;
	struct ov5642 *ov5642 = to_ov5642(client);

	/* enum the supported formats*/
	for (i = 0; i < N_OV5642_FMTS; i++) {
		if (ov5642_fmts[i].code == mf->code){
			ov5642->regs_fmt = ov5642_fmts[i].regs;
			break;
		}
	}
	if (i >= N_OV5642_FMTS){
		printk(KERN_ERR "cam: ov5642 unsupported color format!\n");
		return -EINVAL;
	}


	if(mf->code == V4L2_MBUS_FMT_JPEG_1X8){
		/* enum the supported sizes for JPEG*/
		for (i = 0; i < N_OV5642_SIZES_JPEG; i++) {
			if (mf->width == ov5642_sizes_jpeg[i].width
				&& mf->height == ov5642_sizes_jpeg[i].height) {
				ov5642->regs_size = ov5642_sizes_jpeg[i].regs;
				break;
			}
		}
		if (i >= N_OV5642_SIZES_JPEG){
				printk(KERN_ERR"cam: ov5642 unsupported jpeg size!\n");
				return -EINVAL;
		}
	} else {
		/* enum the supported sizes*/
		for (i = 0; i < N_OV5642_SIZES; i++) {
			if (mf->width == ov5642_sizes[i].width
				&& mf->height == ov5642_sizes[i].height) {
				ov5642->regs_size = ov5642_sizes[i].regs;
				break;
			}
		}
		if (i >= N_OV5642_SIZES){
			printk(KERN_ERR "cam: ov5642 unsupported window size, w%d, h%d!\n",
					mf->width, mf->height);
			return -EINVAL;
		}
	}

	mf->field = V4L2_FIELD_NONE;

	switch (mf->code) {
	case V4L2_MBUS_FMT_RGB565_2X8_LE:
	case V4L2_MBUS_FMT_YUYV8_2X8_LE:
	case V4L2_MBUS_FMT_YVYU8_2X8_LE:
	case V4L2_MBUS_FMT_YUYV8_2X8_BE:
	case V4L2_MBUS_FMT_YVYU8_2X8_BE:
		/* enum the supported sizes*/
		for (i = 0; i < N_OV5642_SIZES; i++) {
			if (mf->width == ov5642_sizes[i].width
				&& mf->height == ov5642_sizes[i].height) {
				ov5642->regs_size = ov5642_sizes[i].regs;
				break;
			}
		}
		if (i >= N_OV5642_SIZES){
			printk(KERN_ERR "cam: ov5642 unsupported window size, w%d, h%d!\n",
					mf->width, mf->height);
			return -EINVAL;
		}

		if (mf->code == V4L2_MBUS_FMT_RGB565_2X8_LE)
			mf->colorspace = V4L2_COLORSPACE_SRGB;
		else
			mf->colorspace = V4L2_COLORSPACE_JPEG;
		break;

	case V4L2_MBUS_FMT_JPEG_1X8:
		/* enum the supported sizes for JPEG*/
		for (i = 0; i < N_OV5642_SIZES_JPEG; i++) {
			if (mf->width == ov5642_sizes_jpeg[i].width
				&& mf->height == ov5642_sizes_jpeg[i].height) {
				ov5642->regs_size = ov5642_sizes_jpeg[i].regs;
				break;
			}
		}
		if (i >= N_OV5642_SIZES_JPEG){
				printk(KERN_ERR"cam: ov5642 unsupported jpeg size!\n");
				return -EINVAL;
		}
		mf->colorspace = V4L2_COLORSPACE_JPEG;
		break;
	default:
		dev_err(&client->dev, "ov5642 doesn't support code %d\n", mf->code);
		break;
	}

	return 0;
}

static int ov5642_s_fmt(struct v4l2_subdev *sd,
			 struct v4l2_mbus_framefmt *mf)
{
	int ret = 0;
	struct i2c_client *client = sd->priv;
	struct ov5642 *ov5642 = to_ov5642(client);

	switch (mf->code) {
		case V4L2_MBUS_FMT_JPEG_1X8:
			ov5642_write_array(client, ov5642_jpg_default);
			ov5642_write_array(client, ov5642->regs_size);
			break;

		case V4L2_MBUS_FMT_YUYV8_2X8_LE:
		case V4L2_MBUS_FMT_YVYU8_2X8_LE:
		case V4L2_MBUS_FMT_YUYV8_2X8_BE:
		case V4L2_MBUS_FMT_YVYU8_2X8_BE:
		case V4L2_MBUS_FMT_RGB565_2X8_LE:
		default:
			if (mf->width == 1920 && mf->height == 1080) {
				ov5642_write_array(client, ov5642_yuv_1080p);
			} else if (mf->width == 1280 && mf->height == 720) {
				ov5642_write_array(client, ov5642_yuv_720p);
			} else {
				ov5642_write_array(client, ov5642_yuv_default);
				ov5642_write_array(client, ov5642->regs_fmt);
				ov5642_write_array(client, ov5642->regs_size);
				ov5642_write_array(client, ov5642_mipi);
			}
			break;
	}

	return ret;
}

static int ov5642_g_fmt(struct v4l2_subdev *sd,
			 struct v4l2_mbus_framefmt *mf)
{
	struct i2c_client *client = sd->priv;
	struct ov5642 *ov5642 = to_ov5642(client);

	mf->width		= ov5642->rect.width;
	mf->height		= ov5642->rect.height;
	mf->code	= V4L2_MBUS_FMT_YUYV8_2X8_BE;
	mf->field		= V4L2_FIELD_NONE;
	mf->colorspace		= V4L2_COLORSPACE_JPEG;
	return 0;
}

static int ov5642_enum_fsizes(struct v4l2_subdev *sd,
			struct v4l2_frmsizeenum *fsize)
{
	struct i2c_client *client = sd->priv;

	fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;

	/* abuse pixel_format, in fact, it is xlate->code*/
	switch (fsize->pixel_format) {
		case V4L2_MBUS_FMT_YUYV8_2X8_BE:
		case V4L2_MBUS_FMT_RGB565_2X8_LE:
			if (fsize->index >= N_OV5642_SIZES) {
				dev_warn(&client->dev, "ov5642 unsupported size!\n");
				return -EINVAL;
			}
			fsize->discrete.height = ov5642_sizes[fsize->index].height;
			fsize->discrete.width = ov5642_sizes[fsize->index].width;
			break;
		case V4L2_MBUS_FMT_JPEG_1X8:
			if (fsize->index >= N_OV5642_SIZES_JPEG) {
				dev_warn(&client->dev, "ov5642 unsupported jpeg size!\n");
				return -EINVAL;
			}
			fsize->discrete.height = ov5642_sizes_jpeg[fsize->index].height;
			fsize->discrete.width = ov5642_sizes_jpeg[fsize->index].width;
			break;
		default:
			dev_err(&client->dev, "ov5642 unsupported format!\n");
			return -EINVAL;
	}

	return 0;
}

static unsigned long ov5642_query_bus_param(struct soc_camera_device *icd)
{
	struct soc_camera_link *icl = to_soc_camera_link(icd);
	unsigned long flags = SOCAM_MIPI | SOCAM_MIPI_1LANE;

	return soc_camera_apply_sensor_flags(icl, flags);
}

static int ov5642_set_bus_param(struct soc_camera_device *icd, unsigned long f)
{
#if 0/*TODO: add mipi and parallel different setting*/
	if (f & SOCAM_MIPI) /* mipi setting*/
		ov5642_write_array(client, ov5642_mipi);
	else /* parallel setting*/
		ov5642_write_array(client, ov5642_mipi);
#endif
	return 0;
}

static struct soc_camera_ops ov5642_ops = {
	.query_bus_param	= ov5642_query_bus_param,
	.set_bus_param		= ov5642_set_bus_param,
	.controls			= ov5642_controls,
	.num_controls		= ARRAY_SIZE(ov5642_controls),
};

static int ov5642_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
	struct i2c_client *client = sd->priv;
	const struct v4l2_queryctrl *qctrl;
	int ret;

	qctrl = soc_camera_find_qctrl(&ov5642_ops, ctrl->id);
	if (!qctrl)
		return -EINVAL;

	switch (ctrl->id) {
	case V4L2_CID_AUTO_WHITE_BALANCE:
		ret = ov5642_set_awb(client, ctrl->value);
		break;
	case V4L2_CID_FOCUS_AUTO:
		ret = ov5642_set_af(client, ctrl->value);
		break;
	case V4L2_CID_EXPOSURE_AUTO:
		ret = ov5642_set_ae(client, ctrl->value);
		break;
	case V4L2_CID_FLASH_DURATION:
		ret =  ov5642_set_flash_time(client, ctrl->value);
		break;
	case V4L2_CID_FLASH_MODE:
		ret =  ov5642_set_flash_mode(client, ctrl->value);
		break;
	case V4L2_CID_FLASH_LUMINANCE:
		ret =  ov5642_set_flash_lum(client, ctrl->value);
		break;
	default:
		ret = -EINVAL;
	}

	return ret;
}

static int ov5642_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
	struct i2c_client *client = sd->priv;
	int ret;

	switch (ctrl->id) {
	case V4L2_CID_AUTO_WHITE_BALANCE:
		ret = ov5642_get_awb(client, &ctrl->value);
		break;
	case V4L2_CID_FOCUS_AUTO:
		ret = ov5642_get_af(client, &ctrl->value);
		break;
	case V4L2_CID_EXPOSURE_AUTO:
		ret = ov5642_get_ae(client, &ctrl->value);
		break;
	case V4L2_CID_FLASH_DURATION:
		ret =  ov5642_get_flash_time(client, &ctrl->value);
		break;
	case V4L2_CID_FLASH_MODE:
		ret =  ov5642_get_flash_mode(client, &ctrl->value);
		break;
	case V4L2_CID_FLASH_LUMINANCE:
		ret =  ov5642_get_flash_lum(client, &ctrl->value);
		break;
	default:
		ret = -EINVAL;
	}

	return 0;
}

static struct v4l2_subdev_core_ops ov5642_subdev_core_ops = {
	.g_ctrl		= ov5642_g_ctrl,
	.s_ctrl		= ov5642_s_ctrl,
	.g_chip_ident	= ov5642_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG
	.g_register	= ov5642_g_register,
	.s_register	= ov5642_s_register,
#endif
};

static struct v4l2_subdev_video_ops ov5642_subdev_video_ops = {
	.s_stream	= ov5642_s_stream,
	.s_mbus_fmt	= ov5642_s_fmt,
	.g_mbus_fmt	= ov5642_g_fmt,
	.try_mbus_fmt	= ov5642_try_fmt,
	.enum_mbus_fmt	= ov5642_enum_fmt,
	.enum_mbus_fsizes = ov5642_enum_fsizes,
};

static struct v4l2_subdev_ops ov5642_subdev_ops = {
	.core	= &ov5642_subdev_core_ops,
	.video	= &ov5642_subdev_video_ops,
};

static int ov5642_probe(struct i2c_client *client,
			 const struct i2c_device_id *did)
{
	struct ov5642 *ov5642;
	struct soc_camera_device *icd = client->dev.platform_data;
	struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
	struct soc_camera_link *icl;
	int ret;
	int i;

	if (!icd) {
		dev_err(&client->dev, "ov5642 missing soc-camera data!\n");
		return -EINVAL;
	}
	if (!icd->dev.parent ||
	    to_soc_camera_host(icd->dev.parent)->nr != icd->iface)
		return -ENODEV;

	icl = to_soc_camera_link(icd);
	if (!icl) {
		dev_err(&client->dev, "ov5642 driver needs platform data\n");
		return -EINVAL;
	}

	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) {
		dev_warn(&adapter->dev,
			 "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n");
		return -EIO;
	}

	ov5642 = kzalloc(sizeof(struct ov5642), GFP_KERNEL);
	if (!ov5642) {
		dev_err(&client->dev, "ov5642 failed to alloc struct!\n");
		return -ENOMEM;
	}

	ov5642->rect.left = 0;
	ov5642->rect.top = 0;
	ov5642->rect.width = 640;
	ov5642->rect.height = 480;
	ov5642->pixfmt = V4L2_PIX_FMT_UYVY;

	icd->ops = &ov5642_ops;

	ov5642->model = V4L2_IDENT_OV5642;
	ov5642->fmts = ov5642_colour_fmts;
	ov5642->num_fmts = ARRAY_SIZE(ov5642_colour_fmts);

	ov5642->flash.brightness = FLASH_ARG_LUMI_OFF;
	ov5642->flash.duration = FLASH_ARG_TIME_OFF;
	ov5642->flash.mode = FLASH_ARG_MODE_EVER;
	v4l2_i2c_subdev_init(&ov5642->subdev, client, &ov5642_subdev_ops);

	for (i = MAX_DETECT_NUM; i > 0; --i) {
		ret = ov5642_detect(client);
		if (!ret) {
			printk(KERN_NOTICE "cam: OmniVision ov5642 sensor detected!\n");
			return 0;
		}
	}
	printk(KERN_ERR "cam: abort retry, failed to detect OmniVision ov5642!\n");

	icd->ops = NULL;
	i2c_set_clientdata(client, NULL);
	if (ov5642)
		kfree(ov5642);

	ret = -ENODEV;
	return ret;
}

static int ov5642_remove(struct i2c_client *client)
{
	struct ov5642 *ov5642 = to_ov5642(client);
	struct soc_camera_device *icd = client->dev.platform_data;
	struct soc_camera_link *icl = to_soc_camera_link(icd);

	icd->ops = NULL;
	if (icl->free_bus)
		icl->free_bus(icl);
	icl->power(icd->pdev, 0);

	i2c_set_clientdata(client, NULL);
	client->driver = NULL;
	kfree(ov5642);
	return 0;
}

static struct i2c_device_id ov5642_idtable[] = {
	{ "ov5642", 0 },
	{ }
};

MODULE_DEVICE_TABLE(i2c, ov5642_idtable);

static struct i2c_driver ov5642_driver = {
	.driver = {
		.name	= "ov5642",
	},
	.id_table	= ov5642_idtable,
	.probe		= ov5642_probe,
	.remove		= ov5642_remove,
};

static int __init ov5642_mod_init(void)
{
	int ret = 0;
	ret = i2c_add_driver(&ov5642_driver);
	return ret;
}

static void __exit ov5642_mod_exit(void)
{
	i2c_del_driver(&ov5642_driver);
}

module_init(ov5642_mod_init);
module_exit(ov5642_mod_exit);

MODULE_DESCRIPTION("OmniVision OV5642 Camera Driver");
MODULE_AUTHOR("Qing Xu");
MODULE_LICENSE("GPL");

/*----------------------------------------------------------

* V4L2 Driver for PXA95x camera host
*
* Based on linux/drivers/media/video/pxa_camera.c
*
* Copyright (C) 2010, Marvell International Ltd.
*              Qing Xu <qingx@xxxxxxxxxxx>
*
* 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/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/spinlock.h>
#include <linux/device.h>
#include <linux/wait.h>
#include <linux/list.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/version.h>
#include <linux/clk.h>

#include <mach/dma.h>

#include <media/v4l2-common.h>
#include <media/v4l2-dev.h>
#include <media/videobuf-dma-contig.h>
#include <media/soc_camera.h>
#include <media/soc_mediabus.h>
#include <media/v4l2-chip-ident.h>

#include "pxa955_csi.h"

MODULE_AUTHOR("Qing Xu <qingx@xxxxxxxxxxx>");
MODULE_DESCRIPTION("Marvell PXA955 Simple Capture Controller Driver");
MODULE_LICENSE("GPL");
MODULE_SUPPORTED_DEVICE("Video");

/* sci register, sci0 base: 0x50000000*/
#define REG_SCICR0	0x0000
#define REG_SCICR1	0x0004
#define REG_SCISR	0x0008
#define REG_SCIMASK	0x000C
#define REG_SCIFIFO	0x00F8
#define REG_SCIFIFOSR	0x00FC
#define REG_SCIDADDR0	0x0200
#define REG_SCISADDR0	0x0204
#define REG_SCITADDR0	0x0208
#define REG_SCIDCMD0	0x020C
#define REG_SCIDADDR1	0x0210
#define REG_SCISADDR1	0x0214
#define REG_SCITADDR1	0x0218
#define REG_SCIDCMD1	0x021C
#define REG_SCIDADDR2	0x0220
#define REG_SCISADDR2	0x0224
#define REG_SCITADDR2	0x0228
#define REG_SCIDCMD2	0x022C
#define REG_SCIDBR0	0x0300
#define REG_SCIDCSR0	0x0304
#define REG_SCIDBR1	0x0310
#define REG_SCIDCSR1	0x0314
#define REG_SCIDBR2	0x0320
#define REG_SCIDCSR2	0x0324

#define	IRQ_EOFX	0x00000001
#define	IRQ_EOF		0x00000002
#define	IRQ_DIS		0x00000004
#define	IRQ_OFO		0x00000008
#define	IRQ_DBS		0x00000200
#define	IRQ_SOFX	0x00000400
#define	IRQ_SOF		0x00000800

/* FMT_YUV420 & FMT_RGB666 only for input format without output format */
#define   FMT_RAW8	  0x0000
#define   FMT_RAW10	  0x0002
#define   FMT_YUV422	  0x0003
#define   FMT_YUV420	  0x0004
#define   FMT_RGB565	  0x0005
#define   FMT_RGB666	  0x0006
#define   FMT_RGB888	  0x0007
#define   FMT_JPEG	  0x0008
#define   FMT_YUV422PACKET	0x0009

#define SCICR1_FMT_OUT(n)       ((n) <<12)	/* Output Pixel Format (4 bits) */
#define SCICR1_FMT_IN(n)        ((n) <<28)	/* Input Pixel Format (4 bits) */

/* REG_SCIDCSR0 */
#define SCIDCSR_DMA_RUN        0x80000000  	/* DMA Channel Enable */

/* REG_SCICR0 */
#define SCICR0_CAP_EN          0x40000000 	/* Capture Enable */
#define SCICR0_CI_EN           0x80000000 	/* Camera Interface Enable */

/* REG_SCIFIFO */
#define SCIFIFO_Fx_EN        0x0010	/* FIFO 0 Enable */

#define SCIFIFO_F0_EN        0x0010	/* FIFO 0 Enable */
#define SCIFIFO_F1_EN        0x0020	/* FIFO 1 Enable */
#define SCIFIFO_F2_EN        0x0040	/* FIFO 2 Enable */
#define SCIFIFO_FX_EN(n)  ((n) <<4)		/* Input Pixel Format (4 bits) */

/* REG_SCIDBRx*/
#define SCIDBR_EN 		(0x1u<<1) /*DMA Branch Enable*/

#define VGA_WIDTH       640
#define VGA_HEIGHT      480

#define SENSOR_MAX 2

#define PXA955_CAM_VERSION_CODE KERNEL_VERSION(0, 0, 5)
#define PXA955_CAM_DRV_NAME "pxa95x-camera"

#define JPEG_COMPRESS_RATIO_HIGH 10
#define CHANNEL_NUM 3 		/*YUV*/
#define MAX_DMA_BUFS 4
#define MIN_DMA_BUFS 2
#define MAX_DMA_SIZE (1920*1080*3/2)
#define JPEG_BUF_SIZE (1024*1024)

static unsigned int vid_mem_limit = 16;	/* Video memory limit, in Mb */

/* descriptor needed for the camera controller DMA engine */
struct pxa_cam_dma {
	dma_addr_t	sg_dma;
	struct pxa_dma_desc	*sg_cpu;
	size_t	sg_size;
	int	sglen;
};

struct pxa_buf_node {
	/* common v4l buffer stuff -- must be first */
	struct videobuf_buffer vb;
	enum v4l2_mbus_pixelcode	code;
	struct page *page;
	struct pxa_cam_dma dma_desc[CHANNEL_NUM];
};

struct pxa955_cam_dev {
	struct soc_camera_host	soc_host;
	struct soc_camera_device *icd;
	struct list_head dev_list;	/* link to other devices */

	struct clk *sci1_clk;
	struct clk *sci2_clk;

	struct pxa955_csi_dev		*csidev;
	unsigned int		irq;
	void __iomem		*regs;
	wait_queue_head_t iowait;	/* waiting on frame data */
	bool streamon;
	bool streamoff;

	struct resource		*res;
	unsigned long		platform_flags;

	/* DMA buffers */
	struct list_head dma_buf_list;	/*dma buffer list, the list member is buf_node*/
	unsigned int dma_buf_size;		/* allocated size */
	unsigned int channels;
	unsigned int channel_size[CHANNEL_NUM];

	/* streaming buffers */
	spinlock_t spin_lock;  /* Access to device */

	struct pxa955_camera_pdata *pdata;
};

/* refer to videobuf-dma-contig.c*/
struct videobuf_dma_contig_memory {
	u32 magic;
	void *vaddr;
	dma_addr_t dma_handle;
	unsigned long size;
	int is_userptr;
};

#define MAGIC_DC_MEM 0x0733ac61
#define MAGIC_CHECK(is, should)					    \
	if (unlikely((is) != (should)))	{				    \
		pr_err("magic mismatch: %x expected %x\n", (is), (should)); \
		BUG();							    \
	}

/* Len should 8 bytes align, bit[2:0] should be 0 */
#define SINGLE_DESC_TRANS_MAX   (1 << 24)

extern struct pxa955_csi_dev *csi_device;
static int csi_attach(struct pxa955_cam_dev *pcdev)
{
	if (csi_device != NULL) {
		pcdev->csidev = csi_device;
		return 0;
	}

	printk(KERN_ERR "cam: failed to find CSI device!\n");
	return -ENODEV;
}

static inline void sci_reg_write(struct pxa955_cam_dev *pcdev, unsigned int reg,
		unsigned int val)
{
	__raw_writel(val, pcdev->regs + reg);
}

static inline unsigned int sci_reg_read(struct pxa955_cam_dev *pcdev,
		unsigned int reg)
{
	return __raw_readl(pcdev->regs + reg);
}


static inline void sci_reg_write_mask(struct pxa955_cam_dev *pcdev, unsigned int reg,
		unsigned int val, unsigned int mask)
{
	unsigned int v = sci_reg_read(pcdev, reg);

	v = (v & ~mask) | (val & mask);
	sci_reg_write(pcdev, reg, v);
}

static inline void sci_reg_clear_bit(struct pxa955_cam_dev *pcdev,
		unsigned int reg, unsigned int val)
{
	sci_reg_write_mask(pcdev, reg, 0, val);
}

static inline void sci_reg_set_bit(struct pxa955_cam_dev *pcdev,
		unsigned int reg, unsigned int val)
{
	sci_reg_write_mask(pcdev, reg, val, val);
}

#ifdef DEBUG
static void sci_dump_registers(struct pxa955_cam_dev *pcdev)
{
	printk(KERN_ERR "SCICR0 0x%x\n", sci_reg_read(pcdev, REG_SCICR0));
	printk(KERN_ERR "SCICR1 0x%x\n", sci_reg_read(pcdev, REG_SCICR1));
	printk(KERN_ERR "SCISR 0x%x\n", sci_reg_read(pcdev, REG_SCISR));
	printk(KERN_ERR "SCIMASK 0x%x\n", sci_reg_read(pcdev, REG_SCIMASK));
	printk(KERN_ERR "SCIFIFO 0x%x\n", sci_reg_read(pcdev, REG_SCIFIFO));
	printk(KERN_ERR "SCIFIFOSR 0x%x\n", sci_reg_read(pcdev, REG_SCIFIFOSR));

	printk(KERN_ERR "SCIDADDR0 0x%x\n", sci_reg_read(pcdev, REG_SCIDADDR0));
	printk(KERN_ERR "SCISADDR0 0x%x\n", sci_reg_read(pcdev, REG_SCISADDR0));
	printk(KERN_ERR "SCITADDR0 0x%x\n", sci_reg_read(pcdev, REG_SCITADDR0));
	printk(KERN_ERR "SCIDCMD0 0x%x\n", sci_reg_read(pcdev, REG_SCIDCMD0));

	printk(KERN_ERR "SCIDADDR1 0x%x\n", sci_reg_read(pcdev, REG_SCIDADDR1));
	printk(KERN_ERR "SCISADDR1 0x%x\n", sci_reg_read(pcdev, REG_SCISADDR1));
	printk(KERN_ERR "SCITADDR1 0x%x\n", sci_reg_read(pcdev, REG_SCITADDR1));
	printk(KERN_ERR "SCIDCMD1 0x%x\n", sci_reg_read(pcdev, REG_SCIDCMD1));

	printk(KERN_ERR "SCIDADDR2 0x%x\n", sci_reg_read(pcdev, REG_SCIDADDR2));
	printk(KERN_ERR "SCISADDR2 0x%x\n", sci_reg_read(pcdev, REG_SCISADDR2));
	printk(KERN_ERR "SCITADDR2 0x%x\n", sci_reg_read(pcdev, REG_SCITADDR2));
	printk(KERN_ERR "SCIDCMD2 0x%x\n", sci_reg_read(pcdev, REG_SCIDCMD2));

	printk(KERN_ERR "SCIDBR0 0x%x\n", sci_reg_read(pcdev, REG_SCIDBR0));
	printk(KERN_ERR "SCIDCSR0 0x%x\n", sci_reg_read(pcdev, REG_SCIDCSR0));

	printk(KERN_ERR "SCIDBR1 0x%x\n", sci_reg_read(pcdev, REG_SCIDBR1));
	printk(KERN_ERR "SCIDCSR1 0x%x\n", sci_reg_read(pcdev, REG_SCIDCSR1));

	printk(KERN_ERR "SCIDBR2 0x%x\n", sci_reg_read(pcdev, REG_SCIDBR2));
	printk(KERN_ERR "SCIDCSR2 0x%x\n", sci_reg_read(pcdev, REG_SCIDCSR2));
}

static void dma_dump_desc(struct pxa955_cam_dev *pcdev)
{
	int i, k;
	struct pxa_buf_node *buf_node;
	printk(KERN_ERR "cam: dump_dma_desc ************+\n");

	printk(KERN_ERR "dma_buf_list head 0x%x\n",(unsigned int)&pcdev->dma_buf_list);
	list_for_each_entry(buf_node, &pcdev->dma_buf_list, vb.queue)  {
			printk(KERN_ERR "buf_node 0x%x\n",(unsigned int)buf_node);

		for (i = 0; i < pcdev->channels; i++){
			printk(KERN_ERR "chnnl %d, chnnl_size %d, sglen %d, sgdma 0x%x, sgsze %d\n",
				i,pcdev->channel_size[i],buf_node->dma_desc[i].sglen,buf_node->dma_desc[i].sg_dma,buf_node->dma_desc[i].sg_size);
			for (k = 0; k < buf_node->dma_desc[i].sglen; k++) {
				printk(KERN_ERR "dcmd [%d]  0x%x\n",k,buf_node->dma_desc[i].sg_cpu[k].dcmd);
				printk(KERN_ERR "ddadr[%d]  0x%x\n",k,buf_node->dma_desc[i].sg_cpu[k].ddadr);
				printk(KERN_ERR "dsadr[%d]  0x%x\n",k,buf_node->dma_desc[i].sg_cpu[k].dsadr);
				printk(KERN_ERR "dtadr[%d]  0x%x\n\n",k,buf_node->dma_desc[i].sg_cpu[k].dtadr);
			}
		}
	}

	printk(KERN_ERR "cam: dump_dma_desc ************-\n\n");
}

static void dma_dump_buf_list(struct pxa955_cam_dev *pcdev)
{
	struct pxa_buf_node *buf_node;
	dma_addr_t dma_handles;

	printk(KERN_ERR "cam: dump_dma_buf_list ************+\n");
	list_for_each_entry(buf_node, &pcdev->dma_buf_list, vb.queue) {
		dma_handles = videobuf_to_dma_contig(&buf_node->vb);
		printk(KERN_ERR "cam: buf_node 0x%x, pa 0x%x\n",
			(unsigned int)buf_node, dma_handles);
	}
	printk(KERN_ERR "cam: dump_dma_buf_list ************-\n\n");
}
#endif

/* only handle in irq context*/
static void dma_fetch_frame(struct pxa955_cam_dev *pcdev)
{
	struct pxa_buf_node *buf_node = NULL;
	int node_num = 0;
	dma_addr_t dma_handles;
	struct device *dev = pcdev->soc_host.v4l2_dev.dev;

	list_for_each_entry(buf_node, &pcdev->dma_buf_list, vb.queue) {
		if (buf_node->vb.state == VIDEOBUF_QUEUED)
			node_num++;
	}
	if (node_num > 1) {
		/*
		* get the first node of dma_list, it must have been filled by dma, and
		* remove it from dma-buf-list.
		*/
		buf_node = list_entry(pcdev->dma_buf_list.next,
			struct pxa_buf_node, vb.queue);
		dma_handles = videobuf_to_dma_contig(&buf_node->vb);

		/* invalid cache, to make sure user could get the real data from DDR*/
		dma_map_page(dev,
				buf_node->page,
				0,
				buf_node->vb.bsize,
				DMA_FROM_DEVICE);

		/* TODO: for JPEG

		if (pcdev->pix_format.pixelformat == V4L2_PIX_FMT_JPEG) {
			dma_sync_single_for_device(&pcdev->pdev->dev,
							sbuf->dma_handles,
							sbuf->v4lbuf.length,
							DMA_FROM_DEVICE);
			if ((((char *)buf_node->dma_bufs)[0] != 0xff)
			|| (((char *)buf_node->dma_bufs)[1] != 0xd8))
				printk(KERN_ERR "cam: JPEG ERROR !!! dropped this frame.\n");
		}
		*/
		buf_node->vb.state = VIDEOBUF_DONE;
		wake_up(&buf_node->vb.done);
		list_del_init(&buf_node->vb.queue);
	} else {
		/*if there is only one left in dma_list, drop it!*/
		printk(KERN_DEBUG "cam: drop a frame!\n");
	}

}

static void dma_append_desc(struct pxa955_cam_dev* pcdev,
								struct pxa_buf_node* pre,
								struct pxa_buf_node* next)
{
	int i = 0;
	struct pxa_cam_dma *pre_dma = NULL, *next_dma = NULL;

	for (i = 0; i < pcdev->channels; i++) {
		pre_dma = &pre->dma_desc[i];
		next_dma = &next->dma_desc[i];
		pre_dma->sg_cpu[pre_dma->sglen-1].ddadr = (u32)next_dma->sg_dma;
	}
	printk(KERN_DEBUG "cam: append new dma 0x%x to 0x%x\n",
		next_dma->sg_dma, pre_dma->sg_dma);
}

static void dma_attach_bufs(struct pxa955_cam_dev *pcdev)
{
	struct pxa_buf_node *buf_node = NULL;
	struct pxa_buf_node *tail_node = NULL;
	unsigned int regval;
	bool dma_branched = false;

	list_for_each_entry(buf_node, &pcdev->dma_buf_list, vb.queue) {

		if (buf_node->vb.state == VIDEOBUF_QUEUED)
			continue;

		/*
		* we got a new en-queue buf which is not in HW dma chain, append it to
		* the tail of HW dma chain, and loop the tail to itself.
		*/
		if (buf_node->vb.state == VIDEOBUF_ACTIVE) {

			tail_node = list_entry(buf_node->vb.queue.prev,
						struct pxa_buf_node, vb.queue);

			/*
			* NOTE!!! only one desc for one frame buffer, if not in this way,
			* need change here, as phy addr might not located between the
			* begin and end, phy addr might not continuous between different
			* desc of one frame buffer.
			*/
			regval = sci_reg_read(pcdev, REG_SCITADDR0);
			if (((regval >= videobuf_to_dma_contig(&tail_node->vb))
				&& (regval < (videobuf_to_dma_contig(&tail_node->vb)
				+ pcdev->channel_size[0])))
				&& (dma_branched == false)) {
				/*
				* if we find DMA is looping in the last buf, and there is new
				* coming buf, (DMA target address shows that DMA is working in
				* the tail buffer) we SHOULD set DMA branch reg, force DMA move
				* to the new buf descriptor in the next frame, so we can pick up
				* this buf when next irq comes.
				*/
				dma_branched = true;
				sci_reg_write(pcdev, REG_SCIDBR0, (buf_node->dma_desc[0].sg_dma | SCIDBR_EN));
				if(pcdev->channels == 3) {
					sci_reg_write(pcdev, REG_SCIDBR1, (buf_node->dma_desc[1].sg_dma | SCIDBR_EN));
					sci_reg_write(pcdev, REG_SCIDBR2, (buf_node->dma_desc[2].sg_dma | SCIDBR_EN));
				}
			} else {
				/*
				* if it is not the last buf which DMA looping in, just append
				* desc to the tail of the DMA chain.
				*/
				dma_append_desc(pcdev, tail_node, buf_node);
			}
			dma_append_desc(pcdev, buf_node, buf_node);
			buf_node->vb.state = VIDEOBUF_QUEUED;
		}
	}

}

static int dma_alloc_desc(struct pxa_buf_node *buf_node,
					struct pxa955_cam_dev *pcdev)
{
	int i;
	unsigned int 	len = 0, len_tmp = 0;
	pxa_dma_desc 	*dma_desc_tmp;
	unsigned long 	dma_desc_phy_tmp;
	unsigned long 	srcphyaddr, dstphyaddr;
	struct pxa_cam_dma *desc;
	struct videobuf_buffer *vb = &buf_node->vb;
	struct device *dev = pcdev->soc_host.v4l2_dev.dev;
	srcphyaddr = 0;	/* TBD */

	dstphyaddr = videobuf_to_dma_contig(vb);

	for (i = 0; i < pcdev->channels; i++) {
		printk(KERN_DEBUG "cam: index %d, channels %d\n",vb->i, i);
		desc = &buf_node->dma_desc[i];
		len = pcdev->channel_size[i];

		desc->sglen = (len + SINGLE_DESC_TRANS_MAX - 1) / SINGLE_DESC_TRANS_MAX;
		desc->sg_size = (desc->sglen) * sizeof(struct pxa_dma_desc);

		if (desc->sg_cpu == NULL){
			desc->sg_cpu = dma_alloc_coherent(dev, desc->sg_size,
					     &desc->sg_dma, GFP_KERNEL);
		}
		printk(KERN_DEBUG "cam: sglen %d, size %d, sg_cpu 0x%x\n",
			desc->sglen, desc->sg_size, (unsigned int)desc->sg_cpu);
		if (!desc->sg_cpu){
			printk(KERN_ERR "cam: dma_alloc_coherent failed at chnnl %d!\n", i);
			goto err;
		}

		dma_desc_tmp = desc->sg_cpu;
		dma_desc_phy_tmp = desc->sg_dma;

		while (len) {
			len_tmp = len > SINGLE_DESC_TRANS_MAX ?
				SINGLE_DESC_TRANS_MAX : len;

			if ((dstphyaddr & 0xf) != 0) {
				printk(KERN_ERR "cam: error: at least we need 16bytes align for DMA!\n");
				goto err;
			}
			dma_desc_tmp->ddadr = dma_desc_phy_tmp + sizeof(pxa_dma_desc);
			dma_desc_tmp->dsadr = srcphyaddr; /* TBD */
			dma_desc_tmp->dtadr = dstphyaddr;
			dma_desc_tmp->dcmd = len_tmp;

			len -= len_tmp;
			dma_desc_tmp++;
			dma_desc_phy_tmp += sizeof(pxa_dma_desc);
			dstphyaddr += len_tmp;

		}
	}
	return 0;

err:
	for (i = 0; i < pcdev->channels; i++) {
		desc = &buf_node->dma_desc[i];
		if (desc->sg_cpu) {
			dma_free_coherent(dev, desc->sg_size,
				    desc->sg_cpu, desc->sg_dma);
			desc->sg_cpu = 0;
		}
	}
	return -ENOMEM;
}

static void dma_free_desc(struct pxa_buf_node *buf_node,
							struct pxa955_cam_dev *pcdev)
{
	int i;
	struct pxa_cam_dma *desc;
	struct device *dev = pcdev->soc_host.v4l2_dev.dev;

	for (i = 0; i < pcdev->channels; i++) {
		desc = &buf_node->dma_desc[i];
		if (desc->sg_cpu){
			dma_free_coherent(dev, desc->sg_size,
				    desc->sg_cpu, desc->sg_dma);
			desc->sg_cpu = 0;
		}
	}
}

static void dma_free_bufs(struct videobuf_queue *vq,
				struct pxa_buf_node *buf,
				struct pxa955_cam_dev *pcdev)
{
	struct videobuf_buffer *vb = &buf->vb;
	struct videobuf_dma_contig_memory *mem = vb->priv;
	/*
	* TODO:
	* This waits until this buffer is out of danger, i.e., until it is no
	* longer in STATE_QUEUED or STATE_ACTIVE
	*/
	/*videobuf_waiton(vb, 0, 0);*/

	dma_free_desc(buf, pcdev);

	/*
	* TODO: as user will call stream-off when he hasn't en-queue all
	* request-buffers, if directlty call videobuf_dma_contig_free here,
	* it will release non-initizlized buffers, it causes kernel panic. So,
	* we implement our own free operation as follow to avoid kernel panic.
	* (refer to videobuf-dma-contig.c)
	*/
	/*videobuf_dma_contig_free(vq, &buf->vb);*/

	/*
	* mmapped memory can't be freed here, otherwise mmapped region
	* would be released, while still needed. In this case, the memory
	* release should happen inside videobuf_vm_close().
	* So, it should free memory only if the memory were allocated for
	* read() operation.
	*/
	if (vb->memory != V4L2_MEMORY_USERPTR)
		return;

	if (!mem)
		return;

	MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);

	/* handle user space pointer case */
	if (vb->baddr) {
		/*videobuf_dma_contig_user_put(mem);*/
		mem->is_userptr = 0;
		mem->dma_handle = 0;
		mem->size = 0;
	}

	buf->vb.state = VIDEOBUF_NEEDS_INIT;
}

static void dma_chain_init(struct pxa955_cam_dev *pcdev)
{
	int i = 0;
	struct pxa_cam_dma *dma_cur = NULL, *dma_pre = NULL;
	struct pxa_buf_node *buf_cur = NULL, *buf_pre = NULL;
	struct pxa_buf_node *buf_node;

	/*chain the buffers in the dma_buf_list list*/
	list_for_each_entry(buf_node, &pcdev->dma_buf_list, vb.queue) {
		if (buf_node->vb.state == VIDEOBUF_ACTIVE) {
			buf_cur = buf_node;

			/*head of the dma_buf_list, this is the first dma desc*/
			if (buf_node->vb.queue.prev == &pcdev->dma_buf_list) {
				for (i = 0; i < pcdev->channels; i++) {
					dma_cur = &buf_cur->dma_desc[i];
					if (buf_pre)
						dma_pre = &buf_pre->dma_desc[i];
					sci_reg_write(pcdev, REG_SCIDADDR0 + i*0x10, dma_cur->sg_dma);
				}
			} else {
				/* link to prev descriptor */
				dma_append_desc(pcdev, buf_pre, buf_cur);
			}

			/* find the tail, loop back to the tail itself */
			if (&pcdev->dma_buf_list == buf_node->vb.queue.next) {
				dma_append_desc(pcdev, buf_cur, buf_cur);
			}
			buf_pre = buf_cur;
			buf_node->vb.state = VIDEOBUF_QUEUED;
		}
	}

}

static int sci_cken(struct pxa955_cam_dev *pcdev, int flag)
{
	if (flag) {
		/* The order of enabling matters! AXI must be the 1st one */
		clk_enable(pcdev->sci1_clk);
		clk_enable(pcdev->sci2_clk);
	} else {
		clk_disable(pcdev->sci2_clk);
		clk_disable(pcdev->sci1_clk);
	};

	return 0;
}

#define pixfmtstr(x) (x) & 0xff, ((x) >> 8) & 0xff, ((x) >> 16) & 0xff, \
	((x) >> 24) & 0xff
static void sci_s_fmt(struct pxa955_cam_dev *pcdev,
				struct v4l2_pix_format *fmt)
{
	unsigned int size = fmt->width*fmt->height;
	printk(KERN_NOTICE "cam: set fmt as %c%c%c%c, %ux%u\n",
		pixfmtstr(fmt->pixelformat), fmt->width, fmt->height);

	switch (fmt->pixelformat) {

	case V4L2_PIX_FMT_RGB565:
		pcdev->channels = 1;
		pcdev->channel_size[0] = size*2;
		sci_reg_write(pcdev, REG_SCICR1, SCICR1_FMT_IN(FMT_RGB565) | SCICR1_FMT_OUT(FMT_RGB565));
	    break;

	case V4L2_PIX_FMT_JPEG:
		pcdev->channels = 1;
		/* use size get from sensor */
		/* pcdev->channel_size[0] = fmt->sizeimage;*/

		pcdev->channel_size[0] = JPEG_BUF_SIZE;
		sci_reg_write(pcdev, REG_SCICR1, SCICR1_FMT_IN(FMT_JPEG) | SCICR1_FMT_OUT(FMT_JPEG));
	    break;

	case V4L2_PIX_FMT_YUV422P:
		pcdev->channels = 3;
		pcdev->channel_size[0] = size;
		pcdev->channel_size[1] = pcdev->channel_size[2] = size/2;
		sci_reg_write(pcdev, REG_SCICR1, SCICR1_FMT_IN(FMT_YUV422) | SCICR1_FMT_OUT(FMT_YUV422));
		break;

	case V4L2_PIX_FMT_YVYU:
	case V4L2_PIX_FMT_YUYV:
	case V4L2_PIX_FMT_UYVY:
	case V4L2_PIX_FMT_VYUY:
		pcdev->channels = 1;
		pcdev->channel_size[0] = size*2;
		sci_reg_write(pcdev, REG_SCICR1, SCICR1_FMT_IN(FMT_YUV422) | SCICR1_FMT_OUT(FMT_YUV422PACKET));
		break;

	case V4L2_PIX_FMT_YUV420:
		/* only accept YUV422 as input */
		pcdev->channels = 3;
		pcdev->channel_size[0] = size;
		pcdev->channel_size[1] = pcdev->channel_size[2] = size/4;
		sci_reg_write(pcdev, REG_SCICR1, SCICR1_FMT_IN(FMT_YUV422) | SCICR1_FMT_OUT(FMT_YUV420));
		break;

	default:
		printk(KERN_ERR "cam: error can not support fmt!\n");
		break;
	}

}

static void sci_irq_enable(struct pxa955_cam_dev *pcdev, unsigned int val)
{
	sci_reg_write(pcdev, REG_SCISR, sci_reg_read(pcdev, REG_SCISR));
	sci_reg_clear_bit(pcdev, REG_SCIMASK, val);
}

static void sci_irq_disable(struct pxa955_cam_dev *pcdev, unsigned int val)
{
	sci_reg_set_bit(pcdev, REG_SCIMASK, val);
}

/*
 * Make the controller start grabbing images.  Everything must
 * be set up before doing this.
 */
static void sci_enable(struct pxa955_cam_dev *pcdev)
{
	int i = 0;
	unsigned int val = 0;

	/* start_fifo */
	for (i = 0; i < pcdev->channels; i++) {
		val = SCIFIFO_F0_EN << i;
		sci_reg_set_bit(pcdev, REG_SCIFIFO, val);
	}

	/* start_dma */
	for (i = 0; i < pcdev->channels; i++)
		sci_reg_set_bit(pcdev, REG_SCIDCSR0 + i*0x10, SCIDCSR_DMA_RUN);

	/* start sci */
	sci_reg_set_bit(pcdev, REG_SCICR0, SCICR0_CAP_EN | SCICR0_CI_EN);
}

static void sci_disable(struct pxa955_cam_dev *pcdev)
{
	int i = 0;
	unsigned int val = 0;

	/* stop_fifo */
	for (i = 0; i < pcdev->channels; i++) {
		val = SCIFIFO_F0_EN << i;
		sci_reg_clear_bit(pcdev, REG_SCIFIFO, val);
	}

	/* stop_dma */
	for (i = 0; i < pcdev->channels; i++) {
		sci_reg_clear_bit(pcdev, REG_SCIDCSR0 + i*0x10, SCIDCSR_DMA_RUN);
		sci_reg_clear_bit(pcdev, REG_SCIDBR0 + i*0x10, SCIDBR_EN);
	}
	/* stop sci */
	sci_reg_clear_bit(pcdev, REG_SCICR0, SCICR0_CAP_EN | SCICR0_CI_EN);
}

void sci_init(struct pxa955_cam_dev *pcdev)
{
	/*
	* Turn off the enable bit.  It sure should be off anyway,
	* but it's good to be sure.
	*/
	sci_reg_clear_bit(pcdev, REG_SCICR0, SCICR0_CI_EN);

	/* Mask all interrupts.*/
	sci_reg_write(pcdev, REG_SCIMASK, ~0);
}

static unsigned long uva_to_pa(unsigned long addr, struct page **page)
{
	unsigned long ret = 0UL;
	pgd_t *pgd;
	pud_t *pud;
	pmd_t *pmd;
	pte_t *pte;

	pgd = pgd_offset(current->mm, addr);
	if (!pgd_none(*pgd)) {
		pud = pud_offset(pgd, addr);
		if (!pud_none(*pud)) {
			pmd = pmd_offset(pud, addr);
			if (!pmd_none(*pmd)) {
				pte = pte_offset_map(pmd, addr);
				if (!pte_none(*pte) && pte_present(*pte)) {
					(*page) = pte_page(*pte);
					ret = page_to_phys(*page);
					ret |= (addr & (PAGE_SIZE-1));
				}
			}
		}
	}
	return ret;
}

struct page* va_to_page(unsigned long user_addr)
{
	struct page *page = NULL;
	unsigned int vaddr = PAGE_ALIGN(user_addr);

	if (uva_to_pa(vaddr, &page) != 0)
		return page;

	return 0;
}
unsigned long va_to_pa(unsigned long user_addr, unsigned int size)
{
	unsigned long  paddr, paddr_tmp;
	unsigned long  size_tmp = 0;
	struct page *page = NULL;
	int page_num = PAGE_ALIGN(size) / PAGE_SIZE;
	unsigned int vaddr = PAGE_ALIGN(user_addr);
	int i = 0;

	if(vaddr == 0)
		return 0;

	paddr = uva_to_pa(vaddr, &page);

	for (i = 0; i < page_num; i++) {
		paddr_tmp = uva_to_pa(vaddr, &page);
		if ((paddr_tmp - paddr) != size_tmp)
			return 0;
		vaddr += PAGE_SIZE;
		size_tmp += PAGE_SIZE;
	}
	return paddr;
}

static int pxa955_videobuf_setup(struct videobuf_queue *vq, unsigned int *count,
			      unsigned int *size)
{
	struct soc_camera_device *icd = vq->priv_data;
	int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
						icd->current_fmt->host_fmt);

	if (bytes_per_line < 0)
		return bytes_per_line;

	if (icd->current_fmt->host_fmt->fourcc != V4L2_PIX_FMT_JPEG) {
		dev_dbg(icd->dev.parent, "count=%d, size=%d\n", *count, *size);

		*size = bytes_per_line * icd->user_height;
		if (0 == *count)
			*count = 32;
		if (*size * *count > vid_mem_limit * 1024 * 1024)
			*count = (vid_mem_limit * 1024 * 1024) / *size;
	} else {
		*size = JPEG_BUF_SIZE;
		if (0 == *count)
			*count = 32;
		if (*size * *count > vid_mem_limit * 1024 * 1024)
			*count = (vid_mem_limit * 1024 * 1024) / *size;
	}
	return 0;
}

static int pxa955_videobuf_prepare(struct videobuf_queue *vq,
		struct videobuf_buffer *vb, enum v4l2_field field)
{
	struct soc_camera_device *icd = vq->priv_data;
	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
	struct pxa955_cam_dev *pcdev = ici->priv;
	struct pxa_buf_node *buf =
		container_of(vb, struct pxa_buf_node, vb);
	struct pxa_cam_dma *desc;
	unsigned int vaddr;
	int ret;
	size_t new_size;
	int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
						icd->current_fmt->host_fmt);
	if (vb->memory == V4L2_MEMORY_USERPTR) {

		vaddr = PAGE_ALIGN(vb->baddr);
		if (vaddr != vb->baddr) {
			printk(KERN_ERR "cam: the memory is not page align!\n");
			return -EPERM;
		}

		buf->page = va_to_page(vaddr);
		if (!buf->page) {
			printk(KERN_ERR "cam: fail to get page info!\n");
			return -EFAULT;
		}
	}

	if (bytes_per_line < 0)
		return bytes_per_line;
	new_size = bytes_per_line * icd->user_height;
	if (icd->current_fmt->host_fmt->fourcc == V4L2_PIX_FMT_JPEG) {
		new_size = JPEG_BUF_SIZE;
	}

	if (buf->code	!= icd->current_fmt->code ||
	    vb->width	!= icd->user_width ||
	    vb->height	!= icd->user_height ||
	    vb->field	!= field) {
		buf->code	= icd->current_fmt->code;
		vb->width	= icd->user_width;
		vb->height	= icd->user_height;
		vb->field	= field;
		if (vb->state != VIDEOBUF_NEEDS_INIT) {
			dma_free_bufs(vq, buf, pcdev);
		}
	}

	if (vb->baddr && (vb->bsize < new_size)) {
		/* User provided buffer, but it is too small */
		printk(KERN_ERR
			"cam: buf in use %d is smaller than required size %d!\n",
			vb->bsize, new_size);
		return -ENOMEM;
	}

	if (vb->state == VIDEOBUF_NEEDS_INIT) {
		/*
		* The total size of video-buffers that will be allocated / mapped.
		* size that we calculated in videobuf_setup gets assigned to
		* vb->bsize, and now we use the same calculation to get vb->size.
		*/
		vb->size = new_size;

		/* This actually (allocates and) maps buffers */
		ret = videobuf_iolock(vq, vb, NULL);

		desc = &buf->dma_desc[0];
		if (desc->sg_cpu == NULL) {
			dma_alloc_desc(buf, pcdev);
		}

		vb->state = VIDEOBUF_PREPARED;
	}

	return 0;
}

static void pxa955_videobuf_queue(struct videobuf_queue *vq,
			       struct videobuf_buffer *vb)
{
	struct soc_camera_device *icd = vq->priv_data;
	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
	struct pxa955_cam_dev *pcdev = ici->priv;
	struct csi_phy_config timing;

	dev_dbg(icd->dev.parent, "%s (vb=0x%p) 0x%08lx %d\n",
		__func__, vb, vb->baddr, vb->bsize);

	list_add_tail(&vb->queue, &pcdev->dma_buf_list);
	vb->state = VIDEOBUF_ACTIVE;

	if (vq->streaming == 1) {

		/* at first time, streamon set to true, means stream-on*/
		if (!pcdev->streamon) {

			if (list_empty(&pcdev->dma_buf_list)) {
				printk(KERN_ERR "cam: internal buffers are not ready!\n");
				return;
			}
			pcdev->streamon = true;
			pcdev->streamoff = false;

			dma_chain_init(pcdev);

			sci_irq_enable(pcdev, IRQ_EOFX|IRQ_OFO);
			pxa955_csi_dphy(pcdev->csidev, timing);
			/* configure enable which camera interface controller*/
			pxa955_csi_enable(pcdev->csidev, 0);
			sci_enable(pcdev);
		}
	}
}

static void pxa955_videobuf_release(struct videobuf_queue *vq,
				 struct videobuf_buffer *vb)
{
	struct soc_camera_device *icd = vq->priv_data;
	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
	struct pxa955_cam_dev *pcdev = ici->priv;
	struct pxa_buf_node *buf = container_of(vb, struct pxa_buf_node, vb);

#ifdef DEBUG
	struct device *dev = icd->dev.parent;

	dev_err(dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
		vb, vb->baddr, vb->bsize);

	switch (vb->state) {
	case VIDEOBUF_ACTIVE:
		dev_dbg(dev, "%s (active)\n", __func__);
		break;
	case VIDEOBUF_QUEUED:
		dev_dbg(dev, "%s (queued)\n", __func__);
		break;
	case VIDEOBUF_PREPARED:
		dev_dbg(dev, "%s (prepared)\n", __func__);
		break;
	default:
		dev_dbg(dev, "%s (unknown)\n", __func__);
		break;
	}
#endif

	if ((vb->state == VIDEOBUF_ACTIVE || vb->state == VIDEOBUF_QUEUED) &&
	    !list_empty(&vb->queue)) {
		vb->state = VIDEOBUF_ERROR;

		list_del_init(&vb->queue);
	}

	if (vq->streaming == 0) {
		if (!pcdev->streamoff) {
			INIT_LIST_HEAD(&pcdev->dma_buf_list);
			pcdev->streamon = false;
			pcdev->streamoff = true;

			pxa955_csi_disable(pcdev->csidev);
			sci_irq_disable(pcdev, IRQ_EOFX|IRQ_OFO);
			sci_disable(pcdev);
		}
	}
	dma_free_bufs(vq, buf, pcdev);

}

static struct videobuf_queue_ops pxa955_videobuf_ops = {
	.buf_setup      = pxa955_videobuf_setup,
	.buf_prepare    = pxa955_videobuf_prepare,
	.buf_queue      = pxa955_videobuf_queue,
	.buf_release    = pxa955_videobuf_release,
};

static int pxa955_cam_add_device(struct soc_camera_device *icd)
{
	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
	struct pxa955_cam_dev *pcdev = ici->priv;

	if (pcdev->icd)
		return -EBUSY;

	pcdev->icd = icd;

	pxa955_csi_cken(pcdev->csidev, 1);
	sci_cken(pcdev, 1);
	sci_init(pcdev);
	pxa955_csi_clkdiv(pcdev->csidev);

	dev_info(icd->dev.parent, "pxa955 camera driver attached to camera %d\n",
		 icd->devnum);

	return 0;
}

static void pxa955_cam_remove_device(struct soc_camera_device *icd)
{
	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
	struct pxa955_cam_dev *pcdev = ici->priv;

	BUG_ON(icd != pcdev->icd);
	sci_cken(pcdev, 0);
	pxa955_csi_cken(pcdev->csidev, 0);

	pcdev->icd = NULL;

	dev_info(icd->dev.parent, "pxa955 Camera driver detached from camera %d\n",
		 icd->devnum);
}

static const struct soc_mbus_pixelfmt pxa955_camera_formats[] = {
	{
			.fourcc 	= V4L2_PIX_FMT_YUV422P,
			.name		= "YUV422P",
			.bits_per_sample	= 8,
			.packing		= SOC_MBUS_PACKING_2X8_PADLO,
			.order			= SOC_MBUS_ORDER_LE,
	},{
		.fourcc			= V4L2_PIX_FMT_YUV420,
		.name			= "YUV420P",
		.bits_per_sample	= 8,
		.packing		= SOC_MBUS_PACKING_2X8_PADLO,
		.order			= SOC_MBUS_ORDER_LE,
	},
};

/* pxa955_cam_get_formats provide all fmts that camera controller support*/
static int pxa955_cam_get_formats(struct soc_camera_device *icd, unsigned int idx,
				  struct soc_camera_format_xlate *xlate)
{
	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
	struct device *dev = icd->dev.parent;
	int formats = 0, ret, i;
	enum v4l2_mbus_pixelcode code;
	const struct soc_mbus_pixelfmt *fmt;

	ret = v4l2_subdev_call(sd, video, enum_mbus_fmt, idx, &code);
	if (ret < 0)
		/* No more formats */
		return 0;

	fmt = soc_mbus_get_fmtdesc(code);
	if (!fmt) {
		dev_err(dev, "Invalid format code #%u: %d\n", idx, code);
		return 0;
	}

	switch (code) {
	/* refer to mbus_fmt struct*/
	case V4L2_MBUS_FMT_YUYV8_2X8_BE:
		formats = ARRAY_SIZE(pxa955_camera_formats);

		if (xlate) {
			for (i = 0; i < ARRAY_SIZE(pxa955_camera_formats); i++) {
				xlate->host_fmt = &pxa955_camera_formats[i];
				xlate->code	= code;
				xlate++;
				dev_err(dev, "Providing format %s\n",
					pxa955_camera_formats[i].name);
			}
			dev_err(dev, "Providing format %s\n", fmt->name);
		}
		break;

	case V4L2_MBUS_FMT_YVYU8_2X8_BE:
	case V4L2_MBUS_FMT_YVYU8_2X8_LE:
	case V4L2_MBUS_FMT_YUYV8_2X8_LE:
	case V4L2_MBUS_FMT_RGB565_2X8_LE:
	case V4L2_MBUS_FMT_RGB565_2X8_BE:
	case V4L2_MBUS_FMT_JPEG_1X8:
		if (xlate)
			dev_err(dev, "Providing format %s\n", fmt->name);
		break;
	default:
		/* camera controller can not support this format, which might supported by the sensor*/
		dev_err(dev, "Not support fmt %s\n", fmt->name);
		return 0;
	}

	/* Generic pass-through */
	formats++;
	if (xlate) {
		xlate->host_fmt	= fmt;
		xlate->code	= code;
		xlate++;
	}

	return formats;
}

static void pxa955_cam_put_formats(struct soc_camera_device *icd)
{
	kfree(icd->host_priv);
	icd->host_priv = NULL;
}

static int pxa955_cam_try_fmt(struct soc_camera_device *icd,
			      struct v4l2_format *f)
{
	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
	const struct soc_camera_format_xlate *xlate;
	struct v4l2_pix_format *pix = &f->fmt.pix;
	struct v4l2_mbus_framefmt mf;
	__u32 pixfmt = pix->pixelformat;
	int ret;

	xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
	if (!xlate) {
		dev_warn(icd->dev.parent, "Format %x not found\n", pixfmt);
		return -EINVAL;
	}

	pix->bytesperline = soc_mbus_bytes_per_line(pix->width,
							xlate->host_fmt);
	if (pix->bytesperline < 0)
		return pix->bytesperline;
	pix->sizeimage = pix->height * pix->bytesperline;

	/* limit to sensor capabilities */
	mf.width	= pix->width;
	mf.height	= pix->height;
	mf.field	= pix->field;
	mf.colorspace	= pix->colorspace;
	mf.code 	= xlate->code;

	ret = v4l2_subdev_call(sd, video, try_mbus_fmt, &mf);
	if (ret < 0)
		return ret;

	pix->width	= mf.width;
	pix->height = mf.height;
	pix->colorspace = mf.colorspace;

	switch (mf.field) {
	case V4L2_FIELD_ANY:
	case V4L2_FIELD_NONE:
		pix->field	= V4L2_FIELD_NONE;
		break;
	default:
		dev_err(icd->dev.parent, "Field type %d unsupported.\n",
			mf.field);
		return -EINVAL;
	}

	return ret;
}

static int pxa955_cam_set_fmt(struct soc_camera_device *icd,
			      struct v4l2_format *f)
{
	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
	struct pxa955_cam_dev *pcdev = ici->priv;
	struct device *dev = icd->dev.parent;
	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
	const struct soc_camera_format_xlate *xlate = NULL;
	struct v4l2_pix_format *pix = &f->fmt.pix;
	__u32 pixfmt = pix->pixelformat;
	struct v4l2_mbus_framefmt mf;
	int ret;

	xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
	if (!xlate) {
		dev_warn(dev, "Format %x not found\n", pixfmt);
		return -EINVAL;
	}

	mf.width	= pix->width;
	mf.height	= pix->height;
	mf.field	= pix->field;
	mf.colorspace	= pix->colorspace;
	mf.code		= xlate->code;

	ret = v4l2_subdev_call(sd, video, s_mbus_fmt, &mf);

	if (mf.code != xlate->code)
		return -EINVAL;

	icd->sense = NULL;
	pix->width		= mf.width;
	pix->height		= mf.height;
	pix->field		= mf.field;
	pix->colorspace		= mf.colorspace;
	icd->current_fmt	= xlate;

	sci_disable(pcdev);
	sci_s_fmt(pcdev, pix);

	return ret;
}

static void pxa955_cam_init_videobuf(struct videobuf_queue *q,
			      struct soc_camera_device *icd)
{
	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
	struct pxa955_cam_dev *pcdev = ici->priv;

	/*
	* We must pass NULL as dev pointer, then all pci_* dma operations
	* transform to normal dma_* ones.
	*/
	videobuf_queue_dma_contig_init(q, &pxa955_videobuf_ops, NULL, &pcdev->spin_lock,
				V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE,
				sizeof(struct pxa_buf_node), icd);
}

static int pxa955_cam_reqbufs(struct soc_camera_file *icf,
			      struct v4l2_requestbuffers *p)
{
	int i;
	if (p->count < MIN_DMA_BUFS) {
		printk(KERN_ERR "cam: need %d buffers at least!\n", MIN_DMA_BUFS);
		return -EINVAL;
	}

	/*
	*This is for locking debugging only. I removed spinlocks and now I
	* check whether .prepare is ever called on a linked buffer, or whether
	* a dma IRQ can occur for an in-work or unlinked buffer. Until now
	* it hadn't triggered
	*/
	for (i = 0; i < p->count; i++) {
		struct pxa_buf_node *buf = container_of(icf->vb_vidq.bufs[i],
						      struct pxa_buf_node, vb);
		INIT_LIST_HEAD(&buf->vb.queue);
	}
	return 0;
}

static unsigned int pxa955_cam_poll(struct file *file, poll_table *pt)
{
	struct soc_camera_file *icf = file->private_data;
	return videobuf_poll_stream(file, &icf->vb_vidq, pt);
}

static int pxa955_cam_querycap(struct soc_camera_host *ici,
			       struct v4l2_capability *cap)
{
	struct v4l2_dbg_chip_ident id;
	struct pxa955_cam_dev *pcdev = ici->priv;
	struct soc_camera_device *icd = pcdev->icd;
	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
	int ret = 0;

	cap->version = PXA955_CAM_VERSION_CODE;
	cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
	strcpy(cap->card, "MG1");
	strcpy(cap->driver, "N/A");

	ret = v4l2_subdev_call(sd, core, g_chip_ident, &id);
	if (ret < 0) {
		printk(KERN_ERR "cam: failed to get sensor's name!\n");
		return ret;
	}
	switch (id.ident) {
		case V4L2_IDENT_OV5642:
			strcpy(cap->driver, "ov5642");
			break;
		case V4L2_IDENT_OV7690:
			strcpy(cap->driver, "ov7690");
			break;
	}
	return 0;
}

static int pxa955_cam_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
{
	struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
	struct pxa955_cam_dev *pcdev = ici->priv;
	unsigned long ctrller_flags, sensor_flags, common_flags;
	int ret;
	int lane = 1;

	ctrller_flags = SOCAM_MIPI | SOCAM_MIPI_1LANE | SOCAM_MIPI_2LANE;
	sensor_flags = icd->ops->query_bus_param(icd);

	common_flags = soc_camera_bus_param_compatible(sensor_flags, ctrller_flags);
	if (!common_flags) {
		return -EINVAL;
	}

	ret = icd->ops->set_bus_param(icd, common_flags);
	if (ret < 0)
		return ret;

	if (common_flags & SOCAM_MIPI_1LANE) {
		lane = 1;
	} else if (common_flags & SOCAM_MIPI_2LANE) {
		lane = 2;
	}
	pxa955_csi_lane(pcdev->csidev, lane);
	return 0;
}

static int pxa955_cam_set_param(struct soc_camera_device *icd,
				  struct v4l2_streamparm *parm)
{
	return 0;
}

static int pxa955_cam_enum_fsizes(struct soc_camera_device *icd,
					struct v4l2_frmsizeenum *fsizes)
{
	int ret;
	struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
	const struct soc_camera_format_xlate *xlate;
	__u32 pixfmt = fsizes->pixel_format;
	struct v4l2_frmsizeenum *fsize_mbus = fsizes;

	xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
	if (!xlate)
		return -EINVAL;

	/* map xlate-code to pixel_format, sensor only handle xlate-code*/
	fsize_mbus->pixel_format = xlate->code;

	ret = v4l2_subdev_call(sd, video, enum_mbus_fsizes, fsize_mbus);
	if (ret < 0)
		return ret;

	fsizes->pixel_format = pixfmt;

	return 0;
}

static struct soc_camera_host_ops pxa955_soc_cam_host_ops = {
	.owner		= THIS_MODULE,
	.add		= pxa955_cam_add_device,
	.remove		= pxa955_cam_remove_device,
	.get_formats	= pxa955_cam_get_formats,
	.put_formats	= pxa955_cam_put_formats,
	.set_fmt	= pxa955_cam_set_fmt,
	.try_fmt	= pxa955_cam_try_fmt,
	.init_videobuf	= pxa955_cam_init_videobuf,
	.reqbufs	= pxa955_cam_reqbufs,
	.poll		= pxa955_cam_poll,
	.querycap	= pxa955_cam_querycap,
	.set_bus_param	= pxa955_cam_set_bus_param,
	.set_parm = pxa955_cam_set_param,
	.enum_fsizes = pxa955_cam_enum_fsizes,
};

static irqreturn_t pxa955_cam_irq(int irq, void *data)
{
	struct pxa955_cam_dev *pcdev = data;
	unsigned int irqs = 0;

	spin_lock(&pcdev->spin_lock);
	irqs = sci_reg_read(pcdev, REG_SCISR);
	sci_reg_write(pcdev, REG_SCISR, irqs);		/*clear irqs here*/
	if (irqs & IRQ_OFO) {

		printk(KERN_ERR "cam: ccic over flow error!\n");
		pxa955_csi_disable(pcdev->csidev);
		sci_disable(pcdev);
		pxa955_csi_enable(pcdev->csidev, 0);
		sci_enable(pcdev);
		spin_unlock(&pcdev->spin_lock);
		return IRQ_HANDLED;
	}

	if (irqs & IRQ_EOFX) {
		dma_fetch_frame(pcdev);
		dma_attach_bufs(pcdev);
	}

	spin_unlock(&pcdev->spin_lock);
	return IRQ_HANDLED;
}

static int pxa955_camera_probe(struct platform_device *pdev)
{
	struct resource *res;
	int err = -ENOMEM;
	struct pxa955_cam_dev *pcdev;

	pcdev = kzalloc(sizeof(struct pxa955_cam_dev), GFP_KERNEL);
	if (pcdev == NULL)
		goto exit;
	memset(pcdev, 0, sizeof(struct pxa955_cam_dev));

	spin_lock_init(&pcdev->spin_lock);
	init_waitqueue_head(&pcdev->iowait);
	INIT_LIST_HEAD(&pcdev->dev_list);
	INIT_LIST_HEAD(&pcdev->dma_buf_list);

	pcdev->pdata = pdev->dev.platform_data;
	if (pcdev->pdata == NULL) {
		printk(KERN_ERR "cam: camera no platform data defined\n");
		return -ENODEV;
	}

	pcdev->irq = platform_get_irq(pdev, 0);
	if (pcdev->irq < 0) {
		printk(KERN_ERR "cam: camera no irq\n");
		return -ENXIO;
	}
	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (res == NULL) {
		printk(KERN_ERR "cam: no IO memory resource defined\n");
		return -ENODEV;
	}

	err = -EIO;
	pcdev->regs = ioremap(res->start, SZ_4K);
	if (!pcdev->regs) {
		printk(KERN_ERR "cam: unable to ioremap pxa95x-camera regs\n");
		goto exit_free;
	}
	err = request_irq(pcdev->irq, pxa955_cam_irq,
		0, PXA955_CAM_DRV_NAME, pcdev);
	if (err) {
		printk(KERN_ERR "cam: unable to create ist\n");
		goto exit_iounmap;
	}

	pcdev->sci1_clk = clk_get(NULL, "SCI1CLK");
	if (!pcdev->sci1_clk) {
		printk(KERN_ERR "cam: unable to get sci clk\n");
		goto exit_free_irq;
	}
	pcdev->sci2_clk = clk_get(NULL, "SCI2CLK");
	if (!pcdev->sci2_clk) {
		printk(KERN_ERR "cam: unable to get sci clk\n");
		goto exit_free_irq;
	}

	csi_attach(pcdev);

	pcdev->soc_host.drv_name	= PXA955_CAM_DRV_NAME;
	pcdev->soc_host.ops		= &pxa955_soc_cam_host_ops;
	pcdev->soc_host.priv		= pcdev;
	pcdev->soc_host.v4l2_dev.dev	= &pdev->dev;
	pcdev->soc_host.nr		= pdev->id;

	err = soc_camera_host_register(&pcdev->soc_host);
	if (err)
		goto exit_free_irq;

	return 0;

exit_free_irq:
	free_irq(pcdev->irq, pcdev);
exit_iounmap:
	iounmap(pcdev->regs);
	clk_put(pcdev->sci1_clk);
	clk_put(pcdev->sci2_clk);
exit_free:
	kfree(pcdev);
exit:
	return err;
}


static int pxa955_camera_remove(struct platform_device *pdev)
{
	struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev);
	struct pxa955_cam_dev *pcdev = container_of(soc_host,
					struct pxa955_cam_dev, soc_host);

	if (pcdev == NULL) {
		printk(KERN_WARNING "cam: remove on unknown pdev %p\n", pdev);
		return -EIO;
	}

	pxa955_csi_disable(pcdev->csidev);

	sci_disable(pcdev);
	sci_irq_disable(pcdev, IRQ_EOFX|IRQ_OFO);
	free_irq(pcdev->irq, pcdev);
	return 0;
}

static struct platform_driver pxa955_camera_driver = {
	.driver = {
		.name = PXA955_CAM_DRV_NAME
	},
	.probe 		= pxa955_camera_probe,
	.remove 	= pxa955_camera_remove,

};

static int __devinit pxa955_camera_init(void)
{
	int ret = 0;

	ret = platform_driver_register(&pxa955_camera_driver);
	return ret;
}

static void __exit pxa955_camera_exit(void)
{
	platform_driver_unregister(&pxa955_camera_driver);
}

module_init(pxa955_camera_init);
module_exit(pxa955_camera_exit);

[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