Re: [PATCH] V4L/DVB: Add support for M5MOLS Mega Pixel camera

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

 



Thanks for the reminder, I missed this patch.

Review comments are below.

On Friday, December 10, 2010 08:40:27 Kim, HeungJun wrote:
> This patch adds support for M5MOLS Mega Pixel Fujitsu camera sensor.
> 
> --
> 
> Hello,
> 
> This is an initial version of I2C/V4L2 subdev driver for M5MOLS camera
> sensor using MIPI interface from Fujitsu. This sensor supports various
> resolution at various fps on Monitoring(as we know preview),
> Preview(camcording), Capture(JPEG) mode. And, each mode supports
> different resolution range. Moreover, there are many image processing
> controls(flip, rotation, focus, zoom) and effects(even like a face
> recognitions). It has been tested with samsung/fimc and s5p-fimc driver
> on Aquila board. And this driver changed until now.
> 
> This version supports just Monitoring mode at it's own many resolutions.
> But, there are many features in this sensor, and it's so hard to express
> all this features using a current CID or something else. so, I hope
> this initial version is merged soon in v4l/dvb, and then I wish the
> new needed CID is being added for this, and any other devices in addition.
> 
> Any ideas are appreciated, Let me know.
> 
> Thanks.
> 
> Regards,
> HeungJun Kim
> 
> Signed-off-by: Heungjun Kim <riverful.kim@xxxxxxxxxxx>
> Signed-off-by: Kyungmin Part <kyungmin.park@xxxxxxxxxxx>
> ---
>  drivers/media/video/Kconfig  |    6 +
>  drivers/media/video/Makefile |    1 +
>  drivers/media/video/m5mols.c | 1410 ++++++++++++++++++++++++++++++++++++++++++
>  include/media/m5mols.h       |   31 +
>  4 files changed, 1448 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/media/video/m5mols.c
>  create mode 100644 include/media/m5mols.h
> 
> diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
> index 6830d28..f2e6080 100644
> --- a/drivers/media/video/Kconfig
> +++ b/drivers/media/video/Kconfig
> @@ -712,6 +712,12 @@ config VIDEO_SR030PC30
>  	---help---
>  	  This driver supports SR030PC30 VGA camera from Siliconfile
>  
> +config VIDEO_M5MOLS
> +	tristate "Fujitsu M5MOLS 8M Pixel sensor support"
> +	depends on I2C && VIDEO_V4L2
> +	---help---
> +	  This driver supports M5MOLS 8 MEGA Pixel camera from Fujitsu
> +
>  config VIDEO_VIA_CAMERA
>  	tristate "VIAFB camera controller support"
>  	depends on FB_VIA
> diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
> index af79d47..c76e44f 100644
> --- a/drivers/media/video/Makefile
> +++ b/drivers/media/video/Makefile
> @@ -72,6 +72,7 @@ obj-$(CONFIG_VIDEO_TCM825X) += tcm825x.o
>  obj-$(CONFIG_VIDEO_TVEEPROM) += tveeprom.o
>  obj-$(CONFIG_VIDEO_MT9V011) += mt9v011.o
>  obj-$(CONFIG_VIDEO_SR030PC30)	+= sr030pc30.o
> +obj-$(CONFIG_VIDEO_M5MOLS)	+= m5mols.o
>  
>  obj-$(CONFIG_SOC_CAMERA_IMX074)		+= imx074.o
>  obj-$(CONFIG_SOC_CAMERA_MT9M001)	+= mt9m001.o
> diff --git a/drivers/media/video/m5mols.c b/drivers/media/video/m5mols.c
> new file mode 100644
> index 0000000..9f5c445
> --- /dev/null
> +++ b/drivers/media/video/m5mols.c
> @@ -0,0 +1,1410 @@
> +/*
> + * Driver for M5MOLS 8M Pixel camera sensor with ISP
> + *
> + * Copyright (C) 2010 Samsung Electronics Co., Ltd
> + * Author: HeungJun Kim, riverful.kim@xxxxxxxxxxx
> + *
> + * Copyright (C) 2009 Samsung Electronics Co., Ltd
> + * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@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/i2c.h>
> +#include <linux/delay.h>
> +#include <linux/slab.h>
> +#include <linux/regulator/consumer.h>
> +
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-subdev.h>
> +#include <media/m5mols.h>
> +
> +static int debug;
> +module_param(debug, int, 0644);
> +
> +/* MACRO */
> +#define e_check_w(fn, cat, byte, val, bitwidth)		do {	\
> +	int ret;						\
> +	ret = (int)(fn);					\
> +	if ((ret) < 0) {					\
> +		dev_err(&client->dev, "fail i2c WRITE [%s] - "	\
> +				"category:0x%02x, "		\
> +				"bytes:0x%02x, "		\
> +				"value:0x%02x\n",		\
> +				(bitwidth),			\
> +				(cat), (byte), (u32)val);	\
> +		return ret;					\
> +	}							\
> +} while (0)
> +
> +#define e_check_r(fn, cat, byte, val, bitwidth)		do {	\
> +	int ret;						\
> +	ret = (int)(fn);					\
> +	if ((ret) < 0) {					\
> +		dev_err(&client->dev, "fail i2c READ [%s] - "	\
> +				"category:0x%02x, "		\
> +				"bytes:0x%02x, "		\
> +				"value:0x%02x\n",		\
> +				(bitwidth),			\
> +				(cat), (byte), (u32)(*val));	\
> +		return ret;					\
> +	}							\
> +} while (0)
> +
> +#define REG_W_8(cat, byte, value)					\
> +	e_check_w(m5mols_write_reg(sd, M5MOLS_8BIT, cat, byte, value),	\
> +			cat, byte, value, "8bit")
> +#define REG_R_8(cat, byte, value)					\
> +	e_check_r(m5mols_read_reg(sd, M5MOLS_8BIT, cat, byte, value),	\
> +			cat, byte, value, "8bit")
> +
> +#define e_check_mode(fn, mode)				do {	\
> +	int ret;						\
> +	ret = (int)(fn);					\
> +	if (ret < 0) {						\
> +		dev_err(&client->dev, "Failed to %s mode\n",	\
> +				(mode));			\
> +		return ret;					\
> +	}							\
> +} while (0)
> +
> +#define mode_monitoring(sd)					\
> +	e_check_mode(m5mols_monitoring_mode(sd), "MONITORING")
> +#define mode_parameter(sd)					\
> +	e_check_mode(m5mols_parameter_mode(sd), "PARAMETER")

All these #defines above can be replaced by static inline functions. That the
better option since static inline functions are type-safe.

> +#define v4l2msg(fmt, arg...)			do {	\
> +	v4l2_dbg(1, debug, &info->sd, fmt, ## arg);	\
> +} while (0)
> +
> +/*
> + * Specification
> + *
> + * Sensor can be attached up to resolution of 4096*1664
> + * Supports the embedded JPEG encoder and object recognition control.
> + *
> + * QXGA format sensor with 1/4" optics
> + * Effective resolution: 2048(H)x1536(V)
> + * JPEG on the fly compression
> + * MJPEG supported
> + * 8-bit parallel video interface
> + * Output format:
> + *  8-Bit ITU-R.656/601 (4:2:2 YCbCr)
> + *  565RGB
> + *  CIS Raw Data
> + * input clock frequency of 13MHz-27MHz
> + * Variable frame rate Up to 7.5fps for QXGA @60MHz PCLK
> + * and up to 30fps for VGA
> + */
> +
> +#define MOD_NAME		"M5MOLS"
> +
> +#define DEFAULT_FPS		30
> +#define DEFAULT_WIDTH		1280
> +#define DEFAULT_HEIGHT		720
> +#define DEFAULT_PRESET		0x21
> +#define DEFAULT_DATABUS		M5MOLS_DATABUS_MIPI
> +#define DEFAULT_CODE		V4L2_MBUS_FMT_YUYV8_2X8
> +
> +#define M5MOLS_I2C_RETRY	3
> +#define M5MOLS_I2C_CHECK_RETRY	50
> +
> +/* chip version */
> +#define M5MOLS_VERSION		0x21
> +#define	M5MOLS_DATABUS_MIPI	2
> +
> +/* category register */
> +#define CAT_SYSTEM		0x00
> +#define CAT_PARAM		0x01
> +#define CAT_MON			0x02
> +#define CAT_AE			0x03
> +#define CAT_WB			0x06
> +#define CAT_CAP_PARAM		0x0B
> +#define CAT_CAP_CTRL		0x0C
> +#define CAT_FLASH		0x0F	/* related with F/W */
> +
> +/* category 00: System register bytes */
> +#define CAT0_PJ_CODE		0x01
> +#define CAT0_SYSMODE		0x0B
> +#define CAT0_INT_ROOTEN		0x12
> +
> +/* category 01: Parameter mode register bytes */
> +#define CAT1_DATABUS		0x00
> +#define CAT1_MONSIZE		0x01
> +#define CAT1_MONFPS		0x02
> +#define CAT1_EFFECT		0x0B
> +#define CAT1_DENOMINATOR	0x31
> +
> +/* category 02: Monitor register bytes */
> +#define CAT2_CFIXB		0x09
> +#define CAT2_CFIXR		0x0A
> +#define CAT2_COLOR_EFFECT	0x0B
> +#define CAT2_CHROMA_LVL		0x0F
> +#define CAT2_CHROMA_EN		0x10
> +
> +/* category 03: AE register bytes */
> +#define CAT3_AE_LOCK		0x00
> +
> +/* category 06: WB register bytes */
> +#define CAT6_AWB_MODE		0x02
> +#define CAT6_AWB_SPEED		0x04
> +
> +enum m5mols_data_length {
> +	M5MOLS_8BIT		= 1,
> +	M5MOLS_16BIT		= 2,
> +	M5MOLS_32BIT		= 4,
> +	M5MOLS_MAXBIT		= 4,
> +};
> +
> +/* register value definitions */
> +enum m5mols_mode {
> +	M5MOLS_SYSINIT		= 0x00,
> +	M5MOLS_PARMSET		= 0x01,
> +	M5MOLS_MONITOR		= 0x02,
> +	M5MOLS_UNKNOWN		= 0xff,
> +};
> +
> +enum m5mols_framepersecond_preset {
> +	M5MOLS_FPS_ERR		= 0x00,
> +	M5MOLS_FPS_AUTO		= 0x01,
> +	M5MOLS_FPS_30		= 0x02,
> +	M5MOLS_FPS_15		= 0x03,
> +	M5mOLS_FPS_12		= 0x04,
> +	M5MOLS_FPS_10		= 0x05,
> +	M5MOLS_FPS_24		= 0x07,
> +	M5MOLS_FPS_20		= 0x08,
> +	M5MOLS_FPS_21		= 0x09,
> +	M5MOLS_FPS_22		= 0x0a,
> +	M5MOLS_FPS_23		= 0x0b,
> +};
> +
> +enum m5mols_wb_mode {
> +	M5MOLS_AWB		= 0x01,
> +	M5MOLS_MWB		= 0x02,
> +};
> +
> +enum m5mols_res_type {
> +	M5MOLS_RES_MON,
> +	M5MOLS_RES_PREVIEW,
> +	M5MOLS_RES_THUMB,
> +	M5MOLS_RES_CAPTURE,
> +	M5MOLS_RES_UNKNOWN,
> +};
> +
> +u8 m5mols_chroma_level[] = {
> +	0x0,	/* default */
> +	0x1c, 0x3e, 0x5f, 0x80, 0xa1, 0xc2, 0xe4,
> +};
> +#define CHROMA_LEVEL_SIZE	ARRAY_SIZE(m5mols_chroma_level)
> +
> +u8 m5mols_ev_speed[] = {
> +	0x0,	/* default */
> +	0xfc, 0xfd, 0xfe, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04,
> +};
> +#define EV_SPEED_SIZE		ARRAY_SIZE(m5mols_ev_speed)
> +
> +struct m5mols_resolution {
> +	unsigned char		value;
> +	enum m5mols_res_type	type;
> +	unsigned long		width;
> +	unsigned long		height;
> +};
> +
> +struct m5mols_format {
> +	struct v4l2_fmtdesc		fmt;
> +	enum v4l2_mbus_pixelcode	code;
> +	enum v4l2_colorspace		colorspace;
> +};
> +
> +struct m5mols_timeperframe {
> +	unsigned char		preset;
> +	int			fps;
> +};
> +
> +/* m5mols user control set */
> +struct m5mols_adjust {
> +	int			saturation;
> +	unsigned int		effect;		/* COLOR-FX */
> +};
> +
> +struct m5mols_wb {
> +	unsigned int		auto_wb;	/* auto or manual */
> +};
> +
> +struct m5mols_exposure {
> +	enum v4l2_exposure_auto_type	exp_mode;
> +	int				exp_bias;
> +};
> +
> +struct m5mols_userset {
> +	struct m5mols_adjust		adjust;
> +	struct m5mols_wb		wb;
> +	struct m5mols_exposure		exposure;
> +};
> +
> +struct m5mols_info {
> +	struct v4l2_subdev		sd;
> +	struct v4l2_mbus_framefmt	fmt, fmt_mon;
> +	struct v4l2_fract		tpf;		/* time per frame */
> +	struct i2c_client		*client;
> +	struct m5mols_platform_data	*pdata;
> +	struct m5mols_userset		uiset;
> +	unsigned char			fmt_preset;
> +	enum m5mols_mode		mode, mode_b;
> +	u8				version;
> +
> +	int				supply_size;
> +	struct regulator_bulk_data	*supply;
> +};
> +
> +/* The DEFAULT names of power supply are referenced with M5MO datasheet. */
> +static const char * const supply_names[] = {
> +	"1.2v_core",		/* CAM_S_ANA_2.8V */
> +	"1.8v_sensor",
> +	"1.8v_digital",
> +	"2.8v_digital",
> +	"2.8v_a_sensor",
> +	"1.2v_digital",
> +	"ext_signal1",		/* Optional: nRST, MCLK */
> +	"ext_i2c_vdd",		/* Optional like a I2C */
> +};
> +#define M5MOLS_NUM_SUPPLIES	 ARRAY_SIZE(supply_names)
> +
> +static const struct m5mols_format m5mols_formats[] = {
> +	{
> +		.fmt		= {
> +			.description	= "YUV422, YUYV",
> +			.pixelformat	= V4L2_PIX_FMT_YUYV,
> +		},
> +		.code		= V4L2_MBUS_FMT_YUYV8_2X8,
> +		.colorspace	= V4L2_COLORSPACE_JPEG,
> +	},
> +};
> +
> +static const struct m5mols_resolution m5mols_size[] = {
> +	/* monitor size */
> +	{0x01, M5MOLS_RES_MON, 128, 96},	/* SUB-QCIF */
> +	{0x03, M5MOLS_RES_MON, 160, 120},	/* QQVGA */
> +	{0x05, M5MOLS_RES_MON, 176, 144},	/* QCIF */
> +	{0x06, M5MOLS_RES_MON, 176, 176},	/* 176*176 */
> +	{0x08, M5MOLS_RES_MON, 240, 320},	/* 1 QVGA */
> +	{0x09, M5MOLS_RES_MON, 320, 240},	/* QVGA */
> +	{0x0c, M5MOLS_RES_MON, 240, 400},	/* l WQVGA */
> +	{0x0d, M5MOLS_RES_MON, 400, 240},	/* WQVGA */
> +	{0x0e, M5MOLS_RES_MON, 352, 288},	/* CIF */
> +	{0x13, M5MOLS_RES_MON, 480, 360},	/* 480*360 */
> +	{0x15, M5MOLS_RES_MON, 640, 360},	/* qHD */
> +	{0x17, M5MOLS_RES_MON, 640, 480},	/* VGA */
> +	{0x18, M5MOLS_RES_MON, 720, 480},	/* 720x480 */
> +	{0x1a, M5MOLS_RES_MON, 800, 480},	/* WVGA */
> +	{0x1f, M5MOLS_RES_MON, 800, 600},	/* SVGA */
> +	{0x21, M5MOLS_RES_MON, 1280, 720},	/* HD */
> +	{0x25, M5MOLS_RES_MON, 1920, 1080},	/* 1080p */
> +	{0x29, M5MOLS_RES_MON, 3264, 2448},	/* 8M (2.63fps@3264*2448) */
> +	{0x30, M5MOLS_RES_MON, 320, 240},	/* 60fps for slow motion */
> +	{0x31, M5MOLS_RES_MON, 320, 240},	/* 120fps for slow motion */
> +	{0x39, M5MOLS_RES_MON, 800, 602},	/* AHS_MON debug */
> +};
> +
> +static const struct m5mols_timeperframe m5mols_fps[] = {
> +	{M5MOLS_FPS_30, 30},
> +	{M5MOLS_FPS_24, 24},
> +	{M5MOLS_FPS_20, 20},
> +	{M5MOLS_FPS_15, 15},
> +	{M5mOLS_FPS_12, 12},
> +	{M5MOLS_FPS_10, 10},
> +	{M5MOLS_FPS_21, 21},
> +	{M5MOLS_FPS_22, 22},
> +	{M5MOLS_FPS_23, 23},
> +	{M5MOLS_FPS_AUTO, 0},		/* AUTO fps */
> +};
> +
> +static const char * const m5mols_qm_ev_prst[] = {
> +	"-4", "-3", "-2", "-1", "0", "1", "2", "3", "4",
> +};
> +
> +static const char * const m5mols_qm_effect_prst[] = {
> +	[V4L2_COLORFX_SEPIA]	= "Sepia",
> +	[V4L2_COLORFX_NEGATIVE]	= "Negtive",
> +	[V4L2_COLORFX_EMBOSS]	= "Emboss",
> +};
> +
> +static struct v4l2_queryctrl m5mols_controls[] = {
> +	/* White balance */
> +	{
> +		.id		= V4L2_CID_AUTO_WHITE_BALANCE,
> +		.type		= V4L2_CTRL_TYPE_BOOLEAN,
> +		.name		= "Auto White Balance",
> +		.minimum	= 0,
> +		.maximum	= 1,
> +		.step		= 1,
> +		.default_value	= 0,
> +	},
> +	/* Exposure metering/control */
> +	{
> +		.id		= V4L2_CID_EXPOSURE_AUTO,
> +		.type		= V4L2_CTRL_TYPE_INTEGER,
> +		.name		= "Exposure control preset",
> +		.minimum	= 0,
> +		.maximum	= 3,
> +		.step		= 1,
> +		.default_value	= V4L2_EXPOSURE_AUTO,
> +	},
> +	{
> +		.id		= V4L2_CID_EXPOSURE,
> +		.type		= V4L2_CTRL_TYPE_MENU,
> +		.name		= "Exposure bias",
> +		.minimum	= 0,
> +		.maximum	= ARRAY_SIZE(m5mols_qm_ev_prst) - 2,
> +		.step		= 1,
> +		.default_value	= 5,	/* 0EV */
> +	},
> +	/* Adjustment features */
> +	{
> +		.id		= V4L2_CID_COLORFX,
> +		.type		= V4L2_CTRL_TYPE_MENU,
> +		.name		= "Image effect",
> +		.minimum	= 0,
> +		.maximum	= ARRAY_SIZE(m5mols_qm_effect_prst) - 2,
> +		.step		= 1,
> +		.default_value	= 0,
> +	},
> +	{
> +		.id		= V4L2_CID_SATURATION,
> +		.type		= V4L2_CTRL_TYPE_INTEGER,
> +		.name		= "Saturation",
> +		.minimum	= 1,
> +		.maximum	= 7,
> +		.step		= 1,
> +		.default_value	= 4,
> +	}, {
> +	}
> +};

New drivers should use the control framework.

See Documentation/video4linux/v4l2-controls.txt.

> +
> +const char **m5mols_ctrl_get_menu(u32 id)
> +{
> +	switch (id) {
> +	case V4L2_CID_COLORFX:
> +		return (const char **)m5mols_qm_effect_prst;
> +	case V4L2_CID_EXPOSURE:
> +		return (const char **)m5mols_qm_ev_prst;
> +	default:
> +		return v4l2_ctrl_get_menu(id);
> +	}
> +}
> +
> +static inline struct m5mols_info *to_m5mols(struct v4l2_subdev *sd)
> +{
> +	return container_of(sd, struct m5mols_info, sd);
> +}
> +
> +/*
> + * m5mols_read_reg
> + * [data size + 4], [0x02(w) or 0x01(r)], [category] + [x]
> + * if 8bit [x] == [val & 0xff]
> + * if 16bit [x] == [val >> 8],[val & 0xff]
> + * if 32bit == [val>>24],[(val>>16)&0xff],[(val>>8)&0xff],[val&0xff]
> + */
> +static int m5mols_read_reg(struct v4l2_subdev *sd, u8 size,
> +		u8 category, u8 byte, u32 *val)
> +{
> +	struct i2c_client *client = v4l2_get_subdevdata(sd);
> +	struct device *cdev = &client->dev;
> +	struct i2c_msg msg[1];
> +	unsigned char buf[5];
> +	unsigned char rd[M5MOLS_MAXBIT + 1];
> +	int ret, retry = 0;
> +
> +	if (!client->adapter)
> +		return -ENODEV;
> +
> +	if ((size != M5MOLS_8BIT)
> +		&& (size != M5MOLS_16BIT)
> +		&& (size != M5MOLS_32BIT)) {
> +		dev_err(cdev, "Wrong data size\n");
> +		return -EINVAL;
> +	}
> +
> +again:
> +	msg->addr = client->addr;
> +	msg->flags =  0;
> +	msg->len = size + 4;
> +	msg->buf = buf;
> +
> +	/* high byte goes first */
> +	buf[0] = size + 4;
> +	buf[1] = 0x01;	/* read */
> +	buf[2] = category;
> +	buf[3] = byte;
> +	buf[4] = size;
> +
> +	ret = i2c_transfer(client->adapter, msg, 1);
> +	if (ret < 0) {
> +		if (retry <= M5MOLS_I2C_RETRY) {
> +			dev_dbg(cdev, "retry ... %d\n", retry);
> +			retry++;
> +			mdelay(20);
> +			goto again;
> +		}

Put the 'return ret' here.

> +
> +	} else {

then the 'else' isn't needed anymore and the code below can be
shifted one indent to the left.

> +		msg->flags = I2C_M_RD;
> +		msg->len = size + 1;
> +		msg->buf = rd;
> +		ret = i2c_transfer(client->adapter, msg, 1);
> +
> +		/* MSB first */
> +		if (size == M5MOLS_8BIT)
> +			*val = rd[1];
> +		else if (size == M5MOLS_16BIT)
> +			*val = rd[2] + (rd[1] << 8);
> +		else
> +			*val = rd[4] + (rd[3] << 8) +
> +				(rd[2] << 16) + (rd[1] << 24);
> +		return 0;
> +	}
> +
> +	return ret;
> +}
> +
> +/*
> + * m5mols_write_reg
> + * [data size + 4], [0x02(w) or 0x01(r)], [category] + [x]
> + * if 8bit [x] == [val & 0xff]
> + * if 16bit [x] == [val >> 8],[val & 0xff]
> + * if 32bit == [val>>24],[(val>>16)&0xff],[(val>>8)&0xff],[val&0xff]
> + */
> +static int m5mols_write_reg(struct v4l2_subdev *sd,
> +		u8 size, u8 category, u8 byte, u32 val)
> +{
> +	struct i2c_client *client = v4l2_get_subdevdata(sd);
> +	struct device *cdev = &client->dev;
> +	struct i2c_msg msg[1];
> +	unsigned char wbuf[M5MOLS_MAXBIT + 4];
> +	int ret, retry = 0;
> +
> +	if (!client->adapter)
> +		return -ENODEV;
> +
> +	if ((size != M5MOLS_8BIT)
> +		&& (size != M5MOLS_16BIT)
> +		&& (size != M5MOLS_32BIT)) {
> +		dev_err(cdev, "Wrong data size\n");
> +		return -EINVAL;
> +	}
> +
> +again:
> +	msg->addr = client->addr;
> +	msg->flags = 0;
> +	msg->len = size + 4;
> +	msg->buf = wbuf;
> +
> +	/* MSB first */
> +	wbuf[0] = size + 4;
> +	wbuf[1] = 0x02;		/* write */
> +	wbuf[2] = category;
> +	wbuf[3] = byte;
> +
> +	/* last byte size depends on data size */
> +	if (size == M5MOLS_8BIT)
> +		wbuf[4] = (u8)(val & 0xff);
> +	else if (size == M5MOLS_16BIT) {
> +		wbuf[4] = (u8)(val >> 8);
> +		wbuf[5] = (u8)(val & 0xff);
> +	} else {
> +		wbuf[4] = (u8)(val >> 24);
> +		wbuf[5] = (u8)((val >> 16) & 0xff);
> +		wbuf[6] = (u8)((val >> 8) & 0xff);
> +		wbuf[7] = (u8)(val & 0xff);
> +	}
> +
> +	ret = i2c_transfer(client->adapter, msg, 1);
> +	if (ret < 0) {
> +		if (retry <= M5MOLS_I2C_RETRY) {
> +			dev_dbg(cdev, "retry ... %d\n", retry);
> +			retry++;
> +			mdelay(20);
> +			goto again;
> +		}
> +		return ret;
> +	}
> +	mdelay(20);
> +
> +	return 0;
> +}
> +
> +static int m5mols_check_busy(struct v4l2_subdev *sd,
> +		u8 category, u8 byte, u32 value)
> +{
> +	struct i2c_client *client = v4l2_get_subdevdata(sd);
> +	u32 busy, i;
> +
> +	for (i = 0; i < M5MOLS_I2C_CHECK_RETRY; i++) {
> +		REG_R_8(category, byte, &busy);
> +		if (busy == value)	/* bingo */
> +			return 0;
> +		mdelay(1);
> +	}
> +	return -EBUSY;
> +}
> +
> +static void m5mols_info_report(struct v4l2_subdev *sd)
> +{
> +	struct i2c_client *client = v4l2_get_subdevdata(sd);
> +	struct device *cdev = &client->dev;
> +	struct m5mols_info *info = to_m5mols(sd);
> +
> +	switch (info->mode) {
> +	case M5MOLS_SYSINIT:
> +		dev_dbg(cdev, "SYSINIT mode\n");
> +		break;
> +	case M5MOLS_PARMSET:
> +		dev_dbg(cdev, "PARMSET mode\n");
> +		break;
> +	case M5MOLS_MONITOR:
> +		dev_dbg(cdev, "MONITOR mode\n");
> +		break;
> +	case M5MOLS_UNKNOWN:
> +		dev_dbg(cdev, "UNKNOWN mode\n");
> +		break;
> +	default:
> +		dev_dbg(cdev, "mode ERROR\n");
> +		break;
> +	}
> +}
> +
> +static int m5mols_parameter_mode(struct v4l2_subdev *sd)
> +{
> +	struct i2c_client *client = v4l2_get_subdevdata(sd);
> +	struct m5mols_info *info = to_m5mols(sd);
> +	int ret = -EBUSY;
> +
> +	if (info->mode == M5MOLS_PARMSET)
> +		return 0;
> +	else
> +		info->mode_b = info->mode;	/* mode_backup */
> +
> +	/* Goto Parameter setting mode */
> +	REG_W_8(CAT_SYSTEM, CAT0_SYSMODE, M5MOLS_PARMSET);
> +	ret = m5mols_check_busy(sd, CAT_SYSTEM, CAT0_SYSMODE, M5MOLS_PARMSET);
> +	if (ret < 0)
> +		return ret;
> +
> +	info->mode = M5MOLS_PARMSET;
> +	m5mols_info_report(sd);
> +
> +	return ret;
> +}
> +
> +static int m5mols_monitoring_mode(struct v4l2_subdev *sd)
> +{
> +	struct i2c_client *client = v4l2_get_subdevdata(sd);
> +	struct m5mols_info *info = to_m5mols(sd);
> +	int ret = -EBUSY;
> +
> +	if (info->mode == M5MOLS_MONITOR)
> +		return 0;
> +	else
> +		info->mode_b = info->mode;	/* mode_backup */
> +
> +	/* Goto monitoring mode */
> +	REG_W_8(CAT_SYSTEM, CAT0_SYSMODE, M5MOLS_MONITOR);
> +	ret = m5mols_check_busy(sd, CAT_SYSTEM, CAT0_SYSMODE, M5MOLS_MONITOR);
> +	if (ret < 0)
> +		return ret;
> +
> +	/* restore monitor format */
> +	memcpy(&info->fmt_mon, &info->fmt, sizeof(struct v4l2_mbus_framefmt));

Just do: info->fmt_mon = info->fmt;

> +
> +	info->mode = M5MOLS_MONITOR;
> +	m5mols_info_report(sd);
> +
> +	return ret;
> +}
> +
> +static int m5mols_check_version(struct v4l2_subdev *sd)
> +{
> +	struct i2c_client *client = v4l2_get_subdevdata(sd);
> +	struct m5mols_info *info = to_m5mols(sd);
> +
> +	REG_R_8(CAT_SYSTEM, CAT0_PJ_CODE, (u32 *)&info->version);
> +	if (info->version != M5MOLS_VERSION)
> +		return -ENODEV;
> +
> +	return 0;
> +}
> +
> +/*
> + * m5mols_resolution_preset
> + * finds out index of requested resolution with i
> + * or returns -einval in case of not supported pixelformat
> + * m5msion] is representing supported resolutions
> + */
> +static int m5mols_resolution_preset(struct v4l2_subdev *sd,
> +		u32 width, u32 height,
> +		enum m5mols_res_type type)
> +{
> +	struct m5mols_info *info = to_m5mols(sd);
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(m5mols_size); i++) {
> +		if ((m5mols_size[i].type == type) &&
> +			(m5mols_size[i].width == width) &&
> +			(m5mols_size[i].height == height))
> +			break;
> +	}
> +
> +	if (i >= ARRAY_SIZE(m5mols_size)) {
> +		v4l2msg("no matching resolution\n");
> +		return -EINVAL;
> +	}
> +
> +	return m5mols_size[i].value;
> +}
> +
> +/*
> + * m5mols_fps_preset
> + * handles v4l2_streamparm.parm.capture.tpf(v4l2_fract)
> + * It returns the closest fps if it has no matching fps
> + * numerator/denominator (30fps = 1/30)
> + * driver considers zero numerator as auto fps
> + */
> +static int m5mols_fps_preset(struct v4l2_subdev *sd,
> +		struct v4l2_captureparm *parm)
> +{
> +	struct m5mols_info *info = to_m5mols(sd);
> +	struct v4l2_fract *fps = &parm->timeperframe;
> +	int numerator = fps->numerator;
> +	int denominator = fps->denominator;
> +	int i;
> +
> +	if (!denominator) {
> +		v4l2msg("Don't give zero for denominator of fps.");
> +		v4l2msg("Going auto fps mode\n");
> +		return M5MOLS_FPS_AUTO;
> +	}
> +
> +	if (!numerator) {
> +		v4l2msg("Going auto fps mode\n");
> +		return M5MOLS_FPS_AUTO;
> +	}
> +
> +	if ((numerator * denominator) < 0)
> +		return -EINVAL;
> +
> +	if (numerator != 1)
> +		denominator = denominator / numerator;
> +
> +	for (i = 0; i < ARRAY_SIZE(m5mols_fps); i++)
> +		if (m5mols_fps[i].fps == denominator)
> +			goto out;
> +
> +	/* If no matching one, get the closest one (excluding Auto FPS) */
> +	for (i = 0; i < ARRAY_SIZE(m5mols_fps) - 2; i++)
> +		if (abs(m5mols_fps[i].fps - denominator)
> +			< abs(m5mols_fps[i+1].fps - denominator))
> +			break;
> +out:
> +	v4l2msg("try numerator[%d], and picking [%d]fps.\n",
> +			numerator,
> +			m5mols_fps[i].fps);
> +
> +	return m5mols_fps[i].preset;
> +}
> +
> +/*
> + * m5mols_effect_preset
> + *
> + * CbCr effect
> + *  read : CAT_PARAM, CAT1_EFFECT => if !0x00 goto param mode and set 0x00
> + *  mode : monitor mode
> + *  write: CAT_MON, CAT2_COLOR_EFFECT 0x01(on)
> + *  write: CAT_MON, CAT2_CFIXB(Cb), CAT2_CFIXR(Cr)
> + *
> + * Gamma effect
> + *  read : CAT_MON, CAT2_COLOR_EFFECT => if !0x00 then set 0x00
> + *  mode : parameter mode
> + *  write: CAT_PARAM, CAT1_EFFECT, index
> + *  mode : monitor mode
> + */
> +static int m5mols_effect_preset(struct v4l2_subdev *sd, signed int value)
> +{
> +	struct i2c_client *client = v4l2_get_subdevdata(sd);
> +	struct m5mols_info *info = to_m5mols(sd);
> +	u32 chroma = 0, gamma = 0;
> +
> +	if (!m5mols_qm_effect_prst[value])
> +		return -EINVAL;
> +
> +	if (value == 0) {
> +		REG_W_8(CAT_MON, CAT2_COLOR_EFFECT, 0x00);
> +		REG_W_8(CAT_PARAM, CAT1_EFFECT, 0x00);
> +
> +		v4l2msg("Failed to change effect mode\n");
> +		mode_monitoring(sd);
> +		return -EINVAL;
> +	}
> +
> +	REG_R_8(CAT_PARAM, CAT1_EFFECT, &gamma);
> +	REG_R_8(CAT_MON, CAT2_COLOR_EFFECT, &chroma);
> +
> +	switch (value) {
> +	case V4L2_COLORFX_SEPIA:	/* Sepia */
> +		if (gamma != 0x00) {
> +			mode_parameter(sd);
> +			REG_W_8(CAT_MON, CAT2_COLOR_EFFECT, 0x00);
> +			mode_monitoring(sd);
> +		}
> +		REG_W_8(CAT_MON, CAT2_COLOR_EFFECT, 0x01);
> +		REG_W_8(CAT_MON, CAT2_CFIXB, 0xD8);
> +		REG_W_8(CAT_MON, CAT2_CFIXR, 0x18);
> +		break;
> +
> +	case V4L2_COLORFX_NEGATIVE:	/* Negative (gamma) */
> +		if (chroma != 0x00)
> +			REG_W_8(CAT_MON, CAT2_COLOR_EFFECT, 0x00);
> +		mode_parameter(sd);
> +		REG_W_8(CAT_PARAM, CAT1_EFFECT, 0x01);
> +		break;
> +
> +	case V4L2_COLORFX_EMBOSS:	/* Emboss (gamma) */
> +		if (chroma != 0x00)
> +			REG_W_8(CAT_MON, CAT2_COLOR_EFFECT, 0x06);
> +		mode_parameter(sd);
> +		REG_W_8(CAT_PARAM, CAT1_EFFECT, 0x06);
> +		break;
> +	default:
> +		v4l2msg("Not supported COLORFX value yet\n");
> +		break;
> +	}
> +
> +	v4l2msg("Effect %s configured\n", m5mols_qm_effect_prst[value]);
> +
> +	return 0;
> +}
> +
> +static void m5mols_set_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
> +{
> +	struct m5mols_info *info = to_m5mols(sd);
> +	struct m5mols_userset *userset = &info->uiset;
> +
> +	switch (ctrl->id) {
> +
> +	/* White balance */
> +	case V4L2_CID_AUTO_WHITE_BALANCE:
> +		userset->wb.auto_wb = ctrl->value;
> +		break;
> +
> +	/* Exposure metering/control */
> +	case V4L2_CID_EXPOSURE_AUTO:
> +		userset->exposure.exp_mode = ctrl->value;
> +		break;
> +	case V4L2_CID_EXPOSURE:
> +		userset->exposure.exp_bias = ctrl->value;
> +		break;
> +
> +	/* Adjustment features */
> +	case V4L2_CID_COLORFX:
> +		userset->adjust.effect = ctrl->value;
> +		break;
> +	case V4L2_CID_SATURATION:
> +		userset->adjust.saturation = ctrl->value;
> +		break;
> +
> +	default:
> +		v4l2msg("UNKNOWN CID [%d]\n", ctrl->id);
> +		break;
> +	}
> +}
> +
> +/*
> + * m5mols_set_default_ctrl - Reset CID values to default
> + */
> +static void m5mols_set_default_ctrl(struct v4l2_subdev *sd)
> +{
> +	struct v4l2_control ctrl;
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(m5mols_controls); i++) {
> +		ctrl.id = m5mols_controls[i].id;
> +		ctrl.value = m5mols_controls[i].default_value;
> +		m5mols_set_ctrl(sd, &ctrl);
> +	}
> +}
> +
> +static int m5mols_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
> +{
> +	struct m5mols_info *info = to_m5mols(sd);
> +
> +	switch (ctrl->id) {
> +
> +	/* White balance */
> +	case V4L2_CID_AUTO_WHITE_BALANCE:
> +		ctrl->value = info->uiset.wb.auto_wb;
> +		break;
> +
> +	/* Exposure metering/control */
> +	case V4L2_CID_EXPOSURE_AUTO:
> +		ctrl->value = info->uiset.exposure.exp_mode;
> +		break;
> +	case V4L2_CID_EXPOSURE:
> +		ctrl->value = info->uiset.exposure.exp_bias;
> +		break;
> +
> +	/* Adjustment features */
> +	case V4L2_CID_SATURATION:
> +		ctrl->value = info->uiset.adjust.saturation;
> +		break;
> +	case V4L2_CID_COLORFX:
> +		ctrl->value = info->uiset.adjust.effect;
> +		break;
> +
> +	default:
> +		v4l2msg("UNKNOWN CID [%d]\n", ctrl->id);
> +		break;
> +	}
> +
> +	return 0;
> +}
> +
> +static int m5mols_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
> +{
> +	struct i2c_client *client = v4l2_get_subdevdata(sd);
> +	struct m5mols_info *info = to_m5mols(sd);
> +	struct m5mols_userset *userset = &info->uiset;
> +	int i, ret = 0;
> +
> +	for (i = 0; i < ARRAY_SIZE(m5mols_controls); i++)
> +		if (m5mols_controls[i].id == ctrl->id)
> +			break;
> +
> +	if (i == ARRAY_SIZE(m5mols_controls))
> +		return -EINVAL;
> +
> +	mode_parameter(sd);
> +
> +	switch (ctrl->id) {
> +
> +	/* White balance */
> +	case V4L2_CID_AUTO_WHITE_BALANCE:
> +		if (ctrl->value == 1)
> +			REG_W_8(CAT_WB, CAT6_AWB_MODE, M5MOLS_AWB);
> +		else if (ctrl->value == 0)
> +			REG_W_8(CAT_WB, CAT6_AWB_MODE, M5MOLS_MWB);
> +		else
> +			return -EINVAL;
> +		userset->wb.auto_wb = ctrl->value;
> +		break;
> +
> +	/* Exposure metering/control */
> +	case V4L2_CID_EXPOSURE_AUTO:
> +		switch (ctrl->value) {
> +		case V4L2_EXPOSURE_AUTO:
> +			REG_W_8(CAT_AE, CAT3_AE_LOCK, 0x01);
> +			break;
> +		case V4L2_EXPOSURE_MANUAL:
> +			REG_W_8(CAT_AE, CAT3_AE_LOCK, 0x00);
> +			break;
> +		case V4L2_EXPOSURE_SHUTTER_PRIORITY:
> +		case V4L2_EXPOSURE_APERTURE_PRIORITY:
> +		default:
> +			v4l2msg("Not supported Exposure mode\n");
> +			return -EINVAL;
> +			break;
> +		};
> +		userset->exposure.exp_mode = ctrl->value;
> +		break;
> +	case V4L2_CID_EXPOSURE:
> +		if (ctrl->value < 1 || ctrl->value > EV_SPEED_SIZE) {
> +			v4l2msg("EV bias out of boundary\n");
> +			return -EINVAL;
> +			break;
> +		}
> +		REG_W_8(CAT_WB, CAT6_AWB_SPEED, m5mols_ev_speed[ctrl->value]);
> +		userset->exposure.exp_bias = ctrl->value;
> +		break;
> +
> +	/* Adjustment features */
> +	case V4L2_CID_SATURATION:
> +		if (ctrl->value < 1 || ctrl->value > CHROMA_LEVEL_SIZE) {
> +			v4l2msg("Saturation out of boundary\n");
> +			return -EINVAL;
> +			break;
> +		}
> +		REG_W_8(CAT_MON, CAT2_CHROMA_LVL,
> +				m5mols_chroma_level[ctrl->value]);
> +		REG_W_8(CAT_MON, CAT2_CHROMA_EN, 0x01);
> +		userset->adjust.saturation = ctrl->value;
> +		break;
> +	case V4L2_CID_COLORFX:
> +		ret = m5mols_effect_preset(sd, ctrl->value);
> +		userset->adjust.effect = ctrl->value;
> +		break;
> +
> +	default:
> +		v4l2msg("UNKNOWN CID[%d]\n", ctrl->id);
> +		break;
> +	}
> +
> +	mode_monitoring(sd);
> +
> +	return ret;
> +}
> +
> +static int m5mols_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
> +{
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(m5mols_controls); i++)
> +		if (m5mols_controls[i].id == qc->id) {
> +			memcpy(qc, &m5mols_controls[i],
> +					sizeof(struct v4l2_queryctrl));
> +			return 0;
> +		}
> +
> +	return -EINVAL;
> +}
> +
> +static int m5mols_querymenu(struct v4l2_subdev *sd, struct v4l2_querymenu *qm)
> +{
> +	struct v4l2_queryctrl qctrl;
> +
> +	qctrl.id = qm->id;
> +	m5mols_queryctrl(sd, &qctrl);
> +
> +	return v4l2_ctrl_query_menu(qm, &qctrl, m5mols_ctrl_get_menu(qm->id));
> +}
> +
> +static int m5mols_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned int index,
> +			      enum v4l2_mbus_pixelcode *code)
> +{
> +	if (!code || index >= ARRAY_SIZE(m5mols_formats))
> +		return -EINVAL;
> +
> +	*code = m5mols_formats[index].code;
> +
> +	return 0;
> +}
> +
> +static int m5mols_g_mbus_fmt(struct v4l2_subdev *sd,
> +		struct v4l2_mbus_framefmt *gfmt)
> +{
> +	struct m5mols_info *info = to_m5mols(sd);
> +	struct v4l2_mbus_framefmt *fmt = &info->fmt;
> +
> +	if (!gfmt)
> +		return -EINVAL;
> +
> +	memset(gfmt, 0, sizeof(struct v4l2_mbus_framefmt));
> +
> +	fmt->width	= gfmt->width;
> +	fmt->height	= gfmt->height;
> +	fmt->code	= gfmt->code;
> +	fmt->colorspace	= gfmt->colorspace;
> +	fmt->field	= V4L2_FIELD_NONE;
> +
> +	return 0;
> +}
> +
> +static int m5mols_s_mbus_fmt(struct v4l2_subdev *sd,
> +		struct v4l2_mbus_framefmt *sfmt)
> +{
> +	struct i2c_client *client = v4l2_get_subdevdata(sd);
> +	struct m5mols_info *info = to_m5mols(sd);
> +	struct v4l2_mbus_framefmt *fmt = &info->pdata->fmt;
> +	int i, preset;
> +
> +	/* checking width,height for preset */
> +	preset = m5mols_resolution_preset(sd, fmt->width, fmt->height,
> +			M5MOLS_RES_MON);
> +	if (preset < 0)
> +		return preset;
> +
> +	/* checking code */
> +	for (i = 0; i < ARRAY_SIZE(m5mols_formats); i++)
> +		if (sfmt->code == m5mols_formats[i].code)
> +			break;
> +
> +	if (i == ARRAY_SIZE(m5mols_formats))
> +		return -EINVAL;
> +
> +	fmt->width	= sfmt->width;
> +	fmt->height	= sfmt->height;
> +	fmt->code	= sfmt->code;
> +	fmt->colorspace	= sfmt->colorspace;
> +	fmt->field	= V4L2_FIELD_NONE;
> +
> +	mode_parameter(sd);
> +
> +	info->fmt_preset = (u8)preset;
> +	REG_W_8(CAT_PARAM, CAT1_MONSIZE, info->fmt_preset);
> +
> +	if (info->mode_b == M5MOLS_MONITOR)
> +		mode_monitoring(sd);
> +
> +	return 0;
> +}
> +
> +static int m5mols_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
> +{
> +	struct m5mols_info *info = to_m5mols(sd);
> +	struct v4l2_captureparm *cp = &parms->parm.capture;
> +
> +	if (!parms)
> +		return -EINVAL;
> +
> +	if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
> +		return -EINVAL;
> +
> +	memset(cp, 0, sizeof(struct v4l2_captureparm));
> +
> +	cp->capability = V4L2_CAP_TIMEPERFRAME;
> +	cp->timeperframe.numerator = info->tpf.numerator;
> +	cp->timeperframe.denominator = info->tpf.denominator;
> +
> +	return 0;
> +}
> +
> +static int m5mols_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
> +{
> +	struct i2c_client *client = v4l2_get_subdevdata(sd);
> +	struct m5mols_info *info = to_m5mols(sd);
> +	struct v4l2_captureparm *cp = &parms->parm.capture;
> +	u32 tpf_denom = 0;
> +	int ret = -EINVAL;
> +
> +	if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
> +		return ret;
> +
> +	cp->capability = V4L2_CAP_TIMEPERFRAME;
> +
> +	/* checking timeperframe */
> +	ret = m5mols_fps_preset(sd, cp);
> +	if (ret < 0)
> +		return ret;
> +
> +	mode_parameter(sd);
> +
> +	/* make it compatible with various fps */
> +	tpf_denom = cp->timeperframe.denominator & 0xff;
> +
> +	REG_W_8(CAT_PARAM, CAT1_DENOMINATOR, tpf_denom);
> +
> +	if (info->mode_b == M5MOLS_MONITOR)
> +		mode_monitoring(sd);
> +
> +	info->tpf = cp->timeperframe;
> +
> +	v4l2msg("%d from app and set %d to camera\n",
> +		cp->timeperframe.denominator, tpf_denom);
> +
> +	return 0;
> +}
> +
> +static int m5mols_s_stream(struct v4l2_subdev *sd, int enable)
> +{
> +	struct i2c_client *client = v4l2_get_subdevdata(sd);
> +	struct m5mols_info *info = to_m5mols(sd);
> +
> +	if (enable) {
> +		if (info->mode != M5MOLS_MONITOR) {
> +			/* streamon @ mode Monitor */
> +			mode_monitoring(sd);
> +			info->mode = M5MOLS_MONITOR;
> +		}
> +	} else {
> +		if (info->mode != M5MOLS_PARMSET) {
> +			/* streamoff @ mode Monitor */
> +			mode_parameter(sd);
> +			info->mode = M5MOLS_PARMSET;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int m5mols_enum_framesizes(struct v4l2_subdev *sd,
> +		struct v4l2_frmsizeenum *fsize)
> +{
> +	int i;
> +
> +	/* checking pixelformat */
> +	for (i = 0; i < ARRAY_SIZE(m5mols_formats); i++)
> +		if (fsize->pixel_format ==
> +				m5mols_formats[i].fmt.pixelformat)
> +			break;
> +
> +	if (i == ARRAY_SIZE(m5mols_formats))
> +		return -EINVAL;
> +
> +	/* checking index */
> +	if (fsize->index >= ARRAY_SIZE(m5mols_size))
> +		return -EINVAL;
> +
> +	fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
> +	fsize->discrete.width = m5mols_size[fsize->index].width;
> +	fsize->discrete.height = m5mols_size[fsize->index].height;
> +
> +	return 0;
> +}
> +
> +static int m5mols_enum_frameintervals(struct v4l2_subdev *sd,
> +		struct v4l2_frmivalenum *fival)
> +{
> +	int i;
> +
> +	/* checking pixelformat */
> +	for (i = 0; i < ARRAY_SIZE(m5mols_formats); i++)
> +		if (fival->pixel_format ==
> +				m5mols_formats[i].fmt.pixelformat)
> +			break;
> +
> +	/* checking index */
> +	if (i == ARRAY_SIZE(m5mols_formats))
> +		return -EINVAL;
> +
> +	fival->type = V4L2_FRMSIZE_TYPE_DISCRETE;
> +	fival->discrete.numerator = 1;
> +	fival->discrete.denominator = m5mols_fps[fival->index].fps;
> +
> +	return 0;
> +}
> +
> +/*
> + * m5mols_s_power
> + * enable power for sensor. The callback from platfoprm data is used
> + */
> +static int m5mols_s_power(struct v4l2_subdev *sd, int enable)
> +{
> +	struct i2c_client *client = v4l2_get_subdevdata(sd);
> +	struct device *cdev = &client->dev;
> +	struct m5mols_info *info = to_m5mols(sd);
> +	struct m5mols_platform_data *pdata = info->pdata;
> +	int ret = 0;
> +
> +	BUG_ON(!pdata);
> +
> +	if (enable) {
> +		if (pdata->set_clock)
> +			pdata->set_clock(cdev, enable);
> +		if (pdata->set_power)
> +			ret = pdata->set_power(1);
> +		return ret;
> +	} else {
> +		if (pdata->set_power)
> +			ret = pdata->set_power(0);
> +		if (pdata->set_clock)
> +			pdata->set_clock(cdev, enable);
> +	}
> +	return ret;
> +}
> +
> +/*
> + * m5mols_s_config
> + * pre-setting related with m5mo by device, on probing.
> + */
> +static int m5mols_s_config(struct v4l2_subdev *sd,
> +		int irq, void *platform_data)
> +{
> +	struct m5mols_platform_data *pdata =
> +		(struct m5mols_platform_data *)platform_data;
> +	struct m5mols_info *info = to_m5mols(sd);
> +
> +	if (pdata->fmt.width && pdata->fmt.height) {
> +		info->fmt = pdata->fmt;
> +	} else {
> +		info->fmt.width = DEFAULT_WIDTH;
> +		info->fmt.height = DEFAULT_HEIGHT;
> +		info->fmt.code = DEFAULT_CODE;
> +	}
> +
> +	info->fmt_mon = info->fmt;
> +
> +	info->tpf.numerator = 1;
> +	info->tpf.denominator = DEFAULT_FPS;
> +
> +	info->mode = M5MOLS_UNKNOWN;
> +	info->mode_b = M5MOLS_UNKNOWN;
> +	info->pdata = pdata;
> +
> +	m5mols_set_default_ctrl(sd);
> +
> +	return 0;
> +}
> +
> +/*
> + * m5mols_init
> + * power on, boot ARM booting, and set m5mo chip default values,
> + * before streaming. Sequence is below:
> + *  1. ARM booting
> + *  2. get & check chip version.
> + *  3. set format, fps, dababus(MIPI)
> + */
> +static int m5mols_init(struct v4l2_subdev *sd, u32 val)
> +{
> +	struct i2c_client *client = v4l2_get_subdevdata(sd);
> +	struct device *cdev = &client->dev;
> +	struct m5mols_info *info = to_m5mols(sd);
> +	int ret = -EINVAL;
> +
> +	/* 1. ARM booting
> +	 * It must be sure boot-up time at least upper 500ms in document. */
> +	REG_W_8(CAT_FLASH, CAT0_INT_ROOTEN, 0x01);
> +	msleep(500);
> +	dev_dbg(cdev, "Success ARM Booting\n");
> +
> +	/* 2. get & check chip version */
> +	ret = m5mols_check_version(sd);
> +	if (ret)
> +		return ret;
> +
> +	/* 3. set format preset, fps, dababus(MIPI) */
> +	REG_W_8(CAT_PARAM, CAT1_DATABUS, DEFAULT_DATABUS);
> +	REG_W_8(CAT_PARAM, CAT1_MONFPS, DEFAULT_FPS);
> +	if (!info->fmt_preset)
> +		REG_W_8(CAT_PARAM, CAT1_MONSIZE, DEFAULT_PRESET);
> +	else
> +		REG_W_8(CAT_PARAM, CAT1_MONSIZE, info->fmt_preset);
> +
> +	return 0;
> +}
> +
> +static const struct v4l2_subdev_core_ops m5mols_core_ops = {
> +	.init			= m5mols_init,
> +	.s_config		= m5mols_s_config,
> +	.s_power		= m5mols_s_power,
> +	.g_ctrl			= m5mols_g_ctrl,
> +	.s_ctrl			= m5mols_s_ctrl,
> +	.queryctrl		= m5mols_queryctrl,
> +	.querymenu		= m5mols_querymenu,
> +};
> +
> +static const struct v4l2_subdev_video_ops m5mols_video_ops = {
> +	.enum_framesizes	= m5mols_enum_framesizes,
> +	.enum_frameintervals	= m5mols_enum_frameintervals,
> +	.enum_mbus_fmt		= m5mols_enum_mbus_fmt,
> +	.g_mbus_fmt		= m5mols_g_mbus_fmt,
> +	.s_mbus_fmt		= m5mols_s_mbus_fmt,
> +	.g_parm			= m5mols_g_parm,
> +	.s_parm			= m5mols_s_parm,
> +	.s_stream		= m5mols_s_stream,
> +};
> +
> +static const struct v4l2_subdev_ops m5mols_ops = {
> +	.core = &m5mols_core_ops,
> +	.video = &m5mols_video_ops,
> +};
> +
> +static int m5mols_get_regulator(struct m5mols_info *info)
> +{
> +	struct m5mols_platform_data *pdata = info->pdata;
> +	struct device *cdev = &info->client->dev;
> +	const char **names;
> +	int i = 0;
> +
> +	/* check supply names & supply size  */
> +	if (!pdata->supply_names || (pdata->supply_size <= 0)) {
> +		info->supply_size = M5MOLS_NUM_SUPPLIES;
> +		names = (const char **)supply_names;
> +	} else {
> +		info->supply_size = pdata->supply_size;
> +		names = pdata->supply_names;
> +	}
> +
> +	/* alloc supply data */
> +	info->supply = kzalloc(sizeof(struct regulator_bulk_data) *
> +			info->supply_size, GFP_KERNEL);
> +	if (!info->supply)
> +		return -ENOMEM;
> +
> +	/* copy regulator names */
> +	for (i = 0; i < info->supply_size; i++)
> +		info->supply[i].supply = names[i];
> +
> +	/* get regulators */
> +	return regulator_bulk_get(cdev, info->supply_size, info->supply);
> +}
> +
> +static int m5mols_probe(struct i2c_client *client,
> +			 const struct i2c_device_id *id)
> +{
> +	struct m5mols_platform_data *pdata = client->dev.platform_data;
> +	struct device *cdev = &client->dev;
> +	struct m5mols_info *info;
> +	struct v4l2_subdev *sd;
> +	int ret = 0;
> +
> +	if (pdata == NULL) {
> +		dev_err(cdev, "No platform data\n");
> +		return -EIO;
> +	}
> +
> +	info = kzalloc(sizeof(struct m5mols_info), GFP_KERNEL);
> +	if (info == NULL) {
> +		dev_err(cdev, "Failed to allocate info\n");
> +		return -ENOMEM;
> +	}
> +	info->pdata = client->dev.platform_data;
> +	info->client = client;
> +
> +	ret = m5mols_get_regulator(info);
> +	if (!ret) {
> +		sd = &info->sd;
> +		strcpy(sd->name, MOD_NAME);
> +		v4l2_i2c_subdev_init(sd, client, &m5mols_ops);
> +		dev_info(cdev, "m5mols has been probed\n");
> +	} else {
> +		dev_err(cdev, "Failed to get regulators, %d\n", ret);
> +	}
> +
> +	return ret;
> +}
> +
> +static int m5mols_remove(struct i2c_client *client)
> +{
> +	struct v4l2_subdev *sd = i2c_get_clientdata(client);
> +	struct m5mols_info *info = to_m5mols(sd);
> +
> +	v4l2_device_unregister_subdev(sd);
> +	regulator_bulk_free(info->supply_size, info->supply);
> +	kfree(info->supply);
> +	kfree(info);
> +
> +	return 0;
> +}
> +
> +static const struct i2c_device_id m5mols_id[] = {
> +	{ MOD_NAME, 0 },
> +	{ },
> +};
> +MODULE_DEVICE_TABLE(i2c, m5mols_id);
> +
> +static struct i2c_driver m5mols_i2c_driver = {
> +	.driver = {
> +		.name	= MOD_NAME,
> +	},
> +	.probe		= m5mols_probe,
> +	.remove		= m5mols_remove,
> +	.id_table	= m5mols_id,
> +};
> +
> +static int __init m5mols_mod_init(void)
> +{
> +	return i2c_add_driver(&m5mols_i2c_driver);
> +}
> +
> +static void __exit m5mols_mod_exit(void)
> +{
> +	i2c_del_driver(&m5mols_i2c_driver);
> +}
> +
> +module_init(m5mols_mod_init);
> +module_exit(m5mols_mod_exit);
> +
> +MODULE_AUTHOR("HeungJun Kim <riverful.kim@xxxxxxxxxxx>");
> +MODULE_AUTHOR("Dongsoo Kim <dongsoo45.kim@xxxxxxxxxxx>");
> +MODULE_DESCRIPTION("Fujitsu M5MOLS 8M Pixel camera sensor with ISP driver");
> +MODULE_LICENSE("GPL");
> diff --git a/include/media/m5mols.h b/include/media/m5mols.h
> new file mode 100644
> index 0000000..af789db
> --- /dev/null
> +++ b/include/media/m5mols.h
> @@ -0,0 +1,31 @@
> +/*
> + * Driver for M5MOLS 8M Pixel camera sensor with ISP
> + *
> + * Copyright (C) 2010 Samsung Electronics Co., Ltd
> + * Author: HeungJun Kim, riverful.kim@xxxxxxxxxxx
> + *
> + * Copyright (C) 2009 Samsung Electronics Co., Ltd
> + * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@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.
> + */
> +
> +#ifndef __M5MOLS_H
> +#define __M5MOLS_H
> +
> +#include <media/v4l2-mediabus.h>
> +#include <linux/regulator/consumer.h>
> +
> +struct m5mols_platform_data {
> +	struct v4l2_mbus_framefmt	fmt;		/* default fmt */
> +	const char			**supply_names;	/* regulator name */
> +	int				supply_size;	/* name string size */
> +
> +	int (*set_power)(int on);
> +	int (*set_clock)(struct device *dev, int on);
> +};
> +
> +#endif	/* __M5MOLS_H */
> 

Regards,

	Hans

-- 
Hans Verkuil - video4linux developer - sponsored by Cisco
--
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