All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] Add support for M-5MOLS 8 Mega Pixel camera
@ 2011-03-16 13:38 Kim, Heungjun
  2011-03-19 13:37 ` Sylwester Nawrocki
                   ` (2 more replies)
  0 siblings, 3 replies; 34+ messages in thread
From: Kim, Heungjun @ 2011-03-16 13:38 UTC (permalink / raw)
  To: linux-media
  Cc: hverkuil, laurent.pinchart, Kim, Heungjun, Sylwester Nawrocki,
	Kyungmin Park

Add I2C/V4L2 subdev driver for M-5MOLS camera sensor with integrated
image signal processor.

Signed-off-by: Heungjun Kim <riverful.kim@samsung.com>
Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---

Hi Hans and everyone,

This is sixth version of M-5MOLS 8 Mega Pixel camera sensor. And, if you see
previous version, you can find at:
http://www.spinics.net/lists/linux-media/msg29350.html

This driver patch is fixed several times, and the important issues is almost
corrected. And, I hope that this is the last version one merged for 2.6.39.
I look forward to be reviewed one more time.

The summary of this version's feature is belows:

1. Add focus control
	: I've suggest menu type focus control, but I agreed this version is
	not yet the level accepted. So, I did not use focus control which
	I suggest.
	The M-5MOLS focus routine takes some time to execute. But, the user
	application calling v4l2 control, should not hanged while streaming
	using q/dqbuf. So, I use workqueue. I want to discuss the focus
	subject on mailnglist next time.

2. Add irq routine
	: M-5MOLS can issues using GPIO pin, and I insert the basic routine
	of irq. This version handles only the Auto focus interrupt source.
	It shows only lens focusing status, don't any action now.

3. Speed-up whole I2C operation
	: I've tested several times for decreasing the stabilization time
	while I2C communication, and I have find proper time. Of course,
	it's more faster than previous version.

4. Let streamon() be called once at the streamming
	: It might be an issue, videobuf2 framework calls streamon when
	qbuf() for enqueueing. It means, the driver's streamon() function
	might be callled continuously if there is no proper handling in the
	subdev driver, and low the framerate by adding unneeded I2C operation.
	The M-5MOLS sensor needs command one time for streaming. If commands
	once, this sensor streams continuously, and this version handles it.

5. Update FW
	: It's a little tricky. Originally, the v4l2 frame provide load_fw(),
	but, there is the occasion which should do in openning the videonode,
	and it's the same occasion with us. So, if it's not wrong or it makes
	any problem, we hope to insert m5mols_update_fw() with weak attribute.
	And, I'm sorry that the fw updating code is confidential. unserstand
	by favor, plz.

And, as always, this driver is tested on s5pc210 board using s5p-fimc driver.

I'll wait for reviewing.

Thanks and Regards,
	Heungjun Kim
	Samsung Electronics DMC R&D Center

 drivers/media/video/Kconfig                  |    2 +
 drivers/media/video/Makefile                 |    1 +
 drivers/media/video/m5mols/Kconfig           |    5 +
 drivers/media/video/m5mols/Makefile          |    3 +
 drivers/media/video/m5mols/m5mols.h          |  251 ++++++
 drivers/media/video/m5mols/m5mols_controls.c |  213 +++++
 drivers/media/video/m5mols/m5mols_core.c     | 1062 ++++++++++++++++++++++++++
 drivers/media/video/m5mols/m5mols_reg.h      |  218 ++++++
 include/media/m5mols.h                       |   35 +
 9 files changed, 1790 insertions(+), 0 deletions(-)
 create mode 100644 drivers/media/video/m5mols/Kconfig
 create mode 100644 drivers/media/video/m5mols/Makefile
 create mode 100644 drivers/media/video/m5mols/m5mols.h
 create mode 100644 drivers/media/video/m5mols/m5mols_controls.c
 create mode 100644 drivers/media/video/m5mols/m5mols_core.c
 create mode 100644 drivers/media/video/m5mols/m5mols_reg.h
 create mode 100644 include/media/m5mols.h

diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index 4498b94..581a0f9 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -746,6 +746,8 @@ config VIDEO_NOON010PC30
 	---help---
 	  This driver supports NOON010PC30 CIF camera from Siliconfile
 
+source "drivers/media/video/m5mols/Kconfig"
+
 config VIDEO_OMAP3
 	tristate "OMAP 3 Camera support (EXPERIMENTAL)"
 	select OMAP_IOMMU
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index ace5d8b..a65458b 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -68,6 +68,7 @@ obj-$(CONFIG_VIDEO_TVEEPROM) += tveeprom.o
 obj-$(CONFIG_VIDEO_MT9V011) += mt9v011.o
 obj-$(CONFIG_VIDEO_SR030PC30)	+= sr030pc30.o
 obj-$(CONFIG_VIDEO_NOON010PC30)	+= noon010pc30.o
+obj-$(CONFIG_VIDEO_M5MOLS)	+= m5mols/
 
 obj-$(CONFIG_SOC_CAMERA_IMX074)		+= imx074.o
 obj-$(CONFIG_SOC_CAMERA_MT9M001)	+= mt9m001.o
diff --git a/drivers/media/video/m5mols/Kconfig b/drivers/media/video/m5mols/Kconfig
new file mode 100644
index 0000000..220282b
--- /dev/null
+++ b/drivers/media/video/m5mols/Kconfig
@@ -0,0 +1,5 @@
+config VIDEO_M5MOLS
+	tristate "Fujitsu M-5MOLS 8MP sensor support"
+	depends on I2C && VIDEO_V4L2
+	---help---
+	  This driver supports Fujitsu M-5MOLS camera sensor with ISP
diff --git a/drivers/media/video/m5mols/Makefile b/drivers/media/video/m5mols/Makefile
new file mode 100644
index 0000000..b5d19bf
--- /dev/null
+++ b/drivers/media/video/m5mols/Makefile
@@ -0,0 +1,3 @@
+m5mols-objs	:= m5mols_core.o m5mols_controls.o
+
+obj-$(CONFIG_VIDEO_M5MOLS)		+= m5mols.o
diff --git a/drivers/media/video/m5mols/m5mols.h b/drivers/media/video/m5mols/m5mols.h
new file mode 100644
index 0000000..fa36536
--- /dev/null
+++ b/drivers/media/video/m5mols/m5mols.h
@@ -0,0 +1,251 @@
+/*
+ * Header for M-5MOLS 8M Pixel camera sensor with ISP
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd
+ * Author: HeungJun Kim, riverful.kim@samsung.com
+ *
+ * Copyright (C) 2009 Samsung Electronics Co., Ltd
+ * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com
+ *
+ * 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-subdev.h>
+#include "m5mols_reg.h"
+
+extern int m5mols_debug;
+
+enum m5mols_mode {
+	MODE_SYSINIT,
+	MODE_PARAM,
+	MODE_MONITOR,
+	MODE_UNKNOWN,
+};
+
+enum m5mols_status {
+	STATUS_SYSINIT,
+	STATUS_PARAM,
+	STATUS_MONITOR,
+	STATUS_AUTO_FOCUS,
+	STATUS_FD,			/* Face Detection */
+	STATUS_DC,			/* Dual Capture */
+	STATUS_SC,			/* Single Capture */
+	STATUS_PREVIEW,
+	STATUS_UNKNOWN,
+};
+
+enum m5mols_i2c_size {
+	I2C_8BIT	= 1,
+	I2C_16BIT	= 2,
+	I2C_32BIT	= 4,
+	I2C_MAX		= I2C_32BIT,
+};
+
+enum m5mols_fps {
+	M5MOLS_FPS_AUTO	= 0,
+	M5MOLS_FPS_10	= 10,
+	M5MOLS_FPS_12	= 12,
+	M5MOLS_FPS_15	= 15,
+	M5MOLS_FPS_20	= 20,
+	M5MOLS_FPS_21	= 21,
+	M5MOLS_FPS_22	= 22,
+	M5MOLS_FPS_23	= 23,
+	M5MOLS_FPS_24	= 24,
+	M5MOLS_FPS_30	= 30,
+	M5MOLS_FPS_MAX	= M5MOLS_FPS_30,
+};
+
+enum m5mols_res_type {
+	M5MOLS_RES_MON,
+	M5MOLS_RES_MAX,
+};
+
+struct m5mols_resolution {
+	u8			value;
+	enum m5mols_res_type	type;
+	u16			width;
+	u16			height;
+};
+
+struct m5mols_format {
+	enum v4l2_mbus_pixelcode code;
+	enum v4l2_colorspace colorspace;
+};
+
+/* version maximum counts = version values + version strings + AF version */
+#define VER_STR_MAX		22
+#define VER_MAX			(10 + VER_STR_MAX + 1)
+
+struct m5mols_version {
+	u8	ctm_code;	/* customer code - address offset 0x0 */
+	u8	pj_code;	/* project code - address offset 0x1 */
+	u16	fw;		/* firmware version - offset 0x2, 0x3 */
+	u16	hw;		/* hardware version - offset 0x4, 0x5 */
+	u16	parm;		/* parameter version - offset 0x6, 0x7 */
+	u16	awb;		/* AWB version - offset 0x8, 0x9 */
+	u8	str[VER_STR_MAX];	/* manufacturer & packging vendor */
+	u8	af;		/* AF version */
+};
+
+struct m5mols_info {
+	struct v4l2_subdev		sd;
+	struct v4l2_mbus_framefmt	fmt[M5MOLS_RES_MAX];
+	struct v4l2_fract		tpf;
+	enum v4l2_mbus_pixelcode	code;		/* last setted. */
+	const struct m5mols_platform_data	*pdata;
+
+	/* additional power function, like GPIO or etc. */
+	int (*set_power)(struct device *dev, int on);
+	struct m5mols_version		ver;
+	struct work_struct		work_irq;
+	struct work_struct		work_af;
+	bool				poweron;
+
+	/* it is used when want to be called only once after probing */
+	bool				do_once;
+
+	/* v4l2 control clusters */
+	struct v4l2_ctrl_handler	handle;
+	struct {
+		struct v4l2_ctrl	*autoexposure;
+		struct v4l2_ctrl	*exposure;
+	};
+	struct v4l2_ctrl		*autowb;
+	struct v4l2_ctrl		*colorfx;
+	struct v4l2_ctrl		*saturation;
+	struct v4l2_ctrl		*zoom;
+	struct v4l2_ctrl		*autofocus;
+
+	/* control & status variables */
+	bool				lock_ae;
+	bool				lock_awb;
+	bool				focusing;
+	bool				streaming;
+	enum m5mols_mode		mode;
+	enum m5mols_mode		mode_backup;
+	enum m5mols_status		status;
+
+	/* resolution size/fps preset variables */
+	u8				res_preset;
+	u8				fps_preset;
+};
+
+/* I2C functions - referenced by below I2C helper functions */
+int m5mols_read_reg(struct v4l2_subdev *sd, enum m5mols_i2c_size size,
+		u8 category, u8 cmd, u32 *val);
+int m5mols_write_reg(struct v4l2_subdev *sd, enum m5mols_i2c_size size,
+		u8 category, u8 cmd, u32 val);
+int m5mols_check_busy(struct v4l2_subdev *sd,
+		u8 category, u8 cmd, u32 value);
+
+/* The system mode & status functions */
+void m5mols_show_mode(struct v4l2_subdev *sd);
+int m5mols_set_mode(struct v4l2_subdev *sd, enum m5mols_mode mode);
+enum m5mols_status m5mols_get_status(struct v4l2_subdev *sd);
+
+/* The utility functions */
+int get_res_preset(struct v4l2_subdev *sd, u16 width, u16 height,
+		enum m5mols_res_type type);
+
+/* The externing camera control functions */
+int m5mols_lock_ae(struct m5mols_info *info, bool lock);
+int m5mols_lock_awb(struct m5mols_info *info, bool lock);
+void m5mols_af_work(struct work_struct *work);
+int m5mols_set_ctrl(struct v4l2_ctrl *ctrl);
+
+/* helper functions */
+static inline struct m5mols_info *to_m5mols(struct v4l2_subdev *sd)
+{
+	return container_of(sd, struct m5mols_info, sd);
+}
+
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+	return &container_of(ctrl->handler, struct m5mols_info, handle)->sd;
+}
+
+static inline bool is_streaming(struct v4l2_subdev *sd)
+{
+	struct m5mols_info *info = to_m5mols(sd);
+	return info->streaming;
+}
+
+static inline bool is_powerup(struct v4l2_subdev *sd)
+{
+	struct m5mols_info *info = to_m5mols(sd);
+	return info->poweron;
+}
+
+static inline int __must_check i2c_w8_system(struct v4l2_subdev *sd,
+		u8 cmd, u32 val)
+{
+	return m5mols_write_reg(sd, I2C_8BIT, CAT_SYSTEM, cmd, val);
+}
+
+static inline int __must_check i2c_w8_param(struct v4l2_subdev *sd,
+		u8 cmd, u32 val)
+{
+	return m5mols_write_reg(sd, I2C_8BIT, CAT_PARAM, cmd, val);
+}
+
+static inline int __must_check i2c_w8_mon(struct v4l2_subdev *sd,
+		u8 cmd, u32 val)
+{
+	return m5mols_write_reg(sd, I2C_8BIT, CAT_MONITOR, cmd, val);
+}
+
+static inline int __must_check i2c_w8_ae(struct v4l2_subdev *sd,
+		u8 cmd, u32 val)
+{
+	return m5mols_write_reg(sd, I2C_8BIT, CAT_AE, cmd, val);
+}
+
+static inline int __must_check i2c_w16_ae(struct v4l2_subdev *sd,
+		u8 cmd, u32 val)
+{
+	return m5mols_write_reg(sd, I2C_16BIT, CAT_AE, cmd, val);
+}
+
+static inline int __must_check i2c_w8_wb(struct v4l2_subdev *sd,
+		u8 cmd, u32 val)
+{
+	return m5mols_write_reg(sd, I2C_8BIT, CAT_WB, cmd, val);
+}
+
+static inline int __must_check i2c_w8_lens(struct v4l2_subdev *sd,
+		u8 cmd, u32 val)
+{
+	return m5mols_write_reg(sd, I2C_8BIT, CAT_LENS, cmd, val);
+}
+
+static inline int __must_check i2c_w8_flash(struct v4l2_subdev *sd,
+		u8 cmd, u32 val)
+{
+	return m5mols_write_reg(sd, I2C_8BIT, CAT_FLASH, cmd, val);
+}
+
+static inline int __must_check i2c_r8_system(struct v4l2_subdev *sd,
+		u8 cmd, u32 *val)
+{
+	return m5mols_read_reg(sd, I2C_8BIT, CAT_SYSTEM, cmd, val);
+}
+
+static inline int __must_check i2c_r16_ae(struct v4l2_subdev *sd,
+		u8 cmd, u32 *val)
+{
+	return m5mols_read_reg(sd, I2C_16BIT, CAT_AE, cmd, val);
+}
+
+static inline int __must_check i2c_r8_lens(struct v4l2_subdev *sd,
+		u8 cmd, u32 *val)
+{
+	return m5mols_read_reg(sd, I2C_8BIT, CAT_LENS, cmd, val);
+}
+
+#endif	/* M5MOLS_H */
diff --git a/drivers/media/video/m5mols/m5mols_controls.c b/drivers/media/video/m5mols/m5mols_controls.c
new file mode 100644
index 0000000..784b764
--- /dev/null
+++ b/drivers/media/video/m5mols/m5mols_controls.c
@@ -0,0 +1,213 @@
+/*
+ * Controls for M-5MOLS 8M Pixel camera sensor with ISP
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd
+ * Author: HeungJun Kim, riverful.kim@samsung.com
+ *
+ * Copyright (C) 2009 Samsung Electronics Co., Ltd
+ * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com
+ *
+ * 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/videodev2.h>
+#include <media/v4l2-ctrls.h>
+
+#include "m5mols.h"
+#include "m5mols_reg.h"
+
+/* The externing camera control functions */
+int m5mols_lock_ae(struct m5mols_info *info, bool lock)
+{
+	struct v4l2_subdev *sd = &info->sd;
+
+	return i2c_w8_ae(sd, CAT3_AE_LOCK, !!(info->lock_ae = lock));
+}
+
+int m5mols_lock_awb(struct m5mols_info *info, bool lock)
+{
+	struct v4l2_subdev *sd = &info->sd;
+
+	info->lock_awb = lock;
+
+	return i2c_w8_wb(sd, CAT6_AWB_LOCK, !!(info->lock_awb = lock));
+}
+
+void m5mols_af_work(struct work_struct *work)
+{
+	struct m5mols_info *info = container_of(work, struct m5mols_info,
+			work_af);
+	struct v4l2_subdev *sd = &info->sd;
+	int ret;
+
+	info->focusing = true;
+
+	/* locking AE/AWB */
+	ret = m5mols_lock_ae(info, true);
+	if (!ret)
+		ret = m5mols_lock_awb(info, true);
+	if (!ret)
+		/* First, stop AF and set the AF mode. Second, start AF. */
+		ret = i2c_w8_lens(sd, CATA_AF_EXCUTE, REG_AF_STOP);
+	if (!ret)
+		ret = i2c_w8_lens(sd, CATA_AF_MODE, REG_AF_MODE_NORMAL);
+	if (!ret)
+		ret = m5mols_check_busy(sd, CAT_LENS, CATA_AF_STATUS,
+				REG_AF_IDLE);
+	if (!ret)
+		ret = i2c_w8_lens(sd, CATA_AF_EXCUTE, REG_AF_EXE_AUTO);
+	if (!ret)
+		ret = m5mols_check_busy(sd, CAT_LENS, CATA_AF_STATUS,
+				REG_AF_SUCCESS);
+	if (!ret)
+		ret = m5mols_lock_ae(info, false);
+	if (!ret)
+		ret = m5mols_lock_awb(info, false);
+
+	/* If focus is failed, just set the focusing done */
+	info->focusing = false;
+}
+
+/* v4l2 control functions */
+static int m5mols_wb_mode(struct m5mols_info *info, struct v4l2_ctrl *ctrl)
+{
+	struct v4l2_subdev *sd = &info->sd;
+	int ret;
+
+	if (info->lock_awb)
+		ret = m5mols_lock_awb(info, false);
+	if (!ret)
+		ret = i2c_w8_wb(sd, CAT6_AWB_MODE, ctrl->val ?
+				REG_AWB_MODE_AUTO : REG_AWB_MODE_MANUAL);
+
+	return ret;
+}
+
+static int m5mols_exposure_mode(struct m5mols_info *info,
+		struct v4l2_ctrl *ctrl)
+{
+	struct v4l2_subdev *sd = &info->sd;
+	int ret;
+
+	if (info->lock_ae)
+		ret = m5mols_lock_ae(info, false);
+	if (!ret)
+		ret = i2c_w8_ae(sd, CAT3_AE_MODE,
+				(ctrl->val == V4L2_EXPOSURE_AUTO) ?
+				REG_AE_MODE_ALL : REG_AE_MODE_OFF);
+	return ret;
+}
+
+static int m5mols_exposure(struct m5mols_info *info)
+{
+	struct v4l2_subdev *sd = &info->sd;
+
+	return i2c_w16_ae(sd, CAT3_MANUAL_GAIN_MON, info->exposure->val);
+}
+
+static int m5mols_zoom(struct m5mols_info *info, struct v4l2_ctrl *ctrl)
+{
+	struct v4l2_subdev *sd = &info->sd;
+
+	return i2c_w8_mon(sd, CAT2_ZOOM, ctrl->val);
+}
+
+static int m5mols_set_saturation(struct m5mols_info *info,
+		struct v4l2_ctrl *ctrl)
+{
+	struct v4l2_subdev *sd = &info->sd;
+	static u8 m5mols_chroma_lvl[] = {
+		REG_CHROMA_LVL0, REG_CHROMA_LVL1, REG_CHROMA_LVL2,
+		REG_CHROMA_LVL3, REG_CHROMA_LVL4, REG_CHROMA_LVL5,
+		REG_CHROMA_LVL6,
+	};
+	int ret;
+
+	ret = i2c_w8_mon(sd, CAT2_CHROMA_LVL, m5mols_chroma_lvl[ctrl->val]);
+	if (!ret)
+		ret = i2c_w8_mon(sd, CAT2_CHROMA_EN, REG_CHROMA_ON);
+
+	return ret;
+}
+
+static int m5mols_set_colorfx(struct m5mols_info *info, struct v4l2_ctrl *ctrl)
+{
+	struct v4l2_subdev *sd = &info->sd;
+	int ret;
+
+	/* This contol uses 2 kinds of register: normal effect & color effect.
+	 * The normal effect belongs to category 1. On the other hand the color
+	 * effect belongs to category 2.
+	 *
+	 * The normal effect uses one register
+	 *	: CAT1_EFFECT
+	 * The color effect uses three register
+	 *	: CAT2_COLOR_EFFECT, CAT2_CFIXR, CAT2_CFIXB */
+
+	ret = i2c_w8_param(sd, CAT1_EFFECT,
+			ctrl->val == V4L2_COLORFX_NEGATIVE ?
+							REG_EFFECT_NEGATIVE :
+			ctrl->val == V4L2_COLORFX_EMBOSS ?
+							REG_EFFECT_EMBOSS :
+		REG_EFFECT_OFF);
+	if (!ret)
+		ret = i2c_w8_mon(sd, CAT2_COLOR_EFFECT,
+				ctrl->val == V4L2_COLORFX_SEPIA ?
+				REG_COLOR_EFFECT_ON : REG_COLOR_EFFECT_OFF);
+	if (!ret)
+		ret = i2c_w8_mon(sd, CAT2_CFIXR,
+				ctrl->val == V4L2_COLORFX_SEPIA ?
+				REG_CFIXR_SEPIA : 0);
+	if (!ret)
+		ret = i2c_w8_mon(sd, CAT2_CFIXB,
+				ctrl->val == V4L2_COLORFX_SEPIA ?
+				REG_CFIXB_SEPIA : 0);
+
+	return ret;
+}
+
+static int m5mols_af(struct m5mols_info *info, struct v4l2_ctrl *ctrl)
+{
+	if (!info->focusing)
+		schedule_work(&info->work_af);
+	return 0;
+}
+
+/* The function called by s_ctrl() */
+int m5mols_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct v4l2_subdev *sd = to_sd(ctrl);
+	struct m5mols_info *info = to_m5mols(sd);
+	int ret;
+
+	switch (ctrl->id) {
+	case V4L2_CID_ZOOM_ABSOLUTE:
+		return m5mols_zoom(info, ctrl);
+	case V4L2_CID_EXPOSURE_AUTO:
+		if (!ctrl->is_new)
+			ctrl->val = V4L2_EXPOSURE_MANUAL;
+		ret = m5mols_exposure_mode(info, ctrl);
+		if (!ret && ctrl->val == V4L2_EXPOSURE_MANUAL)
+			ret = m5mols_exposure(info);
+		return ret;
+	case V4L2_CID_AUTO_WHITE_BALANCE:
+		return m5mols_wb_mode(info, ctrl);
+	case V4L2_CID_SATURATION:
+		return m5mols_set_saturation(info, ctrl);
+	case V4L2_CID_COLORFX:
+		return m5mols_set_colorfx(info, ctrl);
+	case V4L2_CID_FOCUS_AUTO:
+		if (ctrl->val)
+			ret = m5mols_af(info, ctrl);
+		else
+			ret = i2c_w8_lens(sd, CATA_AF_EXCUTE, REG_AF_STOP);
+		return ret;
+	}
+
+	return -EINVAL;
+}
diff --git a/drivers/media/video/m5mols/m5mols_core.c b/drivers/media/video/m5mols/m5mols_core.c
new file mode 100644
index 0000000..634db1f
--- /dev/null
+++ b/drivers/media/video/m5mols/m5mols_core.c
@@ -0,0 +1,1062 @@
+/*
+ * Driver for M-5MOLS 8M Pixel camera sensor with ISP
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd
+ * Author: HeungJun Kim, riverful.kim@samsung.com
+ *
+ * Copyright (C) 2009 Samsung Electronics Co., Ltd
+ * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com
+ *
+ * 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/slab.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/version.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+#include <media/m5mols.h>
+
+#include "m5mols.h"
+#include "m5mols_reg.h"
+
+int m5mols_debug;
+module_param(m5mols_debug, int, 0644);
+
+#define MOD_NAME		"M5MOLS"
+#define M5MOLS_I2C_CHECK_RETRY	300
+
+/* M-5MOLS mode */
+static u8 m5mols_reg_mode[] = {
+	[MODE_SYSINIT]		= REG_MODE_SYSINIT,
+	[MODE_PARAM]		= REG_MODE_PARAM,
+	[MODE_MONITOR]		= REG_MODE_MONITOR,
+};
+
+/* M-5MOLS regulator consumer names */
+/* The DEFAULT names of power are referenced with M-5MOLS datasheet. */
+static struct regulator_bulk_data supplies[] = {
+	{
+		/* core power - 1.2v, generally at the M-5MOLS */
+		.supply		= "core",
+	}, {
+		.supply		= "dig_18",	/* digital power 1 - 1.8v */
+	}, {
+		.supply		= "d_sensor",	/* sensor power 1 - 1.8v */
+	}, {
+		.supply		= "dig_28",	/* digital power 2 - 2.8v */
+	}, {
+		.supply		= "a_sensor",	/* analog power */
+	}, {
+		.supply		= "dig_12",	/* digital power 3 - 1.2v */
+	},
+};
+
+/* M-5MOLS default format (codes, sizes, preset values) */
+static const struct v4l2_mbus_framefmt default_fmt[M5MOLS_RES_MAX] = {
+	[M5MOLS_RES_MON] = {
+		.width		= 1920,
+		.height		= 1080,
+		.code		= V4L2_MBUS_FMT_VYUY8_2X8,
+		.field		= V4L2_FIELD_NONE,
+		.colorspace	= V4L2_COLORSPACE_JPEG,
+	},
+};
+
+static const struct m5mols_format m5mols_formats[] = {
+	[M5MOLS_RES_MON] = {
+		.code		= V4L2_MBUS_FMT_VYUY8_2X8,
+		.colorspace	= V4L2_COLORSPACE_JPEG,
+	},
+};
+
+/* M-5MOLS default FPS */
+static const struct v4l2_fract default_fps = {
+	.numerator		= 1,
+	.denominator		= M5MOLS_FPS_AUTO,
+};
+
+static const struct m5mols_resolution m5mols_reg_res[] = {
+	/* 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 u8 m5mols_reg_fps[] = {
+	[M5MOLS_FPS_AUTO]	= 0x01,
+	[M5MOLS_FPS_10]		= 0x05,
+	[M5MOLS_FPS_12]		= 0x04,
+	[M5MOLS_FPS_15]		= 0x03,
+	[M5MOLS_FPS_20]		= 0x08,
+	[M5MOLS_FPS_21]		= 0x09,
+	[M5MOLS_FPS_22]		= 0x0a,
+	[M5MOLS_FPS_23]		= 0x0b,
+	[M5MOLS_FPS_24]		= 0x07,
+	[M5MOLS_FPS_30]		= 0x02,
+};
+
+/*
+ * m5mols_swap_byte() / to_res_type() - These functions is utility.
+ *
+ * The m5mols_swap_byte() returns value swapped depends on 8/16/32 width.
+ * The value independent system. It generally is used in the I2C function.
+ */
+static u32 m5mols_swap_byte(u8 *data, enum m5mols_i2c_size size)
+{
+	if (size == I2C_8BIT)
+		return *data;
+	else if (size == I2C_16BIT)
+		return be16_to_cpu(*((u16 *)data));
+	else
+		return be32_to_cpu(*((u32 *)data));
+}
+
+static enum m5mols_res_type to_res_type(struct v4l2_subdev *sd,
+		enum v4l2_mbus_pixelcode code)
+{
+	int i = ARRAY_SIZE(m5mols_formats);
+
+	while (i--)
+		if (code == m5mols_formats[i].code)
+			break;
+	if (i < 0)
+		return M5MOLS_RES_MAX;
+	return i;
+}
+
+/*
+ * m5mols_read_reg() / m5mols_write_reg() - handle sensor's I2C communications.
+ *
+ * The I2C command packet of M-5MOLS is made up 3 kinds of I2C bytes(category,
+ * command, bytes). Reference m5mols.h.
+ *
+ * The packet is needed 2, when M-5MOLS is read through I2C.
+ * The packet is needed 1, when M-5MOLS is written through I2C.
+ *
+ * I2C packet common order(including both reading/writing)
+ *   1st : size (data size + 4)
+ *   2nd : READ/WRITE (R - 0x01, W - 0x02)
+ *   3rd : Category
+ *   4th : Command
+ *
+ * I2C packet order for READING operation
+ *   5th : data real size for reading
+ *   And, read another I2C packet again, until data size.
+ *
+ * I2C packet order for WRITING operation
+ *   5th to 8th: an actual data to write
+ */
+
+#define M5MOLS_BYTE_READ	0x01
+#define M5MOLS_BYTE_WRITE	0x02
+
+int m5mols_read_reg(struct v4l2_subdev *sd,
+		enum m5mols_i2c_size size,
+		u8 category, u8 cmd, u32 *val)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct i2c_msg msg[2];
+	u8 wbuf[5], rbuf[I2C_MAX + 1];
+	int ret;
+
+	if (!client->adapter)
+		return -ENODEV;
+
+	if (size != I2C_8BIT && size != I2C_16BIT && size != I2C_32BIT)
+		return -EINVAL;
+
+	/* 1st I2C operation for writing category & command. */
+	msg[0].addr = client->addr;
+	msg[0].flags = 0;
+	msg[0].len = 5;		/* 1(cmd size per bytes) + 4 */
+	msg[0].buf = wbuf;
+	wbuf[0] = 5;		/* same right above this */
+	wbuf[1] = M5MOLS_BYTE_READ;
+	wbuf[2] = category;
+	wbuf[3] = cmd;
+	wbuf[4] = size;
+
+	/* 2nd I2C operation for reading data. */
+	msg[1].addr = client->addr;
+	msg[1].flags = I2C_M_RD;
+	msg[1].len = size + 1;
+	msg[1].buf = rbuf;
+
+	usleep_range(200, 200);		/* must be for stabilization */
+
+	ret = i2c_transfer(client->adapter, msg, 2);
+	if (ret < 0) {
+		dev_err(&client->dev, "failed READ[%d] at "
+				"cat[%02x] cmd[%02x]\n",
+				size, category, cmd);
+		return ret;
+	}
+
+	*val = m5mols_swap_byte(&rbuf[1], size);
+
+	return 0;
+}
+
+int m5mols_write_reg(struct v4l2_subdev *sd,
+		enum m5mols_i2c_size size,
+		u8 category, u8 cmd, u32 val)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct device *cdev = &client->dev;
+	struct i2c_msg msg[1];
+	u8 wbuf[I2C_MAX + 4];
+	u32 *buf = (u32 *)&wbuf[4];
+	int ret;
+
+	if (!client->adapter)
+		return -ENODEV;
+
+	if (size != I2C_8BIT && size != I2C_16BIT && size != I2C_32BIT) {
+		dev_err(cdev, "Wrong data size\n");
+		return -EINVAL;
+	}
+
+	msg->addr = client->addr;
+	msg->flags = 0;
+	msg->len = size + 4;
+	msg->buf = wbuf;
+	wbuf[0] = size + 4;
+	wbuf[1] = M5MOLS_BYTE_WRITE;
+	wbuf[2] = category;
+	wbuf[3] = cmd;
+
+	*buf = m5mols_swap_byte((u8 *)&val, size);
+
+	usleep_range(200, 200);		/* must be for stabilization */
+
+	ret = i2c_transfer(client->adapter, msg, 1);
+	if (ret < 0) {
+		dev_err(&client->dev, "failed WRITE[%d] at "
+				"cat[%02x] cmd[%02x], ret %d\n",
+				size, msg->buf[2], msg->buf[3], ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+int m5mols_check_busy(struct v4l2_subdev *sd, u8 category, u8 cmd, u32 value)
+{
+	u32 busy, i;
+	int ret;
+
+	for (i = 0; i < M5MOLS_I2C_CHECK_RETRY; i++) {
+
+		ret = m5mols_read_reg(sd, I2C_8BIT, category, cmd, &busy);
+		if (ret < 0)
+			return ret;
+
+		if (busy == value)	/* bingo */
+			return 0;
+	}
+
+	return -EBUSY;
+}
+
+/*
+ * m5mols_set_mode() / m5mols_show_mode() / m5mols_get_status()
+ *
+ * The sensor M-5MOLS has system mode and system status. The system mode means
+ * the M-5MOLS's state and this is changable to us. The system mode supported
+ * M-5MOLS is only 4 - System, Parameter, Monitor, Capture. But, the System
+ * mode is not able to be set by us, it's available only after powering up.
+ *
+ * On the other hand, The sensor M-5MOLS's system status is similar with the
+ * system mode, but this is not acceptable to be set by us.
+ */
+void m5mols_show_mode(struct v4l2_subdev *sd)
+{
+	struct m5mols_info *info = to_m5mols(sd);
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct device *cdev = &client->dev;
+
+	dev_info(cdev, "mode: %s\n",
+			info->mode == MODE_SYSINIT ? "System" :
+			info->mode == MODE_PARAM ? "Parameter" :
+			info->mode == MODE_MONITOR ? "Monitor" :
+			"Unknown");
+}
+
+int m5mols_set_mode(struct v4l2_subdev *sd, enum m5mols_mode mode)
+{
+	struct m5mols_info *info = to_m5mols(sd);
+	int ret;
+
+	if (mode < MODE_SYSINIT || mode > MODE_MONITOR)
+		return -EINVAL;
+
+	ret = i2c_w8_system(sd, CAT0_SYSMODE, m5mols_reg_mode[mode]);
+	if (!ret)
+		ret = m5mols_check_busy(sd, CAT_SYSTEM, CAT0_STATUS,
+				m5mols_reg_mode[mode]);
+	if (!ret)
+		info->mode = m5mols_reg_mode[mode];
+
+	return ret;
+}
+
+enum m5mols_status m5mols_get_status(struct v4l2_subdev *sd)
+{
+	struct m5mols_info *info = to_m5mols(sd);
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct device *cdev = &client->dev;
+	int ret;
+
+	ret = i2c_r8_system(sd, CAT0_STATUS, (u32 *)&info->status);
+	if (!ret)
+		dev_info(cdev, "status %s\n",
+			info->status == STATUS_SYSINIT ? "System" :
+			info->status == STATUS_PARAM ? "Parameter" :
+			info->status == STATUS_MONITOR ? "Monitor" :
+			info->status == STATUS_AUTO_FOCUS ? "Auto Focus" :
+			info->status == STATUS_FD ? "Face Detction" :
+			info->status == STATUS_DC ? "Dual Capture" :
+			info->status == STATUS_SC ? "Single Capture" :
+			info->status == STATUS_PREVIEW ? "Preview" :
+			"Unknown");
+
+	return ret;
+}
+
+/*
+ * m5mols_get_version() / m5mols_show_version()
+ *
+ * This function's role is getting M-5MOLS version including basic information
+ * version & AF & even version string. Each information is stored internal
+ * version structure.
+ */
+static int m5mols_get_version(struct v4l2_subdev *sd)
+{
+	struct m5mols_info *info = to_m5mols(sd);
+	union {
+		struct m5mols_version	ver;
+		u8			bytes[VER_MAX];
+	} value;
+	int ret, i;
+
+	info->ver = value.ver;
+
+	/* get from custom code version offset to AWB version offset */
+	for (i = CAT0_CUSTOMER_CODE; i <= CAT0_VERSION_AWB_L; i++) {
+		ret = i2c_r8_system(sd, i, (u32 *)&value.bytes[i]);
+		if (ret)
+			return ret;
+	}
+
+	/* get version string */
+	for (; i < VER_MAX - 1; i++)
+		ret = i2c_r8_system(sd, CAT0_VERSION_STR,
+				(u32 *)&value.bytes[i]);
+	value.bytes[i] = '\0';
+
+	/* get AF version */
+	ret = i2c_r8_lens(sd, CATA_AF_VERSION, (u32 *)&value.bytes[i]);
+	if (ret)
+		return ret;
+
+	info->ver.fw = be16_to_cpu(info->ver.fw);
+	info->ver.hw = be16_to_cpu(info->ver.hw);
+	info->ver.parm = be16_to_cpu(info->ver.parm);
+	info->ver.awb = be16_to_cpu(info->ver.awb);
+
+	return ret;
+}
+
+static void m5mols_show_version(struct v4l2_subdev *sd)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct device *dev = &client->dev;
+	struct m5mols_info *info = to_m5mols(sd);
+
+	dev_info(dev, "customer code\t0x%02x\n", info->ver.ctm_code);
+	dev_info(dev, "project code\t0x%02x\n", info->ver.pj_code);
+	dev_info(dev, "firmware version\t0x%04x\n", info->ver.fw);
+	dev_info(dev, "hardware version\t0x%04x\n", info->ver.hw);
+	dev_info(dev, "parameter version\t0x%04x\n", info->ver.parm);
+	dev_info(dev, "AWB version\t0x%04x\n", info->ver.awb);
+	dev_info(dev, "AF version\t0x%04x\n", info->ver.af);
+	dev_info(dev, "Version string\t%s\n", info->ver.str);
+}
+
+/*
+ * get_res_preset() - find out M-5MOLS register value from requested resolution.
+ *
+ * @width: requested width
+ * @height: requested height
+ * @type: requested type of each modes. It supports only monitor mode now.
+ */
+int get_res_preset(struct v4l2_subdev *sd, u16 width, u16 height,
+		enum m5mols_res_type type)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(m5mols_reg_res); i++) {
+		if ((m5mols_reg_res[i].type == type) &&
+			(m5mols_reg_res[i].width == width) &&
+			(m5mols_reg_res[i].height == height))
+			break;
+	}
+
+	if (i >= ARRAY_SIZE(m5mols_reg_res))
+		return -EINVAL;
+
+	return m5mols_reg_res[i].value;
+}
+
+/*
+ * get_fps() - calc & check FPS from v4l2_captureparm, if FPS is adequate, set.
+ *
+ * In M-5MOLS case, the denominator means FPS. The values of numerator and
+ * denominator should not be minus both. If numerator is 0, it sets AUTO FPS.
+ * If numerator is not 1, it recalculates denominator. After it checks, the
+ * denominator is set to timeperframe.denominator, and used by FPS.
+ */
+static int get_fps(struct v4l2_subdev *sd,
+		struct v4l2_captureparm *parm)
+{
+	int numerator = parm->timeperframe.numerator;
+	int denominator = parm->timeperframe.denominator;
+
+	/* The denominator should be +, except 0. The numerator shoud be +. */
+	if (numerator < 0 || denominator <= 0)
+		return -EINVAL;
+
+	/* The numerator is 0, return auto fps. */
+	if (numerator == 0) {
+		parm->timeperframe.denominator = M5MOLS_FPS_AUTO;
+		return 0;
+	}
+
+	/* calc FPS(not time per frame) per 1 numerator */
+	denominator = denominator / numerator;
+
+	if (denominator < M5MOLS_FPS_AUTO || denominator > M5MOLS_FPS_MAX)
+		return -EINVAL;
+
+	if (!m5mols_reg_fps[denominator])
+		return -EINVAL;
+
+	return 0;
+}
+
+static int m5mols_g_mbus_fmt(struct v4l2_subdev *sd,
+		struct v4l2_mbus_framefmt *ffmt)
+{
+	struct m5mols_info *info = to_m5mols(sd);
+	enum m5mols_res_type res_type;
+
+	res_type = to_res_type(sd, ffmt->code);
+	if (res_type == M5MOLS_RES_MAX)
+		return -EINVAL;
+
+	*ffmt = info->fmt[res_type];
+	info->code = ffmt->code;
+
+	return 0;
+}
+
+static int m5mols_s_mbus_fmt(struct v4l2_subdev *sd,
+		struct v4l2_mbus_framefmt *ffmt)
+{
+	struct m5mols_info *info = to_m5mols(sd);
+	enum m5mols_res_type res_type;
+	int size;
+
+	res_type = to_res_type(sd, ffmt->code);
+	if (res_type == M5MOLS_RES_MAX)
+		return -EINVAL;
+
+	size = get_res_preset(sd, ffmt->width, ffmt->height, res_type);
+	if (size < 0)
+		return -EINVAL;
+
+	if (ffmt->code == m5mols_formats[M5MOLS_RES_MON].code) {
+		info->res_preset = (u8)size;
+
+		info->fmt[res_type]		= default_fmt[res_type];
+		info->fmt[res_type].width	= ffmt->width;
+		info->fmt[res_type].height	= ffmt->height;
+
+		*ffmt = info->fmt[res_type];
+		info->code = ffmt->code;
+	}
+
+	return 0;
+}
+
+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_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->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+			parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+		return -EINVAL;
+
+	cp->capability = V4L2_CAP_TIMEPERFRAME;
+	cp->timeperframe = info->tpf;
+
+	return 0;
+}
+
+static int m5mols_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
+{
+	struct m5mols_info *info = to_m5mols(sd);
+	struct v4l2_captureparm *cp = &parms->parm.capture;
+	int ret;
+
+	if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+			parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+		return -EINVAL;
+
+	ret = get_fps(sd, cp);	/* set right FPS to denominator. */
+	if (!ret)
+		info->fps_preset = m5mols_reg_fps[cp->timeperframe.denominator];
+	if (!ret) {
+		cp->capability = V4L2_CAP_TIMEPERFRAME;
+		info->tpf = cp->timeperframe;
+	}
+
+	return ret;
+}
+
+static int m5mols_start_monitor(struct v4l2_subdev *sd)
+{
+	struct m5mols_info *info = to_m5mols(sd);
+	int ret;
+
+	ret = m5mols_set_mode(sd, MODE_PARAM);
+	if (!ret)
+		ret = i2c_w8_param(sd, CAT1_MONITOR_SIZE, info->res_preset);
+	if (!ret)
+		ret = i2c_w8_param(sd, CAT1_MONITOR_FPS, info->fps_preset);
+	if (!ret)
+		ret = m5mols_set_mode(sd, MODE_MONITOR);
+	if (!ret && info->do_once) {
+		/* After probing the driver, this should be callde once. */
+		v4l2_ctrl_handler_setup(&info->handle);
+		info->do_once = false;
+	}
+
+	return ret;
+}
+
+static int m5mols_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	struct m5mols_info *info = to_m5mols(sd);
+	int ret = -EINVAL;
+
+	/* It happens the driver using this subdev chooses the method doing
+	 * streamon/off when qbuf/dqbuf continuously. But, in the case of
+	 * M-5MOLS, it's no problem to set streaming command just once.
+	 * So, the status of streamon/off of the level of v4l2, should be
+	 * checked in here for controlling separately with no problems. */
+	if (enable && !is_streaming(sd)) {
+
+		/* streaming mode determines whether mediabus code */
+		ret = m5mols_start_monitor(sd);
+		if (!ret)
+			info->streaming = true;
+
+	} else if (!enable && is_streaming(sd)) {
+
+		/* The is_streaming() is the key to determine streamon/off. The
+		 * 'enable' has meaning depends on streaming */
+		ret = m5mols_set_mode(sd, MODE_PARAM);
+		if (!ret)
+			info->streaming = false;
+	}
+
+	return ret;
+}
+
+static const struct v4l2_subdev_video_ops m5mols_video_ops = {
+	.g_mbus_fmt		= m5mols_g_mbus_fmt,
+	.s_mbus_fmt		= m5mols_s_mbus_fmt,
+	.enum_mbus_fmt		= m5mols_enum_mbus_fmt,
+	.g_parm			= m5mols_g_parm,
+	.s_parm			= m5mols_s_parm,
+	.s_stream		= m5mols_s_stream,
+};
+
+static int m5mols_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct v4l2_subdev *sd = to_sd(ctrl);
+	struct m5mols_info *info = to_m5mols(sd);
+	int ret;
+
+	info->mode_backup = info->mode;
+
+	ret = m5mols_set_mode(sd, MODE_PARAM);
+	if (!ret)
+		ret = m5mols_set_ctrl(ctrl);
+	if (!ret)
+		ret = m5mols_set_mode(sd, info->mode_backup);
+
+	return ret;
+}
+
+static const struct v4l2_ctrl_ops m5mols_ctrl_ops = {
+	.s_ctrl = m5mols_s_ctrl,
+};
+
+/*
+ * m5mols_sensor_power() - handle sensor power up/down.
+ *
+ * @enable: If it is true, power up. If is not, power down.
+ */
+static int m5mols_sensor_power(struct m5mols_info *info, bool enable)
+{
+	struct v4l2_subdev *sd = &info->sd;
+	struct i2c_client *c = v4l2_get_subdevdata(sd);
+	int ret;
+
+	if (enable) {
+		if (is_powerup(sd))
+			return 0;
+
+		/* power-on additional power */
+		if (info->set_power) {
+			ret = info->set_power(&c->dev, 1);
+			if (ret)
+				return ret;
+		}
+
+		ret = regulator_bulk_enable(ARRAY_SIZE(supplies), supplies);
+		if (ret)
+			return ret;
+
+		gpio_set_value(info->pdata->gpio_rst, info->pdata->enable_rst);
+		usleep_range(1000, 1000);
+
+		info->poweron = true;
+
+	} else {
+		if (!is_powerup(sd))
+			return 0;
+
+		ret = regulator_bulk_disable(ARRAY_SIZE(supplies), supplies);
+		if (ret)
+			return ret;
+
+		/* power-off additional power */
+		if (info->set_power) {
+			ret = info->set_power(&c->dev, 0);
+			if (ret)
+				return ret;
+		}
+
+		info->poweron = false;
+
+		gpio_set_value(info->pdata->gpio_rst, !info->pdata->enable_rst);
+		usleep_range(1000, 1000);
+	}
+
+	return ret;
+}
+
+/* m5mols_update_fw() - Note that m5mols_update_fw() is optional. */
+int __attribute__ ((weak)) m5mols_update_fw(struct v4l2_subdev *sd,
+		int (*set_power)(struct m5mols_info *, bool))
+{
+	return 0;
+}
+
+/*
+ * m5mols_sensor_armboot() - booting M-5MOLS internal ARM core-controller.
+ *
+ * It makes to ready M-5MOLS for I2C & MIPI interface. After it's powered up,
+ * it activates if it gets armboot command for I2C interface. After getting
+ * cmd, it must wait about least 520ms referenced by M-5MOLS datasheet.
+ */
+static int m5mols_sensor_armboot(struct v4l2_subdev *sd)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	u32 reg;
+	int ret;
+
+	/* ARM(M-5MOLS core) booting */
+	ret = i2c_w8_flash(sd, CATF_CAM_START, true);
+	if (ret < 0)
+		return ret;
+
+	msleep(520);
+	dev_dbg(&client->dev, "Success ARM Booting\n");
+
+	/* checking firmware */
+	ret = m5mols_get_version(sd);
+	if (!ret)
+		ret = m5mols_update_fw(sd, m5mols_sensor_power);
+	if (ret)
+		return ret;
+
+	m5mols_show_version(sd);
+
+	/* clear intterupt */
+	ret = i2c_r8_system(sd, CAT0_INT_FACTOR, &reg);
+	if (!ret)
+		/* Enable only AF intterupt */
+		ret = i2c_w8_system(sd, CAT0_INT_ENABLE, REG_INT_AF);
+	if (!ret)
+		ret = i2c_w8_param(sd, CAT1_DATA_INTERFACE,
+				REG_INTERFACE_MIPI);
+
+	return ret;
+}
+
+/* m5mols_init_controls - initialization for v4l2 control */
+static int m5mols_init_controls(struct m5mols_info *info)
+{
+	struct v4l2_subdev *sd = &info->sd;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	u16 max_ex_mon;
+	int ret;
+
+	/* check minimum & maximum of M-5MOLS controls */
+	ret = i2c_r16_ae(sd, CAT3_MAX_GAIN_MON, (u32 *)&max_ex_mon);
+	if (ret)
+		return ret;
+
+	/* set the controls using v4l2 control frameworks */
+	v4l2_ctrl_handler_init(&info->handle, 7);
+
+	info->colorfx = v4l2_ctrl_new_std_menu(&info->handle,
+			&m5mols_ctrl_ops, V4L2_CID_COLORFX,
+			9, 1, V4L2_COLORFX_NONE);
+	info->autoexposure = v4l2_ctrl_new_std_menu(&info->handle,
+			&m5mols_ctrl_ops, V4L2_CID_EXPOSURE_AUTO,
+			1, 0, V4L2_EXPOSURE_AUTO);
+	info->exposure = v4l2_ctrl_new_std(&info->handle,
+			&m5mols_ctrl_ops, V4L2_CID_EXPOSURE,
+			0, max_ex_mon, 1, (int)max_ex_mon/2);
+	info->autowb = v4l2_ctrl_new_std(&info->handle,
+			&m5mols_ctrl_ops, V4L2_CID_AUTO_WHITE_BALANCE,
+			0, 1, 1, 1);
+	info->saturation = v4l2_ctrl_new_std(&info->handle,
+			&m5mols_ctrl_ops, V4L2_CID_SATURATION,
+			0, 6, 1, 3);
+	info->zoom = v4l2_ctrl_new_std(&info->handle,
+			&m5mols_ctrl_ops, V4L2_CID_ZOOM_ABSOLUTE,
+			0, 70, 1, 0);
+	info->autofocus = v4l2_ctrl_new_std(&info->handle,
+			&m5mols_ctrl_ops, V4L2_CID_FOCUS_AUTO,
+			0, 1, 1, 1);
+
+	sd->ctrl_handler = &info->handle;
+	if (info->handle.error) {
+		dev_err(&client->dev, "Failed to init controls, %d\n", ret);
+		v4l2_ctrl_handler_free(&info->handle);
+		return info->handle.error;
+	}
+
+	v4l2_ctrl_cluster(2, &info->autoexposure);
+
+	return 0;
+}
+
+static int m5mols_s_power(struct v4l2_subdev *sd, int on)
+{
+	struct m5mols_info *info = to_m5mols(sd);
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct device *cdev = &client->dev;
+	int ret;
+
+	if (on) {
+
+		/* power on sequence
+		 * 1. power on sensor - regulator & other power gpio pins.
+		 * 2. armboot armboot, checking version, etc(using I2C).
+		 * 3. init v4l2 control - settle min/max values(using I2C).
+		 * 4. set information values - default resolution/fps */
+
+		ret = m5mols_sensor_power(info, true);
+		if (!ret)
+			ret = m5mols_sensor_armboot(sd);
+		if (!ret)
+			ret = m5mols_init_controls(info);
+		if (!ret) {
+			/* setup defulat size & fps */
+			info->fmt[M5MOLS_RES_MON] = default_fmt[M5MOLS_RES_MON];
+			info->tpf = default_fps;
+		}
+
+	} else {
+
+		/* power off sequence
+		 * 1. power off AF - let the lens do the soft-landing algorithm
+		 * 2. power off sensor - force it, if failed powering off AF */
+
+		/* landing softly lens & poweroff AF. If do not soft-landing
+		 * when powering off, the lens can be damaged. */
+		ret = m5mols_set_mode(sd, MODE_MONITOR);
+		if (!ret)
+			ret = i2c_w8_lens(sd, CATA_AF_EXCUTE, REG_AF_STOP);
+		if (!ret)
+			ret = i2c_w8_lens(sd, CATA_AF_MODE, REG_AF_POWEROFF);
+		if (!ret)
+			ret = m5mols_check_busy(sd, CAT_SYSTEM, CAT0_STATUS,
+					REG_AF_IDLE);
+		if (!ret)
+			dev_info(cdev, "Success AF soft-landing lens\n");
+
+		/* Force powering off the sensor, if AF powering off is failed
+		 * or not */
+		ret = m5mols_sensor_power(info, false);
+	}
+
+	return ret;
+}
+
+static int m5mols_log_status(struct v4l2_subdev *sd)
+{
+	struct m5mols_info *info = to_m5mols(sd);
+
+	v4l2_ctrl_handler_log_status(&info->handle, sd->name);
+
+	return 0;
+}
+
+static const struct v4l2_subdev_core_ops m5mols_core_ops = {
+	.s_power		= m5mols_s_power,
+	.g_ctrl			= v4l2_subdev_g_ctrl,
+	.s_ctrl			= v4l2_subdev_s_ctrl,
+	.queryctrl		= v4l2_subdev_queryctrl,
+	.querymenu		= v4l2_subdev_querymenu,
+	.g_ext_ctrls		= v4l2_subdev_g_ext_ctrls,
+	.try_ext_ctrls		= v4l2_subdev_try_ext_ctrls,
+	.s_ext_ctrls		= v4l2_subdev_s_ext_ctrls,
+	.log_status		= m5mols_log_status,
+};
+
+static const struct v4l2_subdev_ops m5mols_ops = {
+	.core	= &m5mols_core_ops,
+	.video	= &m5mols_video_ops,
+};
+
+static void m5mols_irq_work(struct work_struct *work)
+{
+	struct m5mols_info *info = container_of(work, struct m5mols_info,
+			work_irq);
+	struct v4l2_subdev *sd = &info->sd;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct device *cdev = &client->dev;
+	u32 reg;
+	int ret;
+
+	/* If not powering up and I2C failed, return immediately */
+	if (!is_powerup(sd) || i2c_r8_system(sd, CAT0_INT_FACTOR, &reg))
+		return;
+
+	switch (reg & REG_INT_MASK) {
+	case REG_INT_AF:
+		ret = i2c_r8_lens(sd, CATA_AF_STATUS, &reg);
+		dev_dbg(cdev, "= AF %s\n",
+				reg == REG_AF_FAIL ? "Failed" :
+				reg == REG_AF_SUCCESS ? "Success" :
+				reg == REG_AF_IDLE ? "Idle" : "Busy");
+		break;
+	case REG_INT_CAPTURE:
+	case REG_INT_ZOOM:
+	case REG_INT_FRAMESYNC:
+	case REG_INT_FD:
+	case REG_INT_LENS_INIT:
+	case REG_INT_SOUND:
+	case REG_INT_MODE:
+	default:
+		dev_dbg(cdev, "= Nothing : %02x\n", reg);
+		break;
+	};
+}
+
+static irqreturn_t m5mols_irq_handler(int irq, void *data)
+{
+	struct v4l2_subdev *sd = data;
+	struct m5mols_info *info = to_m5mols(sd);
+
+	schedule_work(&info->work_irq);
+
+	return IRQ_HANDLED;
+}
+
+static int m5mols_probe(struct i2c_client *client,
+			 const struct i2c_device_id *id)
+{
+	const struct m5mols_platform_data *pdata =
+		client->dev.platform_data;
+	struct m5mols_info *info;
+	struct v4l2_subdev *sd;
+	int ret;
+
+	if (pdata == NULL) {
+		dev_err(&client->dev, "No platform data\n");
+		return -EINVAL;
+	}
+
+	if (!gpio_is_valid(pdata->gpio_rst)) {
+		dev_err(&client->dev, "No valid nRST gpio pin.\n");
+		return -EINVAL;
+	}
+
+	if (!pdata->irq) {
+		dev_err(&client->dev, "Interrupt not assigned.\n");
+		return -EINVAL;
+	}
+
+	info = kzalloc(sizeof(struct m5mols_info), GFP_KERNEL);
+	if (info == NULL) {
+		dev_err(&client->dev, "Failed to allocate info\n");
+		return -ENOMEM;
+	}
+
+	info->pdata	= pdata;
+	if (info->pdata->set_power)	/* for additional power if needed. */
+		info->set_power = pdata->set_power;
+
+	ret = gpio_request(info->pdata->gpio_rst, "M5MOLS nRST");
+	if (ret) {
+		dev_err(&client->dev, "Failed to set gpio, %d\n", ret);
+		goto out_gpio;
+	}
+
+	gpio_direction_output(info->pdata->gpio_rst, !info->pdata->enable_rst);
+
+	ret = regulator_bulk_get(&client->dev, ARRAY_SIZE(supplies), supplies);
+	if (ret) {
+		dev_err(&client->dev, "Failed to get regulators, %d\n", ret);
+		goto out_reg;
+	}
+
+	sd = &info->sd;
+	strlcpy(sd->name, MOD_NAME, sizeof(sd->name));
+	v4l2_i2c_subdev_init(sd, client, &m5mols_ops);
+
+	INIT_WORK(&info->work_af, m5mols_af_work);
+
+	/* After calling v4l2_i2c_subdev_init() the I2C is available, and
+	 * then it's not happend the problem when issued irq & done workqueue,
+	 * and then I2C is accessed. */
+	if (info->pdata->irq) {
+		INIT_WORK(&info->work_irq, m5mols_irq_work);
+		ret = request_irq(info->pdata->irq,
+				m5mols_irq_handler,
+				IRQF_TRIGGER_RISING,
+				MOD_NAME, sd);
+		if (ret) {
+			dev_err(&client->dev,
+				"Failed to request irq: %d\n", ret);
+			goto out_irq;
+		}
+	}
+
+	info->do_once = true;
+	v4l2_dbg(1, m5mols_debug, sd, "probed m5mols driver.\n");
+
+	return 0;
+
+out_irq:
+	free_irq(info->pdata->irq, sd);
+out_reg:
+	regulator_bulk_free(ARRAY_SIZE(supplies), supplies);
+out_gpio:
+	gpio_free(info->pdata->gpio_rst);
+	kfree(info);
+
+	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);
+	v4l2_ctrl_handler_free(&info->handle);
+	free_irq(info->pdata->irq, sd);
+
+	regulator_bulk_free(ARRAY_SIZE(supplies), supplies);
+	gpio_free(info->pdata->gpio_rst);
+	kfree(info);
+
+	info->do_once = false;
+	v4l2_dbg(1, m5mols_debug, sd, "removed m5mols driver.\n");
+
+	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@samsung.com>");
+MODULE_AUTHOR("Dongsoo Kim <dongsoo45.kim@samsung.com>");
+MODULE_DESCRIPTION("Fujitsu M-5MOLS 8M Pixel camera sensor with ISP driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/m5mols/m5mols_reg.h b/drivers/media/video/m5mols/m5mols_reg.h
new file mode 100644
index 0000000..707930f
--- /dev/null
+++ b/drivers/media/video/m5mols/m5mols_reg.h
@@ -0,0 +1,218 @@
+/*
+ * Register map for M-5MOLS 8M Pixel camera sensor with ISP
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd
+ * Author: HeungJun Kim, riverful.kim@samsung.com
+ *
+ * Copyright (C) 2009 Samsung Electronics Co., Ltd
+ * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com
+ *
+ * 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_REG_H
+#define M5MOLS_REG_H
+
+/*
+ * Category section register
+ *
+ * The category means set including relevant command of M-5MOLS.
+ */
+#define CAT_SYSTEM		0x00
+#define CAT_PARAM		0x01
+#define CAT_MONITOR		0x02
+#define CAT_AE			0x03
+#define CAT_WB			0x06
+#define CAT_EXIF		0x07
+#define CAT_LENS		0x0a
+#define CAT_FLASH		0x0f	/* related with FW, Verions, booting */
+
+/*
+ * Category 0 - System mode
+ *
+ * The system mode in the M-5MOLS means area available to handle with the whole
+ * & all-round system of sensor. It deals with version/interrupt/setting mode &
+ * even sensor's status. Especially, the M-5MOLS sensor with ISP varys by
+ * packaging & manufactur vendors, even the customer user vendor. And the
+ * detailed function may have a difference. The version information can
+ * determine what method use in the driver.
+ *
+ * There is many registers between customer version address and awb one. For
+ * more specific contents, reference the m5mols.h.
+ */
+#define CAT0_CUSTOMER_CODE	0x00	/* customer version*/
+#define CAT0_VERSION_AWB_L	0x09	/* Auto WB version low byte */
+#define CAT0_VERSION_STR	0x0a	/* string including M-5MOLS */
+#define CAT0_SYSMODE		0x0b	/* system mode register */
+#define CAT0_STATUS		0x0c	/* system status register */
+#define CAT0_INT_FACTOR		0x10	/* interrupt pending register */
+#define CAT0_INT_ENABLE		0x11	/* interrupt enable register */
+
+#define REG_MODE_SYSINIT	0x00	/* system mode */
+#define REG_MODE_PARAM		0x01	/* parameter mode */
+#define REG_MODE_MONITOR	0x02	/* monitor mode */
+#define REG_MODE_CAPTURE	0x03	/* capture mode */
+
+#define REG_INT_MODE		(1 << 0)
+#define REG_INT_AF		(1 << 1)
+#define REG_INT_ZOOM		(1 << 2)
+#define REG_INT_CAPTURE		(1 << 3)
+#define REG_INT_FRAMESYNC	(1 << 4)
+#define REG_INT_FD		(1 << 5)
+#define REG_INT_LENS_INIT	(1 << 6)
+#define REG_INT_SOUND		(1 << 7)
+#define REG_INT_ALLOFF		0x0
+#define REG_INT_MASK		0x0f
+
+/*
+ * category 1 - Parameter mode
+ *
+ * This category supports function of camera features of M-5MOLS. It means we
+ * can handle with preview(monitor) resolution size/frame per second/interface
+ * between the sensor and the Application Processor/even the image effect.
+ */
+#define CAT1_DATA_INTERFACE	0x00	/* interface between sensor and AP */
+#define CAT1_MONITOR_SIZE	0x01	/* resolution at the monitor mode */
+#define CAT1_MONITOR_FPS	0x02	/* frame per second at this mode */
+#define CAT1_EFFECT		0x0b	/* image effects */
+
+#define REG_INTERFACE_YUV	0x00
+#define REG_INTERFACE_HDMI	0x01
+#define REG_INTERFACE_MIPI	0x02	/* only this is used in this version */
+
+#define REG_EFFECT_OFF		0x00
+#define REG_EFFECT_NEGATIVE	0x01
+#define REG_EFFECT_SOLARIZATION	0x02
+#define REG_EFFECT_EMBOSS	0x06
+#define REG_EFFECT_OUTLINE	0x07
+#define REG_EFFECT_WATERCOLOR	0x08
+
+/*
+ * Category 2 - Monitor mode
+ *
+ * The monitor mode is same as preview mode as we said. The M-5MOLS has another
+ * mode named "Preview", but this preview mode is used at the case specific
+ * vider-recording mode. This mmode supports only YUYV format. On the other
+ * hand, the JPEG & RAW formats is supports by capture mode. And, there are
+ * another options like zoom/color effect(different with effect in parameter
+ * mode)/anti hand shaking algorithm.
+ */
+#define CAT2_ZOOM		0x01	/* set the zoom position & execute */
+#define CAT2_ZOOM_POSITION	0x02	/* get the zoom position */
+#define CAT2_ZOOM_STEP		0x03	/* set the zoom step */
+#define CAT2_CFIXB		0x09	/* CB value for color effect */
+#define CAT2_CFIXR		0x0a	/* CR value for color effect */
+#define CAT2_COLOR_EFFECT	0x0b	/* set on/off of color effect */
+#define CAT2_CHROMA_LVL		0x0f	/* set chroma level */
+#define CAT2_CHROMA_EN		0x10	/* set on/off of choroma */
+
+#define REG_CFIXB_SEPIA		0xd8
+#define REG_CFIXB_GRAY		0x00
+#define REG_CFIXB_RED		0x00
+#define REG_CFIXB_GREEN		0xe0
+#define REG_CFIXB_BLUE		0x40
+#define REG_CFIXB_PINK		0x20
+#define REG_CFIXB_YELLOW	0x80
+#define REG_CFIXB_PURPLE	0x50
+#define REG_CFIXB_ANTIQUE	0xd0
+
+#define REG_CFIXR_SEPIA		0x18
+#define REG_CFIXR_GRAY		0x00
+#define REG_CFIXR_RED		0x6b
+#define REG_CFIXR_GREEN		0xe0
+#define REG_CFIXR_BLUE		0x00
+#define REG_CFIXR_PINK		0x40
+#define REG_CFIXR_YELLOW	0x00
+#define REG_CFIXR_PURPLE	0x20
+#define REG_CFIXR_ANTIQUE	0x30
+
+#define REG_COLOR_EFFECT_OFF	0x00
+#define REG_COLOR_EFFECT_ON	0x01
+
+#define REG_CHROMA_LVL0		0x1c
+#define REG_CHROMA_LVL1		0x3e
+#define REG_CHROMA_LVL2		0x5f
+#define REG_CHROMA_LVL3		0x80
+#define REG_CHROMA_LVL4		0xa1
+#define REG_CHROMA_LVL5		0xc2
+#define REG_CHROMA_LVL6		0xe4
+
+#define REG_CHROMA_OFF		0x00
+#define REG_CHROMA_ON		0x01
+
+/*
+ * Category 3 - Auto Exposure
+ *
+ * The M-5MOLS exposure capbility is detailed as which is similar to digital
+ * camera. This category supports AE locking/various AE mode(range of exposure)
+ * /ISO/flickering/EV bias/shutter/meteoring, and anything else. And the
+ * maximum/minimum exposure gain value depending on M-5MOLS firmware, may be
+ * different. So, this category also provide getting the max/min values. And,
+ * each monitor and capture mode has each gain/shutter/max exposure values.
+ */
+#define CAT3_AE_LOCK		0x00	/* locking Auto exposure */
+#define CAT3_AE_MODE		0x01	/* set AE mode, mode means range */
+#define CAT3_MANUAL_GAIN_MON	0x12	/* meteoring value, monitor mode */
+#define CAT3_MANUAL_SHUT_MON	0x14	/* shutter value, monitor mode */
+#define CAT3_MAX_EXPOSURE_MON	0x16	/* max value at the monitor mode */
+#define CAT3_MAX_EXPOSURE_CAP	0x18	/* max value at the capture mode */
+#define CAT3_MAX_GAIN_MON	0x1a	/* max gain value at the monitor */
+#define CAT3_MAX_GAIN_CAP	0x1c	/* max gain value at the capture */
+#define CAT3_MANUAL_GAIN_CAP	0x26	/* meteoring value, capture mode */
+#define CAT3_MANUAL_SHUT_CAP	0x28	/* shutter value, capture mode */
+
+#define REG_AE_MODE_OFF		0x00	/* AE off */
+#define REG_AE_MODE_ALL		0x01	/* calc AE in all block integral */
+#define REG_AE_MODE_CENTER1	0x02	/* calc AE at the center area 1 */
+#define REG_AE_MODE_CENTER2	0x03	/* calc AE at the center area 2 */
+#define REG_AE_MODE_CENTER3	0x04	/* calc AE at the center area 3 */
+#define REG_AE_MODE_SPOT1	0x05	/* spot 1 */
+#define REG_AE_MODE_SPOT2	0x06	/* spot 2 */
+
+/*
+ * Category 6 - White Balance
+ *
+ * This cagetory provide AWB locking/mode/preset/speed/gain bias, etc.
+ */
+#define CAT6_AWB_LOCK		0x00	/* locking Auto Whitebalance */
+#define CAT6_AWB_MODE		0x02	/* set Auto or Manual */
+#define CAT6_AWB_MANUAL		0x03	/* preset, not used in this version */
+
+#define REG_AWB_MODE_AUTO	0x01	/* AWB off */
+#define REG_AWB_MODE_MANUAL	0x02	/* AWB preset */
+
+/*
+ * Category A - Lens parameter
+ */
+#define CATA_INIT_AF_FUNC	0x00
+#define CATA_AF_MODE		0x01
+#define CATA_AF_EXCUTE		0x02
+#define CATA_AF_STATUS		0x03
+#define CATA_AF_VERSION		0x0a
+
+#define REG_AF_FAIL		0x00
+#define REG_AF_SUCCESS		0x02
+#define REG_AF_IDLE		0x04
+#define REG_AF_BUSY		0x05
+
+#define REG_AF_MODE_NORMAL	0x00
+#define REG_AF_MODE_MACRO	0x01
+#define REG_AF_MODE_CAF		0x02
+
+#define REG_AF_STOP		0x00
+#define REG_AF_EXE_AUTO		0x01
+#define REG_AF_EXE_CAF		0x02
+#define REG_AF_POWEROFF		0x07
+
+/*
+ * Category F - Flash
+ *
+ * This mode provides functions about internal flash stuff and system startup.
+ */
+#define CATF_CAM_START		0x12	/* It start internal ARM core booting
+					 * after power-up */
+
+#endif	/* M5MOLS_REG_H */
diff --git a/include/media/m5mols.h b/include/media/m5mols.h
new file mode 100644
index 0000000..6986855
--- /dev/null
+++ b/include/media/m5mols.h
@@ -0,0 +1,35 @@
+/*
+ * Driver header for M-5MOLS 8M Pixel camera sensor with ISP
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd
+ * Author: HeungJun Kim, riverful.kim@samsung.com
+ *
+ * Copyright (C) 2009 Samsung Electronics Co., Ltd
+ * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com
+ *
+ * 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 MEDIA_M5MOLS_H
+#define MEDIA_M5MOLS_H
+
+/**
+ * struct m5mols_platform_data - platform data for M-5MOLS driver
+ * @irq:	GPIO getting the irq pin of M-5MOLS
+ * @gpio_rst:	GPIO driving the reset pin of M-5MOLS
+ * @enable_rst:	the pin state when reset pin is enabled
+ * @set_power:	an additional callback to a board setup code
+ *		to be called after enabling and before disabling
+ *		the sensor device supply regulators
+ */
+struct m5mols_platform_data {
+	int irq;
+	int gpio_rst;
+	bool enable_rst;
+	int (*set_power)(struct device *dev, int on);
+};
+
+#endif	/* MEDIA_M5MOLS_H */
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* Re: [PATCH] Add support for M-5MOLS 8 Mega Pixel camera
  2011-03-16 13:38 [PATCH] Add support for M-5MOLS 8 Mega Pixel camera Kim, Heungjun
@ 2011-03-19 13:37 ` Sylwester Nawrocki
  2011-03-19 15:11   ` Kim HeungJun
  2011-04-04 12:20 ` Sungchun Kang
  2011-05-13 10:06 ` [PATCH v7] Add support for M-5MOLS 8 Mega Pixel camera ISP HeungJun, Kim
  2 siblings, 1 reply; 34+ messages in thread
From: Sylwester Nawrocki @ 2011-03-19 13:37 UTC (permalink / raw)
  To: Kim, Heungjun
  Cc: linux-media, hverkuil, laurent.pinchart, Sylwester Nawrocki,
	Kyungmin Park

Hi HeungJun,

On 03/16/2011 02:38 PM, Kim, Heungjun wrote:
> Add I2C/V4L2 subdev driver for M-5MOLS camera sensor with integrated
> image signal processor.
> 
> Signed-off-by: Heungjun Kim<riverful.kim@samsung.com>
> Signed-off-by: Sylwester Nawrocki<s.nawrocki@samsung.com>
> Signed-off-by: Kyungmin Park<kyungmin.park@samsung.com>
> ---
> 
> Hi Hans and everyone,
> 
> This is sixth version of M-5MOLS 8 Mega Pixel camera sensor. And, if you see

Would be good to indicate the version in the subject too.

> previous version, you can find at:
> http://www.spinics.net/lists/linux-media/msg29350.html
> 
> This driver patch is fixed several times, and the important issues is almost
> corrected. And, I hope that this is the last version one merged for 2.6.39.
> I look forward to be reviewed one more time.
> 
> The summary of this version's feature is belows:
> 
> 1. Add focus control
> 	: I've suggest menu type focus control, but I agreed this version is
> 	not yet the level accepted. So, I did not use focus control which
> 	I suggest.
> 	The M-5MOLS focus routine takes some time to execute. But, the user
> 	application calling v4l2 control, should not hanged while streaming
> 	using q/dqbuf. So, I use workqueue. I want to discuss the focus
> 	subject on mailnglist next time.
> 
> 2. Add irq routine
> 	: M-5MOLS can issues using GPIO pin, and I insert the basic routine
> 	of irq. This version handles only the Auto focus interrupt source.
> 	It shows only lens focusing status, don't any action now.
> 
> 3. Speed-up whole I2C operation
> 	: I've tested several times for decreasing the stabilization time
> 	while I2C communication, and I have find proper time. Of course,
> 	it's more faster than previous version.

That sounds good. Do you think the delays before I2C read/write could
be avoided in some (if not all) cases by using some status registers
polling?

> 
> 4. Let streamon() be called once at the streamming
> 	: It might be an issue, videobuf2 framework calls streamon when
> 	qbuf() for enqueueing. It means, the driver's streamon() function

No, that's not really the case. At last videobuf2 buf_queue op might be
called in response to VIDIOC_STREAMON. Certainly there must be some bug
in the host driver if subdev's s_stream is being called repeatedly.

> 	might be callled continuously if there is no proper handling in the
> 	subdev driver, and low the framerate by adding unneeded I2C operation.
> 	The M-5MOLS sensor needs command one time for streaming. If commands
> 	once, this sensor streams continuously, and this version handles it.
> 
> 5. Update FW
> 	: It's a little tricky. Originally, the v4l2 frame provide load_fw(),
> 	but, there is the occasion which should do in openning the videonode,
> 	and it's the same occasion with us. So, if it's not wrong or it makes
> 	any problem, we hope to insert m5mols_update_fw() with weak attribute.
> 	And, I'm sorry that the fw updating code is confidential. unserstand
> 	by favor, plz.
> 
> And, as always, this driver is tested on s5pc210 board using s5p-fimc driver.
> 
> I'll wait for reviewing.
> 
> Thanks and Regards,
> 	Heungjun Kim
> 	Samsung Electronics DMC R&D Center
> 
>   drivers/media/video/Kconfig                  |    2 +
>   drivers/media/video/Makefile                 |    1 +
>   drivers/media/video/m5mols/Kconfig           |    5 +
>   drivers/media/video/m5mols/Makefile          |    3 +
>   drivers/media/video/m5mols/m5mols.h          |  251 ++++++
>   drivers/media/video/m5mols/m5mols_controls.c |  213 +++++
>   drivers/media/video/m5mols/m5mols_core.c     | 1062 ++++++++++++++++++++++++++
>   drivers/media/video/m5mols/m5mols_reg.h      |  218 ++++++
>   include/media/m5mols.h                       |   35 +
>   9 files changed, 1790 insertions(+), 0 deletions(-)
>   create mode 100644 drivers/media/video/m5mols/Kconfig
>   create mode 100644 drivers/media/video/m5mols/Makefile
>   create mode 100644 drivers/media/video/m5mols/m5mols.h
>   create mode 100644 drivers/media/video/m5mols/m5mols_controls.c
>   create mode 100644 drivers/media/video/m5mols/m5mols_core.c
>   create mode 100644 drivers/media/video/m5mols/m5mols_reg.h
>   create mode 100644 include/media/m5mols.h
> 
...
> diff --git a/drivers/media/video/m5mols/m5mols_controls.c b/drivers/media/video/m5mols/m5mols_controls.c
> new file mode 100644
> index 0000000..784b764
> --- /dev/null
> +++ b/drivers/media/video/m5mols/m5mols_controls.c
> @@ -0,0 +1,213 @@
> +/*
> + * Controls for M-5MOLS 8M Pixel camera sensor with ISP
> + *
> + * Copyright (C) 2011 Samsung Electronics Co., Ltd
> + * Author: HeungJun Kim, riverful.kim@samsung.com
> + *
> + * Copyright (C) 2009 Samsung Electronics Co., Ltd
> + * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com
> + *
> + * 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/videodev2.h>
> +#include<media/v4l2-ctrls.h>
> +
> +#include "m5mols.h"
> +#include "m5mols_reg.h"
> +
> +/* The externing camera control functions */


> +int m5mols_lock_ae(struct m5mols_info *info, bool lock)
> +{
> +	struct v4l2_subdev *sd =&info->sd;
> +
> +	return i2c_w8_ae(sd, CAT3_AE_LOCK, !!(info->lock_ae = lock));

Shouldn't be info->lock_ae assigned a new value only in case I2C write
succeeds?

> +}
> +
> +int m5mols_lock_awb(struct m5mols_info *info, bool lock)
> +{
> +	struct v4l2_subdev *sd =&info->sd;
> +
> +	info->lock_awb = lock;
> +
> +	return i2c_w8_wb(sd, CAT6_AWB_LOCK, !!(info->lock_awb = lock));

Ditto.

> +/*
> + * m5mols_sensor_armboot() - booting M-5MOLS internal ARM core-controller.
> + *
> + * It makes to ready M-5MOLS for I2C&  MIPI interface. After it's powered up,
> + * it activates if it gets armboot command for I2C interface. After getting
> + * cmd, it must wait about least 520ms referenced by M-5MOLS datasheet.
> + */
> +static int m5mols_sensor_armboot(struct v4l2_subdev *sd)
> +{
> +	struct i2c_client *client = v4l2_get_subdevdata(sd);
> +	u32 reg;
> +	int ret;
> +
> +	/* ARM(M-5MOLS core) booting */
> +	ret = i2c_w8_flash(sd, CATF_CAM_START, true);
> +	if (ret<  0)
> +		return ret;
> +
> +	msleep(520);

Don't you consider using a waitqueue and a relevant interrupt
generated by the ISP when it has completed booting?
This would allow to decrease the delay to an optimal minimum.

--
Regards,
Sylwester Nawrocki

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH] Add support for M-5MOLS 8 Mega Pixel camera
  2011-03-19 13:37 ` Sylwester Nawrocki
@ 2011-03-19 15:11   ` Kim HeungJun
  2011-03-19 19:28     ` Sylwester Nawrocki
  0 siblings, 1 reply; 34+ messages in thread
From: Kim HeungJun @ 2011-03-19 15:11 UTC (permalink / raw)
  To: Sylwester Nawrocki
  Cc: Kim HeungJun, Kim, Heungjun, linux-media, hverkuil,
	laurent.pinchart, Sylwester Nawrocki, Kyungmin Park

Hi Sylwester,

Thanks for the reviews. :) 

2011. 3. 19., 오후 10:37, Sylwester Nawrocki 작성:

> Hi HeungJun,
> 
> On 03/16/2011 02:38 PM, Kim, Heungjun wrote:
>> Add I2C/V4L2 subdev driver for M-5MOLS camera sensor with integrated
>> image signal processor.
>> 
>> Signed-off-by: Heungjun Kim<riverful.kim@samsung.com>
>> Signed-off-by: Sylwester Nawrocki<s.nawrocki@samsung.com>
>> Signed-off-by: Kyungmin Park<kyungmin.park@samsung.com>
>> ---
>> 
>> Hi Hans and everyone,
>> 
>> This is sixth version of M-5MOLS 8 Mega Pixel camera sensor. And, if you see
> 
> Would be good to indicate the version in the subject too.
> 
>> previous version, you can find at:
>> http://www.spinics.net/lists/linux-media/msg29350.html
>> 
>> This driver patch is fixed several times, and the important issues is almost
>> corrected. And, I hope that this is the last version one merged for 2.6.39.
>> I look forward to be reviewed one more time.
>> 
>> The summary of this version's feature is belows:
>> 
>> 1. Add focus control
>> 	: I've suggest menu type focus control, but I agreed this version is
>> 	not yet the level accepted. So, I did not use focus control which
>> 	I suggest.
>> 	The M-5MOLS focus routine takes some time to execute. But, the user
>> 	application calling v4l2 control, should not hanged while streaming
>> 	using q/dqbuf. So, I use workqueue. I want to discuss the focus
>> 	subject on mailnglist next time.
>> 
>> 2. Add irq routine
>> 	: M-5MOLS can issues using GPIO pin, and I insert the basic routine
>> 	of irq. This version handles only the Auto focus interrupt source.
>> 	It shows only lens focusing status, don't any action now.
>> 
>> 3. Speed-up whole I2C operation
>> 	: I've tested several times for decreasing the stabilization time
>> 	while I2C communication, and I have find proper time. Of course,
>> 	it's more faster than previous version.
> 
> That sounds good. Do you think the delays before I2C read/write could
> be avoided in some (if not all) cases by using some status registers
> polling?
I don't understand literally. Could you explain more detailed with some examples?
My understanding is that it might be an issues or problem when getting some 
status registers with polling it. is it right?

> 
>> 
>> 4. Let streamon() be called once at the streamming
>> 	: It might be an issue, videobuf2 framework calls streamon when
>> 	qbuf() for enqueueing. It means, the driver's streamon() function
> 
> No, that's not really the case. At last videobuf2 buf_queue op might be
> called in response to VIDIOC_STREAMON. Certainly there must be some bug
> in the host driver if subdev's s_stream is being called repeatedly.
Ah, it's good news. I seemed to use some little old version of vb2.
Then, I would try to merge new vb2 on our branch, and I need some help on that.
I'll contact you. After that, I test new vb2 merged on our branch and chech if there
is not any issues, I'll correct to use just enable variable not using is_streaming(),
and also correct comments.

Actually, that has happened low frame rate issues on M-5MOLS, I had have headache
cause of this some weeks, and it's the suspects. :D
It's my fault not to use newer version.
So, letting me know this new version is very happy for me.

> 
>> 	might be callled continuously if there is no proper handling in the
>> 	subdev driver, and low the framerate by adding unneeded I2C operation.
>> 	The M-5MOLS sensor needs command one time for streaming. If commands
>> 	once, this sensor streams continuously, and this version handles it.
>> 
>> 5. Update FW
>> 	: It's a little tricky. Originally, the v4l2 frame provide load_fw(),
>> 	but, there is the occasion which should do in openning the videonode,
>> 	and it's the same occasion with us. So, if it's not wrong or it makes
>> 	any problem, we hope to insert m5mols_update_fw() with weak attribute.
>> 	And, I'm sorry that the fw updating code is confidential. unserstand
>> 	by favor, plz.
>> 
>> And, as always, this driver is tested on s5pc210 board using s5p-fimc driver.
>> 
>> I'll wait for reviewing.
>> 
>> Thanks and Regards,
>> 	Heungjun Kim
>> 	Samsung Electronics DMC R&D Center
>> 
>>  drivers/media/video/Kconfig                  |    2 +
>>  drivers/media/video/Makefile                 |    1 +
>>  drivers/media/video/m5mols/Kconfig           |    5 +
>>  drivers/media/video/m5mols/Makefile          |    3 +
>>  drivers/media/video/m5mols/m5mols.h          |  251 ++++++
>>  drivers/media/video/m5mols/m5mols_controls.c |  213 +++++
>>  drivers/media/video/m5mols/m5mols_core.c     | 1062 ++++++++++++++++++++++++++
>>  drivers/media/video/m5mols/m5mols_reg.h      |  218 ++++++
>>  include/media/m5mols.h                       |   35 +
>>  9 files changed, 1790 insertions(+), 0 deletions(-)
>>  create mode 100644 drivers/media/video/m5mols/Kconfig
>>  create mode 100644 drivers/media/video/m5mols/Makefile
>>  create mode 100644 drivers/media/video/m5mols/m5mols.h
>>  create mode 100644 drivers/media/video/m5mols/m5mols_controls.c
>>  create mode 100644 drivers/media/video/m5mols/m5mols_core.c
>>  create mode 100644 drivers/media/video/m5mols/m5mols_reg.h
>>  create mode 100644 include/media/m5mols.h
>> 
> ...
>> diff --git a/drivers/media/video/m5mols/m5mols_controls.c b/drivers/media/video/m5mols/m5mols_controls.c
>> new file mode 100644
>> index 0000000..784b764
>> --- /dev/null
>> +++ b/drivers/media/video/m5mols/m5mols_controls.c
>> @@ -0,0 +1,213 @@
>> +/*
>> + * Controls for M-5MOLS 8M Pixel camera sensor with ISP
>> + *
>> + * Copyright (C) 2011 Samsung Electronics Co., Ltd
>> + * Author: HeungJun Kim, riverful.kim@samsung.com
>> + *
>> + * Copyright (C) 2009 Samsung Electronics Co., Ltd
>> + * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com
>> + *
>> + * 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/videodev2.h>
>> +#include<media/v4l2-ctrls.h>
>> +
>> +#include "m5mols.h"
>> +#include "m5mols_reg.h"
>> +
>> +/* The externing camera control functions */
> 
> 
>> +int m5mols_lock_ae(struct m5mols_info *info, bool lock)
>> +{
>> +	struct v4l2_subdev *sd =&info->sd;
>> +
>> +	return i2c_w8_ae(sd, CAT3_AE_LOCK, !!(info->lock_ae = lock));
> 
> Shouldn't be info->lock_ae assigned a new value only in case I2C write
> succeeds?
Good catch. It might be bugs. I'll correct this. It's right that it should be changed
after checking to succeed.  

> 
>> +}
>> +
>> +int m5mols_lock_awb(struct m5mols_info *info, bool lock)
>> +{
>> +	struct v4l2_subdev *sd =&info->sd;
>> +
>> +	info->lock_awb = lock;
>> +
>> +	return i2c_w8_wb(sd, CAT6_AWB_LOCK, !!(info->lock_awb = lock));
> 
> Ditto.
> 
>> +/*
>> + * m5mols_sensor_armboot() - booting M-5MOLS internal ARM core-controller.
>> + *
>> + * It makes to ready M-5MOLS for I2C&  MIPI interface. After it's powered up,
>> + * it activates if it gets armboot command for I2C interface. After getting
>> + * cmd, it must wait about least 520ms referenced by M-5MOLS datasheet.
>> + */
>> +static int m5mols_sensor_armboot(struct v4l2_subdev *sd)
>> +{
>> +	struct i2c_client *client = v4l2_get_subdevdata(sd);
>> +	u32 reg;
>> +	int ret;
>> +
>> +	/* ARM(M-5MOLS core) booting */
>> +	ret = i2c_w8_flash(sd, CATF_CAM_START, true);
>> +	if (ret<  0)
>> +		return ret;
>> +
>> +	msleep(520);
> 
> Don't you consider using a waitqueue and a relevant interrupt
> generated by the ISP when it has completed booting?
> This would allow to decrease the delay to an optimal minimum.
I didn't yet consider it. But, it looks good option. I would try this at the next version. :)

And, but it seems relevant or not, how to think about changing to probe all sensors
when video node (exactly fimc) being opened by userapplication?
I means, as you know, the most sensors has booting delay time, including even
noonxxxxxxx & srxxxxxx sensor series. But, we can choose the calling to probe sensor
be made of workqueue() in the fimc. If doing as this, it can be possible not to wait
probing previous sensor. So, What about your opinion?

Thanks and Regards,
Heungjun Kim

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH] Add support for M-5MOLS 8 Mega Pixel camera
  2011-03-19 15:11   ` Kim HeungJun
@ 2011-03-19 19:28     ` Sylwester Nawrocki
  2011-03-21  6:08       ` Kim, HeungJun
  0 siblings, 1 reply; 34+ messages in thread
From: Sylwester Nawrocki @ 2011-03-19 19:28 UTC (permalink / raw)
  To: Kim HeungJun
  Cc: Kim, Heungjun, Sylwester Nawrocki, Hans Verkuil,
	Laurent Pinchart, Kyungmin Park, linux-media

On 03/19/2011 04:11 PM, Kim HeungJun wrote:
> Hi Sylwester,
> 
> Thanks for the reviews. :)
> 
> 2011. 3. 19., 오후 10:37, Sylwester Nawrocki 작성:
> 
>> Hi HeungJun,
>>
>> On 03/16/2011 02:38 PM, Kim, Heungjun wrote:
>>> Add I2C/V4L2 subdev driver for M-5MOLS camera sensor with integrated
>>> image signal processor.
>>>
>>> Signed-off-by: Heungjun Kim<riverful.kim@samsung.com>
>>> Signed-off-by: Sylwester Nawrocki<s.nawrocki@samsung.com>
>>> Signed-off-by: Kyungmin Park<kyungmin.park@samsung.com>
>>> ---
>>>
>>> Hi Hans and everyone,
>>>
>>> This is sixth version of M-5MOLS 8 Mega Pixel camera sensor. And, if you see
>>
>> Would be good to indicate the version in the subject too.
>>
>>> previous version, you can find at:
>>> http://www.spinics.net/lists/linux-media/msg29350.html
>>>
>>> This driver patch is fixed several times, and the important issues is almost
>>> corrected. And, I hope that this is the last version one merged for 2.6.39.
>>> I look forward to be reviewed one more time.
>>>
>>> The summary of this version's feature is belows:
>>>
>>> 1. Add focus control
>>> 	: I've suggest menu type focus control, but I agreed this version is
>>> 	not yet the level accepted. So, I did not use focus control which
>>> 	I suggest.
>>> 	The M-5MOLS focus routine takes some time to execute. But, the user
>>> 	application calling v4l2 control, should not hanged while streaming
>>> 	using q/dqbuf. So, I use workqueue. I want to discuss the focus
>>> 	subject on mailnglist next time.
>>>
>>> 2. Add irq routine
>>> 	: M-5MOLS can issues using GPIO pin, and I insert the basic routine
>>> 	of irq. This version handles only the Auto focus interrupt source.
>>> 	It shows only lens focusing status, don't any action now.
>>>
>>> 3. Speed-up whole I2C operation
>>> 	: I've tested several times for decreasing the stabilization time
>>> 	while I2C communication, and I have find proper time. Of course,
>>> 	it's more faster than previous version.
>>
>> That sounds good. Do you think the delays before I2C read/write could
>> be avoided in some (if not all) cases by using some status registers
>> polling?
> I don't understand literally. Could you explain more detailed with some examples?
> My understanding is that it might be an issues or problem when getting some
> status registers with polling it. is it right?

My concern is that we might not need an extra delay between consecutive 
read or write operations in every case. Possibly it would be enough
to read the status of some operations instead. But that just what I suspect.

> 
>>
>>>
>>> 4. Let streamon() be called once at the streamming
>>> 	: It might be an issue, videobuf2 framework calls streamon when
>>> 	qbuf() for enqueueing. It means, the driver's streamon() function
>>
>> No, that's not really the case. At last videobuf2 buf_queue op might be
>> called in response to VIDIOC_STREAMON. Certainly there must be some bug
>> in the host driver if subdev's s_stream is being called repeatedly.
>
> Ah, it's good news. I seemed to use some little old version of vb2.
> Then, I would try to merge new vb2 on our branch, and I need some help on that.
> I'll contact you. After that, I test new vb2 merged on our branch and chech if there
> is not any issues, I'll correct to use just enable variable not using is_streaming(),
> and also correct comments.

Sorry, I confused you. Instead of "at last" I should have used "at most".
I think it's the host driver, not vb2 issue. I'll take care of that.
It doesn't bother to do an additional checking though, but your comments
should be slightly changed.

> 
> Actually, that has happened low frame rate issues on M-5MOLS, I had have headache
> cause of this some weeks, and it's the suspects. :D
> It's my fault not to use newer version.
> So, letting me know this new version is very happy for me.
> 
>>
>>> 	might be callled continuously if there is no proper handling in the
>>> 	subdev driver, and low the framerate by adding unneeded I2C operation.
>>> 	The M-5MOLS sensor needs command one time for streaming. If commands
>>> 	once, this sensor streams continuously, and this version handles it.
>>>
>>> 5. Update FW
>>> 	: It's a little tricky. Originally, the v4l2 frame provide load_fw(),
>>> 	but, there is the occasion which should do in openning the videonode,
>>> 	and it's the same occasion with us. So, if it's not wrong or it makes
>>> 	any problem, we hope to insert m5mols_update_fw() with weak attribute.
>>> 	And, I'm sorry that the fw updating code is confidential. unserstand
>>> 	by favor, plz.
>>>
>>> And, as always, this driver is tested on s5pc210 board using s5p-fimc driver.
>>>
>>> I'll wait for reviewing.
>>>
>>> Thanks and Regards,
>>> 	Heungjun Kim
>>> 	Samsung Electronics DMC R&D Center
>>>
>>>   drivers/media/video/Kconfig                  |    2 +
>>>   drivers/media/video/Makefile                 |    1 +
>>>   drivers/media/video/m5mols/Kconfig           |    5 +
>>>   drivers/media/video/m5mols/Makefile          |    3 +
>>>   drivers/media/video/m5mols/m5mols.h          |  251 ++++++
>>>   drivers/media/video/m5mols/m5mols_controls.c |  213 +++++
>>>   drivers/media/video/m5mols/m5mols_core.c     | 1062 ++++++++++++++++++++++++++
>>>   drivers/media/video/m5mols/m5mols_reg.h      |  218 ++++++
>>>   include/media/m5mols.h                       |   35 +
>>>   9 files changed, 1790 insertions(+), 0 deletions(-)
>>>   create mode 100644 drivers/media/video/m5mols/Kconfig
>>>   create mode 100644 drivers/media/video/m5mols/Makefile
>>>   create mode 100644 drivers/media/video/m5mols/m5mols.h
>>>   create mode 100644 drivers/media/video/m5mols/m5mols_controls.c
>>>   create mode 100644 drivers/media/video/m5mols/m5mols_core.c
>>>   create mode 100644 drivers/media/video/m5mols/m5mols_reg.h
>>>   create mode 100644 include/media/m5mols.h
>>>
>> ...
>>
>>> +/*
>>> + * m5mols_sensor_armboot() - booting M-5MOLS internal ARM core-controller.
>>> + *
>>> + * It makes to ready M-5MOLS for I2C&   MIPI interface. After it's powered up,
>>> + * it activates if it gets armboot command for I2C interface. After getting
>>> + * cmd, it must wait about least 520ms referenced by M-5MOLS datasheet.
>>> + */
>>> +static int m5mols_sensor_armboot(struct v4l2_subdev *sd)
>>> +{
>>> +	struct i2c_client *client = v4l2_get_subdevdata(sd);
>>> +	u32 reg;
>>> +	int ret;
>>> +
>>> +	/* ARM(M-5MOLS core) booting */
>>> +	ret = i2c_w8_flash(sd, CATF_CAM_START, true);
>>> +	if (ret<   0)
>>> +		return ret;
>>> +
>>> +	msleep(520);
>>
>> Don't you consider using a waitqueue and a relevant interrupt
>> generated by the ISP when it has completed booting?
>> This would allow to decrease the delay to an optimal minimum.
> I didn't yet consider it. But, it looks good option. I would try this at the next version. :)

Thanks.

> 
> And, but it seems relevant or not, how to think about changing to probe all sensors
> when video node (exactly fimc) being opened by user application?

Currently only the first sensor from the list, passed as platform data,
is initialized when the video device is opened, to make sure the capture
device is usable.

> I means, as you know, the most sensors has booting delay time, including even
> noonxxxxxxx&  srxxxxxx sensor series. But, we can choose the calling to probe sensor
> be made of workqueue() in the fimc. If doing as this, it can be possible not to wait
> probing previous sensor. So, What about your opinion?

Yeah, sounds like a good idea. Possibly that could save us some 100...200ms
in cases when the applications do not want to use the first sensor.
I'll consider that when converting FIMC to the media device.

--
Regards,
Sylwester



^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH] Add support for M-5MOLS 8 Mega Pixel camera
  2011-03-19 19:28     ` Sylwester Nawrocki
@ 2011-03-21  6:08       ` Kim, HeungJun
  0 siblings, 0 replies; 34+ messages in thread
From: Kim, HeungJun @ 2011-03-21  6:08 UTC (permalink / raw)
  To: Sylwester Nawrocki
  Cc: Kim HeungJun, Sylwester Nawrocki, Hans Verkuil, Laurent Pinchart,
	Kyungmin Park, linux-media

Hi Sylwester,

2011-03-20 오전 4:28, Sylwester Nawrocki 쓴 글:
> On 03/19/2011 04:11 PM, Kim HeungJun wrote:
>> Hi Sylwester,
>>
>> Thanks for the reviews. :)
>>
>> 2011. 3. 19., 오후 10:37, Sylwester Nawrocki 작성:
>>
>>> Hi HeungJun,
>>>
>>> On 03/16/2011 02:38 PM, Kim, Heungjun wrote:
>>>> Add I2C/V4L2 subdev driver for M-5MOLS camera sensor with integrated
>>>> image signal processor.
>>>>
[snip]
>>>>
>>>> 3. Speed-up whole I2C operation
>>>> 	: I've tested several times for decreasing the stabilization time
>>>> 	while I2C communication, and I have find proper time. Of course,
>>>> 	it's more faster than previous version.
>>>
>>> That sounds good. Do you think the delays before I2C read/write could
>>> be avoided in some (if not all) cases by using some status registers
>>> polling?
>> I don't understand literally. Could you explain more detailed with some examples?
>> My understanding is that it might be an issues or problem when getting some
>> status registers with polling it. is it right?
> 
> My concern is that we might not need an extra delay between consecutive 
> read or write operations in every case. Possibly it would be enough
> to read the status of some operations instead. But that just what I suspect.
Ah, I understand. The consecutive delay needs to read/write operations.
Because, in non-delay cases, I found the values is not changed to be read
when the driver is reading continuously at the different address. I means,
if there is not regular amount of delay in r/w operations, the value is kept
the previous value to be read by I2C operation. It's the same way in writing
case. It's wierd.

It might be different the kind of M-5MOLS sensor's FW and packaging type. But,
I expect that the internal ARM core of M-5MOLS takes time to proceed something
and to load values through I2C bus for the host process. 

Regards,
Heungjun Kim

^ permalink raw reply	[flat|nested] 34+ messages in thread

* RE: [PATCH] Add support for M-5MOLS 8 Mega Pixel camera
  2011-03-16 13:38 [PATCH] Add support for M-5MOLS 8 Mega Pixel camera Kim, Heungjun
  2011-03-19 13:37 ` Sylwester Nawrocki
@ 2011-04-04 12:20 ` Sungchun Kang
  2011-04-05  5:36   ` Kim, HeungJun
  2011-05-13 10:06 ` [PATCH v7] Add support for M-5MOLS 8 Mega Pixel camera ISP HeungJun, Kim
  2 siblings, 1 reply; 34+ messages in thread
From: Sungchun Kang @ 2011-04-04 12:20 UTC (permalink / raw)
  To: 'Kim, Heungjun', linux-media

Hi heungjun,
I have tested this version for a few days.

On 03/16/2011 10:30 PM, Kim, Heungjun wrote:
> -----Original Message-----
> From: linux-media-owner@vger.kernel.org [mailto:linux-media-
> owner@vger.kernel.org] On Behalf Of Kim, Heungjun
> Sent: Wednesday, March 16, 2011 10:39 PM
> To: linux-media@vger.kernel.org
> Cc: hverkuil@xs4all.nl; laurent.pinchart@ideasonboard.com; Kim,
> Heungjun; Sylwester Nawrocki; Kyungmin Park
> Subject: [PATCH] Add support for M-5MOLS 8 Mega Pixel camera
> 
> Add I2C/V4L2 subdev driver for M-5MOLS camera sensor with integrated
> image signal processor.
> 
> Signed-off-by: Heungjun Kim <riverful.kim@samsung.com>
> Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
> ---
> 
> Hi Hans and everyone,
> 
> This is sixth version of M-5MOLS 8 Mega Pixel camera sensor. And, if
> you see
> previous version, you can find at:
> http://www.spinics.net/lists/linux-media/msg29350.html
> 
> This driver patch is fixed several times, and the important issues is
> almost
> corrected. And, I hope that this is the last version one merged for
> 2.6.39.
> I look forward to be reviewed one more time.
> 
> The summary of this version's feature is belows:
> 
> 1. Add focus control
> 	: I've suggest menu type focus control, but I agreed this
> version is
> 	not yet the level accepted. So, I did not use focus control
> which
> 	I suggest.
> 	The M-5MOLS focus routine takes some time to execute. But, the
> user
> 	application calling v4l2 control, should not hanged while
> streaming
> 	using q/dqbuf. So, I use workqueue. I want to discuss the focus
> 	subject on mailnglist next time.
> 

I wonder this feature is dependent on this firmware version?

.....snip

> +static int m5mols_start_monitor(struct v4l2_subdev *sd)
> +{
> +	struct m5mols_info *info = to_m5mols(sd);
> +	int ret;
> +
> +	ret = m5mols_set_mode(sd, MODE_PARAM);
> +	if (!ret)
> +		ret = i2c_w8_param(sd, CAT1_MONITOR_SIZE, info-
> >res_preset);
> +	if (!ret)
> +		ret = i2c_w8_param(sd, CAT1_MONITOR_FPS, info->fps_preset);
> +	if (!ret)
> +		ret = m5mols_set_mode(sd, MODE_MONITOR);
> +	if (!ret && info->do_once) {
> +		/* After probing the driver, this should be callde once.
> */
> +		v4l2_ctrl_handler_setup(&info->handle);
As test result, When sensor is set monitor mode, if this API is called, 
Preview data(get from sensor) is craked. Surely, it is good working if this API is called in paramset mode.
That waw no problem in Version 5. Because it is returned before v4l2_ctrl_handler_init()
In m5mols_init_controls(version 5) :
	ret = i2c_r16_ae(sd, CAT3_MAX_GAIN_MON, (u32 *)&max_ex_mon);
	if (ret) 
		return ret; // if success, return.

My test case is :
S_power->s_fmt->s_stream.

.....
BRs Sungchun.


^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH] Add support for M-5MOLS 8 Mega Pixel camera
  2011-04-04 12:20 ` Sungchun Kang
@ 2011-04-05  5:36   ` Kim, HeungJun
  2011-04-05  5:38     ` Kim, HeungJun
  0 siblings, 1 reply; 34+ messages in thread
From: Kim, HeungJun @ 2011-04-05  5:36 UTC (permalink / raw)
  To: sungchun.kang; +Cc: linux-media, kyungmin.park, Sylwester Nawrocki

Hi Sungchun,

The below comments and issues looks the firmware issues.
I add another comments, so you can check this out.

The first plan of this driver I have, is to merge the basic driver code
of M-5MOLS. Because, the M-5MOLS has many variation of versions. It makes
to send the driver to ML or be merged, also respond when the problem issued.

The version considered with version, probably will be the next version.


2011-04-04 오후 9:20, Sungchun Kang 쓴 글:
> Hi heungjun,
> I have tested this version for a few days.
> 
> On 03/16/2011 10:30 PM, Kim, Heungjun wrote:
>> -----Original Message-----
>> From: linux-media-owner@vger.kernel.org [mailto:linux-media-
>> owner@vger.kernel.org] On Behalf Of Kim, Heungjun
>> Sent: Wednesday, March 16, 2011 10:39 PM
>> To: linux-media@vger.kernel.org
>> Cc: hverkuil@xs4all.nl; laurent.pinchart@ideasonboard.com; Kim,
>> Heungjun; Sylwester Nawrocki; Kyungmin Park
>> Subject: [PATCH] Add support for M-5MOLS 8 Mega Pixel camera
>>
>> Add I2C/V4L2 subdev driver for M-5MOLS camera sensor with integrated
>> image signal processor.
>>
>> Signed-off-by: Heungjun Kim <riverful.kim@samsung.com>
>> Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
>> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
>> ---
>>
>> Hi Hans and everyone,
>>
>> This is sixth version of M-5MOLS 8 Mega Pixel camera sensor. And, if
>> you see
>> previous version, you can find at:
>> http://www.spinics.net/lists/linux-media/msg29350.html
>>
>> This driver patch is fixed several times, and the important issues is
>> almost
>> corrected. And, I hope that this is the last version one merged for
>> 2.6.39.
>> I look forward to be reviewed one more time.
>>
>> The summary of this version's feature is belows:
>>
>> 1. Add focus control
>> 	: I've suggest menu type focus control, but I agreed this
>> version is
>> 	not yet the level accepted. So, I did not use focus control
>> which
>> 	I suggest.
>> 	The M-5MOLS focus routine takes some time to execute. But, the
>> user
>> 	application calling v4l2 control, should not hanged while
>> streaming
>> 	using q/dqbuf. So, I use workqueue. I want to discuss the focus
>> 	subject on mailnglist next time.
>>
> 
> I wonder this feature is dependent on this firmware version?
> 
> .....snip
The value can be changable by the firmware, but the usage of focus is not.
The specific mode can be added too. But, it also maintains same usage.

It's scheduled at the next time to consider the version. But, it's hard to
consider all cases.

> 
>> +static int m5mols_start_monitor(struct v4l2_subdev *sd)
>> +{
>> +	struct m5mols_info *info = to_m5mols(sd);
>> +	int ret;
>> +
>> +	ret = m5mols_set_mode(sd, MODE_PARAM);
>> +	if (!ret)
>> +		ret = i2c_w8_param(sd, CAT1_MONITOR_SIZE, info-
>>> res_preset);
>> +	if (!ret)
>> +		ret = i2c_w8_param(sd, CAT1_MONITOR_FPS, info->fps_preset);
>> +	if (!ret)
>> +		ret = m5mols_set_mode(sd, MODE_MONITOR);
>> +	if (!ret && info->do_once) {
>> +		/* After probing the driver, this should be callde once.
>> */
>> +		v4l2_ctrl_handler_setup(&info->handle);
> As test result, When sensor is set monitor mode, if this API is called, 
> Preview data(get from sensor) is craked. Surely, it is good working if this API is called in paramset mode.
> That waw no problem in Version 5. Because it is returned before v4l2_ctrl_handler_init()
> In m5mols_init_controls(version 5) :
> 	ret = i2c_r16_ae(sd, CAT3_MAX_GAIN_MON, (u32 *)&max_ex_mon);
> 	if (ret) 
> 		return ret; // if success, return.
> 
> My test case is :
> S_power->s_fmt->s_stream.
It's a little tricky to control parameter & monitor mode in the M-5MOLS sensor.
Any commands or control is specified that it's available in Docunemt, but
it's just literaly "available", doesn't mean "working well". Especially,
the version difference between firmware is the biggest source of this problems.

Probably, the sensor you have is a different firmware I think.
So If there is any other problem, I recommend to use previous version plz.

If you show me the all version strings, I might help you.

> 
> .....
> BRs Sungchun.
> 
> 

Thanks for comments, and any other issues or opinions, let me know.

Regards,
Heungjun Kim

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH] Add support for M-5MOLS 8 Mega Pixel camera
  2011-04-05  5:36   ` Kim, HeungJun
@ 2011-04-05  5:38     ` Kim, HeungJun
  0 siblings, 0 replies; 34+ messages in thread
From: Kim, HeungJun @ 2011-04-05  5:38 UTC (permalink / raw)
  To: riverful.kim
  Cc: sungchun.kang, linux-media, kyungmin.park, Sylwester Nawrocki

2011-04-05 오후 2:36, Kim, HeungJun 쓴 글:
> Hi Sungchun,
> 
> The below comments and issues looks the firmware issues.
> I add another comments, so you can check this out.
> 
> The first plan of this driver I have, is to merge the basic driver code
> of M-5MOLS. Because, the M-5MOLS has many variation of versions. It makes
Missig "hard". :)

> to send the driver to ML or be merged, also respond when the problem issued.
> 
> The version considered with version, probably will be the next version.
> 
> 
> 2011-04-04 오후 9:20, Sungchun Kang 쓴 글:
>> Hi heungjun,
>> I have tested this version for a few days.
>>
>> On 03/16/2011 10:30 PM, Kim, Heungjun wrote:
>>> -----Original Message-----
>>> From: linux-media-owner@vger.kernel.org [mailto:linux-media-
>>> owner@vger.kernel.org] On Behalf Of Kim, Heungjun
>>> Sent: Wednesday, March 16, 2011 10:39 PM
>>> To: linux-media@vger.kernel.org
>>> Cc: hverkuil@xs4all.nl; laurent.pinchart@ideasonboard.com; Kim,
>>> Heungjun; Sylwester Nawrocki; Kyungmin Park
>>> Subject: [PATCH] Add support for M-5MOLS 8 Mega Pixel camera
>>>
>>> Add I2C/V4L2 subdev driver for M-5MOLS camera sensor with integrated
>>> image signal processor.
>>>
>>> Signed-off-by: Heungjun Kim <riverful.kim@samsung.com>
>>> Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
>>> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
>>> ---
>>>
>>> Hi Hans and everyone,
>>>
>>> This is sixth version of M-5MOLS 8 Mega Pixel camera sensor. And, if
>>> you see
>>> previous version, you can find at:
>>> http://www.spinics.net/lists/linux-media/msg29350.html
>>>
>>> This driver patch is fixed several times, and the important issues is
>>> almost
>>> corrected. And, I hope that this is the last version one merged for
>>> 2.6.39.
>>> I look forward to be reviewed one more time.
>>>
>>> The summary of this version's feature is belows:
>>>
>>> 1. Add focus control
>>> 	: I've suggest menu type focus control, but I agreed this
>>> version is
>>> 	not yet the level accepted. So, I did not use focus control
>>> which
>>> 	I suggest.
>>> 	The M-5MOLS focus routine takes some time to execute. But, the
>>> user
>>> 	application calling v4l2 control, should not hanged while
>>> streaming
>>> 	using q/dqbuf. So, I use workqueue. I want to discuss the focus
>>> 	subject on mailnglist next time.
>>>
>>
>> I wonder this feature is dependent on this firmware version?
>>
>> .....snip
> The value can be changable by the firmware, but the usage of focus is not.
> The specific mode can be added too. But, it also maintains same usage.
> 
> It's scheduled at the next time to consider the version. But, it's hard to
> consider all cases.
> 
>>
>>> +static int m5mols_start_monitor(struct v4l2_subdev *sd)
>>> +{
>>> +	struct m5mols_info *info = to_m5mols(sd);
>>> +	int ret;
>>> +
>>> +	ret = m5mols_set_mode(sd, MODE_PARAM);
>>> +	if (!ret)
>>> +		ret = i2c_w8_param(sd, CAT1_MONITOR_SIZE, info-
>>>> res_preset);
>>> +	if (!ret)
>>> +		ret = i2c_w8_param(sd, CAT1_MONITOR_FPS, info->fps_preset);
>>> +	if (!ret)
>>> +		ret = m5mols_set_mode(sd, MODE_MONITOR);
>>> +	if (!ret && info->do_once) {
>>> +		/* After probing the driver, this should be callde once.
>>> */
>>> +		v4l2_ctrl_handler_setup(&info->handle);
>> As test result, When sensor is set monitor mode, if this API is called, 
>> Preview data(get from sensor) is craked. Surely, it is good working if this API is called in paramset mode.
>> That waw no problem in Version 5. Because it is returned before v4l2_ctrl_handler_init()
>> In m5mols_init_controls(version 5) :
>> 	ret = i2c_r16_ae(sd, CAT3_MAX_GAIN_MON, (u32 *)&max_ex_mon);
>> 	if (ret) 
>> 		return ret; // if success, return.
>>
>> My test case is :
>> S_power->s_fmt->s_stream.
> It's a little tricky to control parameter & monitor mode in the M-5MOLS sensor.
> Any commands or control is specified that it's available in Docunemt, but
> it's just literaly "available", doesn't mean "working well". Especially,
> the version difference between firmware is the biggest source of this problems.
> 
> Probably, the sensor you have is a different firmware I think.
> So If there is any other problem, I recommend to use previous version plz.
> 
> If you show me the all version strings, I might help you.
> 
>>
>> .....
>> BRs Sungchun.
>>
>>
> 
> Thanks for comments, and any other issues or opinions, let me know.
> 
> Regards,
> Heungjun Kim
> --
> To unsubscribe from this list: send the line "unsubscribe linux-media" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 


^ permalink raw reply	[flat|nested] 34+ messages in thread

* [PATCH v7] Add support for M-5MOLS 8 Mega Pixel camera ISP
  2011-03-16 13:38 [PATCH] Add support for M-5MOLS 8 Mega Pixel camera Kim, Heungjun
  2011-03-19 13:37 ` Sylwester Nawrocki
  2011-04-04 12:20 ` Sungchun Kang
@ 2011-05-13 10:06 ` HeungJun, Kim
  2011-05-13 11:38   ` HeungJun, Kim
  2 siblings, 1 reply; 34+ messages in thread
From: HeungJun, Kim @ 2011-05-13 10:06 UTC (permalink / raw)
  To: linux-media
  Cc: hverkuil, laurent.pinchart, kyungmin.park, HeungJun, Kim,
	Sylwester Nawrocki

Add I2C/V4L2 subdev driver for M-5MOLS integrated image signal processor
with 8 Mega Pixel sensor.

Signed-off-by: Heungjun Kim <riverful.kim@samsung.com>
Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---

Hello,

This is the seventh version of the subdev for M-5MOLS 8M Pixel camera sensor.
This version has a lot of changes againt previous one. The major change is
that it supports to capture with JPEG format generating in itself.

The capture function and all the other operations are tested on the Universal
Board with EXYNOS4 SoCs using s5p-fimc driver and the NURI board, but it needed
for enabling the capture capability to support V4L2_MBUS_FMT_JPEG_1X8 media code.
The relevant patch can be found here:
http://www.spinics.net/lists/linux-media/msg32231.html

In this patch, the big changes except for capture are here:

1. Convert previous format operation to media bus operation
2. Add scenemode function for setting default scenemode
3. Enable to change the mode more stably
4. Add some variation according to M-5MOLS FW versions and manufacturers

And the full thread about the previous version of M-5MOLS driver can be found
here: http://www.spinics.net/lists/linux-media/msg29350.html

Any comments and reviews are welcome!

--
Regards,
Heungjun Kim
Samsung Electronics DMC R&D Center
---
 drivers/media/video/Kconfig                  |    2 +
 drivers/media/video/Makefile                 |    1 +
 drivers/media/video/m5mols/Kconfig           |    5 +
 drivers/media/video/m5mols/Makefile          |    3 +
 drivers/media/video/m5mols/m5mols.h          |  357 +++++++++
 drivers/media/video/m5mols/m5mols_capture.c  |  217 ++++++
 drivers/media/video/m5mols/m5mols_controls.c |  293 ++++++++
 drivers/media/video/m5mols/m5mols_core.c     | 1034 ++++++++++++++++++++++++++
 drivers/media/video/m5mols/m5mols_reg.h      |  321 ++++++++
 include/media/m5mols.h                       |   35 +
 10 files changed, 2268 insertions(+), 0 deletions(-)
 create mode 100644 drivers/media/video/m5mols/Kconfig
 create mode 100644 drivers/media/video/m5mols/Makefile
 create mode 100644 drivers/media/video/m5mols/m5mols.h
 create mode 100644 drivers/media/video/m5mols/m5mols_capture.c
 create mode 100644 drivers/media/video/m5mols/m5mols_controls.c
 create mode 100644 drivers/media/video/m5mols/m5mols_core.c
 create mode 100644 drivers/media/video/m5mols/m5mols_reg.h
 create mode 100644 include/media/m5mols.h

diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index d61414e..242c80c 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -753,6 +753,8 @@ config VIDEO_NOON010PC30
 	---help---
 	  This driver supports NOON010PC30 CIF camera from Siliconfile
 
+source "drivers/media/video/m5mols/Kconfig"
+
 config VIDEO_OMAP3
 	tristate "OMAP 3 Camera support (EXPERIMENTAL)"
 	select OMAP_IOMMU
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index a10e4c3..d5d6de1 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -69,6 +69,7 @@ obj-$(CONFIG_VIDEO_MT9V011) += mt9v011.o
 obj-$(CONFIG_VIDEO_MT9V032) += mt9v032.o
 obj-$(CONFIG_VIDEO_SR030PC30)	+= sr030pc30.o
 obj-$(CONFIG_VIDEO_NOON010PC30)	+= noon010pc30.o
+obj-$(CONFIG_VIDEO_M5MOLS)	+= m5mols/
 
 obj-$(CONFIG_SOC_CAMERA_IMX074)		+= imx074.o
 obj-$(CONFIG_SOC_CAMERA_MT9M001)	+= mt9m001.o
diff --git a/drivers/media/video/m5mols/Kconfig b/drivers/media/video/m5mols/Kconfig
new file mode 100644
index 0000000..302dc3d
--- /dev/null
+++ b/drivers/media/video/m5mols/Kconfig
@@ -0,0 +1,5 @@
+config VIDEO_M5MOLS
+	tristate "Fujitsu M-5MOLS 8MP sensor support"
+	depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+	---help---
+	  This driver supports Fujitsu M-5MOLS camera sensor with ISP
diff --git a/drivers/media/video/m5mols/Makefile b/drivers/media/video/m5mols/Makefile
new file mode 100644
index 0000000..0a44e02
--- /dev/null
+++ b/drivers/media/video/m5mols/Makefile
@@ -0,0 +1,3 @@
+m5mols-objs	:= m5mols_core.o m5mols_controls.o m5mols_capture.o
+
+obj-$(CONFIG_VIDEO_M5MOLS)		+= m5mols.o
diff --git a/drivers/media/video/m5mols/m5mols.h b/drivers/media/video/m5mols/m5mols.h
new file mode 100644
index 0000000..ba1890e
--- /dev/null
+++ b/drivers/media/video/m5mols/m5mols.h
@@ -0,0 +1,357 @@
+/*
+ * Header for M-5MOLS 8M Pixel camera sensor with ISP
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * Author: HeungJun Kim, riverful.kim@samsung.com
+ *
+ * Copyright (C) 2009 Samsung Electronics Co., Ltd.
+ * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com
+ *
+ * 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-subdev.h>
+#include "m5mols_reg.h"
+
+extern int m5mols_debug;
+
+#define M5MOLS_BYTE_READ	0x01
+#define M5MOLS_BYTE_WRITE	0x02
+
+enum m5mols_i2c_size {
+	I2C_8BIT	= 1,
+	I2C_16BIT	= 2,
+	I2C_32BIT	= 4,
+	I2C_MAX		= I2C_32BIT,
+};
+
+/*
+ * In the MONITOR mode, the M-5MOLS sensor's format is YUV kinds, on the other
+ * hand, in the CAPTURE mode, its format is available to JPEG, RAW.
+ */
+enum m5mols_restype {
+	M5MOLS_RESTYPE_MONITOR,
+	M5MOLS_RESTYPE_CAPTURE,
+	M5MOLS_RESTYPE_MAX,
+};
+
+struct m5mols_resolution {
+	u8			reg;
+	enum m5mols_restype	type;
+	u16			width;
+	u16			height;
+};
+
+struct m5mols_exif {
+	u32			exposure_time;
+	u32			shutter_speed;
+	u32			aperture;
+	u32			brightness;
+	u32			exposure_bias;
+	u16			iso_speed;
+	u16			flash;
+	u16			sdr;	/* Subject(object) Distance Range */
+	u16			qval;	/* Not written exact meanning
+					 * in datasheet */
+};
+
+struct m5mols_capture {
+	struct m5mols_exif	exif;
+	u32			main;
+	u32			thumb;
+	u32			total;
+};
+
+struct m5mols_scenemode {	/* Recommended setting in document */
+	u32			metering;	/* AE light metering */
+	u32			ev_bias;	/* EV bias */
+	u32			wb_mode;	/* WhiteBalance(Auto/Manual) */
+	u32			wb_preset;	/* WhiteBalance Preset */
+	u32			chroma_en;	/* Chroma Enable */
+	u32			chroma_lvl;	/* Chroma Level */
+	u32			edge_en;	/* Edge Enable */
+	u32			edge_lvl;	/* Edge Level */
+	u32			af_range;	/* Auto Focus scan range */
+	u32			fd_mode;	/* Face Detection mode */
+	u32			mcc;		/* Multi-axis Color Conversion:
+						 * (A.K.A Emotion color) */
+	u32			light;		/* Light control */
+	u32			flash;		/* Flash control */
+
+				/* User setting needed for */
+	u32			tone;		/* Tone color(contrast) */
+	u32			iso;		/* ISO */
+	u32			capture_mode;	/* CAPTURE mode for
+						 * the Image stabilization */
+	u32			wdr;		/* Wide Dynamic Range */
+};
+
+#define VERSION_STRING_SIZE	22
+struct m5mols_version {
+	u8	customer;	/* Customer code:	bytes[0] */
+	u8	project;	/* Project code:	bytes[1] */
+	u16	fw;		/* FirmWare version:	bytes[3][2] */
+	u16	hw;		/* HardWare version:	bytes[5][4] */
+	u16	param;		/* Parameter version:	bytes[7][6] */
+	u16	awb;		/* AWB version:		bytes[9][8] */
+	u8	str[VERSION_STRING_SIZE];		/* manufacturer &
+							 * packging vendor */
+	u8	af;		/* AF version:		seperate register */
+};
+#define VERSION_SIZE		sizeof(struct m5mols_version)
+
+/* The LSB 2 bytes of version string means packaging manufacturer */
+#define SAMSUNG_ELECTRO		"SE"	/* Samsung Electro-Mechanics */
+#define SAMSUNG_OPTICS		"OP"	/* Samsung Fiber-Optics */
+#define SAMSUNG_TECHWIN		"TB"	/* Samsung Techwin */
+
+struct m5mols_info {
+	const struct m5mols_platform_data	*pdata;
+	struct v4l2_subdev		sd;
+	struct media_pad		pad;
+	struct v4l2_mbus_framefmt	ffmt[M5MOLS_RESTYPE_MAX];
+	int				curr_res_type;
+	enum v4l2_mbus_pixelcode	code;
+	wait_queue_head_t		wait_capture;
+	struct work_struct		work_irq;
+
+	struct v4l2_ctrl_handler	handle;
+	struct {
+		struct v4l2_ctrl	*autoexposure;
+		struct v4l2_ctrl	*exposure;
+	};
+	struct v4l2_ctrl		*autowb;
+	struct v4l2_ctrl		*colorfx;
+	struct v4l2_ctrl		*saturation;
+	struct v4l2_ctrl		*zoom;
+
+	struct m5mols_version		ver;
+	struct m5mols_capture		cap;
+
+	bool				power;
+	bool				ctrl_sync;
+	bool				capture;
+	bool				lock_ae;
+	bool				lock_awb;
+
+	/* Saved register value */
+	u8				resolution;
+	u32				interrupt;
+	u32				mode;
+	u32				mode_save;
+
+	/* Optional power callback function dealing with like GPIO. */
+	int (*set_power)(struct device *dev, int on);
+};
+
+/* Helper functions */
+static inline struct m5mols_info *to_m5mols(struct v4l2_subdev *sd)
+{
+	return container_of(sd, struct m5mols_info, sd);
+}
+
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+	return &container_of(ctrl->handler, struct m5mols_info, handle)->sd;
+}
+
+static inline bool is_powered(struct m5mols_info *info)
+{
+	return info->power;
+}
+
+static inline bool is_ctrl_synced(struct m5mols_info *info)
+{
+	return info->ctrl_sync;
+}
+
+static inline bool is_captured(struct m5mols_info *info)
+{
+	return info->capture;
+}
+
+static inline bool is_manufacturer(struct m5mols_info *info, u8 *manufacturer)
+{
+	return (info->ver.str[0] == manufacturer[0] &&
+			info->ver.str[1] == manufacturer[1]) ?
+		true : false;
+}
+
+#define is_code(__code, __type) (__code == m5mols_default_ffmt[__type].code)
+
+/*
+ * m5mols_read_reg() / m5mols_write_reg()
+ *
+ * For reading something from M-5MOLS sensor, the I2C operation is needed 2
+ * packets, one is for requesting reading and another is for getting data.
+ * On the other hand, for writing, it's needed just 1. The value's
+ * order in the packet is much similar in both case reading and writing.
+ *
+ * I2C packet common order
+ *   1st : size (data size + 4)
+ *   2nd : flag (R:0x01, W:0x02)
+ *   3rd : category
+ *   4th : command
+ *
+ * Added extra bytes in case reading
+ *   5th: data
+ *   And read another I2C bytes again as much data size
+ *
+ * Added extra bytes in case writing
+ *   5th..8th: data(as much size of data)
+ */
+int m5mols_read_reg(struct v4l2_subdev *sd, enum m5mols_i2c_size size,
+		    u8 category, u8 cmd, u32 *val);
+int m5mols_write_reg(struct v4l2_subdev *sd, enum m5mols_i2c_size size,
+		     u8 category, u8 cmd, u32 val);
+int m5mols_check_busy(struct v4l2_subdev *sd,
+		      u8 category, u8 cmd, u32 value);
+
+/*
+ * The m5mols_change_mode() makes the right order executing command for
+ * changing to the desired operating mode. The three modes(PARAMETER,
+ * MONITOR, CAPTURE) exist, and only these modes can be changed intentionally
+ * by user. The each category is assigned in the given mode.
+ * +============================================================+
+ * | mode	| category					|
+ * +============================================================+
+ * | These mode must be done for using the sensor automatically |
+ * +------------------------------------------------------------+
+ * | FLASH	| FLASH(be only after Stand-by or Power-on)	|
+ * | SYSTEM	| SYSTEM(be only after sensor arm-booting)	|
+ * +============================================================+
+ * | Usually these mode is used on operating sensor		|
+ * +------------------------------------------------------------+
+ * | PARAMETER	| PARAMETER					|
+ * | MONITOR	| MONITOR(preview), Auto Focus, Face Detection	|
+ * | CAPTURE	| Single CAPTURE, Preview(recording)		|
+ * +============================================================+
+ *
+ * The possible executing order between each modes is the following:
+ * ============================================================
+ *   +-----------+         +---------+          +---------+
+ *   | PARAMETER |<------->| MONITOR |<-------->| CAPTURE |
+ *   +-----------+         +---------+          +---------+
+ */
+int m5mols_change_mode(struct m5mols_info *info, u32 mode);
+
+int m5mols_change_scenemode(struct m5mols_info *info, u32 mode);
+int m5mols_enable_interrupt(struct v4l2_subdev *sd, u32 reg);
+int m5mols_start_capture(struct m5mols_info *info);
+int m5mols_sync_control(struct m5mols_info *info);
+int m5mols_lock_3a(struct m5mols_info *info, bool lock);
+int m5mols_set_ctrl(struct v4l2_ctrl *ctrl);
+
+static inline int __must_check
+i2c_w8_system(struct v4l2_subdev *sd, u8 cmd, u32 val)
+{
+	return m5mols_write_reg(sd, I2C_8BIT, CAT_SYSTEM, cmd, val);
+}
+
+static inline int __must_check
+i2c_w8_param(struct v4l2_subdev *sd, u8 cmd, u32 val)
+{
+	return m5mols_write_reg(sd, I2C_8BIT, CAT_PARAM, cmd, val);
+}
+
+static inline int __must_check
+i2c_w8_mon(struct v4l2_subdev *sd, u8 cmd, u32 val)
+{
+	return m5mols_write_reg(sd, I2C_8BIT, CAT_MONITOR, cmd, val);
+}
+
+static inline int __must_check
+i2c_w8_ae(struct v4l2_subdev *sd, u8 cmd, u32 val)
+{
+	return m5mols_write_reg(sd, I2C_8BIT, CAT_AE, cmd, val);
+}
+
+static inline int __must_check
+i2c_w16_ae(struct v4l2_subdev *sd, u8 cmd, u32 val)
+{
+	return m5mols_write_reg(sd, I2C_16BIT, CAT_AE, cmd, val);
+}
+
+static inline int __must_check
+i2c_w8_wb(struct v4l2_subdev *sd, u8 cmd, u32 val)
+{
+	return m5mols_write_reg(sd, I2C_8BIT, CAT_WB, cmd, val);
+}
+
+static inline int __must_check
+i2c_w8_fd(struct v4l2_subdev *sd, u8 cmd, u32 val)
+{
+	return m5mols_write_reg(sd, I2C_8BIT, CAT_FD, cmd, val);
+}
+
+static inline int __must_check
+i2c_w8_lens(struct v4l2_subdev *sd, u8 cmd, u32 val)
+{
+	return m5mols_write_reg(sd, I2C_8BIT, CAT_LENS, cmd, val);
+}
+
+static inline int __must_check
+i2c_w8_capt_param(struct v4l2_subdev *sd, u8 cmd, u32 val)
+{
+	return m5mols_write_reg(sd, I2C_8BIT, CAT_CAPTURE_PARAMETER, cmd, val);
+}
+
+static inline int __must_check
+i2c_w8_capt_ctrl(struct v4l2_subdev *sd, u8 cmd, u32 val)
+{
+	return m5mols_write_reg(sd, I2C_8BIT, CAT_CAPTURE_CONTROL, cmd, val);
+}
+
+static inline int __must_check
+i2c_w8_flash(struct v4l2_subdev *sd, u8 cmd, u32 val)
+{
+	return m5mols_write_reg(sd, I2C_8BIT, CAT_FLASH, cmd, val);
+}
+
+static inline int __must_check
+i2c_r8_system(struct v4l2_subdev *sd, u8 cmd, u32 *val)
+{
+	return m5mols_read_reg(sd, I2C_8BIT, CAT_SYSTEM, cmd, val);
+}
+
+static inline int __must_check
+i2c_r16_ae(struct v4l2_subdev *sd, u8 cmd, u32 *val)
+{
+	return m5mols_read_reg(sd, I2C_16BIT, CAT_AE, cmd, val);
+}
+
+static inline int __must_check
+i2c_r8_lens(struct v4l2_subdev *sd, u8 cmd, u32 *val)
+{
+	return m5mols_read_reg(sd, I2C_8BIT, CAT_LENS, cmd, val);
+}
+
+static inline int __must_check
+i2c_r32_capt_ctrl(struct v4l2_subdev *sd, u8 cmd, u32 *val)
+{
+	return m5mols_read_reg(sd, I2C_32BIT, CAT_CAPTURE_CONTROL, cmd, val);
+}
+
+static inline int __must_check
+i2c_r16_exif(struct v4l2_subdev *sd, u8 cmd, u32 *val)
+{
+	return m5mols_read_reg(sd, I2C_16BIT, CAT_EXIF, cmd, val);
+}
+
+static inline int __must_check
+i2c_r32_exif(struct v4l2_subdev *sd, u8 cmd, u32 *val)
+{
+	return m5mols_read_reg(sd, I2C_32BIT, CAT_EXIF, cmd, val);
+}
+
+/* The firmware function */
+int m5mols_update_fw(struct v4l2_subdev *sd,
+		     int (*set_power)(struct m5mols_info *, bool));
+
+#endif	/* M5MOLS_H */
diff --git a/drivers/media/video/m5mols/m5mols_capture.c b/drivers/media/video/m5mols/m5mols_capture.c
new file mode 100644
index 0000000..32da9b1
--- /dev/null
+++ b/drivers/media/video/m5mols/m5mols_capture.c
@@ -0,0 +1,217 @@
+/*
+ * The Capture code for Fujitsu M-5MOLS ISP
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * Author: HeungJun Kim, riverful.kim@samsung.com
+ *
+ * Copyright (C) 2009 Samsung Electronics Co., Ltd.
+ * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com
+ *
+ * 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/slab.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/version.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/videodev2.h>
+#include <linux/version.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+#include <media/m5mols.h>
+
+#include "m5mols.h"
+#include "m5mols_reg.h"
+
+static int m5mols_capture_error_handler(struct m5mols_info *info,
+					int timeout)
+{
+	int ret;
+
+	/* Disable all interrupt & clear desired interrupt */
+	ret = i2c_w8_system(&info->sd, CAT0_INT_ENABLE,
+			info->interrupt & ~(REG_INT_CAPTURE));
+	if (ret)
+		return ret;
+
+	/* If all timeout exhausted, return error. */
+	if (!timeout)
+		return -ETIMEDOUT;
+
+	/* Clear capture */
+	if (!ret)
+		info->capture = false;
+
+	return 0;
+}
+
+/* m5mols_capture_info() - Gather captured image informations. For now,
+ * it gathers only EXIF information and file size. */
+static int m5mols_capture_info(struct m5mols_info *info, bool msgon)
+{
+	struct v4l2_subdev *sd = &info->sd;
+	struct m5mols_exif *exif = &info->cap.exif;
+	int denominator, numerator;
+	int ret;
+
+	ret = i2c_r32_exif(sd, CAT7_INFO_EXPTIME_NU, &numerator);
+	if (!ret)
+		ret = i2c_r32_exif(sd, CAT7_INFO_EXPTIME_DE, &denominator);
+	if (!ret)
+		exif->exposure_time = (u32)(numerator / denominator);
+	if (ret)
+		return ret;
+
+	ret = i2c_r32_exif(sd, CAT7_INFO_TV_NU, &numerator);
+	if (!ret)
+		ret = i2c_r32_exif(sd, CAT7_INFO_TV_DE, &denominator);
+	if (!ret)
+		exif->shutter_speed = (u32)(numerator / denominator);
+	if (ret)
+		return ret;
+
+	ret = i2c_r32_exif(sd, CAT7_INFO_AV_NU, &numerator);
+	if (!ret)
+		ret = i2c_r32_exif(sd, CAT7_INFO_AV_DE, &denominator);
+	if (!ret)
+		exif->aperture = (u32)(numerator / denominator);
+	if (ret)
+		return ret;
+
+	ret = i2c_r32_exif(sd, CAT7_INFO_BV_NU, &numerator);
+	if (!ret)
+		ret = i2c_r32_exif(sd, CAT7_INFO_BV_DE, &denominator);
+	if (!ret)
+		exif->brightness = (u32)(numerator / denominator);
+	if (ret)
+		return ret;
+
+	ret = i2c_r32_exif(sd, CAT7_INFO_EBV_NU, &numerator);
+	if (!ret)
+		ret = i2c_r32_exif(sd, CAT7_INFO_EBV_DE, &denominator);
+	if (!ret)
+		exif->exposure_bias = (u32)(numerator / denominator);
+	if (ret)
+		return ret;
+
+	ret = i2c_r16_exif(sd, CAT7_INFO_ISO, (u32 *)&exif->iso_speed);
+	if (!ret)
+		ret = i2c_r16_exif(sd, CAT7_INFO_FLASH, (u32 *)&exif->flash);
+	if (!ret)
+		ret = i2c_r16_exif(sd, CAT7_INFO_SDR, (u32 *)&exif->sdr);
+	if (!ret)
+		ret = i2c_r16_exif(sd, CAT7_INFO_QVAL, (u32 *)&exif->qval);
+	if (ret)
+		return ret;
+
+	if (!ret)
+		ret = i2c_r32_capt_ctrl(sd, CATC_CAP_IMAGE_SIZE,
+				&info->cap.main);
+	if (!ret)
+		ret = i2c_r32_capt_ctrl(sd, CATC_CAP_THUMB_SIZE,
+				&info->cap.thumb);
+	if (ret)
+		return ret;
+
+	info->cap.total = info->cap.main + info->cap.thumb;
+
+	if (msgon) {
+		struct i2c_client *client = v4l2_get_subdevdata(sd);
+		struct device *cdev = &client->dev;
+
+		dev_info(cdev, "capture: total size\t%d\n", info->cap.total);
+		dev_info(cdev, "capture: main size\t%d\n", info->cap.main);
+		dev_info(cdev, "capture: thumb size\t%d\n", info->cap.thumb);
+		dev_info(cdev, "capture: exposure_time\t%d\n",
+				exif->exposure_time);
+		dev_info(cdev, "capture: shutter_speed\t%d\n",
+				exif->shutter_speed);
+		dev_info(cdev, "capture: aperture\t%d\n", exif->aperture);
+		dev_info(cdev, "capture: brightness\t%d\n", exif->brightness);
+		dev_info(cdev, "capture: exposure_bias\t%d\n",
+				exif->exposure_bias);
+		dev_info(cdev, "capture: iso_speed\t%d\n", exif->iso_speed);
+		dev_info(cdev, "capture: flash\t%d\n", exif->flash);
+		dev_info(cdev, "capture: sdr\t%d\n", exif->sdr);
+		dev_info(cdev, "capture: qval\t%d\n", exif->qval);
+	}
+
+	return ret;
+}
+
+int m5mols_start_capture(struct m5mols_info *info)
+{
+	struct v4l2_subdev *sd = &info->sd;
+	u32 resolution = info->resolution;
+	int timeout;
+	int ret;
+
+	/*
+	 * Preparing capture. Setting control & interrupt before entering
+	 * capture mode
+	 *
+	 * 1) change to MONITOR mode for operating control & interrupt
+	 * 2) set controls (considering v4l2_control value & lock 3A)
+	 * 3) set interrupt
+	 * 4) change to CAPTURE mode
+	 */
+	ret = m5mols_change_mode(info, REG_MODE_MONITOR);
+	if (!ret)
+		ret = m5mols_sync_control(info);
+	if (!ret)
+		ret = m5mols_lock_3a(info, true);
+	if (!ret)
+		ret = m5mols_enable_interrupt(sd, REG_INT_CAPTURE);
+	if (!ret)
+		ret = m5mols_change_mode(info, REG_MODE_CAPTURE);
+	if (!ret)
+		/* Wait for capture interrupt, after changing capture mode */
+		timeout = wait_event_interruptible_timeout(info->wait_capture,
+				is_captured(info), msecs_to_jiffies(2000));
+	if (!ret && is_captured(info))
+		ret = m5mols_capture_error_handler(info, timeout);
+	if (!ret)
+		ret = m5mols_lock_3a(info, false);
+	if (ret)
+		return ret;
+
+	/*
+	 * Starting capture. Setting capture frame count and resolution and
+	 * the format(available format: JPEG, Bayer RAW, YUV).
+	 *
+	 * 1) select single or multi(enable to 25), format, size
+	 * 2) set interrupt
+	 * 3) start capture(for main image, now)
+	 * 4) get information
+	 * 5) notify file size to v4l2 device(e.g, to s5p-fimc v4l2 device)
+	 */
+	ret = i2c_w8_capt_ctrl(sd, CATC_CAP_SEL_FRAME, 1);
+	if (!ret)
+		ret = i2c_w8_capt_param(sd, CATB_YUVOUT_MAIN, REG_JPEG);
+	if (!ret)
+		ret = i2c_w8_capt_param(sd, CATB_MAIN_IMAGE_SIZE, resolution);
+	if (!ret)
+		ret = m5mols_enable_interrupt(sd, REG_INT_CAPTURE);
+	if (!ret)
+		ret = i2c_w8_capt_ctrl(sd, CATC_CAP_START, REG_CAP_START_MAIN);
+	if (!ret)
+		/* Wait for capture interrupt, after starting capture */
+		timeout = wait_event_interruptible_timeout(info->wait_capture,
+				is_captured(info), msecs_to_jiffies(2000));
+	if (!ret && is_captured(info))
+		ret = m5mols_capture_info(info, false);
+	if (!ret)
+		v4l2_subdev_notify(sd, 0, &info->cap.total);
+	if (!ret)
+		ret = m5mols_capture_error_handler(info, timeout);
+
+	return ret;
+}
diff --git a/drivers/media/video/m5mols/m5mols_controls.c b/drivers/media/video/m5mols/m5mols_controls.c
new file mode 100644
index 0000000..1eb909e
--- /dev/null
+++ b/drivers/media/video/m5mols/m5mols_controls.c
@@ -0,0 +1,293 @@
+/*
+ * Controls for M-5MOLS 8M Pixel camera sensor with ISP
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * Author: HeungJun Kim, riverful.kim@samsung.com
+ *
+ * Copyright (C) 2009 Samsung Electronics Co., Ltd.
+ * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com
+ *
+ * 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/videodev2.h>
+#include <media/v4l2-ctrls.h>
+
+#include "m5mols.h"
+#include "m5mols_reg.h"
+
+static struct m5mols_scenemode m5mols_default_scenemode[] = {
+	[REG_SCENE_NORMAL] = {
+		REG_AE_MODE_CENTER, REG_AE_INDEX_00, REG_AWB_MODE_AUTO, 0,
+		REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
+		REG_AF_MODE_NORMAL, REG_FD_OFF,
+		REG_MCC_NORMAL, REG_LIGHT_OFF, REG_FLASH_OFF,
+		5, REG_ISO_AUTO, REG_CAP_MODE_NONE, REG_WDR_OFF,
+	},
+	[REG_SCENE_PORTRAIT] = {
+		REG_AE_MODE_CENTER, REG_AE_INDEX_00, REG_AWB_MODE_AUTO, 0,
+		REG_CHROMA_ON, 3, REG_EDGE_ON, 4,
+		REG_AF_MODE_NORMAL, BIT_FD_EN | BIT_FD_DRAW_FACE_FRAME,
+		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+		6, REG_ISO_AUTO, REG_CAP_MODE_NONE, REG_WDR_OFF,
+	},
+	[REG_SCENE_LANDSCAPE] = {
+		REG_AE_MODE_ALL, REG_AE_INDEX_00, REG_AWB_MODE_AUTO, 0,
+		REG_CHROMA_ON, 4, REG_EDGE_ON, 6,
+		REG_AF_MODE_NORMAL, REG_FD_OFF,
+		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+		6, REG_ISO_AUTO, REG_CAP_MODE_NONE, REG_WDR_OFF,
+	},
+	[REG_SCENE_SPORTS] = {
+		REG_AE_MODE_CENTER, REG_AE_INDEX_00, REG_AWB_MODE_AUTO, 0,
+		REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
+		REG_AF_MODE_NORMAL, REG_FD_OFF,
+		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+		6, REG_ISO_AUTO, REG_CAP_MODE_NONE, REG_WDR_OFF,
+	},
+	[REG_SCENE_PARTY_INDOOR] = {
+		REG_AE_MODE_CENTER, REG_AE_INDEX_00, REG_AWB_MODE_AUTO, 0,
+		REG_CHROMA_ON, 4, REG_EDGE_ON, 5,
+		REG_AF_MODE_NORMAL, REG_FD_OFF,
+		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+		6, REG_ISO_200, REG_CAP_MODE_NONE, REG_WDR_OFF,
+	},
+	[REG_SCENE_BEACH_SNOW] = {
+		REG_AE_MODE_CENTER, REG_AE_INDEX_10_POS, REG_AWB_MODE_AUTO, 0,
+		REG_CHROMA_ON, 4, REG_EDGE_ON, 5,
+		REG_AF_MODE_NORMAL, REG_FD_OFF,
+		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+		6, REG_ISO_50, REG_CAP_MODE_NONE, REG_WDR_OFF,
+	},
+	[REG_SCENE_SUNSET] = {
+		REG_AE_MODE_CENTER, REG_AE_INDEX_00, REG_AWB_MODE_PRESET,
+		REG_AWB_DAYLIGHT,
+		REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
+		REG_AF_MODE_NORMAL, REG_FD_OFF,
+		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+		6, REG_ISO_AUTO, REG_CAP_MODE_NONE, REG_WDR_OFF,
+	},
+	[REG_SCENE_DAWN_DUSK] = {
+		REG_AE_MODE_CENTER, REG_AE_INDEX_00, REG_AWB_MODE_PRESET,
+		REG_AWB_FLUORESCENT_1,
+		REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
+		REG_AF_MODE_NORMAL, REG_FD_OFF,
+		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+		6, REG_ISO_AUTO, REG_CAP_MODE_NONE, REG_WDR_OFF,
+	},
+	[REG_SCENE_FALL] = {
+		REG_AE_MODE_CENTER, REG_AE_INDEX_00, REG_AWB_MODE_AUTO, 0,
+		REG_CHROMA_ON, 5, REG_EDGE_ON, 5,
+		REG_AF_MODE_NORMAL, REG_FD_OFF,
+		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+		6, REG_ISO_AUTO, REG_CAP_MODE_NONE, REG_WDR_OFF,
+	},
+	[REG_SCENE_NIGHT] = {
+		REG_AE_MODE_CENTER, REG_AE_INDEX_00, REG_AWB_MODE_AUTO, 0,
+		REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
+		REG_AF_MODE_NORMAL, REG_FD_OFF,
+		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+		6, REG_ISO_AUTO, REG_CAP_MODE_NONE, REG_WDR_OFF,
+	},
+	[REG_SCENE_AGAINST_LIGHT] = {
+		REG_AE_MODE_CENTER, REG_AE_INDEX_00, REG_AWB_MODE_AUTO, 0,
+		REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
+		REG_AF_MODE_NORMAL, REG_FD_OFF,
+		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+		6, REG_ISO_AUTO, REG_CAP_MODE_NONE, REG_WDR_OFF,
+	},
+	[REG_SCENE_FIRE] = {
+		REG_AE_MODE_CENTER, REG_AE_INDEX_00, REG_AWB_MODE_AUTO, 0,
+		REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
+		REG_AF_MODE_NORMAL, REG_FD_OFF,
+		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+		6, REG_ISO_50, REG_CAP_MODE_NONE, REG_WDR_OFF,
+	},
+	[REG_SCENE_TEXT] = {
+		REG_AE_MODE_CENTER, REG_AE_INDEX_00, REG_AWB_MODE_AUTO, 0,
+		REG_CHROMA_ON, 3, REG_EDGE_ON, 7,
+		REG_AF_MODE_MACRO, REG_FD_OFF,
+		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+		6, REG_ISO_AUTO, REG_CAP_MODE_ANTI_SHAKE, REG_WDR_ON,
+	},
+	[REG_SCENE_CANDLE] = {
+		REG_AE_MODE_CENTER, REG_AE_INDEX_00, REG_AWB_MODE_AUTO, 0,
+		REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
+		REG_AF_MODE_NORMAL, REG_FD_OFF,
+		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+		6, REG_ISO_AUTO, REG_CAP_MODE_NONE, REG_WDR_OFF,
+	},
+};
+
+/* m5mols_change_scenemode() - Change current scenemode */
+int m5mols_change_scenemode(struct m5mols_info *info, u32 mode)
+{
+	struct v4l2_subdev *sd = &info->sd;
+	struct m5mols_scenemode scenemode = m5mols_default_scenemode[mode];
+	int ret;
+
+	ret = m5mols_lock_3a(info, false);
+	if (!ret)
+		ret = i2c_w8_ae(sd, CAT3_EV_PRESET_MODE_MON, mode);
+	if (!ret)
+		ret = i2c_w8_ae(sd, CAT3_EV_PRESET_MODE_CAP, mode);
+	if (!ret)
+		ret = i2c_w8_ae(sd, CAT3_AE_MODE, scenemode.metering);
+	if (!ret)
+		ret = i2c_w8_ae(sd, CAT3_AE_INDEX, scenemode.ev_bias);
+	if (!ret)
+		ret = i2c_w8_wb(sd, CAT6_AWB_MODE, scenemode.wb_mode);
+	if (!ret)
+		ret = i2c_w8_wb(sd, CAT6_AWB_MANUAL, scenemode.wb_preset);
+	if (!ret)
+		ret = i2c_w8_mon(sd, CAT2_CHROMA_EN, scenemode.chroma_en);
+	if (!ret)
+		ret = i2c_w8_mon(sd, CAT2_CHROMA_LVL, scenemode.chroma_lvl);
+	if (!ret)
+		ret = i2c_w8_mon(sd, CAT2_EDGE_EN, scenemode.edge_en);
+	if (!ret)
+		ret = i2c_w8_mon(sd, CAT2_EDGE_LVL, scenemode.edge_lvl);
+	if (!ret && info->ver.af)
+		ret = i2c_w8_lens(sd, CATA_AF_MODE, scenemode.af_range);
+	if (!ret && info->ver.af)
+		ret = i2c_w8_fd(sd, CAT9_FD_CTL, scenemode.fd_mode);
+	if (!ret)
+		ret = i2c_w8_mon(sd, CAT2_TONE_CTL, scenemode.tone);
+	if (!ret)
+		ret = i2c_w8_mon(sd, CAT3_ISO, scenemode.iso);
+	if (!ret)
+		ret = m5mols_change_mode(info, REG_MODE_CAPTURE);
+	if (!ret)
+		ret = i2c_w8_mon(sd, CATB_WDR_EN, scenemode.wdr);
+	if (!ret)
+		ret = i2c_w8_capt_param(sd, CATB_MCC_MODE, scenemode.mcc);
+	if (!ret)
+		ret = i2c_w8_capt_param(sd, CATB_LIGHT_CTRL, scenemode.light);
+	if (!ret)
+		ret = i2c_w8_capt_param(sd, CATB_FLASH_CTRL, scenemode.flash);
+	if (!ret)
+		ret = i2c_w8_capt_param(sd, CATB_FLASH_CTRL, scenemode.flash);
+	if (!ret)
+		ret = i2c_w8_mon(sd, CATC_CAP_MODE, scenemode.capture_mode);
+	if (!ret)
+		ret = m5mols_change_mode(info, REG_MODE_MONITOR);
+
+	return ret;
+}
+
+static int m5mols_lock_ae(struct m5mols_info *info, bool lock)
+{
+	int ret = 0;
+
+	if (info->lock_ae != lock)
+		ret = i2c_w8_ae(&info->sd, CAT3_AE_LOCK,
+				lock ? REG_AE_LOCK : REG_AE_UNLOCK);
+	if (!ret)
+		info->lock_ae = lock;
+
+	return ret;
+}
+
+static int m5mols_lock_awb(struct m5mols_info *info, bool lock)
+{
+	int ret = 0;
+
+	if (info->lock_awb != lock)
+		ret = i2c_w8_wb(&info->sd, CAT6_AWB_LOCK,
+				lock ? REG_AWB_LOCK : REG_AWB_UNLOCK);
+	if (!ret)
+		info->lock_awb = lock;
+
+	return ret;
+}
+
+/* m5mols_lock_3a() - Lock 3A(Auto Exposure, Whitebalance, Focus) */
+int m5mols_lock_3a(struct m5mols_info *info, bool lock)
+{
+	int ret;
+
+	ret = m5mols_lock_ae(info, lock);
+	if (!ret)
+		ret = m5mols_lock_awb(info, lock);
+	if (!ret && info->ver.af && lock)
+		/* Don't need to handle unlocking AF */
+		ret = i2c_w8_lens(&info->sd, CATA_AF_EXECUTE, REG_AF_STOP);
+
+	return ret;
+}
+
+/* m5mols_set_ctrl() - The main s_ctrl function called by m5mols_set_ctrl() */
+int m5mols_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct v4l2_subdev *sd = to_sd(ctrl);
+	struct m5mols_info *info = to_m5mols(sd);
+	int ret;
+
+	switch (ctrl->id) {
+	case V4L2_CID_ZOOM_ABSOLUTE:
+		return i2c_w8_mon(sd, CAT2_ZOOM, ctrl->val);
+
+	case V4L2_CID_EXPOSURE_AUTO:
+		ret = m5mols_lock_ae(info, ctrl->val == V4L2_EXPOSURE_AUTO ?
+				false : true);
+		if (!ret)
+			ret = i2c_w8_ae(sd, CAT3_AE_MODE,
+				ctrl->val == V4L2_EXPOSURE_AUTO ?
+				REG_AE_MODE_ALL : REG_AE_MODE_OFF);
+		if (!ret && ctrl->val == V4L2_EXPOSURE_MANUAL)
+			ret = i2c_w16_ae(sd, CAT3_MANUAL_GAIN_MON,
+				info->exposure->val);
+		if (!ret && ctrl->val == V4L2_EXPOSURE_MANUAL)
+			ret = i2c_w16_ae(sd, CAT3_MANUAL_GAIN_CAP,
+				info->exposure->val);
+		return ret;
+
+	case V4L2_CID_AUTO_WHITE_BALANCE:
+		ret = m5mols_lock_awb(info, ctrl->val ? false : true);
+		if (!ret)
+			ret = i2c_w8_wb(sd, CAT6_AWB_MODE, ctrl->val ?
+				REG_AWB_MODE_AUTO : REG_AWB_MODE_PRESET);
+		return ret;
+
+	case V4L2_CID_SATURATION:
+		ret = i2c_w8_mon(sd, CAT2_CHROMA_LVL, ctrl->val);
+		if (!ret)
+			ret = i2c_w8_mon(sd, CAT2_CHROMA_EN, REG_CHROMA_ON);
+		return ret;
+
+	case V4L2_CID_COLORFX:
+		/*
+		 * This control uses two kinds of registers: normal & color.
+		 * The normal effect belongs to category 1, while the color
+		 * one belongs to category 2.
+		 *
+		 * The normal effect uses one register: CAT1_EFFECT.
+		 * The color effect uses three registers:
+		 * CAT2_COLOR_EFFECT, CAT2_CFIXR, CAT2_CFIXB.
+		 */
+		ret = i2c_w8_param(sd, CAT1_EFFECT,
+			ctrl->val == V4L2_COLORFX_NEGATIVE ? REG_EFFECT_NEGA :
+			ctrl->val == V4L2_COLORFX_EMBOSS ? REG_EFFECT_EMBOSS :
+			REG_EFFECT_OFF);
+		if (!ret)
+			ret = i2c_w8_mon(sd, CAT2_COLOR_EFFECT,
+				ctrl->val == V4L2_COLORFX_SEPIA ?
+				REG_COLOR_EFFECT_ON : REG_COLOR_EFFECT_OFF);
+		if (!ret)
+			ret = i2c_w8_mon(sd, CAT2_CFIXR,
+				ctrl->val == V4L2_COLORFX_SEPIA ?
+				REG_CFIXR_SEPIA : 0);
+		if (!ret)
+			ret = i2c_w8_mon(sd, CAT2_CFIXB,
+				ctrl->val == V4L2_COLORFX_SEPIA ?
+				REG_CFIXB_SEPIA : 0);
+		return ret;
+	}
+
+	return -EINVAL;
+}
diff --git a/drivers/media/video/m5mols/m5mols_core.c b/drivers/media/video/m5mols/m5mols_core.c
new file mode 100644
index 0000000..943a4e1
--- /dev/null
+++ b/drivers/media/video/m5mols/m5mols_core.c
@@ -0,0 +1,1034 @@
+/*
+ * Driver for M-5MOLS 8M Pixel camera sensor with ISP
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * Author: HeungJun Kim, riverful.kim@samsung.com
+ *
+ * Copyright (C) 2009 Samsung Electronics Co., Ltd.
+ * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com
+ *
+ * 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/slab.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/version.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+#include <media/m5mols.h>
+
+#include "m5mols.h"
+#include "m5mols_reg.h"
+
+int m5mols_debug;
+module_param(m5mols_debug, int, 0644);
+
+#define MOD_NAME		"M5MOLS"
+#define M5MOLS_I2C_CHECK_RETRY	500
+
+/* The regulator consumer names are derived from M-5MOLS datasheets. */
+static struct regulator_bulk_data supplies[] = {
+	{
+		.supply		= "core",	/* ARM core power, 1.2V */
+	}, {
+		.supply		= "dig_18",	/* digital power 1, 1.8V */
+	}, {
+		.supply		= "d_sensor",	/* sensor power 1, 1.8V */
+	}, {
+		.supply		= "dig_28",	/* digital power 2, 2.8V */
+	}, {
+		.supply		= "a_sensor",	/* analog power */
+	}, {
+		.supply		= "dig_12",	/* digital power 3, 1.2V */
+	},
+};
+
+const struct v4l2_mbus_framefmt m5mols_default_ffmt[M5MOLS_RESTYPE_MAX] = {
+	[M5MOLS_RESTYPE_MONITOR] = {
+		.width		= 1920,
+		.height		= 1080,
+		.code		= V4L2_MBUS_FMT_VYUY8_2X8,
+		.field		= V4L2_FIELD_NONE,
+		.colorspace	= V4L2_COLORSPACE_JPEG,
+	},
+	[M5MOLS_RESTYPE_CAPTURE] = {
+		.width		= 1920,
+		.height		= 1080,
+		.code		= V4L2_MBUS_FMT_JPEG_1X8,
+		.field		= V4L2_FIELD_NONE,
+		.colorspace	= V4L2_COLORSPACE_JPEG,
+	},
+};
+#define SIZE_DEFAULT_FFMT	ARRAY_SIZE(m5mols_default_ffmt)
+
+static const struct m5mols_resolution m5mols_reg_res[] = {
+	{ 0x01, M5MOLS_RESTYPE_MONITOR, 128, 96 },	/* SUB-QCIF */
+	{ 0x03, M5MOLS_RESTYPE_MONITOR, 160, 120 },	/* QQVGA */
+	{ 0x05, M5MOLS_RESTYPE_MONITOR, 176, 144 },	/* QCIF */
+	{ 0x06, M5MOLS_RESTYPE_MONITOR, 176, 176 },
+	{ 0x08, M5MOLS_RESTYPE_MONITOR, 240, 320 },	/* QVGA */
+	{ 0x09, M5MOLS_RESTYPE_MONITOR, 320, 240 },	/* QVGA */
+	{ 0x0c, M5MOLS_RESTYPE_MONITOR, 240, 400 },	/* WQVGA */
+	{ 0x0d, M5MOLS_RESTYPE_MONITOR, 400, 240 },	/* WQVGA */
+	{ 0x0e, M5MOLS_RESTYPE_MONITOR, 352, 288 },	/* CIF */
+	{ 0x13, M5MOLS_RESTYPE_MONITOR, 480, 360 },
+	{ 0x15, M5MOLS_RESTYPE_MONITOR, 640, 360 },	/* qHD */
+	{ 0x17, M5MOLS_RESTYPE_MONITOR, 640, 480 },	/* VGA */
+	{ 0x18, M5MOLS_RESTYPE_MONITOR, 720, 480 },
+	{ 0x1a, M5MOLS_RESTYPE_MONITOR, 800, 480 },	/* WVGA */
+	{ 0x1f, M5MOLS_RESTYPE_MONITOR, 800, 600 },	/* SVGA */
+	{ 0x21, M5MOLS_RESTYPE_MONITOR, 1280, 720 },	/* HD */
+	{ 0x25, M5MOLS_RESTYPE_MONITOR, 1920, 1080 },	/* 1080p */
+	{ 0x29, M5MOLS_RESTYPE_MONITOR, 3264, 2448 },	/* 2.63fps 8M */
+	{ 0x39, M5MOLS_RESTYPE_MONITOR, 800, 602 },	/* AHS_MON debug */
+
+	{ 0x02, M5MOLS_RESTYPE_CAPTURE, 320, 240 },	/* QVGA */
+	{ 0x04, M5MOLS_RESTYPE_CAPTURE, 400, 240 },	/* WQVGA */
+	{ 0x07, M5MOLS_RESTYPE_CAPTURE, 480, 360 },
+	{ 0x08, M5MOLS_RESTYPE_CAPTURE, 640, 360 },	/* qHD */
+	{ 0x09, M5MOLS_RESTYPE_CAPTURE, 640, 480 },	/* VGA */
+	{ 0x0a, M5MOLS_RESTYPE_CAPTURE, 800, 480 },	/* WVGA */
+	{ 0x10, M5MOLS_RESTYPE_CAPTURE, 1280, 720 },	/* HD */
+	{ 0x14, M5MOLS_RESTYPE_CAPTURE, 1280, 960 },	/* 1M */
+	{ 0x17, M5MOLS_RESTYPE_CAPTURE, 1600, 1200 },	/* 2M */
+	{ 0x19, M5MOLS_RESTYPE_CAPTURE, 1920, 1080 },	/* Full-HD */
+	{ 0x1a, M5MOLS_RESTYPE_CAPTURE, 2048, 1152 },	/* 3Mega */
+	{ 0x1b, M5MOLS_RESTYPE_CAPTURE, 2048, 1536 },
+	{ 0x1c, M5MOLS_RESTYPE_CAPTURE, 2560, 1440 },	/* 4Mega */
+	{ 0x1d, M5MOLS_RESTYPE_CAPTURE, 2560, 1536 },
+	{ 0x1f, M5MOLS_RESTYPE_CAPTURE, 2560, 1920 },	/* 5Mega */
+	{ 0x21, M5MOLS_RESTYPE_CAPTURE, 3264, 1836 },	/* 6Mega */
+	{ 0x22, M5MOLS_RESTYPE_CAPTURE, 3264, 1960 },
+	{ 0x25, M5MOLS_RESTYPE_CAPTURE, 3264, 2448 },	/* 8Mega */
+};
+
+/*
+ * m5mols_swap_byte() - byte array to integer conversion
+ * @size:	the size's kinds of I2C packet defined in the M-5MOLS datasheet
+ *
+ * Convert I2C data byte array with performing any required byte
+ * reordering to assure proper values for each data type, regardless
+ * of the architecture endianness.
+ */
+static u32 m5mols_swap_byte(u8 *data, enum m5mols_i2c_size size)
+{
+	if (size == I2C_8BIT)
+		return *data;
+	else if (size == I2C_16BIT)
+		return be16_to_cpu(*((u16 *)data));
+	else
+		return be32_to_cpu(*((u32 *)data));
+}
+
+/*
+ * m5mols_read_reg() - raw function of I2C reading operation
+ * @size:	the kinds of I2C packet's size defined in the M-5MOLS datasheet
+ * @category:	the category value of I2C command
+ * @cmd:	the command
+ * @val:	the read values
+ *
+ * The M-5MOLS I2C operation always consists of category, command, value(s)
+ * as an element. The numbers of each operation vary according to reading or
+ * writing. The more specific explanation can be found in the m5mol.h.
+ */
+int m5mols_read_reg(struct v4l2_subdev *sd,
+		    enum m5mols_i2c_size size,
+		    u8 category, u8 cmd, u32 *val)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct i2c_msg msg[2];
+	u8 wbuf[5], rbuf[I2C_MAX + 1];
+	int ret;
+
+	if (!client->adapter)
+		return -ENODEV;
+
+	if (size != I2C_8BIT && size != I2C_16BIT && size != I2C_32BIT)
+		return -EINVAL;
+
+	/* for designating category and command */
+	msg[0].addr = client->addr;
+	msg[0].flags = 0;
+	msg[0].len = 5;
+	msg[0].buf = wbuf;
+	wbuf[0] = 5;
+	wbuf[1] = M5MOLS_BYTE_READ;
+	wbuf[2] = category;
+	wbuf[3] = cmd;
+	wbuf[4] = size;
+
+	/* for reading desired data */
+	msg[1].addr = client->addr;
+	msg[1].flags = I2C_M_RD;
+	msg[1].len = size + 1;
+	msg[1].buf = rbuf;
+
+	/* minimum stabilization time */
+	usleep_range(200, 200);
+
+	ret = i2c_transfer(client->adapter, msg, 2);
+	if (ret < 0) {
+		dev_err(&client->dev,
+			"failed READ[%d] at cat[%02x] cmd[%02x]\n",
+			size, category, cmd);
+		return ret;
+	}
+
+	*val = m5mols_swap_byte(&rbuf[1], size);
+
+	return 0;
+}
+
+/*
+ * m5mols_write_reg() - raw function of I2C writing operation
+ * @size:	the kinds of I2C packet's size defined in the M-5MOLS datasheet
+ * @category:	the category value of I2C command
+ * @cmd:	the I2C command
+ * @val:	the written value(s)
+ */
+int m5mols_write_reg(struct v4l2_subdev *sd,
+		     enum m5mols_i2c_size size,
+		     u8 category, u8 cmd, u32 val)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct device *cdev = &client->dev;
+	struct i2c_msg msg[1];
+	u8 wbuf[I2C_MAX + 4];
+	u32 *buf;
+	int ret;
+
+	if (!client->adapter)
+		return -ENODEV;
+
+	if (size != I2C_8BIT && size != I2C_16BIT && size != I2C_32BIT) {
+		dev_err(cdev, "Wrong data size\n");
+		return -EINVAL;
+	}
+
+	msg->addr = client->addr;
+	msg->flags = 0;
+	msg->len = (u16)size + 4;
+	msg->buf = wbuf;
+	wbuf[0] = size + 4;
+	wbuf[1] = M5MOLS_BYTE_WRITE;
+	wbuf[2] = category;
+	wbuf[3] = cmd;
+
+	buf = (u32 *)&wbuf[4];
+	*buf = m5mols_swap_byte((u8 *)&val, size);
+
+	usleep_range(200, 200);
+
+	ret = i2c_transfer(client->adapter, msg, 1);
+	if (ret < 0) {
+		dev_err(&client->dev,
+			"failed WRITE[%d] at cat[%02x] cmd[%02x], ret %d\n",
+			size, msg->buf[2], msg->buf[3], ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+int m5mols_check_busy(struct v4l2_subdev *sd, u8 category, u8 cmd, u32 mask)
+{
+	u32 busy, i;
+	int ret;
+
+	for (i = 0; i < M5MOLS_I2C_CHECK_RETRY; i++) {
+		ret = m5mols_read_reg(sd, I2C_8BIT, category, cmd, &busy);
+		if (ret < 0)
+			return ret;
+
+		if ((busy & mask) == mask)
+			return 0;
+	}
+
+	return -EBUSY;
+}
+
+/*
+ * m5mols_enable_interrupt() - clear pending register and set interrupt
+ *
+ * Before writing desired interrupt value, should clear INT_FACTOR register
+ * automatically documented in the M-5MOLS datasheet.
+ */
+int m5mols_enable_interrupt(struct v4l2_subdev *sd, u32 reg)
+{
+	struct m5mols_info *info = to_m5mols(sd);
+	u32 dummy;
+	int ret;
+
+	ret = i2c_r8_system(sd, CAT0_INT_FACTOR, &dummy);
+	if (!ret)
+		ret = i2c_w8_system(sd, CAT0_INT_ENABLE,
+				info->ver.af ? reg & ~REG_INT_AF : reg);
+	return ret;
+}
+
+/*
+ * m5mols_write_mode() - Write the mode and check busy status.
+ *
+ * Changing the M-5MOLS mode accompanies a little delay all the time, so
+ * checking current busy status is needed to guarantee changing right mode.
+ */
+static int m5mols_write_mode(struct v4l2_subdev *sd, u32 mode)
+{
+	int ret = i2c_w8_system(sd, CAT0_SYSMODE, mode);
+	if (!ret)
+		ret = m5mols_check_busy(sd, CAT_SYSTEM, CAT0_SYSMODE, mode);
+
+	return ret;
+}
+
+/*
+ * m5mols_change_mode() - manage the M-5MOLS's mode.
+ * @mode:	the desired mode
+ *
+ * All commands of M-5MOLS belong to a specific its own mode. Each functionality
+ * can be guaranteed only when the sensor is operating in the mode which
+ * the command belongs to. The more explanation can be found in the m5mols.h.
+ */
+int m5mols_change_mode(struct m5mols_info *info, u32 mode)
+{
+	struct v4l2_subdev *sd = &info->sd;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	u32 reg;
+	int ret = -EINVAL;
+
+	if (mode != REG_MODE_PARAM && mode != REG_MODE_MONITOR &&
+	    mode != REG_MODE_CAPTURE)
+		return ret;
+
+	ret = i2c_r8_system(sd, CAT0_SYSMODE, &reg);
+	if ((!ret && reg == mode) || ret)
+		return ret;
+
+	switch (reg) {
+	case REG_MODE_PARAM:
+		ret = m5mols_write_mode(sd, REG_MODE_MONITOR);
+		if (!ret && mode == REG_MODE_MONITOR)
+			break;
+		if (!ret)
+			ret = m5mols_write_mode(sd, REG_MODE_CAPTURE);
+		break;
+
+	case REG_MODE_MONITOR:
+		if (mode == REG_MODE_PARAM) {
+			ret = m5mols_write_mode(sd, REG_MODE_PARAM);
+			break;
+		}
+
+		ret = m5mols_write_mode(sd, REG_MODE_CAPTURE);
+		break;
+
+	case REG_MODE_CAPTURE:
+		ret = m5mols_write_mode(sd, REG_MODE_MONITOR);
+		if (!ret && mode == REG_MODE_MONITOR)
+			break;
+		if (!ret)
+			ret = m5mols_write_mode(sd, REG_MODE_PARAM);
+		break;
+
+	default:
+		dev_warn(&client->dev, "Wrong mode: %d", mode);
+	}
+
+	if (!ret)
+		info->mode = mode;
+
+	return ret;
+}
+
+/*
+ * m5mols_get_version() - Retrieve all internal version of M-5MOLS.
+ *
+ * The version information includes revisions hardware and firmware, AutoFocus
+ * alghorithm version and the version string. The version string should be read
+ * at the same register offset until the read value is NULL.
+ */
+static int m5mols_get_version(struct v4l2_subdev *sd)
+{
+	struct m5mols_info *info = to_m5mols(sd);
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct device *cdev = &client->dev;
+	union {
+		struct m5mols_version ver;
+		u8 bytes[VERSION_SIZE];
+	} value;
+	int ret, cmd = CAT0_CUSTOMER_CODE;
+
+	do {
+		ret = i2c_r8_system(sd, cmd, (u32 *)&value.bytes[cmd]);
+		if (ret)
+			return ret;
+	} while (cmd++ != CAT0_VERSION_AWB);
+
+	do {
+		ret = i2c_r8_system(sd, CAT0_VERSION_STRING,
+				(u32 *)&value.bytes[cmd]);
+		if (ret)
+			return ret;
+	} while (value.bytes[cmd++]);
+
+	ret = i2c_r8_lens(sd, CATA_AF_VERSION, (u32 *)&value.bytes[cmd]);
+	if (ret)
+		return ret;
+
+	/* store version information swapped for being readable */
+	info->ver	= value.ver;
+	info->ver.fw	= be16_to_cpu(info->ver.fw);
+	info->ver.hw	= be16_to_cpu(info->ver.hw);
+	info->ver.param	= be16_to_cpu(info->ver.param);
+	info->ver.awb	= be16_to_cpu(info->ver.awb);
+
+	dev_info(cdev, "Manufacturer\t[%s]\n",
+			is_manufacturer(info, SAMSUNG_ELECTRO) ?
+			"Samsung Electro-Machanics" :
+			is_manufacturer(info, SAMSUNG_OPTICS) ?
+			"Samsung Fiber-Optics" :
+			is_manufacturer(info, SAMSUNG_TECHWIN) ?
+			"Samsung Techwin" : "None");
+	dev_info(cdev, "Customer/Project\t[0x%02x/0x%02x]\n",
+			info->ver.customer, info->ver.project);
+	if (!info->ver.af)
+		dev_info(cdev, "No support Auto Focus on this firmware\n");
+
+	return ret;
+}
+
+/*
+ * __find_restype() - look up M-5MOLS resolution type according to pixel code
+ * @code:	pixel code
+ */
+static enum m5mols_restype __find_restype(enum v4l2_mbus_pixelcode code)
+{
+	enum m5mols_restype type = M5MOLS_RESTYPE_MONITOR;
+
+	do {
+		if (code == m5mols_default_ffmt[type].code)
+			return type;
+	} while (type++ != SIZE_DEFAULT_FFMT);
+
+	return 0;
+}
+
+/*
+ * __find_resolution() - look up preset and type of M-5MOLS's resolution
+ * @mf:         pixel format to find/negotiate the resolution preset for
+ * @type:	M-5MOLS resolution type
+ * @resolution:	M-5MOLS resolution preset register value
+ *
+ * Find nearest resolution matching resolution present and adjust mf
+ * to the supported values.
+ */
+static int __find_resolution(struct v4l2_subdev *sd,
+			     struct v4l2_mbus_framefmt *mf,
+			     enum m5mols_restype *type,
+			     u32 *resolution)
+{
+	const struct m5mols_resolution *fsize = &m5mols_reg_res[0],
+				 *match = NULL;
+	enum m5mols_restype stype = __find_restype(mf->code);
+	int i = ARRAY_SIZE(m5mols_reg_res);
+	unsigned int min_err = ~0;
+
+	while (i--) {
+		int err;
+		if (stype != fsize->type)
+			continue;
+
+		err = abs(fsize->width - mf->width)
+			  + abs(fsize->height - mf->height);
+
+		if (err < min_err) {
+			min_err = err;
+			match = fsize;
+		}
+		fsize++;
+	}
+	if (match) {
+		mf->width  = match->width;
+		mf->height = match->height;
+		*resolution = match->reg;
+		*type = stype;
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static struct v4l2_mbus_framefmt *__find_format(struct m5mols_info *info,
+				struct v4l2_subdev_fh *fh,
+				enum v4l2_subdev_format_whence which,
+				enum m5mols_restype type)
+{
+	if (which == V4L2_SUBDEV_FORMAT_TRY)
+		return fh ? v4l2_subdev_get_try_format(fh, 0) : NULL;
+
+	return &info->ffmt[type];
+}
+
+static int m5mols_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+			  struct v4l2_subdev_format *fmt)
+{
+	struct m5mols_info *info = to_m5mols(sd);
+	struct v4l2_mbus_framefmt *format;
+
+	if (fmt->pad != 0)
+		return -EINVAL;
+
+	format = __find_format(info, fh, fmt->which, info->curr_res_type);
+	if (!format)
+		return -EINVAL;
+
+	fmt->format = *format;
+	return 0;
+}
+
+static int m5mols_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+			  struct v4l2_subdev_format *fmt)
+{
+	struct m5mols_info *info = to_m5mols(sd);
+	struct v4l2_mbus_framefmt *format = &fmt->format;
+	struct v4l2_mbus_framefmt *sfmt;
+	enum m5mols_restype type;
+	u32 resolution = 0;
+	int ret;
+
+	if (fmt->pad != 0)
+		return -EINVAL;
+
+	ret = __find_resolution(sd, format, &type, &resolution);
+	if (ret < 0)
+		return ret;
+
+	sfmt = __find_format(info, fh, fmt->which, type);
+	if (!sfmt)
+		return 0;
+
+	*sfmt		= m5mols_default_ffmt[type];
+	sfmt->width	= format->width;
+	sfmt->height	= format->height;
+
+	if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+		info->resolution	= resolution;
+		info->code		= format->code;
+		info->curr_res_type	= type;
+	}
+
+	return 0;
+}
+
+static int m5mols_enum_mbus_code(struct v4l2_subdev *sd,
+				 struct v4l2_subdev_fh *fh,
+				 struct v4l2_subdev_mbus_code_enum *code)
+{
+	if (!code || code->index >= SIZE_DEFAULT_FFMT)
+		return -EINVAL;
+
+	code->code = m5mols_default_ffmt[code->index].code;
+
+	return 0;
+}
+
+static struct v4l2_subdev_pad_ops m5mols_pad_ops = {
+	.enum_mbus_code	= m5mols_enum_mbus_code,
+	.get_fmt	= m5mols_get_fmt,
+	.set_fmt	= m5mols_set_fmt,
+};
+
+/*
+ * m5mols_sync_control() - do default control and v4l2_ctrl_handler_setup()
+ *
+ * This is used only streaming for syncing between v4l2_ctrl framework and
+ * m5mols's controls. First, do the scenemode to the sensor, then call
+ * v4l2_ctrl_handler_setup(). It can be same between some commands and
+ * the scenemode's in the default v4l2_ctrls. But, such commands of control
+ * should be prior to the scenemode's one.
+ */
+int m5mols_sync_control(struct m5mols_info *info)
+{
+	int ret = -EINVAL;
+
+	if (!is_ctrl_synced(info)) {
+		ret = m5mols_change_scenemode(info, REG_SCENE_NORMAL);
+		if (ret)
+			return ret;
+
+		v4l2_ctrl_handler_setup(&info->handle);
+
+		info->ctrl_sync = true;
+	}
+
+	return ret;
+}
+
+/*
+ * m5mols_start_monitor() - start monitor mode
+ *
+ * Before syncing control, do the commands in the available PARAMETER mode,
+ * and then, change the mode as MONITOR.
+ */
+static int m5mols_start_monitor(struct m5mols_info *info)
+{
+	struct v4l2_subdev *sd = &info->sd;
+	int ret;
+
+	ret = m5mols_change_mode(info, REG_MODE_PARAM);
+	if (!ret)
+		ret = i2c_w8_param(sd, CAT1_MONITOR_SIZE, info->resolution);
+	if (!ret)
+		ret = i2c_w8_param(sd, CAT1_MONITOR_FPS, REG_FPS_30);
+	if (!ret)
+		ret = m5mols_change_mode(info, REG_MODE_MONITOR);
+	if (!ret)
+		ret = m5mols_sync_control(info);
+
+	return ret;
+}
+
+static int m5mols_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	struct m5mols_info *info = to_m5mols(sd);
+
+	if (enable) {
+		int ret = -EINVAL;
+		if (is_code(info->code, M5MOLS_RESTYPE_MONITOR))
+			ret = m5mols_start_monitor(info);
+		if (is_code(info->code, M5MOLS_RESTYPE_CAPTURE))
+			ret = m5mols_start_capture(info);
+
+		return ret;
+	}
+
+	return m5mols_change_mode(info, REG_MODE_PARAM);
+}
+
+static const struct v4l2_subdev_video_ops m5mols_video_ops = {
+	.s_stream	= m5mols_s_stream,
+};
+
+static int m5mols_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct v4l2_subdev *sd = to_sd(ctrl);
+	struct m5mols_info *info = to_m5mols(sd);
+	int ret;
+
+	info->mode_save = info->mode;
+
+	ret = m5mols_change_mode(info, REG_MODE_PARAM);
+	if (!ret)
+		ret = m5mols_set_ctrl(ctrl);
+	if (!ret)
+		ret = m5mols_change_mode(info, info->mode_save);
+
+	return ret;
+}
+
+static const struct v4l2_ctrl_ops m5mols_ctrl_ops = {
+	.s_ctrl		= m5mols_s_ctrl,
+};
+
+static int m5mols_sensor_power(struct m5mols_info *info, bool enable)
+{
+	struct v4l2_subdev *sd = &info->sd;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	const struct m5mols_platform_data *pdata = info->pdata;
+	int ret;
+
+	if (enable) {
+		if (is_powered(info))
+			return 0;
+
+		if (info->set_power) {
+			ret = info->set_power(&client->dev, 1);
+			if (ret)
+				return ret;
+		}
+
+		ret = regulator_bulk_enable(ARRAY_SIZE(supplies), supplies);
+		if (ret)
+			return ret;
+
+		gpio_set_value(pdata->gpio_reset, !pdata->reset_polarity);
+		usleep_range(1000, 1000);
+		info->power = true;
+
+		return ret;
+	}
+
+	if (!is_powered(info))
+		return 0;
+
+	ret = regulator_bulk_disable(ARRAY_SIZE(supplies), supplies);
+	if (ret)
+		return ret;
+
+	if (info->set_power) {
+		ret = info->set_power(&client->dev, 0);
+		if (ret)
+			return ret;
+	}
+
+	gpio_set_value(pdata->gpio_reset, pdata->reset_polarity);
+	usleep_range(1000, 1000);
+	info->power = false;
+
+	return ret;
+}
+
+/* m5mols_update_fw() - m5mols_update_fw() is optional. */
+int __attribute__ ((weak)) m5mols_update_fw(struct v4l2_subdev *sd,
+		int (*set_power)(struct m5mols_info *, bool))
+{
+	return 0;
+}
+
+/*
+ * m5mols_sensor_armboot() - booting M-5MOLS internal ARM core.
+ *
+ * Booting internal ARM core makes the M-5MOLS is ready for getting commands
+ * with I2C. It's the first thing to be done after it powered up. It must wait
+ * at least 520ms recommended by M-5MOLS datasheet, after executing arm booting.
+ */
+static int m5mols_sensor_armboot(struct v4l2_subdev *sd)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	int ret;
+
+	ret = i2c_w8_flash(sd, CATF_CAM_START, REG_START_ARM_BOOT);
+	if (ret < 0)
+		return ret;
+
+	msleep(520);
+
+	ret = m5mols_get_version(sd);
+	if (!ret)
+		ret = m5mols_update_fw(sd, m5mols_sensor_power);
+	if (ret)
+		return ret;
+
+	dev_dbg(&client->dev, "Success ARM Booting\n");
+
+	ret = i2c_w8_param(sd, CAT1_DATA_INTERFACE, REG_INTERFACE_MIPI);
+	if (!ret)
+		ret = m5mols_enable_interrupt(sd, REG_INT_AF);
+
+	return ret;
+}
+
+static int m5mols_init_controls(struct m5mols_info *info)
+{
+	struct v4l2_subdev *sd = &info->sd;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	u16 max_exposure, step_zoom;
+	int ret;
+
+	/* Determine value's range & step of controls for various FW version */
+	ret = i2c_r16_ae(sd, CAT3_MAX_GAIN_MON, (u32 *)&max_exposure);
+	if (!ret)
+		step_zoom = is_manufacturer(info, SAMSUNG_OPTICS) ? 31 : 1;
+	if (ret)
+		return ret;
+
+	v4l2_ctrl_handler_init(&info->handle, 6);
+	info->autowb = v4l2_ctrl_new_std(&info->handle,
+			&m5mols_ctrl_ops, V4L2_CID_AUTO_WHITE_BALANCE,
+			0, 1, 1, 0);
+	info->saturation = v4l2_ctrl_new_std(&info->handle,
+			&m5mols_ctrl_ops, V4L2_CID_SATURATION,
+			1, 5, 1, 3);
+	info->zoom = v4l2_ctrl_new_std(&info->handle,
+			&m5mols_ctrl_ops, V4L2_CID_ZOOM_ABSOLUTE,
+			1, 70, step_zoom, 1);
+	info->exposure = v4l2_ctrl_new_std(&info->handle,
+			&m5mols_ctrl_ops, V4L2_CID_EXPOSURE,
+			0, max_exposure, 1, (int)max_exposure/2);
+	info->colorfx = v4l2_ctrl_new_std_menu(&info->handle,
+			&m5mols_ctrl_ops, V4L2_CID_COLORFX,
+			4, (1 << V4L2_COLORFX_BW), V4L2_COLORFX_NONE);
+	info->autoexposure = v4l2_ctrl_new_std_menu(&info->handle,
+			&m5mols_ctrl_ops, V4L2_CID_EXPOSURE_AUTO,
+			1, 0, V4L2_EXPOSURE_MANUAL);
+
+	sd->ctrl_handler = &info->handle;
+	if (info->handle.error) {
+		dev_err(&client->dev, "Failed to init controls, %d\n", ret);
+		v4l2_ctrl_handler_free(&info->handle);
+		return info->handle.error;
+	}
+
+	v4l2_ctrl_cluster(2, &info->autoexposure);
+
+	return 0;
+}
+
+/*
+ * m5mols_s_power() - callback function of s_power in the subdev core operation
+ *
+ * Breaking the lens is considered when the sensor going to power off.
+ * But it provided only in the M-5MOLS with Samsung Techwin firiware. And, It
+ * calls Soft-Landing algorithm.
+ */
+static int m5mols_s_power(struct v4l2_subdev *sd, int on)
+{
+	struct m5mols_info *info = to_m5mols(sd);
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	int ret;
+
+	if (on) {
+		ret = m5mols_sensor_power(info, true);
+		if (!ret)
+			ret = m5mols_sensor_armboot(sd);
+		if (!ret)
+			ret = m5mols_init_controls(info);
+		if (!ret) {
+			info->ffmt[M5MOLS_RESTYPE_MONITOR] =
+				m5mols_default_ffmt[M5MOLS_RESTYPE_MONITOR];
+			info->ffmt[M5MOLS_RESTYPE_CAPTURE] =
+				m5mols_default_ffmt[M5MOLS_RESTYPE_CAPTURE];
+		}
+
+		return ret;
+	}
+
+	if (is_manufacturer(info, SAMSUNG_TECHWIN)) {
+		ret = m5mols_change_mode(info, REG_MODE_MONITOR);
+		if (!ret)
+			ret = i2c_w8_lens(sd, CATA_AF_EXECUTE, REG_AF_STOP);
+		if (!ret)
+			ret = i2c_w8_lens(sd, CATA_AF_MODE,
+					REG_AF_MODE_POWEROFF);
+		if (!ret)
+			ret = m5mols_check_busy(sd, CAT_SYSTEM, CAT0_STATUS,
+					REG_AF_IDLE);
+		if (!ret)
+			dev_info(&client->dev, "Success soft-landing lens\n");
+	}
+
+	ret = m5mols_sensor_power(info, false);
+	if (!ret)
+		info->ctrl_sync = false;
+
+	return ret;
+}
+
+static int m5mols_log_status(struct v4l2_subdev *sd)
+{
+	struct m5mols_info *info = to_m5mols(sd);
+
+	v4l2_ctrl_handler_log_status(&info->handle, sd->name);
+
+	return 0;
+}
+
+static const struct v4l2_subdev_core_ops m5mols_core_ops = {
+	.s_power	= m5mols_s_power,
+	.g_ctrl		= v4l2_subdev_g_ctrl,
+	.s_ctrl		= v4l2_subdev_s_ctrl,
+	.queryctrl	= v4l2_subdev_queryctrl,
+	.querymenu	= v4l2_subdev_querymenu,
+	.g_ext_ctrls	= v4l2_subdev_g_ext_ctrls,
+	.try_ext_ctrls	= v4l2_subdev_try_ext_ctrls,
+	.s_ext_ctrls	= v4l2_subdev_s_ext_ctrls,
+	.log_status	= m5mols_log_status,
+};
+
+static const struct v4l2_subdev_ops m5mols_ops = {
+	.core		= &m5mols_core_ops,
+	.pad		= &m5mols_pad_ops,
+	.video		= &m5mols_video_ops,
+};
+
+static void m5mols_irq_work(struct work_struct *work)
+{
+	struct m5mols_info *info = container_of(work, struct m5mols_info,
+			work_irq);
+	struct v4l2_subdev *sd = &info->sd;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	u32 reg;
+	int ret;
+
+	/*
+	 * For preventing I2C operation when it's not powered and prepared for
+	 * the I2C operation.
+	 */
+	if (!is_powered(info) ||
+			i2c_r8_system(sd, CAT0_INT_FACTOR, &info->interrupt))
+		return;
+
+	switch (info->interrupt & REG_INT_MASK) {
+	case REG_INT_AF:
+		if (info->ver.af) {
+			ret = i2c_r8_lens(sd, CATA_AF_STATUS, &reg);
+			dev_dbg(&client->dev, "= AF %s\n",
+				reg == REG_AF_FAIL ? "Failed" :
+				reg == REG_AF_SUCCESS ? "Success" :
+				reg == REG_AF_IDLE ? "Idle" : "Busy");
+		}
+		break;
+	case REG_INT_CAPTURE:
+		if (!is_captured(info)) {
+			wake_up_interruptible(&info->wait_capture);
+			info->capture = true;
+		}
+		dev_dbg(&client->dev, "= CAPTURE\n");
+		break;
+	default:
+		dev_dbg(&client->dev, "= Nothing : %02x\n", reg);
+		break;
+	};
+}
+
+static irqreturn_t m5mols_irq_handler(int irq, void *data)
+{
+	struct v4l2_subdev *sd = data;
+	struct m5mols_info *info = to_m5mols(sd);
+
+	schedule_work(&info->work_irq);
+
+	return IRQ_HANDLED;
+}
+
+static int m5mols_probe(struct i2c_client *client,
+			 const struct i2c_device_id *id)
+{
+	const struct m5mols_platform_data *pdata =
+		client->dev.platform_data;
+	struct m5mols_info *info;
+	struct v4l2_subdev *sd;
+	int ret;
+
+	if (pdata == NULL) {
+		dev_err(&client->dev, "No platform data\n");
+		return -EINVAL;
+	}
+
+	if (!gpio_is_valid(pdata->gpio_reset)) {
+		dev_err(&client->dev, "No valid RESET GPIO specified\n");
+		return -EINVAL;
+	}
+
+	if (!pdata->irq) {
+		dev_err(&client->dev, "Interrupt not assigned\n");
+		return -EINVAL;
+	}
+
+	info = kzalloc(sizeof(struct m5mols_info), GFP_KERNEL);
+	if (info == NULL) {
+		dev_err(&client->dev, "Failed to allocate info\n");
+		return -ENOMEM;
+	}
+
+	info->pdata	= pdata;
+	info->set_power	= pdata->set_power;
+
+	ret = gpio_request(pdata->gpio_reset, "M5MOLS_NRST");
+	if (ret) {
+		dev_err(&client->dev, "Failed to request gpio: %d\n", ret);
+		goto out_free;
+	}
+
+	gpio_direction_output(pdata->gpio_reset, pdata->reset_polarity);
+
+	ret = regulator_bulk_get(&client->dev, ARRAY_SIZE(supplies), supplies);
+	if (ret) {
+		dev_err(&client->dev, "Failed to get regulators: %d\n", ret);
+		goto out_gpio;
+	}
+
+	sd = &info->sd;
+	strlcpy(sd->name, MOD_NAME, sizeof(sd->name));
+	v4l2_i2c_subdev_init(sd, client, &m5mols_ops);
+	/* sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; */
+
+	info->pad.flags = MEDIA_PAD_FL_SOURCE;
+	ret = media_entity_init(&sd->entity, 1, &info->pad, 0);
+	if (ret < 0)
+		goto out_gpio;
+	sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR;
+
+	init_waitqueue_head(&info->wait_capture);
+	INIT_WORK(&info->work_irq, m5mols_irq_work);
+	ret = request_irq(pdata->irq, m5mols_irq_handler,
+			  IRQF_TRIGGER_RISING, MOD_NAME, sd);
+	if (ret) {
+		dev_err(&client->dev, "Failed to request irq: %d\n", ret);
+		goto out_reg;
+	}
+	info->curr_res_type = M5MOLS_RESTYPE_MONITOR;
+
+	v4l2_dbg(1, m5mols_debug, sd, "Probed m5mols driver.\n");
+
+	return 0;
+
+out_reg:
+	regulator_bulk_free(ARRAY_SIZE(supplies), supplies);
+out_gpio:
+	gpio_free(pdata->gpio_reset);
+out_free:
+	kfree(info);
+	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);
+	v4l2_ctrl_handler_free(&info->handle);
+	free_irq(info->pdata->irq, sd);
+
+	regulator_bulk_free(ARRAY_SIZE(supplies), supplies);
+	gpio_free(info->pdata->gpio_reset);
+	kfree(info);
+
+	v4l2_dbg(1, m5mols_debug, sd, "Removed m5mols driver\n");
+
+	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@samsung.com>");
+MODULE_AUTHOR("Dongsoo Kim <dongsoo45.kim@samsung.com>");
+MODULE_DESCRIPTION("Fujitsu M-5MOLS 8M Pixel camera driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/m5mols/m5mols_reg.h b/drivers/media/video/m5mols/m5mols_reg.h
new file mode 100644
index 0000000..f07b362
--- /dev/null
+++ b/drivers/media/video/m5mols/m5mols_reg.h
@@ -0,0 +1,321 @@
+/*
+ * Register map for M-5MOLS 8M Pixel camera sensor with ISP
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * Author: HeungJun Kim, riverful.kim@samsung.com
+ *
+ * Copyright (C) 2009 Samsung Electronics Co., Ltd.
+ * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com
+ *
+ * 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_REG_H
+#define M5MOLS_REG_H
+
+/*
+ * Category section register
+ *
+ * The category means set including relevant command of M-5MOLS.
+ */
+#define CAT_SYSTEM		0x00
+#define CAT_PARAM		0x01
+#define CAT_MONITOR		0x02
+#define CAT_AE			0x03
+#define CAT_WB			0x06
+#define CAT_EXIF		0x07
+#define CAT_FD			0x09
+#define CAT_LENS		0x0a
+#define CAT_CAPTURE_PARAMETER	0x0b
+#define CAT_CAPTURE_CONTROL	0x0c
+#define CAT_FLASH		0x0f	/* related with FW, Verions, booting */
+
+/*
+ * Category 0 - SYSTEM mode
+ *
+ * The SYSTEM mode in the M-5MOLS means area available to handle with the whole
+ * & all-round system of sensor. It deals with version/interrupt/setting mode &
+ * even sensor's status. Especially, the M-5MOLS sensor with ISP varys by
+ * packaging & manufactur vendors, even the customer user vendor. And the
+ * detailed function may have a difference. The version information can
+ * determine what method use in the driver.
+ *
+ * There is many registers between customer version address and awb one. For
+ * more specific contents, reference the m5mols.h.
+ */
+#define CAT0_CUSTOMER_CODE	0x00	/* customer version*/
+#define CAT0_VERSION_AWB	0x09	/* Auto WB version */
+#define CAT0_VERSION_STRING	0x0a	/* string including M-5MOLS */
+#define CAT0_SYSMODE		0x0b	/* SYSTEM mode register */
+#define CAT0_STATUS		0x0c	/* SYSTEM mode status register */
+#define CAT0_INT_FACTOR		0x10	/* interrupt pending register */
+#define CAT0_INT_ENABLE		0x11	/* interrupt enable register */
+
+#define REG_MODE_SYSINIT	0x00	/* SYSTEM mode */
+#define REG_MODE_PARAM		0x01	/* PARAMETER mode */
+#define REG_MODE_MONITOR	0x02	/* MONITOR mode */
+#define REG_MODE_CAPTURE	0x03	/* CAPTURE mode */
+
+#define REG_INT_MODE		(1 << 0)
+#define REG_INT_AF		(1 << 1)
+#define REG_INT_ZOOM		(1 << 2)
+#define REG_INT_CAPTURE		(1 << 3)
+#define REG_INT_FRAMESYNC	(1 << 4)
+#define REG_INT_FD		(1 << 5)
+#define REG_INT_LENS_INIT	(1 << 6)
+#define REG_INT_SOUND		(1 << 7)
+#define REG_INT_MASK		0x0f
+
+/*
+ * category 1 - PARAMETER mode
+ *
+ * This category supports function of camera features of M-5MOLS. It means we
+ * can handle with preview(MONITOR) resolution size/frame per second/interface
+ * between the sensor and the Application Processor/even the image effect.
+ */
+#define CAT1_DATA_INTERFACE	0x00	/* interface between sensor and AP */
+#define CAT1_MONITOR_SIZE	0x01	/* resolution at the MONITOR mode */
+#define CAT1_MONITOR_FPS	0x02	/* frame per second at this mode */
+#define CAT1_EFFECT		0x0b	/* image effects */
+
+#define REG_INTERFACE_MIPI	0x02
+
+#define REG_FPS_30		0x02
+
+#define REG_EFFECT_OFF		0x00
+#define REG_EFFECT_NEGA		0x01
+#define REG_EFFECT_EMBOSS	0x06
+#define REG_EFFECT_OUTLINE	0x07
+#define REG_EFFECT_WATERCOLOR	0x08
+
+/*
+ * Category 2 - MONITOR mode
+ *
+ * The MONITOR mode is same as preview mode as we said. The M-5MOLS has another
+ * mode named "Preview", but this preview mode is used at the case specific
+ * vider-recording mode. This mmode supports only YUYV format. On the other
+ * hand, the JPEG & RAW formats is supports by CAPTURE mode. And, there are
+ * another options like zoom/color effect(different with effect in PARAMETER
+ * mode)/anti hand shaking algorithm.
+ */
+#define CAT2_ZOOM		0x01	/* set the zoom position & execute */
+#define CAT2_ZOOM_STEP		0x03	/* set the zoom step */
+#define CAT2_CFIXB		0x09	/* CB value for color effect */
+#define CAT2_CFIXR		0x0a	/* CR value for color effect */
+#define CAT2_COLOR_EFFECT	0x0b	/* set on/off of color effect */
+#define CAT2_CHROMA_LVL		0x0f	/* set chroma level */
+#define CAT2_CHROMA_EN		0x10	/* set on/off of choroma */
+#define CAT2_EDGE_LVL		0x11	/* set sharpness level */
+#define CAT2_EDGE_EN		0x12	/* set on/off sharpness */
+#define CAT2_TONE_CTL		0x25	/* set tone color(contrast) */
+
+#define REG_CFIXB_SEPIA		0xd8
+#define REG_CFIXR_SEPIA		0x18
+
+#define REG_COLOR_EFFECT_OFF	0x00
+#define REG_COLOR_EFFECT_ON	0x01
+
+#define REG_CHROMA_OFF		0x00
+#define REG_CHROMA_ON		0x01
+
+#define REG_EDGE_OFF		0x00
+#define REG_EDGE_ON		0x01
+
+/*
+ * Category 3 - Auto Exposure
+ *
+ * The M-5MOLS exposure capbility is detailed as which is similar to digital
+ * camera. This category supports AE locking/various AE mode(range of exposure)
+ * /ISO/flickering/EV bias/shutter/meteoring, and anything else. And the
+ * maximum/minimum exposure gain value depending on M-5MOLS firmware, may be
+ * different. So, this category also provide getting the max/min values. And,
+ * each MONITOR and CAPTURE mode has each gain/shutter/max exposure values.
+ */
+#define CAT3_AE_LOCK		0x00	/* locking Auto exposure */
+#define CAT3_AE_MODE		0x01	/* set AE mode, mode means range */
+#define CAT3_ISO		0x05	/* set ISO */
+#define CAT3_EV_PRESET_MODE_MON	0x0a	/* EV(scenemode) preset for MONITOR */
+#define CAT3_EV_PRESET_MODE_CAP	0x0b	/* EV(scenemode) preset for CAPTURE */
+#define CAT3_MANUAL_GAIN_MON	0x12	/* meteoring value for the MONITOR */
+#define CAT3_MAX_GAIN_MON	0x1a	/* max gain value for the MONITOR */
+#define CAT3_MANUAL_GAIN_CAP	0x26	/* meteoring value for the CAPTURE */
+#define CAT3_AE_INDEX		0x38	/* AE index */
+
+#define REG_AE_UNLOCK		0x00
+#define REG_AE_LOCK		0x01
+
+#define REG_AE_MODE_OFF		0x00	/* AE off */
+#define REG_AE_MODE_ALL		0x01	/* calc AE in all block integral */
+#define REG_AE_MODE_CENTER	0x03	/* calc AE in center weighted */
+#define REG_AE_MODE_SPOT	0x06	/* calc AE in specific spot */
+
+#define REG_ISO_AUTO		0x00
+#define REG_ISO_50		0x0
+#define REG_ISO_100		0x02
+#define REG_ISO_200		0x03
+#define REG_ISO_400		0x04
+#define REG_ISO_800		0x05
+
+#define REG_SCENE_NORMAL	0x00
+#define REG_SCENE_PORTRAIT	0x01
+#define REG_SCENE_LANDSCAPE	0x02
+#define REG_SCENE_SPORTS	0x03
+#define REG_SCENE_PARTY_INDOOR	0x04
+#define REG_SCENE_BEACH_SNOW	0x05
+#define REG_SCENE_SUNSET	0x06
+#define REG_SCENE_DAWN_DUSK	0x07
+#define REG_SCENE_FALL		0x08
+#define REG_SCENE_NIGHT		0x09
+#define REG_SCENE_AGAINST_LIGHT	0x0a
+#define REG_SCENE_FIRE		0x0b
+#define REG_SCENE_TEXT		0x0c
+#define REG_SCENE_CANDLE	0x0d
+#define REG_SCENE_NONE		0xff
+
+#define REG_AE_INDEX_20_NEG	0x00
+#define REG_AE_INDEX_15_NEG	0x01
+#define REG_AE_INDEX_10_NEG	0x02
+#define REG_AE_INDEX_05_NEG	0x03
+#define REG_AE_INDEX_00		0x04
+#define REG_AE_INDEX_05_POS	0x05
+#define REG_AE_INDEX_10_POS	0x06
+#define REG_AE_INDEX_15_POS	0x07
+#define REG_AE_INDEX_20_POS	0x08
+
+/*
+ * Category 6 - White Balance
+ *
+ * This cagetory provide AWB locking/mode/preset/speed/gain bias, etc.
+ */
+#define CAT6_AWB_LOCK		0x00	/* locking Auto Whitebalance */
+#define CAT6_AWB_MODE		0x02	/* set Auto or Manual */
+#define CAT6_AWB_MANUAL		0x03	/* set Manual(preset) value */
+
+#define REG_AWB_UNLOCK		0x00
+#define REG_AWB_LOCK		0x01
+
+#define REG_AWB_MODE_AUTO	0x01	/* AWB off */
+#define REG_AWB_MODE_PRESET	0x02	/* AWB preset */
+
+#define REG_AWB_INCANDESCENT	0x01
+#define REG_AWB_FLUORESCENT_1	0x02
+#define REG_AWB_FLUORESCENT_2	0x03
+#define REG_AWB_DAYLIGHT	0x04
+#define REG_AWB_CLOUDY		0x05
+#define REG_AWB_SHADE		0x06
+#define REG_AWB_HORIZON		0x07
+#define REG_AWB_LEDLIGHT	0x09
+
+/*
+ * Category 7 - EXIF information
+ */
+#define CAT7_INFO_EXPTIME_NU	0x00
+#define CAT7_INFO_EXPTIME_DE	0x04
+#define CAT7_INFO_TV_NU		0x08
+#define CAT7_INFO_TV_DE		0x0c
+#define CAT7_INFO_AV_NU		0x10
+#define CAT7_INFO_AV_DE		0x14
+#define CAT7_INFO_BV_NU		0x18
+#define CAT7_INFO_BV_DE		0x1c
+#define CAT7_INFO_EBV_NU	0x20
+#define CAT7_INFO_EBV_DE	0x24
+#define CAT7_INFO_ISO		0x28
+#define CAT7_INFO_FLASH		0x2a
+#define CAT7_INFO_SDR		0x2c
+#define CAT7_INFO_QVAL		0x2e
+
+/*
+ * Category 9 - Face Detection
+ */
+#define CAT9_FD_CTL		0x00
+
+#define BIT_FD_EN		0
+#define BIT_FD_DRAW_FACE_FRAME	4
+#define BIT_FD_DRAW_SMILE_LVL	6
+#define REG_FD(shift)		(1 << shift)
+#define REG_FD_OFF		0x0
+
+/*
+ * Category A - Lens Parameter
+ */
+#define CATA_AF_MODE		0x01
+#define CATA_AF_EXECUTE		0x02
+#define CATA_AF_STATUS		0x03
+#define CATA_AF_VERSION		0x0a
+
+#define REG_AF_FAIL		0x00
+#define REG_AF_SUCCESS		0x02
+#define REG_AF_IDLE		0x04
+#define REG_AF_BUSY		0x05
+
+#define REG_AF_MODE_NORMAL	0x00	/* Normal AF, one time */
+#define REG_AF_MODE_MACRO	0x01	/* Macro AF, one time */
+#define REG_AF_MODE_POWEROFF	0x07
+
+#define REG_AF_STOP		0x00
+#define REG_AF_EXE_AUTO		0x01
+#define REG_AF_EXE_CAF		0x02
+
+/*
+ * Category B - CAPTURE Parameter
+ */
+#define CATB_YUVOUT_MAIN	0x00
+#define CATB_MAIN_IMAGE_SIZE	0x01
+#define CATB_MCC_MODE		0x1d
+#define CATB_WDR_EN		0x2c
+#define CATB_LIGHT_CTRL		0x40
+#define CATB_FLASH_CTRL		0x41
+
+/* n = 0..3 */
+#define CATB_JPEG_SIZE_MAX(n)	(0x12 - (n))
+#define CATB_JPEG_SIZE_MIN(n)	(0x16 - (n))
+
+#define REG_YUV422		0x00
+#define REG_BAYER10		0x05
+#define REG_BAYER8		0x06
+#define REG_JPEG		0x10
+
+#define REG_MCC_OFF		0x00
+#define REG_MCC_NORMAL		0x01
+
+#define REG_WDR_OFF		0x00
+#define REG_WDR_ON		0x01
+#define REG_WDR_AUTO		0x02
+
+#define REG_LIGHT_OFF		0x00
+#define REG_LIGHT_ON		0x01
+#define REG_LIGHT_AUTO		0x02
+
+#define REG_FLASH_OFF		0x00
+#define REG_FLASH_ON		0x01
+#define REG_FLASH_AUTO		0x02
+
+/*
+ * Category C - CAPTURE Control
+ */
+#define CATC_CAP_MODE		0x00
+#define CATC_CAP_SEL_FRAME	0x06	/* It determines Single or Multi. */
+#define CATC_CAP_START		0x09
+#define CATC_CAP_IMAGE_SIZE	0x0d
+#define CATC_CAP_THUMB_SIZE	0x11
+
+#define REG_CAP_MODE_NONE	0x00
+#define REG_CAP_MODE_ANTI_SHAKE	0x02
+
+#define REG_CAP_START_MAIN	0x01
+#define REG_CAP_START_THUMB	0x03
+
+/*
+ * Category F - Flash
+ *
+ * This mode provides functions about internal flash stuff and system startup.
+ */
+#define CATF_CAM_START		0x12	/* It start internal ARM core booting
+					 * after power-up */
+
+#define REG_START_ARM_BOOT	0x01
+
+#endif	/* M5MOLS_REG_H */
diff --git a/include/media/m5mols.h b/include/media/m5mols.h
new file mode 100644
index 0000000..2d7e7ca
--- /dev/null
+++ b/include/media/m5mols.h
@@ -0,0 +1,35 @@
+/*
+ * Driver header for M-5MOLS 8M Pixel camera sensor with ISP
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * Author: HeungJun Kim, riverful.kim@samsung.com
+ *
+ * Copyright (C) 2009 Samsung Electronics Co., Ltd.
+ * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com
+ *
+ * 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 MEDIA_M5MOLS_H
+#define MEDIA_M5MOLS_H
+
+/**
+ * struct m5mols_platform_data - platform data for M-5MOLS driver
+ * @irq:	GPIO getting the irq pin of M-5MOLS
+ * @gpio_reset:	GPIO driving the reset pin of M-5MOLS
+ * @reset_polarity: active state for gpio_rst pin, 0 or 1
+ * @set_power:	an additional callback to the board setup code
+ *		to be called after enabling and before disabling
+ *		the sensor's supply regulators
+ */
+struct m5mols_platform_data {
+	int irq;
+	int gpio_reset;
+	u8 reset_polarity;
+	int (*set_power)(struct device *dev, int on);
+};
+
+#endif	/* MEDIA_M5MOLS_H */
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH v7] Add support for M-5MOLS 8 Mega Pixel camera ISP
  2011-05-13 10:06 ` [PATCH v7] Add support for M-5MOLS 8 Mega Pixel camera ISP HeungJun, Kim
@ 2011-05-13 11:38   ` HeungJun, Kim
  2011-05-16  1:03     ` [PATCH v8] " HeungJun, Kim
  0 siblings, 1 reply; 34+ messages in thread
From: HeungJun, Kim @ 2011-05-13 11:38 UTC (permalink / raw)
  To: linux-media
  Cc: hverkuil, laurent.pinchart, kyungmin.park, HeungJun, Kim,
	Sylwester Nawrocki

Add I2C/V4L2 subdev driver for M-5MOLS integrated image signal processor
with 8 Mega Pixel sensor.

Signed-off-by: HeungJun, Kim <riverful.kim@samsung.com>
Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---

Hello,

I've just got some misses in this patch, so I re-send same version of the
patch, just fixed misses. I hope to discard the previous email I sent and
let the patch in this email be reviewed, please.

Sorry to confuse and thanks!

===============================================================================

Hello,

This is the seventh version of the subdev for M-5MOLS 8M Pixel camera sensor.
This version has a lot of changes againt previous one. The major change is
that it supports to capture with JPEG format generating in itself.

The capture function and all the other operations are tested on the Universal
Board with EXYNOS4 SoCs using s5p-fimc driver and the NURI board, but it needed
for enabling the capture capability to support V4L2_MBUS_FMT_JPEG_1X8 media code.
The relevant patch can be found here:
http://www.spinics.net/lists/linux-media/msg32231.html

In this patch, the big changes except for capture are here:

1. Convert previous format operation to media bus operation
2. Add scenemode function for setting default scenemode
3. Enable to change the mode more stably
4. Add some variation according to M-5MOLS FW versions and manufacturers

And the full thread about the previous version of M-5MOLS driver can be found
here: http://www.spinics.net/lists/linux-media/msg29350.html

Any comments and reviews are welcome!

--
Regards,
Heungjun Kim
Samsung Electronics DMC R&D Center
---
 drivers/media/video/Kconfig                  |    2 +
 drivers/media/video/Makefile                 |    1 +
 drivers/media/video/m5mols/Kconfig           |    5 +
 drivers/media/video/m5mols/Makefile          |    3 +
 drivers/media/video/m5mols/m5mols.h          |  357 +++++++++
 drivers/media/video/m5mols/m5mols_capture.c  |  217 ++++++
 drivers/media/video/m5mols/m5mols_controls.c |  293 ++++++++
 drivers/media/video/m5mols/m5mols_core.c     | 1034 ++++++++++++++++++++++++++
 drivers/media/video/m5mols/m5mols_reg.h      |  321 ++++++++
 include/media/m5mols.h                       |   35 +
 10 files changed, 2268 insertions(+), 0 deletions(-)
 create mode 100644 drivers/media/video/m5mols/Kconfig
 create mode 100644 drivers/media/video/m5mols/Makefile
 create mode 100644 drivers/media/video/m5mols/m5mols.h
 create mode 100644 drivers/media/video/m5mols/m5mols_capture.c
 create mode 100644 drivers/media/video/m5mols/m5mols_controls.c
 create mode 100644 drivers/media/video/m5mols/m5mols_core.c
 create mode 100644 drivers/media/video/m5mols/m5mols_reg.h
 create mode 100644 include/media/m5mols.h

diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index d61414e..242c80c 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -753,6 +753,8 @@ config VIDEO_NOON010PC30
 	---help---
 	  This driver supports NOON010PC30 CIF camera from Siliconfile
 
+source "drivers/media/video/m5mols/Kconfig"
+
 config VIDEO_OMAP3
 	tristate "OMAP 3 Camera support (EXPERIMENTAL)"
 	select OMAP_IOMMU
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index a10e4c3..d5d6de1 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -69,6 +69,7 @@ obj-$(CONFIG_VIDEO_MT9V011) += mt9v011.o
 obj-$(CONFIG_VIDEO_MT9V032) += mt9v032.o
 obj-$(CONFIG_VIDEO_SR030PC30)	+= sr030pc30.o
 obj-$(CONFIG_VIDEO_NOON010PC30)	+= noon010pc30.o
+obj-$(CONFIG_VIDEO_M5MOLS)	+= m5mols/
 
 obj-$(CONFIG_SOC_CAMERA_IMX074)		+= imx074.o
 obj-$(CONFIG_SOC_CAMERA_MT9M001)	+= mt9m001.o
diff --git a/drivers/media/video/m5mols/Kconfig b/drivers/media/video/m5mols/Kconfig
new file mode 100644
index 0000000..302dc3d
--- /dev/null
+++ b/drivers/media/video/m5mols/Kconfig
@@ -0,0 +1,5 @@
+config VIDEO_M5MOLS
+	tristate "Fujitsu M-5MOLS 8MP sensor support"
+	depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+	---help---
+	  This driver supports Fujitsu M-5MOLS camera sensor with ISP
diff --git a/drivers/media/video/m5mols/Makefile b/drivers/media/video/m5mols/Makefile
new file mode 100644
index 0000000..0a44e02
--- /dev/null
+++ b/drivers/media/video/m5mols/Makefile
@@ -0,0 +1,3 @@
+m5mols-objs	:= m5mols_core.o m5mols_controls.o m5mols_capture.o
+
+obj-$(CONFIG_VIDEO_M5MOLS)		+= m5mols.o
diff --git a/drivers/media/video/m5mols/m5mols.h b/drivers/media/video/m5mols/m5mols.h
new file mode 100644
index 0000000..ba1890e
--- /dev/null
+++ b/drivers/media/video/m5mols/m5mols.h
@@ -0,0 +1,357 @@
+/*
+ * Header for M-5MOLS 8M Pixel camera sensor with ISP
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * Author: HeungJun Kim, riverful.kim@samsung.com
+ *
+ * Copyright (C) 2009 Samsung Electronics Co., Ltd.
+ * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com
+ *
+ * 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-subdev.h>
+#include "m5mols_reg.h"
+
+extern int m5mols_debug;
+
+#define M5MOLS_BYTE_READ	0x01
+#define M5MOLS_BYTE_WRITE	0x02
+
+enum m5mols_i2c_size {
+	I2C_8BIT	= 1,
+	I2C_16BIT	= 2,
+	I2C_32BIT	= 4,
+	I2C_MAX		= I2C_32BIT,
+};
+
+/*
+ * In the MONITOR mode, the M-5MOLS sensor's format is YUV kinds, on the other
+ * hand, in the CAPTURE mode, its format is available to JPEG, RAW.
+ */
+enum m5mols_restype {
+	M5MOLS_RESTYPE_MONITOR,
+	M5MOLS_RESTYPE_CAPTURE,
+	M5MOLS_RESTYPE_MAX,
+};
+
+struct m5mols_resolution {
+	u8			reg;
+	enum m5mols_restype	type;
+	u16			width;
+	u16			height;
+};
+
+struct m5mols_exif {
+	u32			exposure_time;
+	u32			shutter_speed;
+	u32			aperture;
+	u32			brightness;
+	u32			exposure_bias;
+	u16			iso_speed;
+	u16			flash;
+	u16			sdr;	/* Subject(object) Distance Range */
+	u16			qval;	/* Not written exact meanning
+					 * in datasheet */
+};
+
+struct m5mols_capture {
+	struct m5mols_exif	exif;
+	u32			main;
+	u32			thumb;
+	u32			total;
+};
+
+struct m5mols_scenemode {	/* Recommended setting in document */
+	u32			metering;	/* AE light metering */
+	u32			ev_bias;	/* EV bias */
+	u32			wb_mode;	/* WhiteBalance(Auto/Manual) */
+	u32			wb_preset;	/* WhiteBalance Preset */
+	u32			chroma_en;	/* Chroma Enable */
+	u32			chroma_lvl;	/* Chroma Level */
+	u32			edge_en;	/* Edge Enable */
+	u32			edge_lvl;	/* Edge Level */
+	u32			af_range;	/* Auto Focus scan range */
+	u32			fd_mode;	/* Face Detection mode */
+	u32			mcc;		/* Multi-axis Color Conversion:
+						 * (A.K.A Emotion color) */
+	u32			light;		/* Light control */
+	u32			flash;		/* Flash control */
+
+				/* User setting needed for */
+	u32			tone;		/* Tone color(contrast) */
+	u32			iso;		/* ISO */
+	u32			capture_mode;	/* CAPTURE mode for
+						 * the Image stabilization */
+	u32			wdr;		/* Wide Dynamic Range */
+};
+
+#define VERSION_STRING_SIZE	22
+struct m5mols_version {
+	u8	customer;	/* Customer code:	bytes[0] */
+	u8	project;	/* Project code:	bytes[1] */
+	u16	fw;		/* FirmWare version:	bytes[3][2] */
+	u16	hw;		/* HardWare version:	bytes[5][4] */
+	u16	param;		/* Parameter version:	bytes[7][6] */
+	u16	awb;		/* AWB version:		bytes[9][8] */
+	u8	str[VERSION_STRING_SIZE];		/* manufacturer &
+							 * packging vendor */
+	u8	af;		/* AF version:		seperate register */
+};
+#define VERSION_SIZE		sizeof(struct m5mols_version)
+
+/* The LSB 2 bytes of version string means packaging manufacturer */
+#define SAMSUNG_ELECTRO		"SE"	/* Samsung Electro-Mechanics */
+#define SAMSUNG_OPTICS		"OP"	/* Samsung Fiber-Optics */
+#define SAMSUNG_TECHWIN		"TB"	/* Samsung Techwin */
+
+struct m5mols_info {
+	const struct m5mols_platform_data	*pdata;
+	struct v4l2_subdev		sd;
+	struct media_pad		pad;
+	struct v4l2_mbus_framefmt	ffmt[M5MOLS_RESTYPE_MAX];
+	int				curr_res_type;
+	enum v4l2_mbus_pixelcode	code;
+	wait_queue_head_t		wait_capture;
+	struct work_struct		work_irq;
+
+	struct v4l2_ctrl_handler	handle;
+	struct {
+		struct v4l2_ctrl	*autoexposure;
+		struct v4l2_ctrl	*exposure;
+	};
+	struct v4l2_ctrl		*autowb;
+	struct v4l2_ctrl		*colorfx;
+	struct v4l2_ctrl		*saturation;
+	struct v4l2_ctrl		*zoom;
+
+	struct m5mols_version		ver;
+	struct m5mols_capture		cap;
+
+	bool				power;
+	bool				ctrl_sync;
+	bool				capture;
+	bool				lock_ae;
+	bool				lock_awb;
+
+	/* Saved register value */
+	u8				resolution;
+	u32				interrupt;
+	u32				mode;
+	u32				mode_save;
+
+	/* Optional power callback function dealing with like GPIO. */
+	int (*set_power)(struct device *dev, int on);
+};
+
+/* Helper functions */
+static inline struct m5mols_info *to_m5mols(struct v4l2_subdev *sd)
+{
+	return container_of(sd, struct m5mols_info, sd);
+}
+
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+	return &container_of(ctrl->handler, struct m5mols_info, handle)->sd;
+}
+
+static inline bool is_powered(struct m5mols_info *info)
+{
+	return info->power;
+}
+
+static inline bool is_ctrl_synced(struct m5mols_info *info)
+{
+	return info->ctrl_sync;
+}
+
+static inline bool is_captured(struct m5mols_info *info)
+{
+	return info->capture;
+}
+
+static inline bool is_manufacturer(struct m5mols_info *info, u8 *manufacturer)
+{
+	return (info->ver.str[0] == manufacturer[0] &&
+			info->ver.str[1] == manufacturer[1]) ?
+		true : false;
+}
+
+#define is_code(__code, __type) (__code == m5mols_default_ffmt[__type].code)
+
+/*
+ * m5mols_read_reg() / m5mols_write_reg()
+ *
+ * For reading something from M-5MOLS sensor, the I2C operation is needed 2
+ * packets, one is for requesting reading and another is for getting data.
+ * On the other hand, for writing, it's needed just 1. The value's
+ * order in the packet is much similar in both case reading and writing.
+ *
+ * I2C packet common order
+ *   1st : size (data size + 4)
+ *   2nd : flag (R:0x01, W:0x02)
+ *   3rd : category
+ *   4th : command
+ *
+ * Added extra bytes in case reading
+ *   5th: data
+ *   And read another I2C bytes again as much data size
+ *
+ * Added extra bytes in case writing
+ *   5th..8th: data(as much size of data)
+ */
+int m5mols_read_reg(struct v4l2_subdev *sd, enum m5mols_i2c_size size,
+		    u8 category, u8 cmd, u32 *val);
+int m5mols_write_reg(struct v4l2_subdev *sd, enum m5mols_i2c_size size,
+		     u8 category, u8 cmd, u32 val);
+int m5mols_check_busy(struct v4l2_subdev *sd,
+		      u8 category, u8 cmd, u32 value);
+
+/*
+ * The m5mols_change_mode() makes the right order executing command for
+ * changing to the desired operating mode. The three modes(PARAMETER,
+ * MONITOR, CAPTURE) exist, and only these modes can be changed intentionally
+ * by user. The each category is assigned in the given mode.
+ * +============================================================+
+ * | mode	| category					|
+ * +============================================================+
+ * | These mode must be done for using the sensor automatically |
+ * +------------------------------------------------------------+
+ * | FLASH	| FLASH(be only after Stand-by or Power-on)	|
+ * | SYSTEM	| SYSTEM(be only after sensor arm-booting)	|
+ * +============================================================+
+ * | Usually these mode is used on operating sensor		|
+ * +------------------------------------------------------------+
+ * | PARAMETER	| PARAMETER					|
+ * | MONITOR	| MONITOR(preview), Auto Focus, Face Detection	|
+ * | CAPTURE	| Single CAPTURE, Preview(recording)		|
+ * +============================================================+
+ *
+ * The possible executing order between each modes is the following:
+ * ============================================================
+ *   +-----------+         +---------+          +---------+
+ *   | PARAMETER |<------->| MONITOR |<-------->| CAPTURE |
+ *   +-----------+         +---------+          +---------+
+ */
+int m5mols_change_mode(struct m5mols_info *info, u32 mode);
+
+int m5mols_change_scenemode(struct m5mols_info *info, u32 mode);
+int m5mols_enable_interrupt(struct v4l2_subdev *sd, u32 reg);
+int m5mols_start_capture(struct m5mols_info *info);
+int m5mols_sync_control(struct m5mols_info *info);
+int m5mols_lock_3a(struct m5mols_info *info, bool lock);
+int m5mols_set_ctrl(struct v4l2_ctrl *ctrl);
+
+static inline int __must_check
+i2c_w8_system(struct v4l2_subdev *sd, u8 cmd, u32 val)
+{
+	return m5mols_write_reg(sd, I2C_8BIT, CAT_SYSTEM, cmd, val);
+}
+
+static inline int __must_check
+i2c_w8_param(struct v4l2_subdev *sd, u8 cmd, u32 val)
+{
+	return m5mols_write_reg(sd, I2C_8BIT, CAT_PARAM, cmd, val);
+}
+
+static inline int __must_check
+i2c_w8_mon(struct v4l2_subdev *sd, u8 cmd, u32 val)
+{
+	return m5mols_write_reg(sd, I2C_8BIT, CAT_MONITOR, cmd, val);
+}
+
+static inline int __must_check
+i2c_w8_ae(struct v4l2_subdev *sd, u8 cmd, u32 val)
+{
+	return m5mols_write_reg(sd, I2C_8BIT, CAT_AE, cmd, val);
+}
+
+static inline int __must_check
+i2c_w16_ae(struct v4l2_subdev *sd, u8 cmd, u32 val)
+{
+	return m5mols_write_reg(sd, I2C_16BIT, CAT_AE, cmd, val);
+}
+
+static inline int __must_check
+i2c_w8_wb(struct v4l2_subdev *sd, u8 cmd, u32 val)
+{
+	return m5mols_write_reg(sd, I2C_8BIT, CAT_WB, cmd, val);
+}
+
+static inline int __must_check
+i2c_w8_fd(struct v4l2_subdev *sd, u8 cmd, u32 val)
+{
+	return m5mols_write_reg(sd, I2C_8BIT, CAT_FD, cmd, val);
+}
+
+static inline int __must_check
+i2c_w8_lens(struct v4l2_subdev *sd, u8 cmd, u32 val)
+{
+	return m5mols_write_reg(sd, I2C_8BIT, CAT_LENS, cmd, val);
+}
+
+static inline int __must_check
+i2c_w8_capt_param(struct v4l2_subdev *sd, u8 cmd, u32 val)
+{
+	return m5mols_write_reg(sd, I2C_8BIT, CAT_CAPTURE_PARAMETER, cmd, val);
+}
+
+static inline int __must_check
+i2c_w8_capt_ctrl(struct v4l2_subdev *sd, u8 cmd, u32 val)
+{
+	return m5mols_write_reg(sd, I2C_8BIT, CAT_CAPTURE_CONTROL, cmd, val);
+}
+
+static inline int __must_check
+i2c_w8_flash(struct v4l2_subdev *sd, u8 cmd, u32 val)
+{
+	return m5mols_write_reg(sd, I2C_8BIT, CAT_FLASH, cmd, val);
+}
+
+static inline int __must_check
+i2c_r8_system(struct v4l2_subdev *sd, u8 cmd, u32 *val)
+{
+	return m5mols_read_reg(sd, I2C_8BIT, CAT_SYSTEM, cmd, val);
+}
+
+static inline int __must_check
+i2c_r16_ae(struct v4l2_subdev *sd, u8 cmd, u32 *val)
+{
+	return m5mols_read_reg(sd, I2C_16BIT, CAT_AE, cmd, val);
+}
+
+static inline int __must_check
+i2c_r8_lens(struct v4l2_subdev *sd, u8 cmd, u32 *val)
+{
+	return m5mols_read_reg(sd, I2C_8BIT, CAT_LENS, cmd, val);
+}
+
+static inline int __must_check
+i2c_r32_capt_ctrl(struct v4l2_subdev *sd, u8 cmd, u32 *val)
+{
+	return m5mols_read_reg(sd, I2C_32BIT, CAT_CAPTURE_CONTROL, cmd, val);
+}
+
+static inline int __must_check
+i2c_r16_exif(struct v4l2_subdev *sd, u8 cmd, u32 *val)
+{
+	return m5mols_read_reg(sd, I2C_16BIT, CAT_EXIF, cmd, val);
+}
+
+static inline int __must_check
+i2c_r32_exif(struct v4l2_subdev *sd, u8 cmd, u32 *val)
+{
+	return m5mols_read_reg(sd, I2C_32BIT, CAT_EXIF, cmd, val);
+}
+
+/* The firmware function */
+int m5mols_update_fw(struct v4l2_subdev *sd,
+		     int (*set_power)(struct m5mols_info *, bool));
+
+#endif	/* M5MOLS_H */
diff --git a/drivers/media/video/m5mols/m5mols_capture.c b/drivers/media/video/m5mols/m5mols_capture.c
new file mode 100644
index 0000000..32da9b1
--- /dev/null
+++ b/drivers/media/video/m5mols/m5mols_capture.c
@@ -0,0 +1,217 @@
+/*
+ * The Capture code for Fujitsu M-5MOLS ISP
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * Author: HeungJun Kim, riverful.kim@samsung.com
+ *
+ * Copyright (C) 2009 Samsung Electronics Co., Ltd.
+ * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com
+ *
+ * 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/slab.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/version.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/videodev2.h>
+#include <linux/version.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+#include <media/m5mols.h>
+
+#include "m5mols.h"
+#include "m5mols_reg.h"
+
+static int m5mols_capture_error_handler(struct m5mols_info *info,
+					int timeout)
+{
+	int ret;
+
+	/* Disable all interrupt & clear desired interrupt */
+	ret = i2c_w8_system(&info->sd, CAT0_INT_ENABLE,
+			info->interrupt & ~(REG_INT_CAPTURE));
+	if (ret)
+		return ret;
+
+	/* If all timeout exhausted, return error. */
+	if (!timeout)
+		return -ETIMEDOUT;
+
+	/* Clear capture */
+	if (!ret)
+		info->capture = false;
+
+	return 0;
+}
+
+/* m5mols_capture_info() - Gather captured image informations. For now,
+ * it gathers only EXIF information and file size. */
+static int m5mols_capture_info(struct m5mols_info *info, bool msgon)
+{
+	struct v4l2_subdev *sd = &info->sd;
+	struct m5mols_exif *exif = &info->cap.exif;
+	int denominator, numerator;
+	int ret;
+
+	ret = i2c_r32_exif(sd, CAT7_INFO_EXPTIME_NU, &numerator);
+	if (!ret)
+		ret = i2c_r32_exif(sd, CAT7_INFO_EXPTIME_DE, &denominator);
+	if (!ret)
+		exif->exposure_time = (u32)(numerator / denominator);
+	if (ret)
+		return ret;
+
+	ret = i2c_r32_exif(sd, CAT7_INFO_TV_NU, &numerator);
+	if (!ret)
+		ret = i2c_r32_exif(sd, CAT7_INFO_TV_DE, &denominator);
+	if (!ret)
+		exif->shutter_speed = (u32)(numerator / denominator);
+	if (ret)
+		return ret;
+
+	ret = i2c_r32_exif(sd, CAT7_INFO_AV_NU, &numerator);
+	if (!ret)
+		ret = i2c_r32_exif(sd, CAT7_INFO_AV_DE, &denominator);
+	if (!ret)
+		exif->aperture = (u32)(numerator / denominator);
+	if (ret)
+		return ret;
+
+	ret = i2c_r32_exif(sd, CAT7_INFO_BV_NU, &numerator);
+	if (!ret)
+		ret = i2c_r32_exif(sd, CAT7_INFO_BV_DE, &denominator);
+	if (!ret)
+		exif->brightness = (u32)(numerator / denominator);
+	if (ret)
+		return ret;
+
+	ret = i2c_r32_exif(sd, CAT7_INFO_EBV_NU, &numerator);
+	if (!ret)
+		ret = i2c_r32_exif(sd, CAT7_INFO_EBV_DE, &denominator);
+	if (!ret)
+		exif->exposure_bias = (u32)(numerator / denominator);
+	if (ret)
+		return ret;
+
+	ret = i2c_r16_exif(sd, CAT7_INFO_ISO, (u32 *)&exif->iso_speed);
+	if (!ret)
+		ret = i2c_r16_exif(sd, CAT7_INFO_FLASH, (u32 *)&exif->flash);
+	if (!ret)
+		ret = i2c_r16_exif(sd, CAT7_INFO_SDR, (u32 *)&exif->sdr);
+	if (!ret)
+		ret = i2c_r16_exif(sd, CAT7_INFO_QVAL, (u32 *)&exif->qval);
+	if (ret)
+		return ret;
+
+	if (!ret)
+		ret = i2c_r32_capt_ctrl(sd, CATC_CAP_IMAGE_SIZE,
+				&info->cap.main);
+	if (!ret)
+		ret = i2c_r32_capt_ctrl(sd, CATC_CAP_THUMB_SIZE,
+				&info->cap.thumb);
+	if (ret)
+		return ret;
+
+	info->cap.total = info->cap.main + info->cap.thumb;
+
+	if (msgon) {
+		struct i2c_client *client = v4l2_get_subdevdata(sd);
+		struct device *cdev = &client->dev;
+
+		dev_info(cdev, "capture: total size\t%d\n", info->cap.total);
+		dev_info(cdev, "capture: main size\t%d\n", info->cap.main);
+		dev_info(cdev, "capture: thumb size\t%d\n", info->cap.thumb);
+		dev_info(cdev, "capture: exposure_time\t%d\n",
+				exif->exposure_time);
+		dev_info(cdev, "capture: shutter_speed\t%d\n",
+				exif->shutter_speed);
+		dev_info(cdev, "capture: aperture\t%d\n", exif->aperture);
+		dev_info(cdev, "capture: brightness\t%d\n", exif->brightness);
+		dev_info(cdev, "capture: exposure_bias\t%d\n",
+				exif->exposure_bias);
+		dev_info(cdev, "capture: iso_speed\t%d\n", exif->iso_speed);
+		dev_info(cdev, "capture: flash\t%d\n", exif->flash);
+		dev_info(cdev, "capture: sdr\t%d\n", exif->sdr);
+		dev_info(cdev, "capture: qval\t%d\n", exif->qval);
+	}
+
+	return ret;
+}
+
+int m5mols_start_capture(struct m5mols_info *info)
+{
+	struct v4l2_subdev *sd = &info->sd;
+	u32 resolution = info->resolution;
+	int timeout;
+	int ret;
+
+	/*
+	 * Preparing capture. Setting control & interrupt before entering
+	 * capture mode
+	 *
+	 * 1) change to MONITOR mode for operating control & interrupt
+	 * 2) set controls (considering v4l2_control value & lock 3A)
+	 * 3) set interrupt
+	 * 4) change to CAPTURE mode
+	 */
+	ret = m5mols_change_mode(info, REG_MODE_MONITOR);
+	if (!ret)
+		ret = m5mols_sync_control(info);
+	if (!ret)
+		ret = m5mols_lock_3a(info, true);
+	if (!ret)
+		ret = m5mols_enable_interrupt(sd, REG_INT_CAPTURE);
+	if (!ret)
+		ret = m5mols_change_mode(info, REG_MODE_CAPTURE);
+	if (!ret)
+		/* Wait for capture interrupt, after changing capture mode */
+		timeout = wait_event_interruptible_timeout(info->wait_capture,
+				is_captured(info), msecs_to_jiffies(2000));
+	if (!ret && is_captured(info))
+		ret = m5mols_capture_error_handler(info, timeout);
+	if (!ret)
+		ret = m5mols_lock_3a(info, false);
+	if (ret)
+		return ret;
+
+	/*
+	 * Starting capture. Setting capture frame count and resolution and
+	 * the format(available format: JPEG, Bayer RAW, YUV).
+	 *
+	 * 1) select single or multi(enable to 25), format, size
+	 * 2) set interrupt
+	 * 3) start capture(for main image, now)
+	 * 4) get information
+	 * 5) notify file size to v4l2 device(e.g, to s5p-fimc v4l2 device)
+	 */
+	ret = i2c_w8_capt_ctrl(sd, CATC_CAP_SEL_FRAME, 1);
+	if (!ret)
+		ret = i2c_w8_capt_param(sd, CATB_YUVOUT_MAIN, REG_JPEG);
+	if (!ret)
+		ret = i2c_w8_capt_param(sd, CATB_MAIN_IMAGE_SIZE, resolution);
+	if (!ret)
+		ret = m5mols_enable_interrupt(sd, REG_INT_CAPTURE);
+	if (!ret)
+		ret = i2c_w8_capt_ctrl(sd, CATC_CAP_START, REG_CAP_START_MAIN);
+	if (!ret)
+		/* Wait for capture interrupt, after starting capture */
+		timeout = wait_event_interruptible_timeout(info->wait_capture,
+				is_captured(info), msecs_to_jiffies(2000));
+	if (!ret && is_captured(info))
+		ret = m5mols_capture_info(info, false);
+	if (!ret)
+		v4l2_subdev_notify(sd, 0, &info->cap.total);
+	if (!ret)
+		ret = m5mols_capture_error_handler(info, timeout);
+
+	return ret;
+}
diff --git a/drivers/media/video/m5mols/m5mols_controls.c b/drivers/media/video/m5mols/m5mols_controls.c
new file mode 100644
index 0000000..1eb909e
--- /dev/null
+++ b/drivers/media/video/m5mols/m5mols_controls.c
@@ -0,0 +1,293 @@
+/*
+ * Controls for M-5MOLS 8M Pixel camera sensor with ISP
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * Author: HeungJun Kim, riverful.kim@samsung.com
+ *
+ * Copyright (C) 2009 Samsung Electronics Co., Ltd.
+ * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com
+ *
+ * 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/videodev2.h>
+#include <media/v4l2-ctrls.h>
+
+#include "m5mols.h"
+#include "m5mols_reg.h"
+
+static struct m5mols_scenemode m5mols_default_scenemode[] = {
+	[REG_SCENE_NORMAL] = {
+		REG_AE_MODE_CENTER, REG_AE_INDEX_00, REG_AWB_MODE_AUTO, 0,
+		REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
+		REG_AF_MODE_NORMAL, REG_FD_OFF,
+		REG_MCC_NORMAL, REG_LIGHT_OFF, REG_FLASH_OFF,
+		5, REG_ISO_AUTO, REG_CAP_MODE_NONE, REG_WDR_OFF,
+	},
+	[REG_SCENE_PORTRAIT] = {
+		REG_AE_MODE_CENTER, REG_AE_INDEX_00, REG_AWB_MODE_AUTO, 0,
+		REG_CHROMA_ON, 3, REG_EDGE_ON, 4,
+		REG_AF_MODE_NORMAL, BIT_FD_EN | BIT_FD_DRAW_FACE_FRAME,
+		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+		6, REG_ISO_AUTO, REG_CAP_MODE_NONE, REG_WDR_OFF,
+	},
+	[REG_SCENE_LANDSCAPE] = {
+		REG_AE_MODE_ALL, REG_AE_INDEX_00, REG_AWB_MODE_AUTO, 0,
+		REG_CHROMA_ON, 4, REG_EDGE_ON, 6,
+		REG_AF_MODE_NORMAL, REG_FD_OFF,
+		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+		6, REG_ISO_AUTO, REG_CAP_MODE_NONE, REG_WDR_OFF,
+	},
+	[REG_SCENE_SPORTS] = {
+		REG_AE_MODE_CENTER, REG_AE_INDEX_00, REG_AWB_MODE_AUTO, 0,
+		REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
+		REG_AF_MODE_NORMAL, REG_FD_OFF,
+		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+		6, REG_ISO_AUTO, REG_CAP_MODE_NONE, REG_WDR_OFF,
+	},
+	[REG_SCENE_PARTY_INDOOR] = {
+		REG_AE_MODE_CENTER, REG_AE_INDEX_00, REG_AWB_MODE_AUTO, 0,
+		REG_CHROMA_ON, 4, REG_EDGE_ON, 5,
+		REG_AF_MODE_NORMAL, REG_FD_OFF,
+		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+		6, REG_ISO_200, REG_CAP_MODE_NONE, REG_WDR_OFF,
+	},
+	[REG_SCENE_BEACH_SNOW] = {
+		REG_AE_MODE_CENTER, REG_AE_INDEX_10_POS, REG_AWB_MODE_AUTO, 0,
+		REG_CHROMA_ON, 4, REG_EDGE_ON, 5,
+		REG_AF_MODE_NORMAL, REG_FD_OFF,
+		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+		6, REG_ISO_50, REG_CAP_MODE_NONE, REG_WDR_OFF,
+	},
+	[REG_SCENE_SUNSET] = {
+		REG_AE_MODE_CENTER, REG_AE_INDEX_00, REG_AWB_MODE_PRESET,
+		REG_AWB_DAYLIGHT,
+		REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
+		REG_AF_MODE_NORMAL, REG_FD_OFF,
+		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+		6, REG_ISO_AUTO, REG_CAP_MODE_NONE, REG_WDR_OFF,
+	},
+	[REG_SCENE_DAWN_DUSK] = {
+		REG_AE_MODE_CENTER, REG_AE_INDEX_00, REG_AWB_MODE_PRESET,
+		REG_AWB_FLUORESCENT_1,
+		REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
+		REG_AF_MODE_NORMAL, REG_FD_OFF,
+		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+		6, REG_ISO_AUTO, REG_CAP_MODE_NONE, REG_WDR_OFF,
+	},
+	[REG_SCENE_FALL] = {
+		REG_AE_MODE_CENTER, REG_AE_INDEX_00, REG_AWB_MODE_AUTO, 0,
+		REG_CHROMA_ON, 5, REG_EDGE_ON, 5,
+		REG_AF_MODE_NORMAL, REG_FD_OFF,
+		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+		6, REG_ISO_AUTO, REG_CAP_MODE_NONE, REG_WDR_OFF,
+	},
+	[REG_SCENE_NIGHT] = {
+		REG_AE_MODE_CENTER, REG_AE_INDEX_00, REG_AWB_MODE_AUTO, 0,
+		REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
+		REG_AF_MODE_NORMAL, REG_FD_OFF,
+		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+		6, REG_ISO_AUTO, REG_CAP_MODE_NONE, REG_WDR_OFF,
+	},
+	[REG_SCENE_AGAINST_LIGHT] = {
+		REG_AE_MODE_CENTER, REG_AE_INDEX_00, REG_AWB_MODE_AUTO, 0,
+		REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
+		REG_AF_MODE_NORMAL, REG_FD_OFF,
+		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+		6, REG_ISO_AUTO, REG_CAP_MODE_NONE, REG_WDR_OFF,
+	},
+	[REG_SCENE_FIRE] = {
+		REG_AE_MODE_CENTER, REG_AE_INDEX_00, REG_AWB_MODE_AUTO, 0,
+		REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
+		REG_AF_MODE_NORMAL, REG_FD_OFF,
+		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+		6, REG_ISO_50, REG_CAP_MODE_NONE, REG_WDR_OFF,
+	},
+	[REG_SCENE_TEXT] = {
+		REG_AE_MODE_CENTER, REG_AE_INDEX_00, REG_AWB_MODE_AUTO, 0,
+		REG_CHROMA_ON, 3, REG_EDGE_ON, 7,
+		REG_AF_MODE_MACRO, REG_FD_OFF,
+		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+		6, REG_ISO_AUTO, REG_CAP_MODE_ANTI_SHAKE, REG_WDR_ON,
+	},
+	[REG_SCENE_CANDLE] = {
+		REG_AE_MODE_CENTER, REG_AE_INDEX_00, REG_AWB_MODE_AUTO, 0,
+		REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
+		REG_AF_MODE_NORMAL, REG_FD_OFF,
+		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+		6, REG_ISO_AUTO, REG_CAP_MODE_NONE, REG_WDR_OFF,
+	},
+};
+
+/* m5mols_change_scenemode() - Change current scenemode */
+int m5mols_change_scenemode(struct m5mols_info *info, u32 mode)
+{
+	struct v4l2_subdev *sd = &info->sd;
+	struct m5mols_scenemode scenemode = m5mols_default_scenemode[mode];
+	int ret;
+
+	ret = m5mols_lock_3a(info, false);
+	if (!ret)
+		ret = i2c_w8_ae(sd, CAT3_EV_PRESET_MODE_MON, mode);
+	if (!ret)
+		ret = i2c_w8_ae(sd, CAT3_EV_PRESET_MODE_CAP, mode);
+	if (!ret)
+		ret = i2c_w8_ae(sd, CAT3_AE_MODE, scenemode.metering);
+	if (!ret)
+		ret = i2c_w8_ae(sd, CAT3_AE_INDEX, scenemode.ev_bias);
+	if (!ret)
+		ret = i2c_w8_wb(sd, CAT6_AWB_MODE, scenemode.wb_mode);
+	if (!ret)
+		ret = i2c_w8_wb(sd, CAT6_AWB_MANUAL, scenemode.wb_preset);
+	if (!ret)
+		ret = i2c_w8_mon(sd, CAT2_CHROMA_EN, scenemode.chroma_en);
+	if (!ret)
+		ret = i2c_w8_mon(sd, CAT2_CHROMA_LVL, scenemode.chroma_lvl);
+	if (!ret)
+		ret = i2c_w8_mon(sd, CAT2_EDGE_EN, scenemode.edge_en);
+	if (!ret)
+		ret = i2c_w8_mon(sd, CAT2_EDGE_LVL, scenemode.edge_lvl);
+	if (!ret && info->ver.af)
+		ret = i2c_w8_lens(sd, CATA_AF_MODE, scenemode.af_range);
+	if (!ret && info->ver.af)
+		ret = i2c_w8_fd(sd, CAT9_FD_CTL, scenemode.fd_mode);
+	if (!ret)
+		ret = i2c_w8_mon(sd, CAT2_TONE_CTL, scenemode.tone);
+	if (!ret)
+		ret = i2c_w8_mon(sd, CAT3_ISO, scenemode.iso);
+	if (!ret)
+		ret = m5mols_change_mode(info, REG_MODE_CAPTURE);
+	if (!ret)
+		ret = i2c_w8_mon(sd, CATB_WDR_EN, scenemode.wdr);
+	if (!ret)
+		ret = i2c_w8_capt_param(sd, CATB_MCC_MODE, scenemode.mcc);
+	if (!ret)
+		ret = i2c_w8_capt_param(sd, CATB_LIGHT_CTRL, scenemode.light);
+	if (!ret)
+		ret = i2c_w8_capt_param(sd, CATB_FLASH_CTRL, scenemode.flash);
+	if (!ret)
+		ret = i2c_w8_capt_param(sd, CATB_FLASH_CTRL, scenemode.flash);
+	if (!ret)
+		ret = i2c_w8_mon(sd, CATC_CAP_MODE, scenemode.capture_mode);
+	if (!ret)
+		ret = m5mols_change_mode(info, REG_MODE_MONITOR);
+
+	return ret;
+}
+
+static int m5mols_lock_ae(struct m5mols_info *info, bool lock)
+{
+	int ret = 0;
+
+	if (info->lock_ae != lock)
+		ret = i2c_w8_ae(&info->sd, CAT3_AE_LOCK,
+				lock ? REG_AE_LOCK : REG_AE_UNLOCK);
+	if (!ret)
+		info->lock_ae = lock;
+
+	return ret;
+}
+
+static int m5mols_lock_awb(struct m5mols_info *info, bool lock)
+{
+	int ret = 0;
+
+	if (info->lock_awb != lock)
+		ret = i2c_w8_wb(&info->sd, CAT6_AWB_LOCK,
+				lock ? REG_AWB_LOCK : REG_AWB_UNLOCK);
+	if (!ret)
+		info->lock_awb = lock;
+
+	return ret;
+}
+
+/* m5mols_lock_3a() - Lock 3A(Auto Exposure, Whitebalance, Focus) */
+int m5mols_lock_3a(struct m5mols_info *info, bool lock)
+{
+	int ret;
+
+	ret = m5mols_lock_ae(info, lock);
+	if (!ret)
+		ret = m5mols_lock_awb(info, lock);
+	if (!ret && info->ver.af && lock)
+		/* Don't need to handle unlocking AF */
+		ret = i2c_w8_lens(&info->sd, CATA_AF_EXECUTE, REG_AF_STOP);
+
+	return ret;
+}
+
+/* m5mols_set_ctrl() - The main s_ctrl function called by m5mols_set_ctrl() */
+int m5mols_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct v4l2_subdev *sd = to_sd(ctrl);
+	struct m5mols_info *info = to_m5mols(sd);
+	int ret;
+
+	switch (ctrl->id) {
+	case V4L2_CID_ZOOM_ABSOLUTE:
+		return i2c_w8_mon(sd, CAT2_ZOOM, ctrl->val);
+
+	case V4L2_CID_EXPOSURE_AUTO:
+		ret = m5mols_lock_ae(info, ctrl->val == V4L2_EXPOSURE_AUTO ?
+				false : true);
+		if (!ret)
+			ret = i2c_w8_ae(sd, CAT3_AE_MODE,
+				ctrl->val == V4L2_EXPOSURE_AUTO ?
+				REG_AE_MODE_ALL : REG_AE_MODE_OFF);
+		if (!ret && ctrl->val == V4L2_EXPOSURE_MANUAL)
+			ret = i2c_w16_ae(sd, CAT3_MANUAL_GAIN_MON,
+				info->exposure->val);
+		if (!ret && ctrl->val == V4L2_EXPOSURE_MANUAL)
+			ret = i2c_w16_ae(sd, CAT3_MANUAL_GAIN_CAP,
+				info->exposure->val);
+		return ret;
+
+	case V4L2_CID_AUTO_WHITE_BALANCE:
+		ret = m5mols_lock_awb(info, ctrl->val ? false : true);
+		if (!ret)
+			ret = i2c_w8_wb(sd, CAT6_AWB_MODE, ctrl->val ?
+				REG_AWB_MODE_AUTO : REG_AWB_MODE_PRESET);
+		return ret;
+
+	case V4L2_CID_SATURATION:
+		ret = i2c_w8_mon(sd, CAT2_CHROMA_LVL, ctrl->val);
+		if (!ret)
+			ret = i2c_w8_mon(sd, CAT2_CHROMA_EN, REG_CHROMA_ON);
+		return ret;
+
+	case V4L2_CID_COLORFX:
+		/*
+		 * This control uses two kinds of registers: normal & color.
+		 * The normal effect belongs to category 1, while the color
+		 * one belongs to category 2.
+		 *
+		 * The normal effect uses one register: CAT1_EFFECT.
+		 * The color effect uses three registers:
+		 * CAT2_COLOR_EFFECT, CAT2_CFIXR, CAT2_CFIXB.
+		 */
+		ret = i2c_w8_param(sd, CAT1_EFFECT,
+			ctrl->val == V4L2_COLORFX_NEGATIVE ? REG_EFFECT_NEGA :
+			ctrl->val == V4L2_COLORFX_EMBOSS ? REG_EFFECT_EMBOSS :
+			REG_EFFECT_OFF);
+		if (!ret)
+			ret = i2c_w8_mon(sd, CAT2_COLOR_EFFECT,
+				ctrl->val == V4L2_COLORFX_SEPIA ?
+				REG_COLOR_EFFECT_ON : REG_COLOR_EFFECT_OFF);
+		if (!ret)
+			ret = i2c_w8_mon(sd, CAT2_CFIXR,
+				ctrl->val == V4L2_COLORFX_SEPIA ?
+				REG_CFIXR_SEPIA : 0);
+		if (!ret)
+			ret = i2c_w8_mon(sd, CAT2_CFIXB,
+				ctrl->val == V4L2_COLORFX_SEPIA ?
+				REG_CFIXB_SEPIA : 0);
+		return ret;
+	}
+
+	return -EINVAL;
+}
diff --git a/drivers/media/video/m5mols/m5mols_core.c b/drivers/media/video/m5mols/m5mols_core.c
new file mode 100644
index 0000000..cf28dd5
--- /dev/null
+++ b/drivers/media/video/m5mols/m5mols_core.c
@@ -0,0 +1,1034 @@
+/*
+ * Driver for M-5MOLS 8M Pixel camera sensor with ISP
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * Author: HeungJun Kim, riverful.kim@samsung.com
+ *
+ * Copyright (C) 2009 Samsung Electronics Co., Ltd.
+ * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com
+ *
+ * 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/slab.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/version.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+#include <media/m5mols.h>
+
+#include "m5mols.h"
+#include "m5mols_reg.h"
+
+int m5mols_debug;
+module_param(m5mols_debug, int, 0644);
+
+#define MOD_NAME		"M5MOLS"
+#define M5MOLS_I2C_CHECK_RETRY	500
+
+/* The regulator consumer names are derived from M-5MOLS datasheets. */
+static struct regulator_bulk_data supplies[] = {
+	{
+		.supply		= "core",	/* ARM core power, 1.2V */
+	}, {
+		.supply		= "dig_18",	/* digital power 1, 1.8V */
+	}, {
+		.supply		= "d_sensor",	/* sensor power 1, 1.8V */
+	}, {
+		.supply		= "dig_28",	/* digital power 2, 2.8V */
+	}, {
+		.supply		= "a_sensor",	/* analog power */
+	}, {
+		.supply		= "dig_12",	/* digital power 3, 1.2V */
+	},
+};
+
+const struct v4l2_mbus_framefmt m5mols_default_ffmt[M5MOLS_RESTYPE_MAX] = {
+	[M5MOLS_RESTYPE_MONITOR] = {
+		.width		= 1920,
+		.height		= 1080,
+		.code		= V4L2_MBUS_FMT_VYUY8_2X8,
+		.field		= V4L2_FIELD_NONE,
+		.colorspace	= V4L2_COLORSPACE_JPEG,
+	},
+	[M5MOLS_RESTYPE_CAPTURE] = {
+		.width		= 1920,
+		.height		= 1080,
+		.code		= V4L2_MBUS_FMT_JPEG_1X8,
+		.field		= V4L2_FIELD_NONE,
+		.colorspace	= V4L2_COLORSPACE_JPEG,
+	},
+};
+#define SIZE_DEFAULT_FFMT	ARRAY_SIZE(m5mols_default_ffmt)
+
+static const struct m5mols_resolution m5mols_reg_res[] = {
+	{ 0x01, M5MOLS_RESTYPE_MONITOR, 128, 96 },	/* SUB-QCIF */
+	{ 0x03, M5MOLS_RESTYPE_MONITOR, 160, 120 },	/* QQVGA */
+	{ 0x05, M5MOLS_RESTYPE_MONITOR, 176, 144 },	/* QCIF */
+	{ 0x06, M5MOLS_RESTYPE_MONITOR, 176, 176 },
+	{ 0x08, M5MOLS_RESTYPE_MONITOR, 240, 320 },	/* QVGA */
+	{ 0x09, M5MOLS_RESTYPE_MONITOR, 320, 240 },	/* QVGA */
+	{ 0x0c, M5MOLS_RESTYPE_MONITOR, 240, 400 },	/* WQVGA */
+	{ 0x0d, M5MOLS_RESTYPE_MONITOR, 400, 240 },	/* WQVGA */
+	{ 0x0e, M5MOLS_RESTYPE_MONITOR, 352, 288 },	/* CIF */
+	{ 0x13, M5MOLS_RESTYPE_MONITOR, 480, 360 },
+	{ 0x15, M5MOLS_RESTYPE_MONITOR, 640, 360 },	/* qHD */
+	{ 0x17, M5MOLS_RESTYPE_MONITOR, 640, 480 },	/* VGA */
+	{ 0x18, M5MOLS_RESTYPE_MONITOR, 720, 480 },
+	{ 0x1a, M5MOLS_RESTYPE_MONITOR, 800, 480 },	/* WVGA */
+	{ 0x1f, M5MOLS_RESTYPE_MONITOR, 800, 600 },	/* SVGA */
+	{ 0x21, M5MOLS_RESTYPE_MONITOR, 1280, 720 },	/* HD */
+	{ 0x25, M5MOLS_RESTYPE_MONITOR, 1920, 1080 },	/* 1080p */
+	{ 0x29, M5MOLS_RESTYPE_MONITOR, 3264, 2448 },	/* 2.63fps 8M */
+	{ 0x39, M5MOLS_RESTYPE_MONITOR, 800, 602 },	/* AHS_MON debug */
+
+	{ 0x02, M5MOLS_RESTYPE_CAPTURE, 320, 240 },	/* QVGA */
+	{ 0x04, M5MOLS_RESTYPE_CAPTURE, 400, 240 },	/* WQVGA */
+	{ 0x07, M5MOLS_RESTYPE_CAPTURE, 480, 360 },
+	{ 0x08, M5MOLS_RESTYPE_CAPTURE, 640, 360 },	/* qHD */
+	{ 0x09, M5MOLS_RESTYPE_CAPTURE, 640, 480 },	/* VGA */
+	{ 0x0a, M5MOLS_RESTYPE_CAPTURE, 800, 480 },	/* WVGA */
+	{ 0x10, M5MOLS_RESTYPE_CAPTURE, 1280, 720 },	/* HD */
+	{ 0x14, M5MOLS_RESTYPE_CAPTURE, 1280, 960 },	/* 1M */
+	{ 0x17, M5MOLS_RESTYPE_CAPTURE, 1600, 1200 },	/* 2M */
+	{ 0x19, M5MOLS_RESTYPE_CAPTURE, 1920, 1080 },	/* Full-HD */
+	{ 0x1a, M5MOLS_RESTYPE_CAPTURE, 2048, 1152 },	/* 3Mega */
+	{ 0x1b, M5MOLS_RESTYPE_CAPTURE, 2048, 1536 },
+	{ 0x1c, M5MOLS_RESTYPE_CAPTURE, 2560, 1440 },	/* 4Mega */
+	{ 0x1d, M5MOLS_RESTYPE_CAPTURE, 2560, 1536 },
+	{ 0x1f, M5MOLS_RESTYPE_CAPTURE, 2560, 1920 },	/* 5Mega */
+	{ 0x21, M5MOLS_RESTYPE_CAPTURE, 3264, 1836 },	/* 6Mega */
+	{ 0x22, M5MOLS_RESTYPE_CAPTURE, 3264, 1960 },
+	{ 0x25, M5MOLS_RESTYPE_CAPTURE, 3264, 2448 },	/* 8Mega */
+};
+
+/*
+ * m5mols_swap_byte() - byte array to integer conversion
+ * @size:	the size's kinds of I2C packet defined in the M-5MOLS datasheet
+ *
+ * Convert I2C data byte array with performing any required byte
+ * reordering to assure proper values for each data type, regardless
+ * of the architecture endianness.
+ */
+static u32 m5mols_swap_byte(u8 *data, enum m5mols_i2c_size size)
+{
+	if (size == I2C_8BIT)
+		return *data;
+	else if (size == I2C_16BIT)
+		return be16_to_cpu(*((u16 *)data));
+	else
+		return be32_to_cpu(*((u32 *)data));
+}
+
+/*
+ * m5mols_read_reg() - raw function of I2C reading operation
+ * @size:	the kinds of I2C packet's size defined in the M-5MOLS datasheet
+ * @category:	the category value of I2C command
+ * @cmd:	the command
+ * @val:	the read values
+ *
+ * The M-5MOLS I2C operation always consists of category, command, value(s)
+ * as an element. The numbers of each operation vary according to reading or
+ * writing. The more specific explanation can be found in the m5mol.h.
+ */
+int m5mols_read_reg(struct v4l2_subdev *sd,
+		    enum m5mols_i2c_size size,
+		    u8 category, u8 cmd, u32 *val)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct i2c_msg msg[2];
+	u8 wbuf[5], rbuf[I2C_MAX + 1];
+	int ret;
+
+	if (!client->adapter)
+		return -ENODEV;
+
+	if (size != I2C_8BIT && size != I2C_16BIT && size != I2C_32BIT)
+		return -EINVAL;
+
+	/* for designating category and command */
+	msg[0].addr = client->addr;
+	msg[0].flags = 0;
+	msg[0].len = 5;
+	msg[0].buf = wbuf;
+	wbuf[0] = 5;
+	wbuf[1] = M5MOLS_BYTE_READ;
+	wbuf[2] = category;
+	wbuf[3] = cmd;
+	wbuf[4] = size;
+
+	/* for reading desired data */
+	msg[1].addr = client->addr;
+	msg[1].flags = I2C_M_RD;
+	msg[1].len = size + 1;
+	msg[1].buf = rbuf;
+
+	/* minimum stabilization time */
+	usleep_range(200, 200);
+
+	ret = i2c_transfer(client->adapter, msg, 2);
+	if (ret < 0) {
+		dev_err(&client->dev,
+			"failed READ[%d] at cat[%02x] cmd[%02x]\n",
+			size, category, cmd);
+		return ret;
+	}
+
+	*val = m5mols_swap_byte(&rbuf[1], size);
+
+	return 0;
+}
+
+/*
+ * m5mols_write_reg() - raw function of I2C writing operation
+ * @size:	the kinds of I2C packet's size defined in the M-5MOLS datasheet
+ * @category:	the category value of I2C command
+ * @cmd:	the I2C command
+ * @val:	the written value(s)
+ */
+int m5mols_write_reg(struct v4l2_subdev *sd,
+		     enum m5mols_i2c_size size,
+		     u8 category, u8 cmd, u32 val)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct device *cdev = &client->dev;
+	struct i2c_msg msg[1];
+	u8 wbuf[I2C_MAX + 4];
+	u32 *buf;
+	int ret;
+
+	if (!client->adapter)
+		return -ENODEV;
+
+	if (size != I2C_8BIT && size != I2C_16BIT && size != I2C_32BIT) {
+		dev_err(cdev, "Wrong data size\n");
+		return -EINVAL;
+	}
+
+	msg->addr = client->addr;
+	msg->flags = 0;
+	msg->len = (u16)size + 4;
+	msg->buf = wbuf;
+	wbuf[0] = size + 4;
+	wbuf[1] = M5MOLS_BYTE_WRITE;
+	wbuf[2] = category;
+	wbuf[3] = cmd;
+
+	buf = (u32 *)&wbuf[4];
+	*buf = m5mols_swap_byte((u8 *)&val, size);
+
+	usleep_range(200, 200);
+
+	ret = i2c_transfer(client->adapter, msg, 1);
+	if (ret < 0) {
+		dev_err(&client->dev,
+			"failed WRITE[%d] at cat[%02x] cmd[%02x], ret %d\n",
+			size, msg->buf[2], msg->buf[3], ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+int m5mols_check_busy(struct v4l2_subdev *sd, u8 category, u8 cmd, u32 mask)
+{
+	u32 busy, i;
+	int ret;
+
+	for (i = 0; i < M5MOLS_I2C_CHECK_RETRY; i++) {
+		ret = m5mols_read_reg(sd, I2C_8BIT, category, cmd, &busy);
+		if (ret < 0)
+			return ret;
+
+		if ((busy & mask) == mask)
+			return 0;
+	}
+
+	return -EBUSY;
+}
+
+/*
+ * m5mols_enable_interrupt() - clear pending register and set interrupt
+ *
+ * Before writing desired interrupt value, should clear INT_FACTOR register
+ * automatically documented in the M-5MOLS datasheet.
+ */
+int m5mols_enable_interrupt(struct v4l2_subdev *sd, u32 reg)
+{
+	struct m5mols_info *info = to_m5mols(sd);
+	u32 dummy;
+	int ret;
+
+	ret = i2c_r8_system(sd, CAT0_INT_FACTOR, &dummy);
+	if (!ret)
+		ret = i2c_w8_system(sd, CAT0_INT_ENABLE,
+				info->ver.af ? reg & ~REG_INT_AF : reg);
+	return ret;
+}
+
+/*
+ * m5mols_write_mode() - Write the mode and check busy status.
+ *
+ * Changing the M-5MOLS mode accompanies a little delay all the time, so
+ * checking current busy status is needed to guarantee changing right mode.
+ */
+static int m5mols_write_mode(struct v4l2_subdev *sd, u32 mode)
+{
+	int ret = i2c_w8_system(sd, CAT0_SYSMODE, mode);
+	if (!ret)
+		ret = m5mols_check_busy(sd, CAT_SYSTEM, CAT0_SYSMODE, mode);
+
+	return ret;
+}
+
+/*
+ * m5mols_change_mode() - manage the M-5MOLS's mode.
+ * @mode:	the desired mode
+ *
+ * All commands of M-5MOLS belong to a specific its own mode. Each functionality
+ * can be guaranteed only when the sensor is operating in the mode which
+ * the command belongs to. The more explanation can be found in the m5mols.h.
+ */
+int m5mols_change_mode(struct m5mols_info *info, u32 mode)
+{
+	struct v4l2_subdev *sd = &info->sd;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	u32 reg;
+	int ret = -EINVAL;
+
+	if (mode != REG_MODE_PARAM && mode != REG_MODE_MONITOR &&
+	    mode != REG_MODE_CAPTURE)
+		return ret;
+
+	ret = i2c_r8_system(sd, CAT0_SYSMODE, &reg);
+	if ((!ret && reg == mode) || ret)
+		return ret;
+
+	switch (reg) {
+	case REG_MODE_PARAM:
+		ret = m5mols_write_mode(sd, REG_MODE_MONITOR);
+		if (!ret && mode == REG_MODE_MONITOR)
+			break;
+		if (!ret)
+			ret = m5mols_write_mode(sd, REG_MODE_CAPTURE);
+		break;
+
+	case REG_MODE_MONITOR:
+		if (mode == REG_MODE_PARAM) {
+			ret = m5mols_write_mode(sd, REG_MODE_PARAM);
+			break;
+		}
+
+		ret = m5mols_write_mode(sd, REG_MODE_CAPTURE);
+		break;
+
+	case REG_MODE_CAPTURE:
+		ret = m5mols_write_mode(sd, REG_MODE_MONITOR);
+		if (!ret && mode == REG_MODE_MONITOR)
+			break;
+		if (!ret)
+			ret = m5mols_write_mode(sd, REG_MODE_PARAM);
+		break;
+
+	default:
+		dev_warn(&client->dev, "Wrong mode: %d", mode);
+	}
+
+	if (!ret)
+		info->mode = mode;
+
+	return ret;
+}
+
+/*
+ * m5mols_get_version() - Retrieve all internal version of M-5MOLS.
+ *
+ * The version information includes revisions hardware and firmware, AutoFocus
+ * alghorithm version and the version string. The version string should be read
+ * at the same register offset until the read value is NULL.
+ */
+static int m5mols_get_version(struct v4l2_subdev *sd)
+{
+	struct m5mols_info *info = to_m5mols(sd);
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct device *cdev = &client->dev;
+	union {
+		struct m5mols_version ver;
+		u8 bytes[VERSION_SIZE];
+	} value;
+	int ret, cmd = CAT0_CUSTOMER_CODE;
+
+	do {
+		ret = i2c_r8_system(sd, cmd, (u32 *)&value.bytes[cmd]);
+		if (ret)
+			return ret;
+	} while (cmd++ != CAT0_VERSION_AWB);
+
+	do {
+		ret = i2c_r8_system(sd, CAT0_VERSION_STRING,
+				(u32 *)&value.bytes[cmd]);
+		if (ret)
+			return ret;
+	} while (value.bytes[cmd++]);
+
+	ret = i2c_r8_lens(sd, CATA_AF_VERSION, (u32 *)&value.bytes[cmd]);
+	if (ret)
+		return ret;
+
+	/* store version information swapped for being readable */
+	info->ver	= value.ver;
+	info->ver.fw	= be16_to_cpu(info->ver.fw);
+	info->ver.hw	= be16_to_cpu(info->ver.hw);
+	info->ver.param	= be16_to_cpu(info->ver.param);
+	info->ver.awb	= be16_to_cpu(info->ver.awb);
+
+	dev_info(cdev, "Manufacturer\t[%s]\n",
+			is_manufacturer(info, SAMSUNG_ELECTRO) ?
+			"Samsung Electro-Machanics" :
+			is_manufacturer(info, SAMSUNG_OPTICS) ?
+			"Samsung Fiber-Optics" :
+			is_manufacturer(info, SAMSUNG_TECHWIN) ?
+			"Samsung Techwin" : "None");
+	dev_info(cdev, "Customer/Project\t[0x%02x/0x%02x]\n",
+			info->ver.customer, info->ver.project);
+	if (!info->ver.af)
+		dev_info(cdev, "No support Auto Focus on this firmware\n");
+
+	return ret;
+}
+
+/*
+ * __find_restype() - look up M-5MOLS resolution type according to pixel code
+ * @code:	pixel code
+ */
+static enum m5mols_restype __find_restype(enum v4l2_mbus_pixelcode code)
+{
+	enum m5mols_restype type = M5MOLS_RESTYPE_MONITOR;
+
+	do {
+		if (code == m5mols_default_ffmt[type].code)
+			return type;
+	} while (type++ != SIZE_DEFAULT_FFMT);
+
+	return 0;
+}
+
+/*
+ * __find_resolution() - look up preset and type of M-5MOLS's resolution
+ * @mf:         pixel format to find/negotiate the resolution preset for
+ * @type:	M-5MOLS resolution type
+ * @resolution:	M-5MOLS resolution preset register value
+ *
+ * Find nearest resolution matching resolution preset and adjust mf
+ * to supported values.
+ */
+static int __find_resolution(struct v4l2_subdev *sd,
+			     struct v4l2_mbus_framefmt *mf,
+			     enum m5mols_restype *type,
+			     u32 *resolution)
+{
+	const struct m5mols_resolution *fsize = &m5mols_reg_res[0],
+				 *match = NULL;
+	enum m5mols_restype stype = __find_restype(mf->code);
+	int i = ARRAY_SIZE(m5mols_reg_res);
+	unsigned int min_err = ~0;
+
+	while (i--) {
+		int err;
+		if (stype != fsize->type)
+			continue;
+
+		err = abs(fsize->width - mf->width)
+			  + abs(fsize->height - mf->height);
+
+		if (err < min_err) {
+			min_err = err;
+			match = fsize;
+		}
+		fsize++;
+	}
+	if (match) {
+		mf->width  = match->width;
+		mf->height = match->height;
+		*resolution = match->reg;
+		*type = stype;
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static struct v4l2_mbus_framefmt *__find_format(struct m5mols_info *info,
+				struct v4l2_subdev_fh *fh,
+				enum v4l2_subdev_format_whence which,
+				enum m5mols_restype type)
+{
+	if (which == V4L2_SUBDEV_FORMAT_TRY)
+		return fh ? v4l2_subdev_get_try_format(fh, 0) : NULL;
+
+	return &info->ffmt[type];
+}
+
+static int m5mols_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+			  struct v4l2_subdev_format *fmt)
+{
+	struct m5mols_info *info = to_m5mols(sd);
+	struct v4l2_mbus_framefmt *format;
+
+	if (fmt->pad != 0)
+		return -EINVAL;
+
+	format = __find_format(info, fh, fmt->which, info->curr_res_type);
+	if (!format)
+		return -EINVAL;
+
+	fmt->format = *format;
+	return 0;
+}
+
+static int m5mols_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+			  struct v4l2_subdev_format *fmt)
+{
+	struct m5mols_info *info = to_m5mols(sd);
+	struct v4l2_mbus_framefmt *format = &fmt->format;
+	struct v4l2_mbus_framefmt *sfmt;
+	enum m5mols_restype type;
+	u32 resolution = 0;
+	int ret;
+
+	if (fmt->pad != 0)
+		return -EINVAL;
+
+	ret = __find_resolution(sd, format, &type, &resolution);
+	if (ret < 0)
+		return ret;
+
+	sfmt = __find_format(info, fh, fmt->which, type);
+	if (!sfmt)
+		return 0;
+
+	*sfmt		= m5mols_default_ffmt[type];
+	sfmt->width	= format->width;
+	sfmt->height	= format->height;
+
+	if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+		info->resolution	= resolution;
+		info->code		= format->code;
+		info->curr_res_type	= type;
+	}
+
+	return 0;
+}
+
+static int m5mols_enum_mbus_code(struct v4l2_subdev *sd,
+				 struct v4l2_subdev_fh *fh,
+				 struct v4l2_subdev_mbus_code_enum *code)
+{
+	if (!code || code->index >= SIZE_DEFAULT_FFMT)
+		return -EINVAL;
+
+	code->code = m5mols_default_ffmt[code->index].code;
+
+	return 0;
+}
+
+static struct v4l2_subdev_pad_ops m5mols_pad_ops = {
+	.enum_mbus_code	= m5mols_enum_mbus_code,
+	.get_fmt	= m5mols_get_fmt,
+	.set_fmt	= m5mols_set_fmt,
+};
+
+/*
+ * m5mols_sync_control() - do default control and v4l2_ctrl_handler_setup()
+ *
+ * This is used only streaming for syncing between v4l2_ctrl framework and
+ * m5mols's controls. First, do the scenemode to the sensor, then call
+ * v4l2_ctrl_handler_setup(). It can be same between some commands and
+ * the scenemode's in the default v4l2_ctrls. But, such commands of control
+ * should be prior to the scenemode's one.
+ */
+int m5mols_sync_control(struct m5mols_info *info)
+{
+	int ret = -EINVAL;
+
+	if (!is_ctrl_synced(info)) {
+		ret = m5mols_change_scenemode(info, REG_SCENE_NORMAL);
+		if (ret)
+			return ret;
+
+		v4l2_ctrl_handler_setup(&info->handle);
+
+		info->ctrl_sync = true;
+	}
+
+	return ret;
+}
+
+/*
+ * m5mols_start_monitor() - start monitor mode
+ *
+ * Before syncing control, do the commands in the available PARAMETER mode,
+ * and then, change the mode as MONITOR.
+ */
+static int m5mols_start_monitor(struct m5mols_info *info)
+{
+	struct v4l2_subdev *sd = &info->sd;
+	int ret;
+
+	ret = m5mols_change_mode(info, REG_MODE_PARAM);
+	if (!ret)
+		ret = i2c_w8_param(sd, CAT1_MONITOR_SIZE, info->resolution);
+	if (!ret)
+		ret = i2c_w8_param(sd, CAT1_MONITOR_FPS, REG_FPS_30);
+	if (!ret)
+		ret = m5mols_change_mode(info, REG_MODE_MONITOR);
+	if (!ret)
+		ret = m5mols_sync_control(info);
+
+	return ret;
+}
+
+static int m5mols_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	struct m5mols_info *info = to_m5mols(sd);
+
+	if (enable) {
+		int ret = -EINVAL;
+		if (is_code(info->code, M5MOLS_RESTYPE_MONITOR))
+			ret = m5mols_start_monitor(info);
+		if (is_code(info->code, M5MOLS_RESTYPE_CAPTURE))
+			ret = m5mols_start_capture(info);
+
+		return ret;
+	}
+
+	return m5mols_change_mode(info, REG_MODE_PARAM);
+}
+
+static const struct v4l2_subdev_video_ops m5mols_video_ops = {
+	.s_stream	= m5mols_s_stream,
+};
+
+static int m5mols_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct v4l2_subdev *sd = to_sd(ctrl);
+	struct m5mols_info *info = to_m5mols(sd);
+	int ret;
+
+	info->mode_save = info->mode;
+
+	ret = m5mols_change_mode(info, REG_MODE_PARAM);
+	if (!ret)
+		ret = m5mols_set_ctrl(ctrl);
+	if (!ret)
+		ret = m5mols_change_mode(info, info->mode_save);
+
+	return ret;
+}
+
+static const struct v4l2_ctrl_ops m5mols_ctrl_ops = {
+	.s_ctrl		= m5mols_s_ctrl,
+};
+
+static int m5mols_sensor_power(struct m5mols_info *info, bool enable)
+{
+	struct v4l2_subdev *sd = &info->sd;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	const struct m5mols_platform_data *pdata = info->pdata;
+	int ret;
+
+	if (enable) {
+		if (is_powered(info))
+			return 0;
+
+		if (info->set_power) {
+			ret = info->set_power(&client->dev, 1);
+			if (ret)
+				return ret;
+		}
+
+		ret = regulator_bulk_enable(ARRAY_SIZE(supplies), supplies);
+		if (ret)
+			return ret;
+
+		gpio_set_value(pdata->gpio_reset, !pdata->reset_polarity);
+		usleep_range(1000, 1000);
+		info->power = true;
+
+		return ret;
+	}
+
+	if (!is_powered(info))
+		return 0;
+
+	ret = regulator_bulk_disable(ARRAY_SIZE(supplies), supplies);
+	if (ret)
+		return ret;
+
+	if (info->set_power) {
+		ret = info->set_power(&client->dev, 0);
+		if (ret)
+			return ret;
+	}
+
+	gpio_set_value(pdata->gpio_reset, pdata->reset_polarity);
+	usleep_range(1000, 1000);
+	info->power = false;
+
+	return ret;
+}
+
+/* m5mols_update_fw() - m5mols_update_fw() is optional. */
+int __attribute__ ((weak)) m5mols_update_fw(struct v4l2_subdev *sd,
+		int (*set_power)(struct m5mols_info *, bool))
+{
+	return 0;
+}
+
+/*
+ * m5mols_sensor_armboot() - booting M-5MOLS internal ARM core.
+ *
+ * Booting internal ARM core makes the M-5MOLS is ready for getting commands
+ * with I2C. It's the first thing to be done after it powered up. It must wait
+ * at least 520ms recommended by M-5MOLS datasheet, after executing arm booting.
+ */
+static int m5mols_sensor_armboot(struct v4l2_subdev *sd)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	int ret;
+
+	ret = i2c_w8_flash(sd, CATF_CAM_START, REG_START_ARM_BOOT);
+	if (ret < 0)
+		return ret;
+
+	msleep(520);
+
+	ret = m5mols_get_version(sd);
+	if (!ret)
+		ret = m5mols_update_fw(sd, m5mols_sensor_power);
+	if (ret)
+		return ret;
+
+	dev_dbg(&client->dev, "Success ARM Booting\n");
+
+	ret = i2c_w8_param(sd, CAT1_DATA_INTERFACE, REG_INTERFACE_MIPI);
+	if (!ret)
+		ret = m5mols_enable_interrupt(sd, REG_INT_AF);
+
+	return ret;
+}
+
+static int m5mols_init_controls(struct m5mols_info *info)
+{
+	struct v4l2_subdev *sd = &info->sd;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	u16 max_exposure, step_zoom;
+	int ret;
+
+	/* Determine value's range & step of controls for various FW version */
+	ret = i2c_r16_ae(sd, CAT3_MAX_GAIN_MON, (u32 *)&max_exposure);
+	if (!ret)
+		step_zoom = is_manufacturer(info, SAMSUNG_OPTICS) ? 31 : 1;
+	if (ret)
+		return ret;
+
+	v4l2_ctrl_handler_init(&info->handle, 6);
+	info->autowb = v4l2_ctrl_new_std(&info->handle,
+			&m5mols_ctrl_ops, V4L2_CID_AUTO_WHITE_BALANCE,
+			0, 1, 1, 0);
+	info->saturation = v4l2_ctrl_new_std(&info->handle,
+			&m5mols_ctrl_ops, V4L2_CID_SATURATION,
+			1, 5, 1, 3);
+	info->zoom = v4l2_ctrl_new_std(&info->handle,
+			&m5mols_ctrl_ops, V4L2_CID_ZOOM_ABSOLUTE,
+			1, 70, step_zoom, 1);
+	info->exposure = v4l2_ctrl_new_std(&info->handle,
+			&m5mols_ctrl_ops, V4L2_CID_EXPOSURE,
+			0, max_exposure, 1, (int)max_exposure/2);
+	info->colorfx = v4l2_ctrl_new_std_menu(&info->handle,
+			&m5mols_ctrl_ops, V4L2_CID_COLORFX,
+			4, (1 << V4L2_COLORFX_BW), V4L2_COLORFX_NONE);
+	info->autoexposure = v4l2_ctrl_new_std_menu(&info->handle,
+			&m5mols_ctrl_ops, V4L2_CID_EXPOSURE_AUTO,
+			1, 0, V4L2_EXPOSURE_MANUAL);
+
+	sd->ctrl_handler = &info->handle;
+	if (info->handle.error) {
+		dev_err(&client->dev, "Failed to init controls, %d\n", ret);
+		v4l2_ctrl_handler_free(&info->handle);
+		return info->handle.error;
+	}
+
+	v4l2_ctrl_cluster(2, &info->autoexposure);
+
+	return 0;
+}
+
+/*
+ * m5mols_s_power() - callback function of s_power in the subdev core operation
+ *
+ * Breaking the lens is considered when the sensor going to power off.
+ * But it provided only in the M-5MOLS with Samsung Techwin firiware. And, It
+ * calls Soft-Landing algorithm.
+ */
+static int m5mols_s_power(struct v4l2_subdev *sd, int on)
+{
+	struct m5mols_info *info = to_m5mols(sd);
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	int ret;
+
+	if (on) {
+		ret = m5mols_sensor_power(info, true);
+		if (!ret)
+			ret = m5mols_sensor_armboot(sd);
+		if (!ret)
+			ret = m5mols_init_controls(info);
+		if (!ret) {
+			info->ffmt[M5MOLS_RESTYPE_MONITOR] =
+				m5mols_default_ffmt[M5MOLS_RESTYPE_MONITOR];
+			info->ffmt[M5MOLS_RESTYPE_CAPTURE] =
+				m5mols_default_ffmt[M5MOLS_RESTYPE_CAPTURE];
+		}
+
+		return ret;
+	}
+
+	if (is_manufacturer(info, SAMSUNG_TECHWIN)) {
+		ret = m5mols_change_mode(info, REG_MODE_MONITOR);
+		if (!ret)
+			ret = i2c_w8_lens(sd, CATA_AF_EXECUTE, REG_AF_STOP);
+		if (!ret)
+			ret = i2c_w8_lens(sd, CATA_AF_MODE,
+					REG_AF_MODE_POWEROFF);
+		if (!ret)
+			ret = m5mols_check_busy(sd, CAT_SYSTEM, CAT0_STATUS,
+					REG_AF_IDLE);
+		if (!ret)
+			dev_info(&client->dev, "Success soft-landing lens\n");
+	}
+
+	ret = m5mols_sensor_power(info, false);
+	if (!ret) {
+		v4l2_ctrl_handler_free(&info->handle);
+		info->ctrl_sync = false;
+	}
+
+	return ret;
+}
+
+static int m5mols_log_status(struct v4l2_subdev *sd)
+{
+	struct m5mols_info *info = to_m5mols(sd);
+
+	v4l2_ctrl_handler_log_status(&info->handle, sd->name);
+
+	return 0;
+}
+
+static const struct v4l2_subdev_core_ops m5mols_core_ops = {
+	.s_power	= m5mols_s_power,
+	.g_ctrl		= v4l2_subdev_g_ctrl,
+	.s_ctrl		= v4l2_subdev_s_ctrl,
+	.queryctrl	= v4l2_subdev_queryctrl,
+	.querymenu	= v4l2_subdev_querymenu,
+	.g_ext_ctrls	= v4l2_subdev_g_ext_ctrls,
+	.try_ext_ctrls	= v4l2_subdev_try_ext_ctrls,
+	.s_ext_ctrls	= v4l2_subdev_s_ext_ctrls,
+	.log_status	= m5mols_log_status,
+};
+
+static const struct v4l2_subdev_ops m5mols_ops = {
+	.core		= &m5mols_core_ops,
+	.pad		= &m5mols_pad_ops,
+	.video		= &m5mols_video_ops,
+};
+
+static void m5mols_irq_work(struct work_struct *work)
+{
+	struct m5mols_info *info = container_of(work, struct m5mols_info,
+			work_irq);
+	struct v4l2_subdev *sd = &info->sd;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	u32 reg;
+	int ret;
+
+	/*
+	 * For preventing I2C operation when it's not powered and prepared for
+	 * the I2C operation.
+	 */
+	if (!is_powered(info) ||
+			i2c_r8_system(sd, CAT0_INT_FACTOR, &info->interrupt))
+		return;
+
+	switch (info->interrupt & REG_INT_MASK) {
+	case REG_INT_AF:
+		if (info->ver.af) {
+			ret = i2c_r8_lens(sd, CATA_AF_STATUS, &reg);
+			dev_dbg(&client->dev, "= AF %s\n",
+				reg == REG_AF_FAIL ? "Failed" :
+				reg == REG_AF_SUCCESS ? "Success" :
+				reg == REG_AF_IDLE ? "Idle" : "Busy");
+		}
+		break;
+	case REG_INT_CAPTURE:
+		if (!is_captured(info)) {
+			wake_up_interruptible(&info->wait_capture);
+			info->capture = true;
+		}
+		dev_dbg(&client->dev, "= CAPTURE\n");
+		break;
+	default:
+		dev_dbg(&client->dev, "= Nothing : %02x\n", reg);
+		break;
+	};
+}
+
+static irqreturn_t m5mols_irq_handler(int irq, void *data)
+{
+	struct v4l2_subdev *sd = data;
+	struct m5mols_info *info = to_m5mols(sd);
+
+	schedule_work(&info->work_irq);
+
+	return IRQ_HANDLED;
+}
+
+static int m5mols_probe(struct i2c_client *client,
+			 const struct i2c_device_id *id)
+{
+	const struct m5mols_platform_data *pdata =
+		client->dev.platform_data;
+	struct m5mols_info *info;
+	struct v4l2_subdev *sd;
+	int ret;
+
+	if (pdata == NULL) {
+		dev_err(&client->dev, "No platform data\n");
+		return -EINVAL;
+	}
+
+	if (!gpio_is_valid(pdata->gpio_reset)) {
+		dev_err(&client->dev, "No valid RESET GPIO specified\n");
+		return -EINVAL;
+	}
+
+	if (!pdata->irq) {
+		dev_err(&client->dev, "Interrupt not assigned\n");
+		return -EINVAL;
+	}
+
+	info = kzalloc(sizeof(struct m5mols_info), GFP_KERNEL);
+	if (info == NULL) {
+		dev_err(&client->dev, "Failed to allocate info\n");
+		return -ENOMEM;
+	}
+
+	info->pdata	= pdata;
+	info->set_power	= pdata->set_power;
+
+	ret = gpio_request(pdata->gpio_reset, "M5MOLS_NRST");
+	if (ret) {
+		dev_err(&client->dev, "Failed to request gpio: %d\n", ret);
+		goto out_free;
+	}
+
+	gpio_direction_output(pdata->gpio_reset, pdata->reset_polarity);
+
+	ret = regulator_bulk_get(&client->dev, ARRAY_SIZE(supplies), supplies);
+	if (ret) {
+		dev_err(&client->dev, "Failed to get regulators: %d\n", ret);
+		goto out_gpio;
+	}
+
+	sd = &info->sd;
+	strlcpy(sd->name, MOD_NAME, sizeof(sd->name));
+	v4l2_i2c_subdev_init(sd, client, &m5mols_ops);
+
+	info->pad.flags = MEDIA_PAD_FL_SOURCE;
+	ret = media_entity_init(&sd->entity, 1, &info->pad, 0);
+	if (ret < 0)
+		goto out_gpio;
+	sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR;
+
+	init_waitqueue_head(&info->wait_capture);
+	INIT_WORK(&info->work_irq, m5mols_irq_work);
+	ret = request_irq(pdata->irq, m5mols_irq_handler,
+			  IRQF_TRIGGER_RISING, MOD_NAME, sd);
+	if (ret) {
+		dev_err(&client->dev, "Failed to request irq: %d\n", ret);
+		goto out_reg;
+	}
+	info->curr_res_type = M5MOLS_RESTYPE_MONITOR;
+
+	v4l2_dbg(1, m5mols_debug, sd, "Probed m5mols driver.\n");
+
+	return 0;
+
+out_reg:
+	regulator_bulk_free(ARRAY_SIZE(supplies), supplies);
+out_gpio:
+	gpio_free(pdata->gpio_reset);
+out_free:
+	kfree(info);
+	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);
+	free_irq(info->pdata->irq, sd);
+
+	regulator_bulk_free(ARRAY_SIZE(supplies), supplies);
+	gpio_free(info->pdata->gpio_reset);
+	kfree(info);
+
+	v4l2_dbg(1, m5mols_debug, sd, "Removed m5mols driver\n");
+
+	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@samsung.com>");
+MODULE_AUTHOR("Dongsoo Kim <dongsoo45.kim@samsung.com>");
+MODULE_DESCRIPTION("Fujitsu M-5MOLS 8M Pixel camera driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/m5mols/m5mols_reg.h b/drivers/media/video/m5mols/m5mols_reg.h
new file mode 100644
index 0000000..f07b362
--- /dev/null
+++ b/drivers/media/video/m5mols/m5mols_reg.h
@@ -0,0 +1,321 @@
+/*
+ * Register map for M-5MOLS 8M Pixel camera sensor with ISP
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * Author: HeungJun Kim, riverful.kim@samsung.com
+ *
+ * Copyright (C) 2009 Samsung Electronics Co., Ltd.
+ * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com
+ *
+ * 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_REG_H
+#define M5MOLS_REG_H
+
+/*
+ * Category section register
+ *
+ * The category means set including relevant command of M-5MOLS.
+ */
+#define CAT_SYSTEM		0x00
+#define CAT_PARAM		0x01
+#define CAT_MONITOR		0x02
+#define CAT_AE			0x03
+#define CAT_WB			0x06
+#define CAT_EXIF		0x07
+#define CAT_FD			0x09
+#define CAT_LENS		0x0a
+#define CAT_CAPTURE_PARAMETER	0x0b
+#define CAT_CAPTURE_CONTROL	0x0c
+#define CAT_FLASH		0x0f	/* related with FW, Verions, booting */
+
+/*
+ * Category 0 - SYSTEM mode
+ *
+ * The SYSTEM mode in the M-5MOLS means area available to handle with the whole
+ * & all-round system of sensor. It deals with version/interrupt/setting mode &
+ * even sensor's status. Especially, the M-5MOLS sensor with ISP varys by
+ * packaging & manufactur vendors, even the customer user vendor. And the
+ * detailed function may have a difference. The version information can
+ * determine what method use in the driver.
+ *
+ * There is many registers between customer version address and awb one. For
+ * more specific contents, reference the m5mols.h.
+ */
+#define CAT0_CUSTOMER_CODE	0x00	/* customer version*/
+#define CAT0_VERSION_AWB	0x09	/* Auto WB version */
+#define CAT0_VERSION_STRING	0x0a	/* string including M-5MOLS */
+#define CAT0_SYSMODE		0x0b	/* SYSTEM mode register */
+#define CAT0_STATUS		0x0c	/* SYSTEM mode status register */
+#define CAT0_INT_FACTOR		0x10	/* interrupt pending register */
+#define CAT0_INT_ENABLE		0x11	/* interrupt enable register */
+
+#define REG_MODE_SYSINIT	0x00	/* SYSTEM mode */
+#define REG_MODE_PARAM		0x01	/* PARAMETER mode */
+#define REG_MODE_MONITOR	0x02	/* MONITOR mode */
+#define REG_MODE_CAPTURE	0x03	/* CAPTURE mode */
+
+#define REG_INT_MODE		(1 << 0)
+#define REG_INT_AF		(1 << 1)
+#define REG_INT_ZOOM		(1 << 2)
+#define REG_INT_CAPTURE		(1 << 3)
+#define REG_INT_FRAMESYNC	(1 << 4)
+#define REG_INT_FD		(1 << 5)
+#define REG_INT_LENS_INIT	(1 << 6)
+#define REG_INT_SOUND		(1 << 7)
+#define REG_INT_MASK		0x0f
+
+/*
+ * category 1 - PARAMETER mode
+ *
+ * This category supports function of camera features of M-5MOLS. It means we
+ * can handle with preview(MONITOR) resolution size/frame per second/interface
+ * between the sensor and the Application Processor/even the image effect.
+ */
+#define CAT1_DATA_INTERFACE	0x00	/* interface between sensor and AP */
+#define CAT1_MONITOR_SIZE	0x01	/* resolution at the MONITOR mode */
+#define CAT1_MONITOR_FPS	0x02	/* frame per second at this mode */
+#define CAT1_EFFECT		0x0b	/* image effects */
+
+#define REG_INTERFACE_MIPI	0x02
+
+#define REG_FPS_30		0x02
+
+#define REG_EFFECT_OFF		0x00
+#define REG_EFFECT_NEGA		0x01
+#define REG_EFFECT_EMBOSS	0x06
+#define REG_EFFECT_OUTLINE	0x07
+#define REG_EFFECT_WATERCOLOR	0x08
+
+/*
+ * Category 2 - MONITOR mode
+ *
+ * The MONITOR mode is same as preview mode as we said. The M-5MOLS has another
+ * mode named "Preview", but this preview mode is used at the case specific
+ * vider-recording mode. This mmode supports only YUYV format. On the other
+ * hand, the JPEG & RAW formats is supports by CAPTURE mode. And, there are
+ * another options like zoom/color effect(different with effect in PARAMETER
+ * mode)/anti hand shaking algorithm.
+ */
+#define CAT2_ZOOM		0x01	/* set the zoom position & execute */
+#define CAT2_ZOOM_STEP		0x03	/* set the zoom step */
+#define CAT2_CFIXB		0x09	/* CB value for color effect */
+#define CAT2_CFIXR		0x0a	/* CR value for color effect */
+#define CAT2_COLOR_EFFECT	0x0b	/* set on/off of color effect */
+#define CAT2_CHROMA_LVL		0x0f	/* set chroma level */
+#define CAT2_CHROMA_EN		0x10	/* set on/off of choroma */
+#define CAT2_EDGE_LVL		0x11	/* set sharpness level */
+#define CAT2_EDGE_EN		0x12	/* set on/off sharpness */
+#define CAT2_TONE_CTL		0x25	/* set tone color(contrast) */
+
+#define REG_CFIXB_SEPIA		0xd8
+#define REG_CFIXR_SEPIA		0x18
+
+#define REG_COLOR_EFFECT_OFF	0x00
+#define REG_COLOR_EFFECT_ON	0x01
+
+#define REG_CHROMA_OFF		0x00
+#define REG_CHROMA_ON		0x01
+
+#define REG_EDGE_OFF		0x00
+#define REG_EDGE_ON		0x01
+
+/*
+ * Category 3 - Auto Exposure
+ *
+ * The M-5MOLS exposure capbility is detailed as which is similar to digital
+ * camera. This category supports AE locking/various AE mode(range of exposure)
+ * /ISO/flickering/EV bias/shutter/meteoring, and anything else. And the
+ * maximum/minimum exposure gain value depending on M-5MOLS firmware, may be
+ * different. So, this category also provide getting the max/min values. And,
+ * each MONITOR and CAPTURE mode has each gain/shutter/max exposure values.
+ */
+#define CAT3_AE_LOCK		0x00	/* locking Auto exposure */
+#define CAT3_AE_MODE		0x01	/* set AE mode, mode means range */
+#define CAT3_ISO		0x05	/* set ISO */
+#define CAT3_EV_PRESET_MODE_MON	0x0a	/* EV(scenemode) preset for MONITOR */
+#define CAT3_EV_PRESET_MODE_CAP	0x0b	/* EV(scenemode) preset for CAPTURE */
+#define CAT3_MANUAL_GAIN_MON	0x12	/* meteoring value for the MONITOR */
+#define CAT3_MAX_GAIN_MON	0x1a	/* max gain value for the MONITOR */
+#define CAT3_MANUAL_GAIN_CAP	0x26	/* meteoring value for the CAPTURE */
+#define CAT3_AE_INDEX		0x38	/* AE index */
+
+#define REG_AE_UNLOCK		0x00
+#define REG_AE_LOCK		0x01
+
+#define REG_AE_MODE_OFF		0x00	/* AE off */
+#define REG_AE_MODE_ALL		0x01	/* calc AE in all block integral */
+#define REG_AE_MODE_CENTER	0x03	/* calc AE in center weighted */
+#define REG_AE_MODE_SPOT	0x06	/* calc AE in specific spot */
+
+#define REG_ISO_AUTO		0x00
+#define REG_ISO_50		0x0
+#define REG_ISO_100		0x02
+#define REG_ISO_200		0x03
+#define REG_ISO_400		0x04
+#define REG_ISO_800		0x05
+
+#define REG_SCENE_NORMAL	0x00
+#define REG_SCENE_PORTRAIT	0x01
+#define REG_SCENE_LANDSCAPE	0x02
+#define REG_SCENE_SPORTS	0x03
+#define REG_SCENE_PARTY_INDOOR	0x04
+#define REG_SCENE_BEACH_SNOW	0x05
+#define REG_SCENE_SUNSET	0x06
+#define REG_SCENE_DAWN_DUSK	0x07
+#define REG_SCENE_FALL		0x08
+#define REG_SCENE_NIGHT		0x09
+#define REG_SCENE_AGAINST_LIGHT	0x0a
+#define REG_SCENE_FIRE		0x0b
+#define REG_SCENE_TEXT		0x0c
+#define REG_SCENE_CANDLE	0x0d
+#define REG_SCENE_NONE		0xff
+
+#define REG_AE_INDEX_20_NEG	0x00
+#define REG_AE_INDEX_15_NEG	0x01
+#define REG_AE_INDEX_10_NEG	0x02
+#define REG_AE_INDEX_05_NEG	0x03
+#define REG_AE_INDEX_00		0x04
+#define REG_AE_INDEX_05_POS	0x05
+#define REG_AE_INDEX_10_POS	0x06
+#define REG_AE_INDEX_15_POS	0x07
+#define REG_AE_INDEX_20_POS	0x08
+
+/*
+ * Category 6 - White Balance
+ *
+ * This cagetory provide AWB locking/mode/preset/speed/gain bias, etc.
+ */
+#define CAT6_AWB_LOCK		0x00	/* locking Auto Whitebalance */
+#define CAT6_AWB_MODE		0x02	/* set Auto or Manual */
+#define CAT6_AWB_MANUAL		0x03	/* set Manual(preset) value */
+
+#define REG_AWB_UNLOCK		0x00
+#define REG_AWB_LOCK		0x01
+
+#define REG_AWB_MODE_AUTO	0x01	/* AWB off */
+#define REG_AWB_MODE_PRESET	0x02	/* AWB preset */
+
+#define REG_AWB_INCANDESCENT	0x01
+#define REG_AWB_FLUORESCENT_1	0x02
+#define REG_AWB_FLUORESCENT_2	0x03
+#define REG_AWB_DAYLIGHT	0x04
+#define REG_AWB_CLOUDY		0x05
+#define REG_AWB_SHADE		0x06
+#define REG_AWB_HORIZON		0x07
+#define REG_AWB_LEDLIGHT	0x09
+
+/*
+ * Category 7 - EXIF information
+ */
+#define CAT7_INFO_EXPTIME_NU	0x00
+#define CAT7_INFO_EXPTIME_DE	0x04
+#define CAT7_INFO_TV_NU		0x08
+#define CAT7_INFO_TV_DE		0x0c
+#define CAT7_INFO_AV_NU		0x10
+#define CAT7_INFO_AV_DE		0x14
+#define CAT7_INFO_BV_NU		0x18
+#define CAT7_INFO_BV_DE		0x1c
+#define CAT7_INFO_EBV_NU	0x20
+#define CAT7_INFO_EBV_DE	0x24
+#define CAT7_INFO_ISO		0x28
+#define CAT7_INFO_FLASH		0x2a
+#define CAT7_INFO_SDR		0x2c
+#define CAT7_INFO_QVAL		0x2e
+
+/*
+ * Category 9 - Face Detection
+ */
+#define CAT9_FD_CTL		0x00
+
+#define BIT_FD_EN		0
+#define BIT_FD_DRAW_FACE_FRAME	4
+#define BIT_FD_DRAW_SMILE_LVL	6
+#define REG_FD(shift)		(1 << shift)
+#define REG_FD_OFF		0x0
+
+/*
+ * Category A - Lens Parameter
+ */
+#define CATA_AF_MODE		0x01
+#define CATA_AF_EXECUTE		0x02
+#define CATA_AF_STATUS		0x03
+#define CATA_AF_VERSION		0x0a
+
+#define REG_AF_FAIL		0x00
+#define REG_AF_SUCCESS		0x02
+#define REG_AF_IDLE		0x04
+#define REG_AF_BUSY		0x05
+
+#define REG_AF_MODE_NORMAL	0x00	/* Normal AF, one time */
+#define REG_AF_MODE_MACRO	0x01	/* Macro AF, one time */
+#define REG_AF_MODE_POWEROFF	0x07
+
+#define REG_AF_STOP		0x00
+#define REG_AF_EXE_AUTO		0x01
+#define REG_AF_EXE_CAF		0x02
+
+/*
+ * Category B - CAPTURE Parameter
+ */
+#define CATB_YUVOUT_MAIN	0x00
+#define CATB_MAIN_IMAGE_SIZE	0x01
+#define CATB_MCC_MODE		0x1d
+#define CATB_WDR_EN		0x2c
+#define CATB_LIGHT_CTRL		0x40
+#define CATB_FLASH_CTRL		0x41
+
+/* n = 0..3 */
+#define CATB_JPEG_SIZE_MAX(n)	(0x12 - (n))
+#define CATB_JPEG_SIZE_MIN(n)	(0x16 - (n))
+
+#define REG_YUV422		0x00
+#define REG_BAYER10		0x05
+#define REG_BAYER8		0x06
+#define REG_JPEG		0x10
+
+#define REG_MCC_OFF		0x00
+#define REG_MCC_NORMAL		0x01
+
+#define REG_WDR_OFF		0x00
+#define REG_WDR_ON		0x01
+#define REG_WDR_AUTO		0x02
+
+#define REG_LIGHT_OFF		0x00
+#define REG_LIGHT_ON		0x01
+#define REG_LIGHT_AUTO		0x02
+
+#define REG_FLASH_OFF		0x00
+#define REG_FLASH_ON		0x01
+#define REG_FLASH_AUTO		0x02
+
+/*
+ * Category C - CAPTURE Control
+ */
+#define CATC_CAP_MODE		0x00
+#define CATC_CAP_SEL_FRAME	0x06	/* It determines Single or Multi. */
+#define CATC_CAP_START		0x09
+#define CATC_CAP_IMAGE_SIZE	0x0d
+#define CATC_CAP_THUMB_SIZE	0x11
+
+#define REG_CAP_MODE_NONE	0x00
+#define REG_CAP_MODE_ANTI_SHAKE	0x02
+
+#define REG_CAP_START_MAIN	0x01
+#define REG_CAP_START_THUMB	0x03
+
+/*
+ * Category F - Flash
+ *
+ * This mode provides functions about internal flash stuff and system startup.
+ */
+#define CATF_CAM_START		0x12	/* It start internal ARM core booting
+					 * after power-up */
+
+#define REG_START_ARM_BOOT	0x01
+
+#endif	/* M5MOLS_REG_H */
diff --git a/include/media/m5mols.h b/include/media/m5mols.h
new file mode 100644
index 0000000..2d7e7ca
--- /dev/null
+++ b/include/media/m5mols.h
@@ -0,0 +1,35 @@
+/*
+ * Driver header for M-5MOLS 8M Pixel camera sensor with ISP
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * Author: HeungJun Kim, riverful.kim@samsung.com
+ *
+ * Copyright (C) 2009 Samsung Electronics Co., Ltd.
+ * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com
+ *
+ * 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 MEDIA_M5MOLS_H
+#define MEDIA_M5MOLS_H
+
+/**
+ * struct m5mols_platform_data - platform data for M-5MOLS driver
+ * @irq:	GPIO getting the irq pin of M-5MOLS
+ * @gpio_reset:	GPIO driving the reset pin of M-5MOLS
+ * @reset_polarity: active state for gpio_rst pin, 0 or 1
+ * @set_power:	an additional callback to the board setup code
+ *		to be called after enabling and before disabling
+ *		the sensor's supply regulators
+ */
+struct m5mols_platform_data {
+	int irq;
+	int gpio_reset;
+	u8 reset_polarity;
+	int (*set_power)(struct device *dev, int on);
+};
+
+#endif	/* MEDIA_M5MOLS_H */
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH v8] Add support for M-5MOLS 8 Mega Pixel camera ISP
  2011-05-13 11:38   ` HeungJun, Kim
@ 2011-05-16  1:03     ` HeungJun, Kim
  2011-05-20  5:56       ` [PATCH v9] " HeungJun, Kim
  0 siblings, 1 reply; 34+ messages in thread
From: HeungJun, Kim @ 2011-05-16  1:03 UTC (permalink / raw)
  To: linux-media
  Cc: hverkuil, laurent.pinchart, sungchun.kang, HeungJun, Kim,
	Sylwester Nawrocki, Kyungmin Park

Add I2C/V4L2 subdev driver for M-5MOLS integrated image signal processor
with 8 Mega Pixel sensor.

Signed-off-by: HeungJun, Kim <riverful.kim@samsung.com>
Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---

Hello everyone,

This is the eighth version of the subdev for M-5MOLS 8M Pixel camera sensor.
The seventh version has a critical bug about missing media_entity_cleanup(),
but there is few changes than the previous version.

The differences against the previous version is the following:
1. add missing media_entity_cleanup()
2. add the array index check for preventing buffer overrun in m5mols_get_version()
3. use v4l2_dbg/v4l2_err instead of dev_dbg/dev_err
4. rename m5mols_sync_control to m5mols_sync_controls
5. improved description m5mols_start_monitor() function
6. call info->set_power(0) in m5mols_sensor_power when the regulator enable fails

And, the re-sending seventh version is the following:
http://www.spinics.net/lists/linux-media/msg32319.html

I'm sorry about this frequent version up again, but I hope you understand that
it was necessary for more perfect code.

--
Thanks and Regards,
Heungjun Kim
Samsung Electronics DMC R&D Center
---
 drivers/media/video/Kconfig                  |    2 +
 drivers/media/video/Makefile                 |    1 +
 drivers/media/video/m5mols/Kconfig           |    5 +
 drivers/media/video/m5mols/Makefile          |    3 +
 drivers/media/video/m5mols/m5mols.h          |  357 +++++++++
 drivers/media/video/m5mols/m5mols_capture.c  |  217 ++++++
 drivers/media/video/m5mols/m5mols_controls.c |  293 ++++++++
 drivers/media/video/m5mols/m5mols_core.c     | 1031 ++++++++++++++++++++++++++
 drivers/media/video/m5mols/m5mols_reg.h      |  321 ++++++++
 include/media/m5mols.h                       |   35 +
 10 files changed, 2265 insertions(+), 0 deletions(-)
 create mode 100644 drivers/media/video/m5mols/Kconfig
 create mode 100644 drivers/media/video/m5mols/Makefile
 create mode 100644 drivers/media/video/m5mols/m5mols.h
 create mode 100644 drivers/media/video/m5mols/m5mols_capture.c
 create mode 100644 drivers/media/video/m5mols/m5mols_controls.c
 create mode 100644 drivers/media/video/m5mols/m5mols_core.c
 create mode 100644 drivers/media/video/m5mols/m5mols_reg.h
 create mode 100644 include/media/m5mols.h

diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index d61414e..242c80c 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -753,6 +753,8 @@ config VIDEO_NOON010PC30
 	---help---
 	  This driver supports NOON010PC30 CIF camera from Siliconfile
 
+source "drivers/media/video/m5mols/Kconfig"
+
 config VIDEO_OMAP3
 	tristate "OMAP 3 Camera support (EXPERIMENTAL)"
 	select OMAP_IOMMU
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index a10e4c3..d5d6de1 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -69,6 +69,7 @@ obj-$(CONFIG_VIDEO_MT9V011) += mt9v011.o
 obj-$(CONFIG_VIDEO_MT9V032) += mt9v032.o
 obj-$(CONFIG_VIDEO_SR030PC30)	+= sr030pc30.o
 obj-$(CONFIG_VIDEO_NOON010PC30)	+= noon010pc30.o
+obj-$(CONFIG_VIDEO_M5MOLS)	+= m5mols/
 
 obj-$(CONFIG_SOC_CAMERA_IMX074)		+= imx074.o
 obj-$(CONFIG_SOC_CAMERA_MT9M001)	+= mt9m001.o
diff --git a/drivers/media/video/m5mols/Kconfig b/drivers/media/video/m5mols/Kconfig
new file mode 100644
index 0000000..302dc3d
--- /dev/null
+++ b/drivers/media/video/m5mols/Kconfig
@@ -0,0 +1,5 @@
+config VIDEO_M5MOLS
+	tristate "Fujitsu M-5MOLS 8MP sensor support"
+	depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+	---help---
+	  This driver supports Fujitsu M-5MOLS camera sensor with ISP
diff --git a/drivers/media/video/m5mols/Makefile b/drivers/media/video/m5mols/Makefile
new file mode 100644
index 0000000..0a44e02
--- /dev/null
+++ b/drivers/media/video/m5mols/Makefile
@@ -0,0 +1,3 @@
+m5mols-objs	:= m5mols_core.o m5mols_controls.o m5mols_capture.o
+
+obj-$(CONFIG_VIDEO_M5MOLS)		+= m5mols.o
diff --git a/drivers/media/video/m5mols/m5mols.h b/drivers/media/video/m5mols/m5mols.h
new file mode 100644
index 0000000..107a8d6
--- /dev/null
+++ b/drivers/media/video/m5mols/m5mols.h
@@ -0,0 +1,357 @@
+/*
+ * Header for M-5MOLS 8M Pixel camera sensor with ISP
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * Author: HeungJun Kim, riverful.kim@samsung.com
+ *
+ * Copyright (C) 2009 Samsung Electronics Co., Ltd.
+ * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com
+ *
+ * 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-subdev.h>
+#include "m5mols_reg.h"
+
+extern int m5mols_debug;
+
+#define M5MOLS_BYTE_READ	0x01
+#define M5MOLS_BYTE_WRITE	0x02
+
+enum m5mols_i2c_size {
+	I2C_8BIT	= 1,
+	I2C_16BIT	= 2,
+	I2C_32BIT	= 4,
+	I2C_MAX		= I2C_32BIT,
+};
+
+/*
+ * In the MONITOR mode, the M-5MOLS sensor's format is YUV kinds, on the other
+ * hand, in the CAPTURE mode, its format is available to JPEG, RAW.
+ */
+enum m5mols_restype {
+	M5MOLS_RESTYPE_MONITOR,
+	M5MOLS_RESTYPE_CAPTURE,
+	M5MOLS_RESTYPE_MAX,
+};
+
+struct m5mols_resolution {
+	u8			reg;
+	enum m5mols_restype	type;
+	u16			width;
+	u16			height;
+};
+
+struct m5mols_exif {
+	u32			exposure_time;
+	u32			shutter_speed;
+	u32			aperture;
+	u32			brightness;
+	u32			exposure_bias;
+	u16			iso_speed;
+	u16			flash;
+	u16			sdr;	/* Subject(object) Distance Range */
+	u16			qval;	/* Not written exact meanning
+					 * in datasheet */
+};
+
+struct m5mols_capture {
+	struct m5mols_exif	exif;
+	u32			main;
+	u32			thumb;
+	u32			total;
+};
+
+struct m5mols_scenemode {	/* Recommended setting in document */
+	u32			metering;	/* AE light metering */
+	u32			ev_bias;	/* EV bias */
+	u32			wb_mode;	/* WhiteBalance(Auto/Manual) */
+	u32			wb_preset;	/* WhiteBalance Preset */
+	u32			chroma_en;	/* Chroma Enable */
+	u32			chroma_lvl;	/* Chroma Level */
+	u32			edge_en;	/* Edge Enable */
+	u32			edge_lvl;	/* Edge Level */
+	u32			af_range;	/* Auto Focus scan range */
+	u32			fd_mode;	/* Face Detection mode */
+	u32			mcc;		/* Multi-axis Color Conversion:
+						 * (A.K.A Emotion color) */
+	u32			light;		/* Light control */
+	u32			flash;		/* Flash control */
+
+				/* User setting needed for */
+	u32			tone;		/* Tone color(contrast) */
+	u32			iso;		/* ISO */
+	u32			capture_mode;	/* CAPTURE mode for
+						 * the Image stabilization */
+	u32			wdr;		/* Wide Dynamic Range */
+};
+
+#define VERSION_STRING_SIZE	22
+struct m5mols_version {
+	u8	customer;	/* Customer code:	bytes[0] */
+	u8	project;	/* Project code:	bytes[1] */
+	u16	fw;		/* FirmWare version:	bytes[3][2] */
+	u16	hw;		/* HardWare version:	bytes[5][4] */
+	u16	param;		/* Parameter version:	bytes[7][6] */
+	u16	awb;		/* AWB version:		bytes[9][8] */
+	u8	str[VERSION_STRING_SIZE];		/* manufacturer &
+							 * packging vendor */
+	u8	af;		/* AF version:		seperate register */
+};
+#define VERSION_SIZE		sizeof(struct m5mols_version)
+
+/* The LSB 2 bytes of version string means packaging manufacturer */
+#define SAMSUNG_ELECTRO		"SE"	/* Samsung Electro-Mechanics */
+#define SAMSUNG_OPTICS		"OP"	/* Samsung Fiber-Optics */
+#define SAMSUNG_TECHWIN		"TB"	/* Samsung Techwin */
+
+struct m5mols_info {
+	const struct m5mols_platform_data	*pdata;
+	struct v4l2_subdev		sd;
+	struct media_pad		pad;
+	struct v4l2_mbus_framefmt	ffmt[M5MOLS_RESTYPE_MAX];
+	int				curr_res_type;
+	enum v4l2_mbus_pixelcode	code;
+	wait_queue_head_t		wait_capture;
+	struct work_struct		work_irq;
+
+	struct v4l2_ctrl_handler	handle;
+	struct {
+		struct v4l2_ctrl	*autoexposure;
+		struct v4l2_ctrl	*exposure;
+	};
+	struct v4l2_ctrl		*autowb;
+	struct v4l2_ctrl		*colorfx;
+	struct v4l2_ctrl		*saturation;
+	struct v4l2_ctrl		*zoom;
+
+	struct m5mols_version		ver;
+	struct m5mols_capture		cap;
+
+	bool				power;
+	bool				ctrl_sync;
+	bool				capture;
+	bool				lock_ae;
+	bool				lock_awb;
+
+	/* Saved register value */
+	u8				resolution;
+	u32				interrupt;
+	u32				mode;
+	u32				mode_save;
+
+	/* Optional power callback function dealing with like GPIO. */
+	int (*set_power)(struct device *dev, int on);
+};
+
+/* Helper functions */
+static inline struct m5mols_info *to_m5mols(struct v4l2_subdev *sd)
+{
+	return container_of(sd, struct m5mols_info, sd);
+}
+
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+	return &container_of(ctrl->handler, struct m5mols_info, handle)->sd;
+}
+
+static inline bool is_powered(struct m5mols_info *info)
+{
+	return info->power;
+}
+
+static inline bool is_ctrl_synced(struct m5mols_info *info)
+{
+	return info->ctrl_sync;
+}
+
+static inline bool is_captured(struct m5mols_info *info)
+{
+	return info->capture;
+}
+
+static inline bool is_manufacturer(struct m5mols_info *info, u8 *manufacturer)
+{
+	return (info->ver.str[0] == manufacturer[0] &&
+			info->ver.str[1] == manufacturer[1]) ?
+		true : false;
+}
+
+#define is_code(__code, __type) (__code == m5mols_default_ffmt[__type].code)
+
+/*
+ * m5mols_read_reg() / m5mols_write_reg()
+ *
+ * For reading something from M-5MOLS sensor, the I2C operation is needed 2
+ * packets, one is for requesting reading and another is for getting data.
+ * On the other hand, for writing, it's needed just 1. The value's
+ * order in the packet is much similar in both case reading and writing.
+ *
+ * I2C packet common order
+ *   1st : size (data size + 4)
+ *   2nd : flag (R:0x01, W:0x02)
+ *   3rd : category
+ *   4th : command
+ *
+ * Added extra bytes in case reading
+ *   5th: data
+ *   And read another I2C bytes again as much data size
+ *
+ * Added extra bytes in case writing
+ *   5th..8th: data(as much size of data)
+ */
+int m5mols_read_reg(struct v4l2_subdev *sd, enum m5mols_i2c_size size,
+		    u8 category, u8 cmd, u32 *val);
+int m5mols_write_reg(struct v4l2_subdev *sd, enum m5mols_i2c_size size,
+		     u8 category, u8 cmd, u32 val);
+int m5mols_check_busy(struct v4l2_subdev *sd,
+		      u8 category, u8 cmd, u32 value);
+
+/*
+ * The m5mols_change_mode() makes the right order executing command for
+ * changing to the desired operating mode. The three modes(PARAMETER,
+ * MONITOR, CAPTURE) exist, and only these modes can be changed intentionally
+ * by user. The each category is assigned in the given mode.
+ * +============================================================+
+ * | mode	| category					|
+ * +============================================================+
+ * | These mode must be done for using the sensor automatically |
+ * +------------------------------------------------------------+
+ * | FLASH	| FLASH(be only after Stand-by or Power-on)	|
+ * | SYSTEM	| SYSTEM(be only after sensor arm-booting)	|
+ * +============================================================+
+ * | Usually these mode is used on operating sensor		|
+ * +------------------------------------------------------------+
+ * | PARAMETER	| PARAMETER					|
+ * | MONITOR	| MONITOR(preview), Auto Focus, Face Detection	|
+ * | CAPTURE	| Single CAPTURE, Preview(recording)		|
+ * +============================================================+
+ *
+ * The possible executing order between each modes is the following:
+ * ============================================================
+ *   +-----------+         +---------+          +---------+
+ *   | PARAMETER |<------->| MONITOR |<-------->| CAPTURE |
+ *   +-----------+         +---------+          +---------+
+ */
+int m5mols_change_mode(struct m5mols_info *info, u32 mode);
+
+int m5mols_change_scenemode(struct m5mols_info *info, u32 mode);
+int m5mols_enable_interrupt(struct v4l2_subdev *sd, u32 reg);
+int m5mols_start_capture(struct m5mols_info *info);
+int m5mols_sync_controls(struct m5mols_info *info);
+int m5mols_lock_3a(struct m5mols_info *info, bool lock);
+int m5mols_set_ctrl(struct v4l2_ctrl *ctrl);
+
+static inline int __must_check
+i2c_w8_system(struct v4l2_subdev *sd, u8 cmd, u32 val)
+{
+	return m5mols_write_reg(sd, I2C_8BIT, CAT_SYSTEM, cmd, val);
+}
+
+static inline int __must_check
+i2c_w8_param(struct v4l2_subdev *sd, u8 cmd, u32 val)
+{
+	return m5mols_write_reg(sd, I2C_8BIT, CAT_PARAM, cmd, val);
+}
+
+static inline int __must_check
+i2c_w8_mon(struct v4l2_subdev *sd, u8 cmd, u32 val)
+{
+	return m5mols_write_reg(sd, I2C_8BIT, CAT_MONITOR, cmd, val);
+}
+
+static inline int __must_check
+i2c_w8_ae(struct v4l2_subdev *sd, u8 cmd, u32 val)
+{
+	return m5mols_write_reg(sd, I2C_8BIT, CAT_AE, cmd, val);
+}
+
+static inline int __must_check
+i2c_w16_ae(struct v4l2_subdev *sd, u8 cmd, u32 val)
+{
+	return m5mols_write_reg(sd, I2C_16BIT, CAT_AE, cmd, val);
+}
+
+static inline int __must_check
+i2c_w8_wb(struct v4l2_subdev *sd, u8 cmd, u32 val)
+{
+	return m5mols_write_reg(sd, I2C_8BIT, CAT_WB, cmd, val);
+}
+
+static inline int __must_check
+i2c_w8_fd(struct v4l2_subdev *sd, u8 cmd, u32 val)
+{
+	return m5mols_write_reg(sd, I2C_8BIT, CAT_FD, cmd, val);
+}
+
+static inline int __must_check
+i2c_w8_lens(struct v4l2_subdev *sd, u8 cmd, u32 val)
+{
+	return m5mols_write_reg(sd, I2C_8BIT, CAT_LENS, cmd, val);
+}
+
+static inline int __must_check
+i2c_w8_capt_param(struct v4l2_subdev *sd, u8 cmd, u32 val)
+{
+	return m5mols_write_reg(sd, I2C_8BIT, CAT_CAPTURE_PARAMETER, cmd, val);
+}
+
+static inline int __must_check
+i2c_w8_capt_ctrl(struct v4l2_subdev *sd, u8 cmd, u32 val)
+{
+	return m5mols_write_reg(sd, I2C_8BIT, CAT_CAPTURE_CONTROL, cmd, val);
+}
+
+static inline int __must_check
+i2c_w8_flash(struct v4l2_subdev *sd, u8 cmd, u32 val)
+{
+	return m5mols_write_reg(sd, I2C_8BIT, CAT_FLASH, cmd, val);
+}
+
+static inline int __must_check
+i2c_r8_system(struct v4l2_subdev *sd, u8 cmd, u32 *val)
+{
+	return m5mols_read_reg(sd, I2C_8BIT, CAT_SYSTEM, cmd, val);
+}
+
+static inline int __must_check
+i2c_r16_ae(struct v4l2_subdev *sd, u8 cmd, u32 *val)
+{
+	return m5mols_read_reg(sd, I2C_16BIT, CAT_AE, cmd, val);
+}
+
+static inline int __must_check
+i2c_r8_lens(struct v4l2_subdev *sd, u8 cmd, u32 *val)
+{
+	return m5mols_read_reg(sd, I2C_8BIT, CAT_LENS, cmd, val);
+}
+
+static inline int __must_check
+i2c_r32_capt_ctrl(struct v4l2_subdev *sd, u8 cmd, u32 *val)
+{
+	return m5mols_read_reg(sd, I2C_32BIT, CAT_CAPTURE_CONTROL, cmd, val);
+}
+
+static inline int __must_check
+i2c_r16_exif(struct v4l2_subdev *sd, u8 cmd, u32 *val)
+{
+	return m5mols_read_reg(sd, I2C_16BIT, CAT_EXIF, cmd, val);
+}
+
+static inline int __must_check
+i2c_r32_exif(struct v4l2_subdev *sd, u8 cmd, u32 *val)
+{
+	return m5mols_read_reg(sd, I2C_32BIT, CAT_EXIF, cmd, val);
+}
+
+/* The firmware function */
+int m5mols_update_fw(struct v4l2_subdev *sd,
+		     int (*set_power)(struct m5mols_info *, bool));
+
+#endif	/* M5MOLS_H */
diff --git a/drivers/media/video/m5mols/m5mols_capture.c b/drivers/media/video/m5mols/m5mols_capture.c
new file mode 100644
index 0000000..f2f1045
--- /dev/null
+++ b/drivers/media/video/m5mols/m5mols_capture.c
@@ -0,0 +1,217 @@
+/*
+ * The Capture code for Fujitsu M-5MOLS ISP
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * Author: HeungJun Kim, riverful.kim@samsung.com
+ *
+ * Copyright (C) 2009 Samsung Electronics Co., Ltd.
+ * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com
+ *
+ * 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/slab.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/version.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/videodev2.h>
+#include <linux/version.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+#include <media/m5mols.h>
+
+#include "m5mols.h"
+#include "m5mols_reg.h"
+
+static int m5mols_capture_error_handler(struct m5mols_info *info,
+					int timeout)
+{
+	int ret;
+
+	/* Disable all interrupt & clear desired interrupt */
+	ret = i2c_w8_system(&info->sd, CAT0_INT_ENABLE,
+			info->interrupt & ~(REG_INT_CAPTURE));
+	if (ret)
+		return ret;
+
+	/* If all timeout exhausted, return error. */
+	if (!timeout)
+		return -ETIMEDOUT;
+
+	/* Clear capture */
+	if (!ret)
+		info->capture = false;
+
+	return 0;
+}
+
+/* m5mols_capture_info() - Gather captured image informations. For now,
+ * it gathers only EXIF information and file size. */
+static int m5mols_capture_info(struct m5mols_info *info, bool msgon)
+{
+	struct v4l2_subdev *sd = &info->sd;
+	struct m5mols_exif *exif = &info->cap.exif;
+	int denominator, numerator;
+	int ret;
+
+	ret = i2c_r32_exif(sd, CAT7_INFO_EXPTIME_NU, &numerator);
+	if (!ret)
+		ret = i2c_r32_exif(sd, CAT7_INFO_EXPTIME_DE, &denominator);
+	if (!ret)
+		exif->exposure_time = (u32)(numerator / denominator);
+	if (ret)
+		return ret;
+
+	ret = i2c_r32_exif(sd, CAT7_INFO_TV_NU, &numerator);
+	if (!ret)
+		ret = i2c_r32_exif(sd, CAT7_INFO_TV_DE, &denominator);
+	if (!ret)
+		exif->shutter_speed = (u32)(numerator / denominator);
+	if (ret)
+		return ret;
+
+	ret = i2c_r32_exif(sd, CAT7_INFO_AV_NU, &numerator);
+	if (!ret)
+		ret = i2c_r32_exif(sd, CAT7_INFO_AV_DE, &denominator);
+	if (!ret)
+		exif->aperture = (u32)(numerator / denominator);
+	if (ret)
+		return ret;
+
+	ret = i2c_r32_exif(sd, CAT7_INFO_BV_NU, &numerator);
+	if (!ret)
+		ret = i2c_r32_exif(sd, CAT7_INFO_BV_DE, &denominator);
+	if (!ret)
+		exif->brightness = (u32)(numerator / denominator);
+	if (ret)
+		return ret;
+
+	ret = i2c_r32_exif(sd, CAT7_INFO_EBV_NU, &numerator);
+	if (!ret)
+		ret = i2c_r32_exif(sd, CAT7_INFO_EBV_DE, &denominator);
+	if (!ret)
+		exif->exposure_bias = (u32)(numerator / denominator);
+	if (ret)
+		return ret;
+
+	ret = i2c_r16_exif(sd, CAT7_INFO_ISO, (u32 *)&exif->iso_speed);
+	if (!ret)
+		ret = i2c_r16_exif(sd, CAT7_INFO_FLASH, (u32 *)&exif->flash);
+	if (!ret)
+		ret = i2c_r16_exif(sd, CAT7_INFO_SDR, (u32 *)&exif->sdr);
+	if (!ret)
+		ret = i2c_r16_exif(sd, CAT7_INFO_QVAL, (u32 *)&exif->qval);
+	if (ret)
+		return ret;
+
+	if (!ret)
+		ret = i2c_r32_capt_ctrl(sd, CATC_CAP_IMAGE_SIZE,
+				&info->cap.main);
+	if (!ret)
+		ret = i2c_r32_capt_ctrl(sd, CATC_CAP_THUMB_SIZE,
+				&info->cap.thumb);
+	if (ret)
+		return ret;
+
+	info->cap.total = info->cap.main + info->cap.thumb;
+
+	if (msgon) {
+		struct i2c_client *client = v4l2_get_subdevdata(sd);
+		struct device *cdev = &client->dev;
+
+		dev_info(cdev, "capture: total size\t%d\n", info->cap.total);
+		dev_info(cdev, "capture: main size\t%d\n", info->cap.main);
+		dev_info(cdev, "capture: thumb size\t%d\n", info->cap.thumb);
+		dev_info(cdev, "capture: exposure_time\t%d\n",
+				exif->exposure_time);
+		dev_info(cdev, "capture: shutter_speed\t%d\n",
+				exif->shutter_speed);
+		dev_info(cdev, "capture: aperture\t%d\n", exif->aperture);
+		dev_info(cdev, "capture: brightness\t%d\n", exif->brightness);
+		dev_info(cdev, "capture: exposure_bias\t%d\n",
+				exif->exposure_bias);
+		dev_info(cdev, "capture: iso_speed\t%d\n", exif->iso_speed);
+		dev_info(cdev, "capture: flash\t%d\n", exif->flash);
+		dev_info(cdev, "capture: sdr\t%d\n", exif->sdr);
+		dev_info(cdev, "capture: qval\t%d\n", exif->qval);
+	}
+
+	return ret;
+}
+
+int m5mols_start_capture(struct m5mols_info *info)
+{
+	struct v4l2_subdev *sd = &info->sd;
+	u32 resolution = info->resolution;
+	int timeout;
+	int ret;
+
+	/*
+	 * Preparing capture. Setting control & interrupt before entering
+	 * capture mode
+	 *
+	 * 1) change to MONITOR mode for operating control & interrupt
+	 * 2) set controls (considering v4l2_control value & lock 3A)
+	 * 3) set interrupt
+	 * 4) change to CAPTURE mode
+	 */
+	ret = m5mols_change_mode(info, REG_MODE_MONITOR);
+	if (!ret)
+		ret = m5mols_sync_controls(info);
+	if (!ret)
+		ret = m5mols_lock_3a(info, true);
+	if (!ret)
+		ret = m5mols_enable_interrupt(sd, REG_INT_CAPTURE);
+	if (!ret)
+		ret = m5mols_change_mode(info, REG_MODE_CAPTURE);
+	if (!ret)
+		/* Wait for capture interrupt, after changing capture mode */
+		timeout = wait_event_interruptible_timeout(info->wait_capture,
+				is_captured(info), msecs_to_jiffies(2000));
+	if (!ret && is_captured(info))
+		ret = m5mols_capture_error_handler(info, timeout);
+	if (!ret)
+		ret = m5mols_lock_3a(info, false);
+	if (ret)
+		return ret;
+
+	/*
+	 * Starting capture. Setting capture frame count and resolution and
+	 * the format(available format: JPEG, Bayer RAW, YUV).
+	 *
+	 * 1) select single or multi(enable to 25), format, size
+	 * 2) set interrupt
+	 * 3) start capture(for main image, now)
+	 * 4) get information
+	 * 5) notify file size to v4l2 device(e.g, to s5p-fimc v4l2 device)
+	 */
+	ret = i2c_w8_capt_ctrl(sd, CATC_CAP_SEL_FRAME, 1);
+	if (!ret)
+		ret = i2c_w8_capt_param(sd, CATB_YUVOUT_MAIN, REG_JPEG);
+	if (!ret)
+		ret = i2c_w8_capt_param(sd, CATB_MAIN_IMAGE_SIZE, resolution);
+	if (!ret)
+		ret = m5mols_enable_interrupt(sd, REG_INT_CAPTURE);
+	if (!ret)
+		ret = i2c_w8_capt_ctrl(sd, CATC_CAP_START, REG_CAP_START_MAIN);
+	if (!ret)
+		/* Wait for capture interrupt, after starting capture */
+		timeout = wait_event_interruptible_timeout(info->wait_capture,
+				is_captured(info), msecs_to_jiffies(2000));
+	if (!ret && is_captured(info))
+		ret = m5mols_capture_info(info, false);
+	if (!ret)
+		v4l2_subdev_notify(sd, 0, &info->cap.total);
+	if (!ret)
+		ret = m5mols_capture_error_handler(info, timeout);
+
+	return ret;
+}
diff --git a/drivers/media/video/m5mols/m5mols_controls.c b/drivers/media/video/m5mols/m5mols_controls.c
new file mode 100644
index 0000000..1eb909e
--- /dev/null
+++ b/drivers/media/video/m5mols/m5mols_controls.c
@@ -0,0 +1,293 @@
+/*
+ * Controls for M-5MOLS 8M Pixel camera sensor with ISP
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * Author: HeungJun Kim, riverful.kim@samsung.com
+ *
+ * Copyright (C) 2009 Samsung Electronics Co., Ltd.
+ * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com
+ *
+ * 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/videodev2.h>
+#include <media/v4l2-ctrls.h>
+
+#include "m5mols.h"
+#include "m5mols_reg.h"
+
+static struct m5mols_scenemode m5mols_default_scenemode[] = {
+	[REG_SCENE_NORMAL] = {
+		REG_AE_MODE_CENTER, REG_AE_INDEX_00, REG_AWB_MODE_AUTO, 0,
+		REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
+		REG_AF_MODE_NORMAL, REG_FD_OFF,
+		REG_MCC_NORMAL, REG_LIGHT_OFF, REG_FLASH_OFF,
+		5, REG_ISO_AUTO, REG_CAP_MODE_NONE, REG_WDR_OFF,
+	},
+	[REG_SCENE_PORTRAIT] = {
+		REG_AE_MODE_CENTER, REG_AE_INDEX_00, REG_AWB_MODE_AUTO, 0,
+		REG_CHROMA_ON, 3, REG_EDGE_ON, 4,
+		REG_AF_MODE_NORMAL, BIT_FD_EN | BIT_FD_DRAW_FACE_FRAME,
+		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+		6, REG_ISO_AUTO, REG_CAP_MODE_NONE, REG_WDR_OFF,
+	},
+	[REG_SCENE_LANDSCAPE] = {
+		REG_AE_MODE_ALL, REG_AE_INDEX_00, REG_AWB_MODE_AUTO, 0,
+		REG_CHROMA_ON, 4, REG_EDGE_ON, 6,
+		REG_AF_MODE_NORMAL, REG_FD_OFF,
+		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+		6, REG_ISO_AUTO, REG_CAP_MODE_NONE, REG_WDR_OFF,
+	},
+	[REG_SCENE_SPORTS] = {
+		REG_AE_MODE_CENTER, REG_AE_INDEX_00, REG_AWB_MODE_AUTO, 0,
+		REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
+		REG_AF_MODE_NORMAL, REG_FD_OFF,
+		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+		6, REG_ISO_AUTO, REG_CAP_MODE_NONE, REG_WDR_OFF,
+	},
+	[REG_SCENE_PARTY_INDOOR] = {
+		REG_AE_MODE_CENTER, REG_AE_INDEX_00, REG_AWB_MODE_AUTO, 0,
+		REG_CHROMA_ON, 4, REG_EDGE_ON, 5,
+		REG_AF_MODE_NORMAL, REG_FD_OFF,
+		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+		6, REG_ISO_200, REG_CAP_MODE_NONE, REG_WDR_OFF,
+	},
+	[REG_SCENE_BEACH_SNOW] = {
+		REG_AE_MODE_CENTER, REG_AE_INDEX_10_POS, REG_AWB_MODE_AUTO, 0,
+		REG_CHROMA_ON, 4, REG_EDGE_ON, 5,
+		REG_AF_MODE_NORMAL, REG_FD_OFF,
+		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+		6, REG_ISO_50, REG_CAP_MODE_NONE, REG_WDR_OFF,
+	},
+	[REG_SCENE_SUNSET] = {
+		REG_AE_MODE_CENTER, REG_AE_INDEX_00, REG_AWB_MODE_PRESET,
+		REG_AWB_DAYLIGHT,
+		REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
+		REG_AF_MODE_NORMAL, REG_FD_OFF,
+		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+		6, REG_ISO_AUTO, REG_CAP_MODE_NONE, REG_WDR_OFF,
+	},
+	[REG_SCENE_DAWN_DUSK] = {
+		REG_AE_MODE_CENTER, REG_AE_INDEX_00, REG_AWB_MODE_PRESET,
+		REG_AWB_FLUORESCENT_1,
+		REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
+		REG_AF_MODE_NORMAL, REG_FD_OFF,
+		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+		6, REG_ISO_AUTO, REG_CAP_MODE_NONE, REG_WDR_OFF,
+	},
+	[REG_SCENE_FALL] = {
+		REG_AE_MODE_CENTER, REG_AE_INDEX_00, REG_AWB_MODE_AUTO, 0,
+		REG_CHROMA_ON, 5, REG_EDGE_ON, 5,
+		REG_AF_MODE_NORMAL, REG_FD_OFF,
+		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+		6, REG_ISO_AUTO, REG_CAP_MODE_NONE, REG_WDR_OFF,
+	},
+	[REG_SCENE_NIGHT] = {
+		REG_AE_MODE_CENTER, REG_AE_INDEX_00, REG_AWB_MODE_AUTO, 0,
+		REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
+		REG_AF_MODE_NORMAL, REG_FD_OFF,
+		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+		6, REG_ISO_AUTO, REG_CAP_MODE_NONE, REG_WDR_OFF,
+	},
+	[REG_SCENE_AGAINST_LIGHT] = {
+		REG_AE_MODE_CENTER, REG_AE_INDEX_00, REG_AWB_MODE_AUTO, 0,
+		REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
+		REG_AF_MODE_NORMAL, REG_FD_OFF,
+		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+		6, REG_ISO_AUTO, REG_CAP_MODE_NONE, REG_WDR_OFF,
+	},
+	[REG_SCENE_FIRE] = {
+		REG_AE_MODE_CENTER, REG_AE_INDEX_00, REG_AWB_MODE_AUTO, 0,
+		REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
+		REG_AF_MODE_NORMAL, REG_FD_OFF,
+		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+		6, REG_ISO_50, REG_CAP_MODE_NONE, REG_WDR_OFF,
+	},
+	[REG_SCENE_TEXT] = {
+		REG_AE_MODE_CENTER, REG_AE_INDEX_00, REG_AWB_MODE_AUTO, 0,
+		REG_CHROMA_ON, 3, REG_EDGE_ON, 7,
+		REG_AF_MODE_MACRO, REG_FD_OFF,
+		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+		6, REG_ISO_AUTO, REG_CAP_MODE_ANTI_SHAKE, REG_WDR_ON,
+	},
+	[REG_SCENE_CANDLE] = {
+		REG_AE_MODE_CENTER, REG_AE_INDEX_00, REG_AWB_MODE_AUTO, 0,
+		REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
+		REG_AF_MODE_NORMAL, REG_FD_OFF,
+		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+		6, REG_ISO_AUTO, REG_CAP_MODE_NONE, REG_WDR_OFF,
+	},
+};
+
+/* m5mols_change_scenemode() - Change current scenemode */
+int m5mols_change_scenemode(struct m5mols_info *info, u32 mode)
+{
+	struct v4l2_subdev *sd = &info->sd;
+	struct m5mols_scenemode scenemode = m5mols_default_scenemode[mode];
+	int ret;
+
+	ret = m5mols_lock_3a(info, false);
+	if (!ret)
+		ret = i2c_w8_ae(sd, CAT3_EV_PRESET_MODE_MON, mode);
+	if (!ret)
+		ret = i2c_w8_ae(sd, CAT3_EV_PRESET_MODE_CAP, mode);
+	if (!ret)
+		ret = i2c_w8_ae(sd, CAT3_AE_MODE, scenemode.metering);
+	if (!ret)
+		ret = i2c_w8_ae(sd, CAT3_AE_INDEX, scenemode.ev_bias);
+	if (!ret)
+		ret = i2c_w8_wb(sd, CAT6_AWB_MODE, scenemode.wb_mode);
+	if (!ret)
+		ret = i2c_w8_wb(sd, CAT6_AWB_MANUAL, scenemode.wb_preset);
+	if (!ret)
+		ret = i2c_w8_mon(sd, CAT2_CHROMA_EN, scenemode.chroma_en);
+	if (!ret)
+		ret = i2c_w8_mon(sd, CAT2_CHROMA_LVL, scenemode.chroma_lvl);
+	if (!ret)
+		ret = i2c_w8_mon(sd, CAT2_EDGE_EN, scenemode.edge_en);
+	if (!ret)
+		ret = i2c_w8_mon(sd, CAT2_EDGE_LVL, scenemode.edge_lvl);
+	if (!ret && info->ver.af)
+		ret = i2c_w8_lens(sd, CATA_AF_MODE, scenemode.af_range);
+	if (!ret && info->ver.af)
+		ret = i2c_w8_fd(sd, CAT9_FD_CTL, scenemode.fd_mode);
+	if (!ret)
+		ret = i2c_w8_mon(sd, CAT2_TONE_CTL, scenemode.tone);
+	if (!ret)
+		ret = i2c_w8_mon(sd, CAT3_ISO, scenemode.iso);
+	if (!ret)
+		ret = m5mols_change_mode(info, REG_MODE_CAPTURE);
+	if (!ret)
+		ret = i2c_w8_mon(sd, CATB_WDR_EN, scenemode.wdr);
+	if (!ret)
+		ret = i2c_w8_capt_param(sd, CATB_MCC_MODE, scenemode.mcc);
+	if (!ret)
+		ret = i2c_w8_capt_param(sd, CATB_LIGHT_CTRL, scenemode.light);
+	if (!ret)
+		ret = i2c_w8_capt_param(sd, CATB_FLASH_CTRL, scenemode.flash);
+	if (!ret)
+		ret = i2c_w8_capt_param(sd, CATB_FLASH_CTRL, scenemode.flash);
+	if (!ret)
+		ret = i2c_w8_mon(sd, CATC_CAP_MODE, scenemode.capture_mode);
+	if (!ret)
+		ret = m5mols_change_mode(info, REG_MODE_MONITOR);
+
+	return ret;
+}
+
+static int m5mols_lock_ae(struct m5mols_info *info, bool lock)
+{
+	int ret = 0;
+
+	if (info->lock_ae != lock)
+		ret = i2c_w8_ae(&info->sd, CAT3_AE_LOCK,
+				lock ? REG_AE_LOCK : REG_AE_UNLOCK);
+	if (!ret)
+		info->lock_ae = lock;
+
+	return ret;
+}
+
+static int m5mols_lock_awb(struct m5mols_info *info, bool lock)
+{
+	int ret = 0;
+
+	if (info->lock_awb != lock)
+		ret = i2c_w8_wb(&info->sd, CAT6_AWB_LOCK,
+				lock ? REG_AWB_LOCK : REG_AWB_UNLOCK);
+	if (!ret)
+		info->lock_awb = lock;
+
+	return ret;
+}
+
+/* m5mols_lock_3a() - Lock 3A(Auto Exposure, Whitebalance, Focus) */
+int m5mols_lock_3a(struct m5mols_info *info, bool lock)
+{
+	int ret;
+
+	ret = m5mols_lock_ae(info, lock);
+	if (!ret)
+		ret = m5mols_lock_awb(info, lock);
+	if (!ret && info->ver.af && lock)
+		/* Don't need to handle unlocking AF */
+		ret = i2c_w8_lens(&info->sd, CATA_AF_EXECUTE, REG_AF_STOP);
+
+	return ret;
+}
+
+/* m5mols_set_ctrl() - The main s_ctrl function called by m5mols_set_ctrl() */
+int m5mols_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct v4l2_subdev *sd = to_sd(ctrl);
+	struct m5mols_info *info = to_m5mols(sd);
+	int ret;
+
+	switch (ctrl->id) {
+	case V4L2_CID_ZOOM_ABSOLUTE:
+		return i2c_w8_mon(sd, CAT2_ZOOM, ctrl->val);
+
+	case V4L2_CID_EXPOSURE_AUTO:
+		ret = m5mols_lock_ae(info, ctrl->val == V4L2_EXPOSURE_AUTO ?
+				false : true);
+		if (!ret)
+			ret = i2c_w8_ae(sd, CAT3_AE_MODE,
+				ctrl->val == V4L2_EXPOSURE_AUTO ?
+				REG_AE_MODE_ALL : REG_AE_MODE_OFF);
+		if (!ret && ctrl->val == V4L2_EXPOSURE_MANUAL)
+			ret = i2c_w16_ae(sd, CAT3_MANUAL_GAIN_MON,
+				info->exposure->val);
+		if (!ret && ctrl->val == V4L2_EXPOSURE_MANUAL)
+			ret = i2c_w16_ae(sd, CAT3_MANUAL_GAIN_CAP,
+				info->exposure->val);
+		return ret;
+
+	case V4L2_CID_AUTO_WHITE_BALANCE:
+		ret = m5mols_lock_awb(info, ctrl->val ? false : true);
+		if (!ret)
+			ret = i2c_w8_wb(sd, CAT6_AWB_MODE, ctrl->val ?
+				REG_AWB_MODE_AUTO : REG_AWB_MODE_PRESET);
+		return ret;
+
+	case V4L2_CID_SATURATION:
+		ret = i2c_w8_mon(sd, CAT2_CHROMA_LVL, ctrl->val);
+		if (!ret)
+			ret = i2c_w8_mon(sd, CAT2_CHROMA_EN, REG_CHROMA_ON);
+		return ret;
+
+	case V4L2_CID_COLORFX:
+		/*
+		 * This control uses two kinds of registers: normal & color.
+		 * The normal effect belongs to category 1, while the color
+		 * one belongs to category 2.
+		 *
+		 * The normal effect uses one register: CAT1_EFFECT.
+		 * The color effect uses three registers:
+		 * CAT2_COLOR_EFFECT, CAT2_CFIXR, CAT2_CFIXB.
+		 */
+		ret = i2c_w8_param(sd, CAT1_EFFECT,
+			ctrl->val == V4L2_COLORFX_NEGATIVE ? REG_EFFECT_NEGA :
+			ctrl->val == V4L2_COLORFX_EMBOSS ? REG_EFFECT_EMBOSS :
+			REG_EFFECT_OFF);
+		if (!ret)
+			ret = i2c_w8_mon(sd, CAT2_COLOR_EFFECT,
+				ctrl->val == V4L2_COLORFX_SEPIA ?
+				REG_COLOR_EFFECT_ON : REG_COLOR_EFFECT_OFF);
+		if (!ret)
+			ret = i2c_w8_mon(sd, CAT2_CFIXR,
+				ctrl->val == V4L2_COLORFX_SEPIA ?
+				REG_CFIXR_SEPIA : 0);
+		if (!ret)
+			ret = i2c_w8_mon(sd, CAT2_CFIXB,
+				ctrl->val == V4L2_COLORFX_SEPIA ?
+				REG_CFIXB_SEPIA : 0);
+		return ret;
+	}
+
+	return -EINVAL;
+}
diff --git a/drivers/media/video/m5mols/m5mols_core.c b/drivers/media/video/m5mols/m5mols_core.c
new file mode 100644
index 0000000..85ec068
--- /dev/null
+++ b/drivers/media/video/m5mols/m5mols_core.c
@@ -0,0 +1,1031 @@
+/*
+ * Driver for M-5MOLS 8M Pixel camera sensor with ISP
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * Author: HeungJun Kim, riverful.kim@samsung.com
+ *
+ * Copyright (C) 2009 Samsung Electronics Co., Ltd.
+ * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com
+ *
+ * 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/slab.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/version.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+#include <media/m5mols.h>
+
+#include "m5mols.h"
+#include "m5mols_reg.h"
+
+int m5mols_debug;
+module_param(m5mols_debug, int, 0644);
+
+#define MOD_NAME		"M5MOLS"
+#define M5MOLS_I2C_CHECK_RETRY	500
+
+/* The regulator consumer names are derived from M-5MOLS datasheets. */
+static struct regulator_bulk_data supplies[] = {
+	{
+		.supply		= "core",	/* ARM core power, 1.2V */
+	}, {
+		.supply		= "dig_18",	/* digital power 1, 1.8V */
+	}, {
+		.supply		= "d_sensor",	/* sensor power 1, 1.8V */
+	}, {
+		.supply		= "dig_28",	/* digital power 2, 2.8V */
+	}, {
+		.supply		= "a_sensor",	/* analog power */
+	}, {
+		.supply		= "dig_12",	/* digital power 3, 1.2V */
+	},
+};
+
+const struct v4l2_mbus_framefmt m5mols_default_ffmt[M5MOLS_RESTYPE_MAX] = {
+	[M5MOLS_RESTYPE_MONITOR] = {
+		.width		= 1920,
+		.height		= 1080,
+		.code		= V4L2_MBUS_FMT_VYUY8_2X8,
+		.field		= V4L2_FIELD_NONE,
+		.colorspace	= V4L2_COLORSPACE_JPEG,
+	},
+	[M5MOLS_RESTYPE_CAPTURE] = {
+		.width		= 1920,
+		.height		= 1080,
+		.code		= V4L2_MBUS_FMT_JPEG_1X8,
+		.field		= V4L2_FIELD_NONE,
+		.colorspace	= V4L2_COLORSPACE_JPEG,
+	},
+};
+#define SIZE_DEFAULT_FFMT	ARRAY_SIZE(m5mols_default_ffmt)
+
+static const struct m5mols_resolution m5mols_reg_res[] = {
+	{ 0x01, M5MOLS_RESTYPE_MONITOR, 128, 96 },	/* SUB-QCIF */
+	{ 0x03, M5MOLS_RESTYPE_MONITOR, 160, 120 },	/* QQVGA */
+	{ 0x05, M5MOLS_RESTYPE_MONITOR, 176, 144 },	/* QCIF */
+	{ 0x06, M5MOLS_RESTYPE_MONITOR, 176, 176 },
+	{ 0x08, M5MOLS_RESTYPE_MONITOR, 240, 320 },	/* QVGA */
+	{ 0x09, M5MOLS_RESTYPE_MONITOR, 320, 240 },	/* QVGA */
+	{ 0x0c, M5MOLS_RESTYPE_MONITOR, 240, 400 },	/* WQVGA */
+	{ 0x0d, M5MOLS_RESTYPE_MONITOR, 400, 240 },	/* WQVGA */
+	{ 0x0e, M5MOLS_RESTYPE_MONITOR, 352, 288 },	/* CIF */
+	{ 0x13, M5MOLS_RESTYPE_MONITOR, 480, 360 },
+	{ 0x15, M5MOLS_RESTYPE_MONITOR, 640, 360 },	/* qHD */
+	{ 0x17, M5MOLS_RESTYPE_MONITOR, 640, 480 },	/* VGA */
+	{ 0x18, M5MOLS_RESTYPE_MONITOR, 720, 480 },
+	{ 0x1a, M5MOLS_RESTYPE_MONITOR, 800, 480 },	/* WVGA */
+	{ 0x1f, M5MOLS_RESTYPE_MONITOR, 800, 600 },	/* SVGA */
+	{ 0x21, M5MOLS_RESTYPE_MONITOR, 1280, 720 },	/* HD */
+	{ 0x25, M5MOLS_RESTYPE_MONITOR, 1920, 1080 },	/* 1080p */
+	{ 0x29, M5MOLS_RESTYPE_MONITOR, 3264, 2448 },	/* 2.63fps 8M */
+	{ 0x39, M5MOLS_RESTYPE_MONITOR, 800, 602 },	/* AHS_MON debug */
+
+	{ 0x02, M5MOLS_RESTYPE_CAPTURE, 320, 240 },	/* QVGA */
+	{ 0x04, M5MOLS_RESTYPE_CAPTURE, 400, 240 },	/* WQVGA */
+	{ 0x07, M5MOLS_RESTYPE_CAPTURE, 480, 360 },
+	{ 0x08, M5MOLS_RESTYPE_CAPTURE, 640, 360 },	/* qHD */
+	{ 0x09, M5MOLS_RESTYPE_CAPTURE, 640, 480 },	/* VGA */
+	{ 0x0a, M5MOLS_RESTYPE_CAPTURE, 800, 480 },	/* WVGA */
+	{ 0x10, M5MOLS_RESTYPE_CAPTURE, 1280, 720 },	/* HD */
+	{ 0x14, M5MOLS_RESTYPE_CAPTURE, 1280, 960 },	/* 1M */
+	{ 0x17, M5MOLS_RESTYPE_CAPTURE, 1600, 1200 },	/* 2M */
+	{ 0x19, M5MOLS_RESTYPE_CAPTURE, 1920, 1080 },	/* Full-HD */
+	{ 0x1a, M5MOLS_RESTYPE_CAPTURE, 2048, 1152 },	/* 3Mega */
+	{ 0x1b, M5MOLS_RESTYPE_CAPTURE, 2048, 1536 },
+	{ 0x1c, M5MOLS_RESTYPE_CAPTURE, 2560, 1440 },	/* 4Mega */
+	{ 0x1d, M5MOLS_RESTYPE_CAPTURE, 2560, 1536 },
+	{ 0x1f, M5MOLS_RESTYPE_CAPTURE, 2560, 1920 },	/* 5Mega */
+	{ 0x21, M5MOLS_RESTYPE_CAPTURE, 3264, 1836 },	/* 6Mega */
+	{ 0x22, M5MOLS_RESTYPE_CAPTURE, 3264, 1960 },
+	{ 0x25, M5MOLS_RESTYPE_CAPTURE, 3264, 2448 },	/* 8Mega */
+};
+
+/*
+ * m5mols_swap_byte() - byte array to integer conversion
+ * @size:	the size's kinds of I2C packet defined in the M-5MOLS datasheet
+ *
+ * Convert I2C data byte array with performing any required byte
+ * reordering to assure proper values for each data type, regardless
+ * of the architecture endianness.
+ */
+static u32 m5mols_swap_byte(u8 *data, enum m5mols_i2c_size size)
+{
+	if (size == I2C_8BIT)
+		return *data;
+	else if (size == I2C_16BIT)
+		return be16_to_cpu(*((u16 *)data));
+	else
+		return be32_to_cpu(*((u32 *)data));
+}
+
+/*
+ * m5mols_read_reg() - raw function of I2C reading operation
+ * @size:	the kinds of I2C packet's size defined in the M-5MOLS datasheet
+ * @category:	the category value of I2C command
+ * @cmd:	the command
+ * @val:	the read values
+ *
+ * The M-5MOLS I2C operation always consists of category, command, value(s)
+ * as an element. The numbers of each operation vary according to reading or
+ * writing. The more specific explanation can be found in the m5mol.h.
+ */
+int m5mols_read_reg(struct v4l2_subdev *sd,
+		    enum m5mols_i2c_size size,
+		    u8 category, u8 cmd, u32 *val)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct i2c_msg msg[2];
+	u8 wbuf[5], rbuf[I2C_MAX + 1];
+	int ret;
+
+	if (!client->adapter)
+		return -ENODEV;
+
+	if (size != I2C_8BIT && size != I2C_16BIT && size != I2C_32BIT)
+		return -EINVAL;
+
+	/* for designating category and command */
+	msg[0].addr = client->addr;
+	msg[0].flags = 0;
+	msg[0].len = 5;
+	msg[0].buf = wbuf;
+	wbuf[0] = 5;
+	wbuf[1] = M5MOLS_BYTE_READ;
+	wbuf[2] = category;
+	wbuf[3] = cmd;
+	wbuf[4] = size;
+
+	/* for reading desired data */
+	msg[1].addr = client->addr;
+	msg[1].flags = I2C_M_RD;
+	msg[1].len = size + 1;
+	msg[1].buf = rbuf;
+
+	/* minimum stabilization time */
+	usleep_range(200, 200);
+
+	ret = i2c_transfer(client->adapter, msg, 2);
+	if (ret < 0) {
+		dev_err(&client->dev,
+			"failed READ[%d] at cat[%02x] cmd[%02x]\n",
+			size, category, cmd);
+		return ret;
+	}
+
+	*val = m5mols_swap_byte(&rbuf[1], size);
+
+	return 0;
+}
+
+/*
+ * m5mols_write_reg() - raw function of I2C writing operation
+ * @size:	the kinds of I2C packet's size defined in the M-5MOLS datasheet
+ * @category:	the category value of I2C command
+ * @cmd:	the I2C command
+ * @val:	the written value(s)
+ */
+int m5mols_write_reg(struct v4l2_subdev *sd,
+		     enum m5mols_i2c_size size,
+		     u8 category, u8 cmd, u32 val)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct device *cdev = &client->dev;
+	struct i2c_msg msg[1];
+	u8 wbuf[I2C_MAX + 4];
+	u32 *buf;
+	int ret;
+
+	if (!client->adapter)
+		return -ENODEV;
+
+	if (size != I2C_8BIT && size != I2C_16BIT && size != I2C_32BIT) {
+		dev_err(cdev, "Wrong data size\n");
+		return -EINVAL;
+	}
+
+	msg->addr = client->addr;
+	msg->flags = 0;
+	msg->len = (u16)size + 4;
+	msg->buf = wbuf;
+	wbuf[0] = size + 4;
+	wbuf[1] = M5MOLS_BYTE_WRITE;
+	wbuf[2] = category;
+	wbuf[3] = cmd;
+
+	buf = (u32 *)&wbuf[4];
+	*buf = m5mols_swap_byte((u8 *)&val, size);
+
+	usleep_range(200, 200);
+
+	ret = i2c_transfer(client->adapter, msg, 1);
+	if (ret < 0) {
+		dev_err(&client->dev,
+			"failed WRITE[%d] at cat[%02x] cmd[%02x], ret %d\n",
+			size, msg->buf[2], msg->buf[3], ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+int m5mols_check_busy(struct v4l2_subdev *sd, u8 category, u8 cmd, u32 mask)
+{
+	u32 busy, i;
+	int ret;
+
+	for (i = 0; i < M5MOLS_I2C_CHECK_RETRY; i++) {
+		ret = m5mols_read_reg(sd, I2C_8BIT, category, cmd, &busy);
+		if (ret < 0)
+			return ret;
+
+		if ((busy & mask) == mask)
+			return 0;
+	}
+
+	return -EBUSY;
+}
+
+/*
+ * m5mols_enable_interrupt() - clear pending register and set interrupt
+ *
+ * Before writing desired interrupt value, should clear INT_FACTOR register
+ * automatically documented in the M-5MOLS datasheet.
+ */
+int m5mols_enable_interrupt(struct v4l2_subdev *sd, u32 reg)
+{
+	struct m5mols_info *info = to_m5mols(sd);
+	u32 dummy;
+	int ret;
+
+	ret = i2c_r8_system(sd, CAT0_INT_FACTOR, &dummy);
+	if (!ret)
+		ret = i2c_w8_system(sd, CAT0_INT_ENABLE,
+				info->ver.af ? reg & ~REG_INT_AF : reg);
+	return ret;
+}
+
+/*
+ * m5mols_write_mode() - Write the mode and check busy status.
+ *
+ * Changing the M-5MOLS mode accompanies a little delay all the time, so
+ * checking current busy status is needed to guarantee changing right mode.
+ */
+static int m5mols_write_mode(struct v4l2_subdev *sd, u32 mode)
+{
+	int ret = i2c_w8_system(sd, CAT0_SYSMODE, mode);
+	if (!ret)
+		ret = m5mols_check_busy(sd, CAT_SYSTEM, CAT0_SYSMODE, mode);
+
+	return ret;
+}
+
+/*
+ * m5mols_change_mode() - manage the M-5MOLS's mode.
+ * @mode:	the desired mode
+ *
+ * All commands of M-5MOLS belong to a specific its own mode. Each functionality
+ * can be guaranteed only when the sensor is operating in the mode which
+ * the command belongs to. The more explanation can be found in the m5mols.h.
+ */
+int m5mols_change_mode(struct m5mols_info *info, u32 mode)
+{
+	struct v4l2_subdev *sd = &info->sd;
+	int ret = -EINVAL;
+	u32 reg;
+
+	if (mode != REG_MODE_PARAM && mode != REG_MODE_MONITOR &&
+	    mode != REG_MODE_CAPTURE)
+		return ret;
+
+	ret = i2c_r8_system(sd, CAT0_SYSMODE, &reg);
+	if ((!ret && reg == mode) || ret)
+		return ret;
+
+	switch (reg) {
+	case REG_MODE_PARAM:
+		ret = m5mols_write_mode(sd, REG_MODE_MONITOR);
+		if (!ret && mode == REG_MODE_MONITOR)
+			break;
+		if (!ret)
+			ret = m5mols_write_mode(sd, REG_MODE_CAPTURE);
+		break;
+
+	case REG_MODE_MONITOR:
+		if (mode == REG_MODE_PARAM) {
+			ret = m5mols_write_mode(sd, REG_MODE_PARAM);
+			break;
+		}
+
+		ret = m5mols_write_mode(sd, REG_MODE_CAPTURE);
+		break;
+
+	case REG_MODE_CAPTURE:
+		ret = m5mols_write_mode(sd, REG_MODE_MONITOR);
+		if (!ret && mode == REG_MODE_MONITOR)
+			break;
+		if (!ret)
+			ret = m5mols_write_mode(sd, REG_MODE_PARAM);
+		break;
+
+	default:
+		v4l2_warn(sd, "Wrong mode: %d\n", mode);
+	}
+
+	if (!ret)
+		info->mode = mode;
+
+	return ret;
+}
+
+/*
+ * m5mols_get_version() - Retrieve all internal version of M-5MOLS.
+ *
+ * The version information includes revisions hardware and firmware, AutoFocus
+ * alghorithm version and the version string. The version string should be read
+ * at the same register offset until the read value is NULL.
+ */
+static int m5mols_get_version(struct v4l2_subdev *sd)
+{
+	struct m5mols_info *info = to_m5mols(sd);
+	union {
+		struct m5mols_version ver;
+		u8 bytes[VERSION_SIZE];
+	} value;
+	int ret, cmd = CAT0_CUSTOMER_CODE;
+
+	do {
+		ret = i2c_r8_system(sd, cmd, (u32 *)&value.bytes[cmd]);
+		if (ret)
+			return ret;
+	} while (cmd++ != CAT0_VERSION_AWB);
+
+	do {
+		ret = i2c_r8_system(sd, CAT0_VERSION_STRING,
+				(u32 *)&value.bytes[cmd]);
+		if (ret)
+			return ret;
+		if (cmd >= VERSION_SIZE - 1)
+			return -EINVAL;
+	} while (value.bytes[cmd++]);
+
+	ret = i2c_r8_lens(sd, CATA_AF_VERSION, (u32 *)&value.bytes[cmd]);
+	if (ret)
+		return ret;
+
+	/* store version information swapped for being readable */
+	info->ver	= value.ver;
+	info->ver.fw	= be16_to_cpu(info->ver.fw);
+	info->ver.hw	= be16_to_cpu(info->ver.hw);
+	info->ver.param	= be16_to_cpu(info->ver.param);
+	info->ver.awb	= be16_to_cpu(info->ver.awb);
+
+	v4l2_info(sd, "Manufacturer\t[%s]\n",
+			is_manufacturer(info, SAMSUNG_ELECTRO) ?
+			"Samsung Electro-Machanics" :
+			is_manufacturer(info, SAMSUNG_OPTICS) ?
+			"Samsung Fiber-Optics" :
+			is_manufacturer(info, SAMSUNG_TECHWIN) ?
+			"Samsung Techwin" : "None");
+	v4l2_info(sd, "Customer/Project\t[0x%02x/0x%02x]\n",
+			info->ver.customer, info->ver.project);
+	if (!info->ver.af)
+		v4l2_info(sd, "No support Auto Focus on this firmware\n");
+
+	return ret;
+}
+
+/*
+ * __find_restype() - look up M-5MOLS resolution type according to pixel code
+ * @code:	pixel code
+ */
+static enum m5mols_restype __find_restype(enum v4l2_mbus_pixelcode code)
+{
+	enum m5mols_restype type = M5MOLS_RESTYPE_MONITOR;
+
+	do {
+		if (code == m5mols_default_ffmt[type].code)
+			return type;
+	} while (type++ != SIZE_DEFAULT_FFMT);
+
+	return 0;
+}
+
+/*
+ * __find_resolution() - look up preset and type of M-5MOLS's resolution
+ * @mf:         pixel format to find/negotiate the resolution preset for
+ * @type:	M-5MOLS resolution type
+ * @resolution:	M-5MOLS resolution preset register value
+ *
+ * Find nearest resolution matching resolution preset and adjust mf
+ * to supported values.
+ */
+static int __find_resolution(struct v4l2_subdev *sd,
+			     struct v4l2_mbus_framefmt *mf,
+			     enum m5mols_restype *type,
+			     u32 *resolution)
+{
+	const struct m5mols_resolution *fsize = &m5mols_reg_res[0],
+				 *match = NULL;
+	enum m5mols_restype stype = __find_restype(mf->code);
+	int i = ARRAY_SIZE(m5mols_reg_res);
+	unsigned int min_err = ~0;
+
+	while (i--) {
+		int err;
+		if (stype != fsize->type)
+			continue;
+
+		err = abs(fsize->width - mf->width)
+			  + abs(fsize->height - mf->height);
+
+		if (err < min_err) {
+			min_err = err;
+			match = fsize;
+		}
+		fsize++;
+	}
+	if (match) {
+		mf->width  = match->width;
+		mf->height = match->height;
+		*resolution = match->reg;
+		*type = stype;
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static struct v4l2_mbus_framefmt *__find_format(struct m5mols_info *info,
+				struct v4l2_subdev_fh *fh,
+				enum v4l2_subdev_format_whence which,
+				enum m5mols_restype type)
+{
+	if (which == V4L2_SUBDEV_FORMAT_TRY)
+		return fh ? v4l2_subdev_get_try_format(fh, 0) : NULL;
+
+	return &info->ffmt[type];
+}
+
+static int m5mols_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+			  struct v4l2_subdev_format *fmt)
+{
+	struct m5mols_info *info = to_m5mols(sd);
+	struct v4l2_mbus_framefmt *format;
+
+	if (fmt->pad != 0)
+		return -EINVAL;
+
+	format = __find_format(info, fh, fmt->which, info->curr_res_type);
+	if (!format)
+		return -EINVAL;
+
+	fmt->format = *format;
+	return 0;
+}
+
+static int m5mols_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+			  struct v4l2_subdev_format *fmt)
+{
+	struct m5mols_info *info = to_m5mols(sd);
+	struct v4l2_mbus_framefmt *format = &fmt->format;
+	struct v4l2_mbus_framefmt *sfmt;
+	enum m5mols_restype type;
+	u32 resolution = 0;
+	int ret;
+
+	if (fmt->pad != 0)
+		return -EINVAL;
+
+	ret = __find_resolution(sd, format, &type, &resolution);
+	if (ret < 0)
+		return ret;
+
+	sfmt = __find_format(info, fh, fmt->which, type);
+	if (!sfmt)
+		return 0;
+
+	*sfmt		= m5mols_default_ffmt[type];
+	sfmt->width	= format->width;
+	sfmt->height	= format->height;
+
+	if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+		info->resolution	= resolution;
+		info->code		= format->code;
+		info->curr_res_type	= type;
+	}
+
+	return 0;
+}
+
+static int m5mols_enum_mbus_code(struct v4l2_subdev *sd,
+				 struct v4l2_subdev_fh *fh,
+				 struct v4l2_subdev_mbus_code_enum *code)
+{
+	if (!code || code->index >= SIZE_DEFAULT_FFMT)
+		return -EINVAL;
+
+	code->code = m5mols_default_ffmt[code->index].code;
+
+	return 0;
+}
+
+static struct v4l2_subdev_pad_ops m5mols_pad_ops = {
+	.enum_mbus_code	= m5mols_enum_mbus_code,
+	.get_fmt	= m5mols_get_fmt,
+	.set_fmt	= m5mols_set_fmt,
+};
+
+/*
+ * m5mols_sync_controls() - apply default scene mode and the current controls
+ *
+ * This is used only streaming for syncing between v4l2_ctrl framework and
+ * m5mols's controls. First, do the scenemode to the sensor, then call
+ * v4l2_ctrl_handler_setup(). It can be same between some commands and
+ * the scenemode's in the default v4l2_ctrls. But, such commands of control
+ * should be prior to the scenemode's one.
+ */
+int m5mols_sync_controls(struct m5mols_info *info)
+{
+	int ret = -EINVAL;
+
+	if (!is_ctrl_synced(info)) {
+		ret = m5mols_change_scenemode(info, REG_SCENE_NORMAL);
+		if (ret)
+			return ret;
+
+		v4l2_ctrl_handler_setup(&info->handle);
+
+		info->ctrl_sync = true;
+	}
+
+	return ret;
+}
+
+/*
+ * m5mols_start_monitor() - start the monitor mode
+ *
+ * Before applying the controls setup the resolution and frame rate
+ * in PARAMETER mode, and then switch over to MONITOR mode.
+ */
+static int m5mols_start_monitor(struct m5mols_info *info)
+{
+	struct v4l2_subdev *sd = &info->sd;
+	int ret;
+
+	ret = m5mols_change_mode(info, REG_MODE_PARAM);
+	if (!ret)
+		ret = i2c_w8_param(sd, CAT1_MONITOR_SIZE, info->resolution);
+	if (!ret)
+		ret = i2c_w8_param(sd, CAT1_MONITOR_FPS, REG_FPS_30);
+	if (!ret)
+		ret = m5mols_change_mode(info, REG_MODE_MONITOR);
+	if (!ret)
+		ret = m5mols_sync_controls(info);
+
+	return ret;
+}
+
+static int m5mols_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	struct m5mols_info *info = to_m5mols(sd);
+
+	if (enable) {
+		int ret = -EINVAL;
+		if (is_code(info->code, M5MOLS_RESTYPE_MONITOR))
+			ret = m5mols_start_monitor(info);
+		if (is_code(info->code, M5MOLS_RESTYPE_CAPTURE))
+			ret = m5mols_start_capture(info);
+
+		return ret;
+	}
+
+	return m5mols_change_mode(info, REG_MODE_PARAM);
+}
+
+static const struct v4l2_subdev_video_ops m5mols_video_ops = {
+	.s_stream	= m5mols_s_stream,
+};
+
+static int m5mols_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct v4l2_subdev *sd = to_sd(ctrl);
+	struct m5mols_info *info = to_m5mols(sd);
+	int ret;
+
+	info->mode_save = info->mode;
+
+	ret = m5mols_change_mode(info, REG_MODE_PARAM);
+	if (!ret)
+		ret = m5mols_set_ctrl(ctrl);
+	if (!ret)
+		ret = m5mols_change_mode(info, info->mode_save);
+
+	return ret;
+}
+
+static const struct v4l2_ctrl_ops m5mols_ctrl_ops = {
+	.s_ctrl		= m5mols_s_ctrl,
+};
+
+static int m5mols_sensor_power(struct m5mols_info *info, bool enable)
+{
+	struct v4l2_subdev *sd = &info->sd;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	const struct m5mols_platform_data *pdata = info->pdata;
+	int ret;
+
+	if (enable) {
+		if (is_powered(info))
+			return 0;
+
+		if (info->set_power) {
+			ret = info->set_power(&client->dev, 1);
+			if (ret)
+				return ret;
+		}
+
+		ret = regulator_bulk_enable(ARRAY_SIZE(supplies), supplies);
+		if (ret) {
+			info->set_power(&client->dev, 0);
+			return ret;
+		}
+
+		gpio_set_value(pdata->gpio_reset, !pdata->reset_polarity);
+		usleep_range(1000, 1000);
+		info->power = true;
+
+		return ret;
+	}
+
+	if (!is_powered(info))
+		return 0;
+
+	ret = regulator_bulk_disable(ARRAY_SIZE(supplies), supplies);
+	if (ret)
+		return ret;
+
+	if (info->set_power)
+		info->set_power(&client->dev, 0);
+
+	gpio_set_value(pdata->gpio_reset, pdata->reset_polarity);
+	usleep_range(1000, 1000);
+	info->power = false;
+
+	return ret;
+}
+
+/* m5mols_update_fw() - m5mols_update_fw() is optional. */
+int __attribute__ ((weak)) m5mols_update_fw(struct v4l2_subdev *sd,
+		int (*set_power)(struct m5mols_info *, bool))
+{
+	return 0;
+}
+
+/*
+ * m5mols_sensor_armboot() - booting M-5MOLS internal ARM core.
+ *
+ * Booting internal ARM core makes the M-5MOLS is ready for getting commands
+ * with I2C. It's the first thing to be done after it powered up. It must wait
+ * at least 520ms recommended by M-5MOLS datasheet, after executing arm booting.
+ */
+static int m5mols_sensor_armboot(struct v4l2_subdev *sd)
+{
+	int ret;
+
+	ret = i2c_w8_flash(sd, CATF_CAM_START, REG_START_ARM_BOOT);
+	if (ret < 0)
+		return ret;
+
+	msleep(520);
+
+	ret = m5mols_get_version(sd);
+	if (!ret)
+		ret = m5mols_update_fw(sd, m5mols_sensor_power);
+	if (ret)
+		return ret;
+
+	v4l2_dbg(1, m5mols_debug, sd, "Success ARM Booting\n");
+
+	ret = i2c_w8_param(sd, CAT1_DATA_INTERFACE, REG_INTERFACE_MIPI);
+	if (!ret)
+		ret = m5mols_enable_interrupt(sd, REG_INT_AF);
+
+	return ret;
+}
+
+static int m5mols_init_controls(struct m5mols_info *info)
+{
+	struct v4l2_subdev *sd = &info->sd;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	u16 max_exposure, step_zoom;
+	int ret;
+
+	/* Determine value's range & step of controls for various FW version */
+	ret = i2c_r16_ae(sd, CAT3_MAX_GAIN_MON, (u32 *)&max_exposure);
+	if (!ret)
+		step_zoom = is_manufacturer(info, SAMSUNG_OPTICS) ? 31 : 1;
+	if (ret)
+		return ret;
+
+	v4l2_ctrl_handler_init(&info->handle, 6);
+	info->autowb = v4l2_ctrl_new_std(&info->handle,
+			&m5mols_ctrl_ops, V4L2_CID_AUTO_WHITE_BALANCE,
+			0, 1, 1, 0);
+	info->saturation = v4l2_ctrl_new_std(&info->handle,
+			&m5mols_ctrl_ops, V4L2_CID_SATURATION,
+			1, 5, 1, 3);
+	info->zoom = v4l2_ctrl_new_std(&info->handle,
+			&m5mols_ctrl_ops, V4L2_CID_ZOOM_ABSOLUTE,
+			1, 70, step_zoom, 1);
+	info->exposure = v4l2_ctrl_new_std(&info->handle,
+			&m5mols_ctrl_ops, V4L2_CID_EXPOSURE,
+			0, max_exposure, 1, (int)max_exposure/2);
+	info->colorfx = v4l2_ctrl_new_std_menu(&info->handle,
+			&m5mols_ctrl_ops, V4L2_CID_COLORFX,
+			4, (1 << V4L2_COLORFX_BW), V4L2_COLORFX_NONE);
+	info->autoexposure = v4l2_ctrl_new_std_menu(&info->handle,
+			&m5mols_ctrl_ops, V4L2_CID_EXPOSURE_AUTO,
+			1, 0, V4L2_EXPOSURE_MANUAL);
+
+	sd->ctrl_handler = &info->handle;
+	if (info->handle.error) {
+		dev_err(&client->dev, "Failed to init controls, %d\n", ret);
+		v4l2_ctrl_handler_free(&info->handle);
+		return info->handle.error;
+	}
+
+	v4l2_ctrl_cluster(2, &info->autoexposure);
+
+	return 0;
+}
+
+/*
+ * m5mols_s_power() - callback function of s_power in the subdev core operation
+ *
+ * Breaking the lens is considered when the sensor going to power off.
+ * But it provided only in the M-5MOLS with Samsung Techwin firiware. And, It
+ * calls Soft-Landing algorithm.
+ */
+static int m5mols_s_power(struct v4l2_subdev *sd, int on)
+{
+	struct m5mols_info *info = to_m5mols(sd);
+	int ret;
+
+	if (on) {
+		ret = m5mols_sensor_power(info, true);
+		if (!ret)
+			ret = m5mols_sensor_armboot(sd);
+		if (!ret)
+			ret = m5mols_init_controls(info);
+		if (!ret) {
+			info->ffmt[M5MOLS_RESTYPE_MONITOR] =
+				m5mols_default_ffmt[M5MOLS_RESTYPE_MONITOR];
+			info->ffmt[M5MOLS_RESTYPE_CAPTURE] =
+				m5mols_default_ffmt[M5MOLS_RESTYPE_CAPTURE];
+		}
+
+		return ret;
+	}
+
+	if (is_manufacturer(info, SAMSUNG_TECHWIN)) {
+		ret = m5mols_change_mode(info, REG_MODE_MONITOR);
+		if (!ret)
+			ret = i2c_w8_lens(sd, CATA_AF_EXECUTE, REG_AF_STOP);
+		if (!ret)
+			ret = i2c_w8_lens(sd, CATA_AF_MODE,
+					REG_AF_MODE_POWEROFF);
+		if (!ret)
+			ret = m5mols_check_busy(sd, CAT_SYSTEM, CAT0_STATUS,
+					REG_AF_IDLE);
+		if (!ret)
+			v4l2_info(sd, "Success soft-landing lens\n");
+	}
+
+	ret = m5mols_sensor_power(info, false);
+	if (!ret) {
+		v4l2_ctrl_handler_free(&info->handle);
+		info->ctrl_sync = false;
+	}
+
+	return ret;
+}
+
+static int m5mols_log_status(struct v4l2_subdev *sd)
+{
+	struct m5mols_info *info = to_m5mols(sd);
+
+	v4l2_ctrl_handler_log_status(&info->handle, sd->name);
+
+	return 0;
+}
+
+static const struct v4l2_subdev_core_ops m5mols_core_ops = {
+	.s_power	= m5mols_s_power,
+	.g_ctrl		= v4l2_subdev_g_ctrl,
+	.s_ctrl		= v4l2_subdev_s_ctrl,
+	.queryctrl	= v4l2_subdev_queryctrl,
+	.querymenu	= v4l2_subdev_querymenu,
+	.g_ext_ctrls	= v4l2_subdev_g_ext_ctrls,
+	.try_ext_ctrls	= v4l2_subdev_try_ext_ctrls,
+	.s_ext_ctrls	= v4l2_subdev_s_ext_ctrls,
+	.log_status	= m5mols_log_status,
+};
+
+static const struct v4l2_subdev_ops m5mols_ops = {
+	.core		= &m5mols_core_ops,
+	.pad		= &m5mols_pad_ops,
+	.video		= &m5mols_video_ops,
+};
+
+static void m5mols_irq_work(struct work_struct *work)
+{
+	struct m5mols_info *info = container_of(work, struct m5mols_info,
+			work_irq);
+	struct v4l2_subdev *sd = &info->sd;
+	u32 reg;
+	int ret;
+
+	/*
+	 * For preventing I2C operation when it's not powered and prepared for
+	 * the I2C operation.
+	 */
+	if (!is_powered(info) ||
+			i2c_r8_system(sd, CAT0_INT_FACTOR, &info->interrupt))
+		return;
+
+	switch (info->interrupt & REG_INT_MASK) {
+	case REG_INT_AF:
+		if (info->ver.af) {
+			ret = i2c_r8_lens(sd, CATA_AF_STATUS, &reg);
+			v4l2_dbg(2, m5mols_debug, sd, "= AF %s\n",
+				reg == REG_AF_FAIL ? "Failed" :
+				reg == REG_AF_SUCCESS ? "Success" :
+				reg == REG_AF_IDLE ? "Idle" : "Busy");
+		}
+		break;
+	case REG_INT_CAPTURE:
+		if (!is_captured(info)) {
+			wake_up_interruptible(&info->wait_capture);
+			info->capture = true;
+		}
+		v4l2_dbg(2, m5mols_debug, sd, "= CAPTURE\n");
+		break;
+	default:
+		v4l2_dbg(2, m5mols_debug, sd, "= Nothing : %02x\n", reg);
+		break;
+	};
+}
+
+static irqreturn_t m5mols_irq_handler(int irq, void *data)
+{
+	struct v4l2_subdev *sd = data;
+	struct m5mols_info *info = to_m5mols(sd);
+
+	schedule_work(&info->work_irq);
+
+	return IRQ_HANDLED;
+}
+
+static int m5mols_probe(struct i2c_client *client,
+			 const struct i2c_device_id *id)
+{
+	const struct m5mols_platform_data *pdata =
+		client->dev.platform_data;
+	struct m5mols_info *info;
+	struct v4l2_subdev *sd;
+	int ret;
+
+	if (pdata == NULL) {
+		dev_err(&client->dev, "No platform data\n");
+		return -EINVAL;
+	}
+
+	if (!gpio_is_valid(pdata->gpio_reset)) {
+		dev_err(&client->dev, "No valid RESET GPIO specified\n");
+		return -EINVAL;
+	}
+
+	if (!pdata->irq) {
+		dev_err(&client->dev, "Interrupt not assigned\n");
+		return -EINVAL;
+	}
+
+	info = kzalloc(sizeof(struct m5mols_info), GFP_KERNEL);
+	if (info == NULL) {
+		dev_err(&client->dev, "Failed to allocate info\n");
+		return -ENOMEM;
+	}
+
+	info->pdata	= pdata;
+	info->set_power	= pdata->set_power;
+
+	ret = gpio_request(pdata->gpio_reset, "M5MOLS_NRST");
+	if (ret) {
+		dev_err(&client->dev, "Failed to request gpio: %d\n", ret);
+		goto out_free;
+	}
+
+	gpio_direction_output(pdata->gpio_reset, pdata->reset_polarity);
+
+	ret = regulator_bulk_get(&client->dev, ARRAY_SIZE(supplies), supplies);
+	if (ret) {
+		dev_err(&client->dev, "Failed to get regulators: %d\n", ret);
+		goto out_gpio;
+	}
+
+	sd = &info->sd;
+	strlcpy(sd->name, MOD_NAME, sizeof(sd->name));
+	v4l2_i2c_subdev_init(sd, client, &m5mols_ops);
+
+	info->pad.flags = MEDIA_PAD_FL_SOURCE;
+	ret = media_entity_init(&sd->entity, 1, &info->pad, 0);
+	if (ret < 0)
+		goto out_reg;
+	sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR;
+
+	init_waitqueue_head(&info->wait_capture);
+	INIT_WORK(&info->work_irq, m5mols_irq_work);
+	ret = request_irq(pdata->irq, m5mols_irq_handler,
+			  IRQF_TRIGGER_RISING, MOD_NAME, sd);
+	if (ret) {
+		dev_err(&client->dev, "Failed to request irq: %d\n", ret);
+		goto out_me;
+	}
+	info->curr_res_type = M5MOLS_RESTYPE_MONITOR;
+
+	v4l2_dbg(1, m5mols_debug, sd, "Probed m5mols driver.\n");
+
+	return 0;
+out_me:
+	media_entity_cleanup(&sd->entity);
+out_reg:
+	regulator_bulk_free(ARRAY_SIZE(supplies), supplies);
+out_gpio:
+	gpio_free(pdata->gpio_reset);
+out_free:
+	kfree(info);
+	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);
+	free_irq(info->pdata->irq, sd);
+
+	regulator_bulk_free(ARRAY_SIZE(supplies), supplies);
+	gpio_free(info->pdata->gpio_reset);
+	media_entity_cleanup(&sd->entity);
+	kfree(info);
+
+	v4l2_dbg(1, m5mols_debug, sd, "Removed m5mols driver\n");
+
+	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@samsung.com>");
+MODULE_AUTHOR("Dongsoo Kim <dongsoo45.kim@samsung.com>");
+MODULE_DESCRIPTION("Fujitsu M-5MOLS 8M Pixel camera driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/m5mols/m5mols_reg.h b/drivers/media/video/m5mols/m5mols_reg.h
new file mode 100644
index 0000000..f07b362
--- /dev/null
+++ b/drivers/media/video/m5mols/m5mols_reg.h
@@ -0,0 +1,321 @@
+/*
+ * Register map for M-5MOLS 8M Pixel camera sensor with ISP
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * Author: HeungJun Kim, riverful.kim@samsung.com
+ *
+ * Copyright (C) 2009 Samsung Electronics Co., Ltd.
+ * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com
+ *
+ * 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_REG_H
+#define M5MOLS_REG_H
+
+/*
+ * Category section register
+ *
+ * The category means set including relevant command of M-5MOLS.
+ */
+#define CAT_SYSTEM		0x00
+#define CAT_PARAM		0x01
+#define CAT_MONITOR		0x02
+#define CAT_AE			0x03
+#define CAT_WB			0x06
+#define CAT_EXIF		0x07
+#define CAT_FD			0x09
+#define CAT_LENS		0x0a
+#define CAT_CAPTURE_PARAMETER	0x0b
+#define CAT_CAPTURE_CONTROL	0x0c
+#define CAT_FLASH		0x0f	/* related with FW, Verions, booting */
+
+/*
+ * Category 0 - SYSTEM mode
+ *
+ * The SYSTEM mode in the M-5MOLS means area available to handle with the whole
+ * & all-round system of sensor. It deals with version/interrupt/setting mode &
+ * even sensor's status. Especially, the M-5MOLS sensor with ISP varys by
+ * packaging & manufactur vendors, even the customer user vendor. And the
+ * detailed function may have a difference. The version information can
+ * determine what method use in the driver.
+ *
+ * There is many registers between customer version address and awb one. For
+ * more specific contents, reference the m5mols.h.
+ */
+#define CAT0_CUSTOMER_CODE	0x00	/* customer version*/
+#define CAT0_VERSION_AWB	0x09	/* Auto WB version */
+#define CAT0_VERSION_STRING	0x0a	/* string including M-5MOLS */
+#define CAT0_SYSMODE		0x0b	/* SYSTEM mode register */
+#define CAT0_STATUS		0x0c	/* SYSTEM mode status register */
+#define CAT0_INT_FACTOR		0x10	/* interrupt pending register */
+#define CAT0_INT_ENABLE		0x11	/* interrupt enable register */
+
+#define REG_MODE_SYSINIT	0x00	/* SYSTEM mode */
+#define REG_MODE_PARAM		0x01	/* PARAMETER mode */
+#define REG_MODE_MONITOR	0x02	/* MONITOR mode */
+#define REG_MODE_CAPTURE	0x03	/* CAPTURE mode */
+
+#define REG_INT_MODE		(1 << 0)
+#define REG_INT_AF		(1 << 1)
+#define REG_INT_ZOOM		(1 << 2)
+#define REG_INT_CAPTURE		(1 << 3)
+#define REG_INT_FRAMESYNC	(1 << 4)
+#define REG_INT_FD		(1 << 5)
+#define REG_INT_LENS_INIT	(1 << 6)
+#define REG_INT_SOUND		(1 << 7)
+#define REG_INT_MASK		0x0f
+
+/*
+ * category 1 - PARAMETER mode
+ *
+ * This category supports function of camera features of M-5MOLS. It means we
+ * can handle with preview(MONITOR) resolution size/frame per second/interface
+ * between the sensor and the Application Processor/even the image effect.
+ */
+#define CAT1_DATA_INTERFACE	0x00	/* interface between sensor and AP */
+#define CAT1_MONITOR_SIZE	0x01	/* resolution at the MONITOR mode */
+#define CAT1_MONITOR_FPS	0x02	/* frame per second at this mode */
+#define CAT1_EFFECT		0x0b	/* image effects */
+
+#define REG_INTERFACE_MIPI	0x02
+
+#define REG_FPS_30		0x02
+
+#define REG_EFFECT_OFF		0x00
+#define REG_EFFECT_NEGA		0x01
+#define REG_EFFECT_EMBOSS	0x06
+#define REG_EFFECT_OUTLINE	0x07
+#define REG_EFFECT_WATERCOLOR	0x08
+
+/*
+ * Category 2 - MONITOR mode
+ *
+ * The MONITOR mode is same as preview mode as we said. The M-5MOLS has another
+ * mode named "Preview", but this preview mode is used at the case specific
+ * vider-recording mode. This mmode supports only YUYV format. On the other
+ * hand, the JPEG & RAW formats is supports by CAPTURE mode. And, there are
+ * another options like zoom/color effect(different with effect in PARAMETER
+ * mode)/anti hand shaking algorithm.
+ */
+#define CAT2_ZOOM		0x01	/* set the zoom position & execute */
+#define CAT2_ZOOM_STEP		0x03	/* set the zoom step */
+#define CAT2_CFIXB		0x09	/* CB value for color effect */
+#define CAT2_CFIXR		0x0a	/* CR value for color effect */
+#define CAT2_COLOR_EFFECT	0x0b	/* set on/off of color effect */
+#define CAT2_CHROMA_LVL		0x0f	/* set chroma level */
+#define CAT2_CHROMA_EN		0x10	/* set on/off of choroma */
+#define CAT2_EDGE_LVL		0x11	/* set sharpness level */
+#define CAT2_EDGE_EN		0x12	/* set on/off sharpness */
+#define CAT2_TONE_CTL		0x25	/* set tone color(contrast) */
+
+#define REG_CFIXB_SEPIA		0xd8
+#define REG_CFIXR_SEPIA		0x18
+
+#define REG_COLOR_EFFECT_OFF	0x00
+#define REG_COLOR_EFFECT_ON	0x01
+
+#define REG_CHROMA_OFF		0x00
+#define REG_CHROMA_ON		0x01
+
+#define REG_EDGE_OFF		0x00
+#define REG_EDGE_ON		0x01
+
+/*
+ * Category 3 - Auto Exposure
+ *
+ * The M-5MOLS exposure capbility is detailed as which is similar to digital
+ * camera. This category supports AE locking/various AE mode(range of exposure)
+ * /ISO/flickering/EV bias/shutter/meteoring, and anything else. And the
+ * maximum/minimum exposure gain value depending on M-5MOLS firmware, may be
+ * different. So, this category also provide getting the max/min values. And,
+ * each MONITOR and CAPTURE mode has each gain/shutter/max exposure values.
+ */
+#define CAT3_AE_LOCK		0x00	/* locking Auto exposure */
+#define CAT3_AE_MODE		0x01	/* set AE mode, mode means range */
+#define CAT3_ISO		0x05	/* set ISO */
+#define CAT3_EV_PRESET_MODE_MON	0x0a	/* EV(scenemode) preset for MONITOR */
+#define CAT3_EV_PRESET_MODE_CAP	0x0b	/* EV(scenemode) preset for CAPTURE */
+#define CAT3_MANUAL_GAIN_MON	0x12	/* meteoring value for the MONITOR */
+#define CAT3_MAX_GAIN_MON	0x1a	/* max gain value for the MONITOR */
+#define CAT3_MANUAL_GAIN_CAP	0x26	/* meteoring value for the CAPTURE */
+#define CAT3_AE_INDEX		0x38	/* AE index */
+
+#define REG_AE_UNLOCK		0x00
+#define REG_AE_LOCK		0x01
+
+#define REG_AE_MODE_OFF		0x00	/* AE off */
+#define REG_AE_MODE_ALL		0x01	/* calc AE in all block integral */
+#define REG_AE_MODE_CENTER	0x03	/* calc AE in center weighted */
+#define REG_AE_MODE_SPOT	0x06	/* calc AE in specific spot */
+
+#define REG_ISO_AUTO		0x00
+#define REG_ISO_50		0x0
+#define REG_ISO_100		0x02
+#define REG_ISO_200		0x03
+#define REG_ISO_400		0x04
+#define REG_ISO_800		0x05
+
+#define REG_SCENE_NORMAL	0x00
+#define REG_SCENE_PORTRAIT	0x01
+#define REG_SCENE_LANDSCAPE	0x02
+#define REG_SCENE_SPORTS	0x03
+#define REG_SCENE_PARTY_INDOOR	0x04
+#define REG_SCENE_BEACH_SNOW	0x05
+#define REG_SCENE_SUNSET	0x06
+#define REG_SCENE_DAWN_DUSK	0x07
+#define REG_SCENE_FALL		0x08
+#define REG_SCENE_NIGHT		0x09
+#define REG_SCENE_AGAINST_LIGHT	0x0a
+#define REG_SCENE_FIRE		0x0b
+#define REG_SCENE_TEXT		0x0c
+#define REG_SCENE_CANDLE	0x0d
+#define REG_SCENE_NONE		0xff
+
+#define REG_AE_INDEX_20_NEG	0x00
+#define REG_AE_INDEX_15_NEG	0x01
+#define REG_AE_INDEX_10_NEG	0x02
+#define REG_AE_INDEX_05_NEG	0x03
+#define REG_AE_INDEX_00		0x04
+#define REG_AE_INDEX_05_POS	0x05
+#define REG_AE_INDEX_10_POS	0x06
+#define REG_AE_INDEX_15_POS	0x07
+#define REG_AE_INDEX_20_POS	0x08
+
+/*
+ * Category 6 - White Balance
+ *
+ * This cagetory provide AWB locking/mode/preset/speed/gain bias, etc.
+ */
+#define CAT6_AWB_LOCK		0x00	/* locking Auto Whitebalance */
+#define CAT6_AWB_MODE		0x02	/* set Auto or Manual */
+#define CAT6_AWB_MANUAL		0x03	/* set Manual(preset) value */
+
+#define REG_AWB_UNLOCK		0x00
+#define REG_AWB_LOCK		0x01
+
+#define REG_AWB_MODE_AUTO	0x01	/* AWB off */
+#define REG_AWB_MODE_PRESET	0x02	/* AWB preset */
+
+#define REG_AWB_INCANDESCENT	0x01
+#define REG_AWB_FLUORESCENT_1	0x02
+#define REG_AWB_FLUORESCENT_2	0x03
+#define REG_AWB_DAYLIGHT	0x04
+#define REG_AWB_CLOUDY		0x05
+#define REG_AWB_SHADE		0x06
+#define REG_AWB_HORIZON		0x07
+#define REG_AWB_LEDLIGHT	0x09
+
+/*
+ * Category 7 - EXIF information
+ */
+#define CAT7_INFO_EXPTIME_NU	0x00
+#define CAT7_INFO_EXPTIME_DE	0x04
+#define CAT7_INFO_TV_NU		0x08
+#define CAT7_INFO_TV_DE		0x0c
+#define CAT7_INFO_AV_NU		0x10
+#define CAT7_INFO_AV_DE		0x14
+#define CAT7_INFO_BV_NU		0x18
+#define CAT7_INFO_BV_DE		0x1c
+#define CAT7_INFO_EBV_NU	0x20
+#define CAT7_INFO_EBV_DE	0x24
+#define CAT7_INFO_ISO		0x28
+#define CAT7_INFO_FLASH		0x2a
+#define CAT7_INFO_SDR		0x2c
+#define CAT7_INFO_QVAL		0x2e
+
+/*
+ * Category 9 - Face Detection
+ */
+#define CAT9_FD_CTL		0x00
+
+#define BIT_FD_EN		0
+#define BIT_FD_DRAW_FACE_FRAME	4
+#define BIT_FD_DRAW_SMILE_LVL	6
+#define REG_FD(shift)		(1 << shift)
+#define REG_FD_OFF		0x0
+
+/*
+ * Category A - Lens Parameter
+ */
+#define CATA_AF_MODE		0x01
+#define CATA_AF_EXECUTE		0x02
+#define CATA_AF_STATUS		0x03
+#define CATA_AF_VERSION		0x0a
+
+#define REG_AF_FAIL		0x00
+#define REG_AF_SUCCESS		0x02
+#define REG_AF_IDLE		0x04
+#define REG_AF_BUSY		0x05
+
+#define REG_AF_MODE_NORMAL	0x00	/* Normal AF, one time */
+#define REG_AF_MODE_MACRO	0x01	/* Macro AF, one time */
+#define REG_AF_MODE_POWEROFF	0x07
+
+#define REG_AF_STOP		0x00
+#define REG_AF_EXE_AUTO		0x01
+#define REG_AF_EXE_CAF		0x02
+
+/*
+ * Category B - CAPTURE Parameter
+ */
+#define CATB_YUVOUT_MAIN	0x00
+#define CATB_MAIN_IMAGE_SIZE	0x01
+#define CATB_MCC_MODE		0x1d
+#define CATB_WDR_EN		0x2c
+#define CATB_LIGHT_CTRL		0x40
+#define CATB_FLASH_CTRL		0x41
+
+/* n = 0..3 */
+#define CATB_JPEG_SIZE_MAX(n)	(0x12 - (n))
+#define CATB_JPEG_SIZE_MIN(n)	(0x16 - (n))
+
+#define REG_YUV422		0x00
+#define REG_BAYER10		0x05
+#define REG_BAYER8		0x06
+#define REG_JPEG		0x10
+
+#define REG_MCC_OFF		0x00
+#define REG_MCC_NORMAL		0x01
+
+#define REG_WDR_OFF		0x00
+#define REG_WDR_ON		0x01
+#define REG_WDR_AUTO		0x02
+
+#define REG_LIGHT_OFF		0x00
+#define REG_LIGHT_ON		0x01
+#define REG_LIGHT_AUTO		0x02
+
+#define REG_FLASH_OFF		0x00
+#define REG_FLASH_ON		0x01
+#define REG_FLASH_AUTO		0x02
+
+/*
+ * Category C - CAPTURE Control
+ */
+#define CATC_CAP_MODE		0x00
+#define CATC_CAP_SEL_FRAME	0x06	/* It determines Single or Multi. */
+#define CATC_CAP_START		0x09
+#define CATC_CAP_IMAGE_SIZE	0x0d
+#define CATC_CAP_THUMB_SIZE	0x11
+
+#define REG_CAP_MODE_NONE	0x00
+#define REG_CAP_MODE_ANTI_SHAKE	0x02
+
+#define REG_CAP_START_MAIN	0x01
+#define REG_CAP_START_THUMB	0x03
+
+/*
+ * Category F - Flash
+ *
+ * This mode provides functions about internal flash stuff and system startup.
+ */
+#define CATF_CAM_START		0x12	/* It start internal ARM core booting
+					 * after power-up */
+
+#define REG_START_ARM_BOOT	0x01
+
+#endif	/* M5MOLS_REG_H */
diff --git a/include/media/m5mols.h b/include/media/m5mols.h
new file mode 100644
index 0000000..2d7e7ca
--- /dev/null
+++ b/include/media/m5mols.h
@@ -0,0 +1,35 @@
+/*
+ * Driver header for M-5MOLS 8M Pixel camera sensor with ISP
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * Author: HeungJun Kim, riverful.kim@samsung.com
+ *
+ * Copyright (C) 2009 Samsung Electronics Co., Ltd.
+ * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com
+ *
+ * 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 MEDIA_M5MOLS_H
+#define MEDIA_M5MOLS_H
+
+/**
+ * struct m5mols_platform_data - platform data for M-5MOLS driver
+ * @irq:	GPIO getting the irq pin of M-5MOLS
+ * @gpio_reset:	GPIO driving the reset pin of M-5MOLS
+ * @reset_polarity: active state for gpio_rst pin, 0 or 1
+ * @set_power:	an additional callback to the board setup code
+ *		to be called after enabling and before disabling
+ *		the sensor's supply regulators
+ */
+struct m5mols_platform_data {
+	int irq;
+	int gpio_reset;
+	u8 reset_polarity;
+	int (*set_power)(struct device *dev, int on);
+};
+
+#endif	/* MEDIA_M5MOLS_H */
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH v9] Add support for M-5MOLS 8 Mega Pixel camera ISP
  2011-05-16  1:03     ` [PATCH v8] " HeungJun, Kim
@ 2011-05-20  5:56       ` HeungJun, Kim
  2011-05-25 13:54         ` Sakari Ailus
  0 siblings, 1 reply; 34+ messages in thread
From: HeungJun, Kim @ 2011-05-20  5:56 UTC (permalink / raw)
  To: linux-media
  Cc: mchehab, hverkuil, laurent.pinchart, HeungJun, Kim,
	Sylwester Nawrocki, Kyungmin Park

Add I2C/V4L2 subdev driver for M-5MOLS integrated image signal processor
with 8 Mega Pixel sensor.

Signed-off-by: HeungJun, Kim <riverful.kim@samsung.com>
Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---

Hello everyone,

This is the ninth version of the subdev for M-5MOLS 8M Pixel camera sensor.
If you see previous version, you can find at:
http://www.spinics.net/lists/linux-media/msg32387.html

The major changes including points commented in the IRC is as follows.
1. remove a lot of inline I2C functions for being easy to read
  : rename m5mols_read/write() to m5mols_read/write()
  : add I2C_XXX() macro to combinine the arguments of the I2C raw functions
2. use atomic macro for checking capture interrupt
3. add comments for DocBook

Thanks to read, and any comments are welcome!

--
Thanks and Regards,
Heungjun Kim
Samsung Electronics DMC R&D Center
---
 drivers/media/video/Kconfig                  |    2 +
 drivers/media/video/Makefile                 |    1 +
 drivers/media/video/m5mols/Kconfig           |    5 +
 drivers/media/video/m5mols/Makefile          |    3 +
 drivers/media/video/m5mols/m5mols.h          |  296 ++++++++
 drivers/media/video/m5mols/m5mols_capture.c  |  191 +++++
 drivers/media/video/m5mols/m5mols_controls.c |  299 ++++++++
 drivers/media/video/m5mols/m5mols_core.c     | 1004 ++++++++++++++++++++++++++
 drivers/media/video/m5mols/m5mols_reg.h      |  399 ++++++++++
 include/media/m5mols.h                       |   35 +
 10 files changed, 2235 insertions(+), 0 deletions(-)
 create mode 100644 drivers/media/video/m5mols/Kconfig
 create mode 100644 drivers/media/video/m5mols/Makefile
 create mode 100644 drivers/media/video/m5mols/m5mols.h
 create mode 100644 drivers/media/video/m5mols/m5mols_capture.c
 create mode 100644 drivers/media/video/m5mols/m5mols_controls.c
 create mode 100644 drivers/media/video/m5mols/m5mols_core.c
 create mode 100644 drivers/media/video/m5mols/m5mols_reg.h
 create mode 100644 include/media/m5mols.h

diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index d61414e..242c80c 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -753,6 +753,8 @@ config VIDEO_NOON010PC30
 	---help---
 	  This driver supports NOON010PC30 CIF camera from Siliconfile
 
+source "drivers/media/video/m5mols/Kconfig"
+
 config VIDEO_OMAP3
 	tristate "OMAP 3 Camera support (EXPERIMENTAL)"
 	select OMAP_IOMMU
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index a10e4c3..d5d6de1 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -69,6 +69,7 @@ obj-$(CONFIG_VIDEO_MT9V011) += mt9v011.o
 obj-$(CONFIG_VIDEO_MT9V032) += mt9v032.o
 obj-$(CONFIG_VIDEO_SR030PC30)	+= sr030pc30.o
 obj-$(CONFIG_VIDEO_NOON010PC30)	+= noon010pc30.o
+obj-$(CONFIG_VIDEO_M5MOLS)	+= m5mols/
 
 obj-$(CONFIG_SOC_CAMERA_IMX074)		+= imx074.o
 obj-$(CONFIG_SOC_CAMERA_MT9M001)	+= mt9m001.o
diff --git a/drivers/media/video/m5mols/Kconfig b/drivers/media/video/m5mols/Kconfig
new file mode 100644
index 0000000..302dc3d
--- /dev/null
+++ b/drivers/media/video/m5mols/Kconfig
@@ -0,0 +1,5 @@
+config VIDEO_M5MOLS
+	tristate "Fujitsu M-5MOLS 8MP sensor support"
+	depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+	---help---
+	  This driver supports Fujitsu M-5MOLS camera sensor with ISP
diff --git a/drivers/media/video/m5mols/Makefile b/drivers/media/video/m5mols/Makefile
new file mode 100644
index 0000000..0a44e02
--- /dev/null
+++ b/drivers/media/video/m5mols/Makefile
@@ -0,0 +1,3 @@
+m5mols-objs	:= m5mols_core.o m5mols_controls.o m5mols_capture.o
+
+obj-$(CONFIG_VIDEO_M5MOLS)		+= m5mols.o
diff --git a/drivers/media/video/m5mols/m5mols.h b/drivers/media/video/m5mols/m5mols.h
new file mode 100644
index 0000000..10b55c8
--- /dev/null
+++ b/drivers/media/video/m5mols/m5mols.h
@@ -0,0 +1,296 @@
+/*
+ * Header for M-5MOLS 8M Pixel camera sensor with ISP
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * Author: HeungJun Kim, riverful.kim@samsung.com
+ *
+ * Copyright (C) 2009 Samsung Electronics Co., Ltd.
+ * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com
+ *
+ * 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-subdev.h>
+#include "m5mols_reg.h"
+
+extern int m5mols_debug;
+
+#define to_m5mols(__sd)	container_of(__sd, struct m5mols_info, sd)
+
+#define to_sd(__ctrl) \
+	(&container_of(__ctrl->handler, struct m5mols_info, handle)->sd)
+
+enum m5mols_restype {
+	M5MOLS_RESTYPE_MONITOR,
+	M5MOLS_RESTYPE_CAPTURE,
+	M5MOLS_RESTYPE_MAX,
+};
+
+/**
+ * struct m5mols_resolution - structure for the resolution
+ * @type: resolution type according to the pixel code
+ * @width: width of the resolution
+ * @height: height of the resolution
+ * @reg: resolution preset register value
+ */
+struct m5mols_resolution {
+	u8 reg;
+	enum m5mols_restype type;
+	u16 width;
+	u16 height;
+};
+
+/**
+ * struct m5mols_exif - structure for the EXIF information of M-5MOLS
+ * @exposure_time: exposure time register value
+ * @shutter_speed: speed of the shutter register value
+ * @aperture: aperture register value
+ * @exposure_bias: it calls also EV bias
+ * @iso_speed: ISO register value
+ * @flash: status register value of the flash
+ * @sdr: status register value of the Subject Distance Range
+ * @qval: not written exact meaning in document
+ */
+struct m5mols_exif {
+	u32 exposure_time;
+	u32 shutter_speed;
+	u32 aperture;
+	u32 brightness;
+	u32 exposure_bias;
+	u16 iso_speed;
+	u16 flash;
+	u16 sdr;
+	u16 qval;
+};
+
+/**
+ * struct m5mols_capture - Structure for the capture capability
+ * @exif: EXIF information
+ * @main: size in bytes of the main image
+ * @thumb: size in bytes of the thumb image, if it was accompanied
+ * @total: total size in bytes of the produced image
+ */
+struct m5mols_capture {
+	struct m5mols_exif exif;
+	u32 main;
+	u32 thumb;
+	u32 total;
+};
+
+/**
+ * struct m5mols_scenemode - structure for the scenemode capability
+ * @metering: metering light register value
+ * @ev_bias: EV bias register value
+ * @wb_mode: mode which means the WhiteBalance is Auto or Manual
+ * @wb_preset: whitebalance preset register value in the Manual mode
+ * @chroma_en: register value whether the Chroma capability is enabled or not
+ * @chroma_lvl: chroma's level register value
+ * @edge_en: register value Whether the Edge capability is enabled or not
+ * @edge_lvl: edge's level register value
+ * @af_range: Auto Focus's range
+ * @fd_mode: Face Detection mode
+ * @mcc: Multi-axis Color Conversion which means emotion color
+ * @light: status of the Light
+ * @flash: status of the Flash
+ * @tone: Tone color which means Contrast
+ * @iso: ISO register value
+ * @capt_mode: Mode of the Image Stabilization while the camera capturing
+ * @wdr: Wide Dynamic Range register value
+ *
+ * The each value according to each scenemode is recommended in the documents.
+ */
+struct m5mols_scenemode {
+	u32 metering;
+	u32 ev_bias;
+	u32 wb_mode;
+	u32 wb_preset;
+	u32 chroma_en;
+	u32 chroma_lvl;
+	u32 edge_en;
+	u32 edge_lvl;
+	u32 af_range;
+	u32 fd_mode;
+	u32 mcc;
+	u32 light;
+	u32 flash;
+	u32 tone;
+	u32 iso;
+	u32 capt_mode;
+	u32 wdr;
+};
+
+/**
+ * struct m5mols_version - firmware version information
+ * @customer:	customer information
+ * @project:	version of project information according to customer
+ * @fw:		firmware revision
+ * @hw:		hardware revision
+ * @param:	version of the parameter
+ * @awb:	Auto WhiteBalance algorithm version
+ * @str:	information about manufacturer and packaging vendor
+ * @af:		Auto Focus version
+ *
+ * The register offset starts the customer version at 0x0, and it ends
+ * the awb version at 0x09. The customer, project information occupies 1 bytes
+ * each. And also the fw, hw, param, awb each requires 2 bytes. The str is
+ * unique string associated with firmware's version. It includes information
+ * about manufacturer and the vendor of the sensor's packaging. The least
+ * significant 2 bytes of the string indicate packaging manufacturer.
+ */
+#define VERSION_STRING_SIZE	22
+struct m5mols_version {
+	u8	customer;
+	u8	project;
+	u16	fw;
+	u16	hw;
+	u16	param;
+	u16	awb;
+	u8	str[VERSION_STRING_SIZE];
+	u8	af;
+};
+#define VERSION_SIZE sizeof(struct m5mols_version)
+
+/**
+ * struct m5mols_info - M-5MOLS driver data structure
+ * @pdata: platform data
+ * @sd: v4l-subdev instance
+ * @pad: media pad
+ * @ffmt: current fmt according to resolution type
+ * @res_type: current resolution type
+ * @code: current code
+ * @irq_waitq: waitqueue for the capture
+ * @work_irq: workqueue for the IRQ
+ * @flags: state variable for the interrupt handler
+ * @handle: control handler
+ * @autoexposure: Auto Exposure control
+ * @exposure: Exposure control
+ * @autowb: Auto White Balance control
+ * @colorfx: Color effect control
+ * @saturation:	Saturation control
+ * @zoom: Zoom control
+ * @ver: information of the version
+ * @cap: the capture mode attributes
+ * @power: current sensor's power status
+ * @ctrl_sync: true means all controls of the sensor are initialized
+ * @int_capture: true means the capture interrupt is issued once
+ * @lock_ae: true means the Auto Exposure is locked
+ * @lock_awb: true means the Aut WhiteBalance is locked
+ * @resolution:	register value for current resolution
+ * @interrupt: register value for current interrupt status
+ * @mode: register value for current operation mode
+ * @mode_save: register value for current operation mode for saving
+ * @set_power: optional power callback to the board code
+ */
+struct m5mols_info {
+	const struct m5mols_platform_data *pdata;
+	struct v4l2_subdev sd;
+	struct media_pad pad;
+	struct v4l2_mbus_framefmt ffmt[M5MOLS_RESTYPE_MAX];
+	int res_type;
+	enum v4l2_mbus_pixelcode code;
+	wait_queue_head_t irq_waitq;
+	struct work_struct work_irq;
+	unsigned long flags;
+
+	struct v4l2_ctrl_handler handle;
+	/* Autoexposure/exposure control cluster */
+	struct {
+		struct v4l2_ctrl *autoexposure;
+		struct v4l2_ctrl *exposure;
+	};
+	struct v4l2_ctrl *autowb;
+	struct v4l2_ctrl *colorfx;
+	struct v4l2_ctrl *saturation;
+	struct v4l2_ctrl *zoom;
+
+	struct m5mols_version ver;
+	struct m5mols_capture cap;
+	bool power;
+	bool ctrl_sync;
+	bool lock_ae;
+	bool lock_awb;
+	u8 resolution;
+	u32 interrupt;
+	u32 mode;
+	u32 mode_save;
+	int (*set_power)(struct device *dev, int on);
+};
+
+#define ST_CAPT_IRQ 0
+
+#define is_powered(__info) (__info->power)
+#define is_ctrl_synced(__info) (__info->ctrl_sync)
+#define is_available_af(__info)	(__info->ver.af)
+#define is_code(__code, __type) (__code == m5mols_default_ffmt[__type].code)
+#define is_manufacturer(__info, __manufacturer)	\
+				(__info->ver.str[0] == __manufacturer[0] && \
+				 __info->ver.str[1] == __manufacturer[1])
+/*
+ * I2C operation of the M-5MOLS
+ *
+ * The I2C read operation of the M-5MOLS requires 2 messages. The first
+ * message sends the information about the command, command category, and total
+ * message size. The second message is used to retrieve the data specifed in
+ * the first message
+ *
+ *   1st message                                2nd message
+ *   +-------+---+----------+-----+-------+     +------+------+------+------+
+ *   | size1 | R | category | cmd | size2 |     | d[0] | d[1] | d[2] | d[3] |
+ *   +-------+---+----------+-----+-------+     +------+------+------+------+
+ *   - size1: message data size(5 in this case)
+ *   - size2: desired buffer size of the 2nd message
+ *   - d[0..3]: according to size2
+ *
+ * The I2C write operation needs just one message. The message includes
+ * category, command, total size, and desired data.
+ *
+ *   1st message
+ *   +-------+---+----------+-----+------+------+------+------+
+ *   | size1 | W | category | cmd | d[0] | d[1] | d[2] | d[3] |
+ *   +-------+---+----------+-----+------+------+------+------+
+ *   - d[0..3]: according to size1
+ */
+int m5mols_read(struct v4l2_subdev *sd, u32 reg_comb, u32 *val);
+int m5mols_write(struct v4l2_subdev *sd, u32 reg_comb, u32 val);
+int m5mols_busy(struct v4l2_subdev *sd, u8 category, u8 cmd, u32 value);
+
+/*
+ * Mode operation of the M-5MOLS
+ *
+ * Changing the mode of the M-5MOLS is needed right executing order.
+ * There are three modes(PARAMETER, MONITOR, CAPTURE) which can be changed
+ * by user. There are various categories associated with each mode.
+ *
+ * +============================================================+
+ * | mode	| category					|
+ * +============================================================+
+ * | FLASH	| FLASH(only after Stand-by or Power-on)	|
+ * | SYSTEM	| SYSTEM(only after sensor arm-booting)		|
+ * | PARAMETER	| PARAMETER					|
+ * | MONITOR	| MONITOR(preview), Auto Focus, Face Detection	|
+ * | CAPTURE	| Single CAPTURE, Preview(recording)		|
+ * +============================================================+
+ *
+ * The available executing order between each modes are as follows:
+ *   PARAMETER <---> MONITOR <---> CAPTURE
+ */
+int m5mols_mode(struct m5mols_info *info, u32 mode);
+
+int m5mols_enable_interrupt(struct v4l2_subdev *sd, u32 reg);
+int m5mols_sync_controls(struct m5mols_info *info);
+int m5mols_start_capture(struct m5mols_info *info);
+int m5mols_do_scenemode(struct m5mols_info *info, u32 mode);
+int m5mols_lock_3a(struct m5mols_info *info, bool lock);
+int m5mols_set_ctrl(struct v4l2_ctrl *ctrl);
+
+/* The firmware function */
+int m5mols_update_fw(struct v4l2_subdev *sd,
+		     int (*set_power)(struct m5mols_info *, bool));
+
+#endif	/* M5MOLS_H */
diff --git a/drivers/media/video/m5mols/m5mols_capture.c b/drivers/media/video/m5mols/m5mols_capture.c
new file mode 100644
index 0000000..d71a390
--- /dev/null
+++ b/drivers/media/video/m5mols/m5mols_capture.c
@@ -0,0 +1,191 @@
+/*
+ * The Capture code for Fujitsu M-5MOLS ISP
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * Author: HeungJun Kim, riverful.kim@samsung.com
+ *
+ * Copyright (C) 2009 Samsung Electronics Co., Ltd.
+ * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com
+ *
+ * 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/slab.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/version.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/videodev2.h>
+#include <linux/version.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+#include <media/m5mols.h>
+
+#include "m5mols.h"
+#include "m5mols_reg.h"
+
+static int m5mols_capture_error_handler(struct m5mols_info *info,
+					int timeout)
+{
+	int ret;
+
+	/* Disable all interrupts and clear relevant interrupt staus bits */
+	ret = m5mols_write(&info->sd, SYSTEM_INT_ENABLE,
+			   info->interrupt & ~(REG_INT_CAPTURE));
+	if (ret)
+		return ret;
+
+	if (timeout == 0)
+		return -ETIMEDOUT;
+
+	return 0;
+}
+/**
+ * m5mols_read_rational - I2C read of a rational number
+ *
+ * Read numerator and denominator from registers @addr_num and @addr_den
+ * respectively and return the division result in @val.
+ */
+static int m5mols_read_rational(struct v4l2_subdev *sd, u32 addr_num,
+				u32 addr_den, u32 *val)
+{
+	u32 num, den;
+
+	int ret = m5mols_read(sd, addr_num, &num);
+	if (!ret)
+		ret = m5mols_read(sd, addr_den, &den);
+	if (ret)
+		return ret;
+	*val = den == 0 ? 0 : num / den;
+	return ret;
+}
+
+/**
+ * m5mols_capture_info - Gather captured image information
+ *
+ * For now it gathers only EXIF information and file size.
+ */
+static int m5mols_capture_info(struct m5mols_info *info)
+{
+	struct m5mols_exif *exif = &info->cap.exif;
+	struct v4l2_subdev *sd = &info->sd;
+	int ret;
+
+	ret = m5mols_read_rational(sd, EXIF_INFO_EXPTIME_NU,
+				   EXIF_INFO_EXPTIME_DE, &exif->exposure_time);
+	if (ret)
+		return ret;
+	ret = m5mols_read_rational(sd, EXIF_INFO_TV_NU, EXIF_INFO_TV_DE,
+				   &exif->shutter_speed);
+	if (ret)
+		return ret;
+	ret = m5mols_read_rational(sd, EXIF_INFO_AV_NU, EXIF_INFO_AV_DE,
+				   &exif->aperture);
+	if (ret)
+		return ret;
+	ret = m5mols_read_rational(sd, EXIF_INFO_BV_NU, EXIF_INFO_BV_DE,
+				   &exif->brightness);
+	if (ret)
+		return ret;
+	ret = m5mols_read_rational(sd, EXIF_INFO_EBV_NU, EXIF_INFO_EBV_DE,
+				   &exif->exposure_bias);
+	if (ret)
+		return ret;
+
+	ret = m5mols_read(sd, EXIF_INFO_ISO, (u32 *)&exif->iso_speed);
+	if (!ret)
+		ret = m5mols_read(sd, EXIF_INFO_FLASH, (u32 *)&exif->flash);
+	if (!ret)
+		ret = m5mols_read(sd, EXIF_INFO_SDR, (u32 *)&exif->sdr);
+	if (!ret)
+		ret = m5mols_read(sd, EXIF_INFO_QVAL, (u32 *)&exif->qval);
+	if (ret)
+		return ret;
+
+	if (!ret)
+		ret = m5mols_read(sd, CAPC_IMAGE_SIZE, &info->cap.main);
+	if (!ret)
+		ret = m5mols_read(sd, CAPC_THUMB_SIZE, &info->cap.thumb);
+	if (!ret)
+		info->cap.total = info->cap.main + info->cap.thumb;
+
+	return ret;
+}
+
+int m5mols_start_capture(struct m5mols_info *info)
+{
+	struct v4l2_subdev *sd = &info->sd;
+	u32 resolution = info->resolution;
+	int timeout;
+	int ret;
+
+	/*
+	 * Preparing capture. Setting control & interrupt before entering
+	 * capture mode
+	 *
+	 * 1) change to MONITOR mode for operating control & interrupt
+	 * 2) set controls (considering v4l2_control value & lock 3A)
+	 * 3) set interrupt
+	 * 4) change to CAPTURE mode
+	 */
+	ret = m5mols_mode(info, REG_MONITOR);
+	if (!ret)
+		ret = m5mols_sync_controls(info);
+	if (!ret)
+		ret = m5mols_lock_3a(info, true);
+	if (!ret)
+		ret = m5mols_enable_interrupt(sd, REG_INT_CAPTURE);
+	if (!ret)
+		ret = m5mols_mode(info, REG_CAPTURE);
+	if (!ret) {
+		/* Wait for capture interrupt, after changing capture mode */
+		timeout = wait_event_interruptible_timeout(info->irq_waitq,
+					   test_bit(ST_CAPT_IRQ, &info->flags),
+					   msecs_to_jiffies(2000));
+		if (test_and_clear_bit(ST_CAPT_IRQ, &info->flags))
+			ret = m5mols_capture_error_handler(info, timeout);
+	}
+	if (!ret)
+		ret = m5mols_lock_3a(info, false);
+	if (ret)
+		return ret;
+	/*
+	 * Starting capture. Setting capture frame count and resolution and
+	 * the format(available format: JPEG, Bayer RAW, YUV).
+	 *
+	 * 1) select single or multi(enable to 25), format, size
+	 * 2) set interrupt
+	 * 3) start capture(for main image, now)
+	 * 4) get information
+	 * 5) notify file size to v4l2 device(e.g, to s5p-fimc v4l2 device)
+	 */
+	ret = m5mols_write(sd, CAPC_SEL_FRAME, 1);
+	if (!ret)
+		ret = m5mols_write(sd, CAPP_YUVOUT_MAIN, REG_JPEG);
+	if (!ret)
+		ret = m5mols_write(sd, CAPP_MAIN_IMAGE_SIZE, resolution);
+	if (!ret)
+		ret = m5mols_enable_interrupt(sd, REG_INT_CAPTURE);
+	if (!ret)
+		ret = m5mols_write(sd, CAPC_START, REG_CAP_START_MAIN);
+	if (!ret) {
+		/* Wait for the capture completion interrupt */
+		timeout = wait_event_interruptible_timeout(info->irq_waitq,
+					   test_bit(ST_CAPT_IRQ, &info->flags),
+					   msecs_to_jiffies(2000));
+		if (test_and_clear_bit(ST_CAPT_IRQ, &info->flags)) {
+			ret = m5mols_capture_info(info);
+			if (!ret)
+				v4l2_subdev_notify(sd, 0, &info->cap.total);
+		}
+	}
+
+	return m5mols_capture_error_handler(info, timeout);
+}
diff --git a/drivers/media/video/m5mols/m5mols_controls.c b/drivers/media/video/m5mols/m5mols_controls.c
new file mode 100644
index 0000000..817c16f
--- /dev/null
+++ b/drivers/media/video/m5mols/m5mols_controls.c
@@ -0,0 +1,299 @@
+/*
+ * Controls for M-5MOLS 8M Pixel camera sensor with ISP
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * Author: HeungJun Kim, riverful.kim@samsung.com
+ *
+ * Copyright (C) 2009 Samsung Electronics Co., Ltd.
+ * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com
+ *
+ * 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/videodev2.h>
+#include <media/v4l2-ctrls.h>
+
+#include "m5mols.h"
+#include "m5mols_reg.h"
+
+static struct m5mols_scenemode m5mols_default_scenemode[] = {
+	[REG_SCENE_NORMAL] = {
+		REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
+		REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
+		REG_AF_NORMAL, REG_FD_OFF,
+		REG_MCC_NORMAL, REG_LIGHT_OFF, REG_FLASH_OFF,
+		5, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF,
+	},
+	[REG_SCENE_PORTRAIT] = {
+		REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
+		REG_CHROMA_ON, 3, REG_EDGE_ON, 4,
+		REG_AF_NORMAL, BIT_FD_EN | BIT_FD_DRAW_FACE_FRAME,
+		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+		6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF,
+	},
+	[REG_SCENE_LANDSCAPE] = {
+		REG_AE_ALL, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
+		REG_CHROMA_ON, 4, REG_EDGE_ON, 6,
+		REG_AF_NORMAL, REG_FD_OFF,
+		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+		6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF,
+	},
+	[REG_SCENE_SPORTS] = {
+		REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
+		REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
+		REG_AF_NORMAL, REG_FD_OFF,
+		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+		6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF,
+	},
+	[REG_SCENE_PARTY_INDOOR] = {
+		REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
+		REG_CHROMA_ON, 4, REG_EDGE_ON, 5,
+		REG_AF_NORMAL, REG_FD_OFF,
+		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+		6, REG_ISO_200, REG_CAP_NONE, REG_WDR_OFF,
+	},
+	[REG_SCENE_BEACH_SNOW] = {
+		REG_AE_CENTER, REG_AE_INDEX_10_POS, REG_AWB_AUTO, 0,
+		REG_CHROMA_ON, 4, REG_EDGE_ON, 5,
+		REG_AF_NORMAL, REG_FD_OFF,
+		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+		6, REG_ISO_50, REG_CAP_NONE, REG_WDR_OFF,
+	},
+	[REG_SCENE_SUNSET] = {
+		REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_PRESET,
+		REG_AWB_DAYLIGHT,
+		REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
+		REG_AF_NORMAL, REG_FD_OFF,
+		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+		6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF,
+	},
+	[REG_SCENE_DAWN_DUSK] = {
+		REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_PRESET,
+		REG_AWB_FLUORESCENT_1,
+		REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
+		REG_AF_NORMAL, REG_FD_OFF,
+		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+		6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF,
+	},
+	[REG_SCENE_FALL] = {
+		REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
+		REG_CHROMA_ON, 5, REG_EDGE_ON, 5,
+		REG_AF_NORMAL, REG_FD_OFF,
+		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+		6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF,
+	},
+	[REG_SCENE_NIGHT] = {
+		REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
+		REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
+		REG_AF_NORMAL, REG_FD_OFF,
+		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+		6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF,
+	},
+	[REG_SCENE_AGAINST_LIGHT] = {
+		REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
+		REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
+		REG_AF_NORMAL, REG_FD_OFF,
+		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+		6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF,
+	},
+	[REG_SCENE_FIRE] = {
+		REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
+		REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
+		REG_AF_NORMAL, REG_FD_OFF,
+		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+		6, REG_ISO_50, REG_CAP_NONE, REG_WDR_OFF,
+	},
+	[REG_SCENE_TEXT] = {
+		REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
+		REG_CHROMA_ON, 3, REG_EDGE_ON, 7,
+		REG_AF_MACRO, REG_FD_OFF,
+		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+		6, REG_ISO_AUTO, REG_CAP_ANTI_SHAKE, REG_WDR_ON,
+	},
+	[REG_SCENE_CANDLE] = {
+		REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
+		REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
+		REG_AF_NORMAL, REG_FD_OFF,
+		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
+		6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF,
+	},
+};
+
+/**
+ * m5mols_do_scenemode() - Change current scenemode
+ * @mode:	Desired mode of the scenemode
+ *
+ * WARNING: The execution order is important. Do not change the order.
+ */
+int m5mols_do_scenemode(struct m5mols_info *info, u32 mode)
+{
+	struct v4l2_subdev *sd = &info->sd;
+	struct m5mols_scenemode scenemode = m5mols_default_scenemode[mode];
+	int ret;
+
+	if (mode > REG_SCENE_CANDLE)
+		return -EINVAL;
+
+	ret = m5mols_lock_3a(info, false);
+	if (!ret)
+		ret = m5mols_write(sd, AE_EV_PRESET_MONITOR, mode);
+	if (!ret)
+		ret = m5mols_write(sd, AE_EV_PRESET_CAPTURE, mode);
+	if (!ret)
+		ret = m5mols_write(sd, AE_MODE, scenemode.metering);
+	if (!ret)
+		ret = m5mols_write(sd, AE_INDEX, scenemode.ev_bias);
+	if (!ret)
+		ret = m5mols_write(sd, AWB_MODE, scenemode.wb_mode);
+	if (!ret)
+		ret = m5mols_write(sd, AWB_MANUAL, scenemode.wb_preset);
+	if (!ret)
+		ret = m5mols_write(sd, MON_CHROMA_EN, scenemode.chroma_en);
+	if (!ret)
+		ret = m5mols_write(sd, MON_CHROMA_LVL, scenemode.chroma_lvl);
+	if (!ret)
+		ret = m5mols_write(sd, MON_EDGE_EN, scenemode.edge_en);
+	if (!ret)
+		ret = m5mols_write(sd, MON_EDGE_LVL, scenemode.edge_lvl);
+	if (!ret && is_available_af(info))
+		ret = m5mols_write(sd, AF_MODE, scenemode.af_range);
+	if (!ret && is_available_af(info))
+		ret = m5mols_write(sd, FD_CTL, scenemode.fd_mode);
+	if (!ret)
+		ret = m5mols_write(sd, MON_TONE_CTL, scenemode.tone);
+	if (!ret)
+		ret = m5mols_write(sd, AE_ISO, scenemode.iso);
+	if (!ret)
+		ret = m5mols_mode(info, REG_CAPTURE);
+	if (!ret)
+		ret = m5mols_write(sd, CAPP_WDR_EN, scenemode.wdr);
+	if (!ret)
+		ret = m5mols_write(sd, CAPP_MCC_MODE, scenemode.mcc);
+	if (!ret)
+		ret = m5mols_write(sd, CAPP_LIGHT_CTRL, scenemode.light);
+	if (!ret)
+		ret = m5mols_write(sd, CAPP_FLASH_CTRL, scenemode.flash);
+	if (!ret)
+		ret = m5mols_write(sd, CAPC_MODE, scenemode.capt_mode);
+	if (!ret)
+		ret = m5mols_mode(info, REG_MONITOR);
+
+	return ret;
+}
+
+static int m5mols_lock_ae(struct m5mols_info *info, bool lock)
+{
+	int ret = 0;
+
+	if (info->lock_ae != lock)
+		ret = m5mols_write(&info->sd, AE_LOCK,
+				lock ? REG_AE_LOCK : REG_AE_UNLOCK);
+	if (!ret)
+		info->lock_ae = lock;
+
+	return ret;
+}
+
+static int m5mols_lock_awb(struct m5mols_info *info, bool lock)
+{
+	int ret = 0;
+
+	if (info->lock_awb != lock)
+		ret = m5mols_write(&info->sd, AWB_LOCK,
+				lock ? REG_AWB_LOCK : REG_AWB_UNLOCK);
+	if (!ret)
+		info->lock_awb = lock;
+
+	return ret;
+}
+
+/* m5mols_lock_3a() - Lock 3A(Auto Exposure, Auto Whitebalance, Auto Focus) */
+int m5mols_lock_3a(struct m5mols_info *info, bool lock)
+{
+	int ret;
+
+	ret = m5mols_lock_ae(info, lock);
+	if (!ret)
+		ret = m5mols_lock_awb(info, lock);
+	/* Don't need to handle unlocking AF */
+	if (!ret && is_available_af(info) && lock)
+		ret = m5mols_write(&info->sd, AF_EXECUTE, REG_AF_STOP);
+
+	return ret;
+}
+
+/* m5mols_set_ctrl() - The main s_ctrl function called by m5mols_set_ctrl() */
+int m5mols_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct v4l2_subdev *sd = to_sd(ctrl);
+	struct m5mols_info *info = to_m5mols(sd);
+	int ret;
+
+	switch (ctrl->id) {
+	case V4L2_CID_ZOOM_ABSOLUTE:
+		return m5mols_write(sd, MON_ZOOM, ctrl->val);
+
+	case V4L2_CID_EXPOSURE_AUTO:
+		ret = m5mols_lock_ae(info,
+			ctrl->val == V4L2_EXPOSURE_AUTO ? false : true);
+		if (!ret && ctrl->val == V4L2_EXPOSURE_AUTO)
+			ret = m5mols_write(sd, AE_MODE, REG_AE_ALL);
+		if (!ret && ctrl->val == V4L2_EXPOSURE_MANUAL) {
+			int val = info->exposure->val;
+			ret = m5mols_write(sd, AE_MODE, REG_AE_OFF);
+			if (!ret)
+				ret = m5mols_write(sd, AE_MAN_GAIN_MON, val);
+			if (!ret)
+				ret = m5mols_write(sd, AE_MAN_GAIN_CAP, val);
+		}
+		return ret;
+
+	case V4L2_CID_AUTO_WHITE_BALANCE:
+		ret = m5mols_lock_awb(info, ctrl->val ? false : true);
+		if (!ret)
+			ret = m5mols_write(sd, AWB_MODE, ctrl->val ?
+				REG_AWB_AUTO : REG_AWB_PRESET);
+		return ret;
+
+	case V4L2_CID_SATURATION:
+		ret = m5mols_write(sd, MON_CHROMA_LVL, ctrl->val);
+		if (!ret)
+			ret = m5mols_write(sd, MON_CHROMA_EN, REG_CHROMA_ON);
+		return ret;
+
+	case V4L2_CID_COLORFX:
+		/*
+		 * This control uses two kinds of registers: normal & color.
+		 * The normal effect belongs to category 1, while the color
+		 * one belongs to category 2.
+		 *
+		 * The normal effect uses one register: CAT1_EFFECT.
+		 * The color effect uses three registers:
+		 * CAT2_COLOR_EFFECT, CAT2_CFIXR, CAT2_CFIXB.
+		 */
+		ret = m5mols_write(sd, PARM_EFFECT,
+			ctrl->val == V4L2_COLORFX_NEGATIVE ? REG_EFFECT_NEGA :
+			ctrl->val == V4L2_COLORFX_EMBOSS ? REG_EFFECT_EMBOSS :
+			REG_EFFECT_OFF);
+		if (!ret)
+			ret = m5mols_write(sd, MON_EFFECT,
+				ctrl->val == V4L2_COLORFX_SEPIA ?
+				REG_COLOR_EFFECT_ON : REG_COLOR_EFFECT_OFF);
+		if (!ret)
+			ret = m5mols_write(sd, MON_CFIXR,
+				ctrl->val == V4L2_COLORFX_SEPIA ?
+				REG_CFIXR_SEPIA : 0);
+		if (!ret)
+			ret = m5mols_write(sd, MON_CFIXB,
+				ctrl->val == V4L2_COLORFX_SEPIA ?
+				REG_CFIXB_SEPIA : 0);
+		return ret;
+	}
+
+	return -EINVAL;
+}
diff --git a/drivers/media/video/m5mols/m5mols_core.c b/drivers/media/video/m5mols/m5mols_core.c
new file mode 100644
index 0000000..76eac26
--- /dev/null
+++ b/drivers/media/video/m5mols/m5mols_core.c
@@ -0,0 +1,1004 @@
+/*
+ * Driver for M-5MOLS 8M Pixel camera sensor with ISP
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * Author: HeungJun Kim, riverful.kim@samsung.com
+ *
+ * Copyright (C) 2009 Samsung Electronics Co., Ltd.
+ * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com
+ *
+ * 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/slab.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/version.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+#include <media/m5mols.h>
+
+#include "m5mols.h"
+#include "m5mols_reg.h"
+
+int m5mols_debug;
+module_param(m5mols_debug, int, 0644);
+
+#define MODULE_NAME		"M5MOLS"
+#define M5MOLS_I2C_CHECK_RETRY	500
+
+/* The regulator consumer names for external voltage regulators */
+static struct regulator_bulk_data supplies[] = {
+	{
+		.supply = "core",	/* ARM core power, 1.2V */
+	}, {
+		.supply	= "dig_18",	/* digital power 1, 1.8V */
+	}, {
+		.supply	= "d_sensor",	/* sensor power 1, 1.8V */
+	}, {
+		.supply	= "dig_28",	/* digital power 2, 2.8V */
+	}, {
+		.supply	= "a_sensor",	/* analog power */
+	}, {
+		.supply	= "dig_12",	/* digital power 3, 1.2V */
+	},
+};
+
+static struct v4l2_mbus_framefmt m5mols_default_ffmt[M5MOLS_RESTYPE_MAX] = {
+	[M5MOLS_RESTYPE_MONITOR] = {
+		.width		= 1920,
+		.height		= 1080,
+		.code		= V4L2_MBUS_FMT_VYUY8_2X8,
+		.field		= V4L2_FIELD_NONE,
+		.colorspace	= V4L2_COLORSPACE_JPEG,
+	},
+	[M5MOLS_RESTYPE_CAPTURE] = {
+		.width		= 1920,
+		.height		= 1080,
+		.code		= V4L2_MBUS_FMT_JPEG_1X8,
+		.field		= V4L2_FIELD_NONE,
+		.colorspace	= V4L2_COLORSPACE_JPEG,
+	},
+};
+#define SIZE_DEFAULT_FFMT	ARRAY_SIZE(m5mols_default_ffmt)
+
+static const struct m5mols_resolution m5mols_reg_res[] = {
+	{ 0x01, M5MOLS_RESTYPE_MONITOR, 128, 96 },	/* SUB-QCIF */
+	{ 0x03, M5MOLS_RESTYPE_MONITOR, 160, 120 },	/* QQVGA */
+	{ 0x05, M5MOLS_RESTYPE_MONITOR, 176, 144 },	/* QCIF */
+	{ 0x06, M5MOLS_RESTYPE_MONITOR, 176, 176 },
+	{ 0x08, M5MOLS_RESTYPE_MONITOR, 240, 320 },	/* QVGA */
+	{ 0x09, M5MOLS_RESTYPE_MONITOR, 320, 240 },	/* QVGA */
+	{ 0x0c, M5MOLS_RESTYPE_MONITOR, 240, 400 },	/* WQVGA */
+	{ 0x0d, M5MOLS_RESTYPE_MONITOR, 400, 240 },	/* WQVGA */
+	{ 0x0e, M5MOLS_RESTYPE_MONITOR, 352, 288 },	/* CIF */
+	{ 0x13, M5MOLS_RESTYPE_MONITOR, 480, 360 },
+	{ 0x15, M5MOLS_RESTYPE_MONITOR, 640, 360 },	/* qHD */
+	{ 0x17, M5MOLS_RESTYPE_MONITOR, 640, 480 },	/* VGA */
+	{ 0x18, M5MOLS_RESTYPE_MONITOR, 720, 480 },
+	{ 0x1a, M5MOLS_RESTYPE_MONITOR, 800, 480 },	/* WVGA */
+	{ 0x1f, M5MOLS_RESTYPE_MONITOR, 800, 600 },	/* SVGA */
+	{ 0x21, M5MOLS_RESTYPE_MONITOR, 1280, 720 },	/* HD */
+	{ 0x25, M5MOLS_RESTYPE_MONITOR, 1920, 1080 },	/* 1080p */
+	{ 0x29, M5MOLS_RESTYPE_MONITOR, 3264, 2448 },	/* 2.63fps 8M */
+	{ 0x39, M5MOLS_RESTYPE_MONITOR, 800, 602 },	/* AHS_MON debug */
+
+	{ 0x02, M5MOLS_RESTYPE_CAPTURE, 320, 240 },	/* QVGA */
+	{ 0x04, M5MOLS_RESTYPE_CAPTURE, 400, 240 },	/* WQVGA */
+	{ 0x07, M5MOLS_RESTYPE_CAPTURE, 480, 360 },
+	{ 0x08, M5MOLS_RESTYPE_CAPTURE, 640, 360 },	/* qHD */
+	{ 0x09, M5MOLS_RESTYPE_CAPTURE, 640, 480 },	/* VGA */
+	{ 0x0a, M5MOLS_RESTYPE_CAPTURE, 800, 480 },	/* WVGA */
+	{ 0x10, M5MOLS_RESTYPE_CAPTURE, 1280, 720 },	/* HD */
+	{ 0x14, M5MOLS_RESTYPE_CAPTURE, 1280, 960 },	/* 1M */
+	{ 0x17, M5MOLS_RESTYPE_CAPTURE, 1600, 1200 },	/* 2M */
+	{ 0x19, M5MOLS_RESTYPE_CAPTURE, 1920, 1080 },	/* Full-HD */
+	{ 0x1a, M5MOLS_RESTYPE_CAPTURE, 2048, 1152 },	/* 3Mega */
+	{ 0x1b, M5MOLS_RESTYPE_CAPTURE, 2048, 1536 },
+	{ 0x1c, M5MOLS_RESTYPE_CAPTURE, 2560, 1440 },	/* 4Mega */
+	{ 0x1d, M5MOLS_RESTYPE_CAPTURE, 2560, 1536 },
+	{ 0x1f, M5MOLS_RESTYPE_CAPTURE, 2560, 1920 },	/* 5Mega */
+	{ 0x21, M5MOLS_RESTYPE_CAPTURE, 3264, 1836 },	/* 6Mega */
+	{ 0x22, M5MOLS_RESTYPE_CAPTURE, 3264, 1960 },
+	{ 0x25, M5MOLS_RESTYPE_CAPTURE, 3264, 2448 },	/* 8Mega */
+};
+
+/**
+ * m5mols_swap_byte - an byte array to integer conversion function
+ * @size: size in bytes of I2C packet defined in the M-5MOLS datasheet
+ *
+ * Convert I2C data byte array with performing any required byte
+ * reordering to assure proper values for each data type, regardless
+ * of the architecture endianness.
+ */
+static u32 m5mols_swap_byte(u8 *data, u8 length)
+{
+	if (length == 1)
+		return *data;
+	else if (length == 2)
+		return be16_to_cpu(*((u16 *)data));
+	else
+		return be32_to_cpu(*((u32 *)data));
+}
+
+/**
+ * m5mols_read -  I2C read function
+ * @reg: combination of size, category and command for the I2C packet
+ * @val: read value
+ */
+int m5mols_read(struct v4l2_subdev *sd, u32 reg, u32 *val)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	u8 rbuf[M5MOLS_I2C_MAX_SIZE + 1];
+	u8 size = I2C_SIZE(reg);
+	u8 category = I2C_CATEGORY(reg);
+	u8 cmd = I2C_COMMAND(reg);
+	struct i2c_msg msg[2];
+	u8 wbuf[5];
+	int ret;
+
+	if (!client->adapter)
+		return -ENODEV;
+
+	if (size != 1 && size != 2 && size != 4) {
+		v4l2_err(sd, "Wrong data size\n");
+		return -EINVAL;
+	}
+
+	msg[0].addr = client->addr;
+	msg[0].flags = 0;
+	msg[0].len = 5;
+	msg[0].buf = wbuf;
+	wbuf[0] = 5;
+	wbuf[1] = M5MOLS_BYTE_READ;
+	wbuf[2] = category;
+	wbuf[3] = cmd;
+	wbuf[4] = size;
+
+	msg[1].addr = client->addr;
+	msg[1].flags = I2C_M_RD;
+	msg[1].len = size + 1;
+	msg[1].buf = rbuf;
+
+	/* minimum stabilization time */
+	usleep_range(200, 200);
+
+	ret = i2c_transfer(client->adapter, msg, 2);
+	if (ret < 0) {
+		v4l2_err(sd, "read failed: size:%d cat:%02x cmd:%02x. %d\n",
+			 size, category, cmd, ret);
+		return ret;
+	}
+
+	*val = m5mols_swap_byte(&rbuf[1], size);
+
+	return 0;
+}
+
+/**
+ * m5mols_write - I2C command write function
+ * @reg: combination of size, category and command for the I2C packet
+ * @val: value to write
+ */
+int m5mols_write(struct v4l2_subdev *sd, u32 reg, u32 val)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	u8 wbuf[M5MOLS_I2C_MAX_SIZE + 4];
+	u8 category = I2C_CATEGORY(reg);
+	u8 cmd = I2C_COMMAND(reg);
+	u8 size	= I2C_SIZE(reg);
+	u32 *buf = (u32 *)&wbuf[4];
+	struct i2c_msg msg[1];
+	int ret;
+
+	if (!client->adapter)
+		return -ENODEV;
+
+	if (size != 1 && size != 2 && size != 4) {
+		v4l2_err(sd, "Wrong data size\n");
+		return -EINVAL;
+	}
+
+	msg->addr = client->addr;
+	msg->flags = 0;
+	msg->len = (u16)size + 4;
+	msg->buf = wbuf;
+	wbuf[0] = size + 4;
+	wbuf[1] = M5MOLS_BYTE_WRITE;
+	wbuf[2] = category;
+	wbuf[3] = cmd;
+
+	*buf = m5mols_swap_byte((u8 *)&val, size);
+
+	usleep_range(200, 200);
+
+	ret = i2c_transfer(client->adapter, msg, 1);
+	if (ret < 0) {
+		v4l2_err(sd, "write failed: size:%d cat:%02x cmd:%02x. %d\n",
+			size, category, cmd, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+int m5mols_busy(struct v4l2_subdev *sd, u8 category, u8 cmd, u32 mask)
+{
+	u32 busy, i;
+	int ret;
+
+	for (i = 0; i < M5MOLS_I2C_CHECK_RETRY; i++) {
+		ret = m5mols_read(sd, I2C_REG(category, cmd, 1), &busy);
+		if (ret < 0)
+			return ret;
+		if ((busy & mask) == mask)
+			return 0;
+	}
+	return -EBUSY;
+}
+
+/**
+ * m5mols_enable_interrupt - Clear interrupt pending bits and unmask interrupts
+ *
+ * Before writing desired interrupt value the INT_FACTOR register should
+ * be read to clear pending interrupts.
+ */
+int m5mols_enable_interrupt(struct v4l2_subdev *sd, u32 reg)
+{
+	struct m5mols_info *info = to_m5mols(sd);
+	u32 mask = is_available_af(info) ? REG_INT_AF : 0;
+	u32 dummy;
+	int ret;
+
+	ret = m5mols_read(sd, SYSTEM_INT_FACTOR, &dummy);
+	if (!ret)
+		ret = m5mols_write(sd, SYSTEM_INT_ENABLE, reg & ~mask);
+	return ret;
+}
+
+/**
+ * m5mols_reg_mode - Write the mode and check busy status
+ *
+ * It always accompanies a little delay changing the M-5MOLS mode, so it is
+ * needed checking current busy status to guarantee right mode.
+ */
+static int m5mols_reg_mode(struct v4l2_subdev *sd, u32 mode)
+{
+	int ret = m5mols_write(sd, SYSTEM_SYSMODE, mode);
+
+	return ret ? ret : m5mols_busy(sd, CAT_SYSTEM, CAT0_SYSMODE, mode);
+}
+
+/**
+ * m5mols_mode - manage the M-5MOLS's mode
+ * @mode: the required operation mode
+ *
+ * The commands of M-5MOLS are grouped into specific modes. Each functionality
+ * can be guaranteed only when the sensor is operating in mode which which
+ * a command belongs to.
+ */
+int m5mols_mode(struct m5mols_info *info, u32 mode)
+{
+	struct v4l2_subdev *sd = &info->sd;
+	int ret = -EINVAL;
+	u32 reg;
+
+	if (mode < REG_PARAMETER && mode > REG_CAPTURE)
+		return ret;
+
+	ret = m5mols_read(sd, SYSTEM_SYSMODE, &reg);
+	if ((!ret && reg == mode) || ret)
+		return ret;
+
+	switch (reg) {
+	case REG_PARAMETER:
+		ret = m5mols_reg_mode(sd, REG_MONITOR);
+		if (!ret && mode == REG_MONITOR)
+			break;
+		if (!ret)
+			ret = m5mols_reg_mode(sd, REG_CAPTURE);
+		break;
+
+	case REG_MONITOR:
+		if (mode == REG_PARAMETER) {
+			ret = m5mols_reg_mode(sd, REG_PARAMETER);
+			break;
+		}
+
+		ret = m5mols_reg_mode(sd, REG_CAPTURE);
+		break;
+
+	case REG_CAPTURE:
+		ret = m5mols_reg_mode(sd, REG_MONITOR);
+		if (!ret && mode == REG_MONITOR)
+			break;
+		if (!ret)
+			ret = m5mols_reg_mode(sd, REG_PARAMETER);
+		break;
+
+	default:
+		v4l2_warn(sd, "Wrong mode: %d\n", mode);
+	}
+
+	if (!ret)
+		info->mode = mode;
+
+	return ret;
+}
+
+/**
+ * m5mols_get_version - retrieve full revisions information of M-5MOLS
+ *
+ * The version information includes revisions of hardware and firmware,
+ * AutoFocus alghorithm version and the version string.
+ */
+static int m5mols_get_version(struct v4l2_subdev *sd)
+{
+	struct m5mols_info *info = to_m5mols(sd);
+	union {
+		struct m5mols_version ver;
+		u8 bytes[VERSION_SIZE];
+	} version;
+	u32 *value;
+	u8 cmd = CAT0_VER_CUSTOMER;
+	int ret;
+
+	do {
+		value = (u32 *)&version.bytes[cmd];
+		ret = m5mols_read(sd, SYSTEM_CMD(cmd), value);
+		if (ret)
+			return ret;
+	} while (cmd++ != CAT0_VER_AWB);
+
+	do {
+		value = (u32 *)&version.bytes[cmd];
+		ret = m5mols_read(sd, SYSTEM_VER_STRING, value);
+		if (ret)
+			return ret;
+		if (cmd >= VERSION_SIZE - 1)
+			return -EINVAL;
+	} while (version.bytes[cmd++]);
+
+	value = (u32 *)&version.bytes[cmd];
+	ret = m5mols_read(sd, AF_VERSION, value);
+	if (ret)
+		return ret;
+
+	/* store version information swapped for being readable */
+	info->ver	= version.ver;
+	info->ver.fw	= be16_to_cpu(info->ver.fw);
+	info->ver.hw	= be16_to_cpu(info->ver.hw);
+	info->ver.param	= be16_to_cpu(info->ver.param);
+	info->ver.awb	= be16_to_cpu(info->ver.awb);
+
+	v4l2_info(sd, "Manufacturer\t[%s]\n",
+			is_manufacturer(info, REG_SAMSUNG_ELECTRO) ?
+			"Samsung Electro-Machanics" :
+			is_manufacturer(info, REG_SAMSUNG_OPTICS) ?
+			"Samsung Fiber-Optics" :
+			is_manufacturer(info, REG_SAMSUNG_TECHWIN) ?
+			"Samsung Techwin" : "None");
+	v4l2_info(sd, "Customer/Project\t[0x%02x/0x%02x]\n",
+			info->ver.customer, info->ver.project);
+
+	if (!is_available_af(info))
+		v4l2_info(sd, "No support Auto Focus on this firmware\n");
+
+	return ret;
+}
+
+/**
+ * __find_restype - Lookup M-5MOLS resolution type according to pixel code
+ * @code: pixel code
+ */
+static enum m5mols_restype __find_restype(enum v4l2_mbus_pixelcode code)
+{
+	enum m5mols_restype type = M5MOLS_RESTYPE_MONITOR;
+
+	do {
+		if (code == m5mols_default_ffmt[type].code)
+			return type;
+	} while (type++ != SIZE_DEFAULT_FFMT);
+
+	return 0;
+}
+
+/**
+ * __find_resolution - Lookup preset and type of M-5MOLS's resolution
+ * @mf: pixel format to find/negotiate the resolution preset for
+ * @type: M-5MOLS resolution type
+ * @resolution:	M-5MOLS resolution preset register value
+ *
+ * Find nearest resolution matching resolution preset and adjust mf
+ * to supported values.
+ */
+static int __find_resolution(struct v4l2_subdev *sd,
+			     struct v4l2_mbus_framefmt *mf,
+			     enum m5mols_restype *type,
+			     u32 *resolution)
+{
+	const struct m5mols_resolution *fsize = &m5mols_reg_res[0];
+	const struct m5mols_resolution *match = NULL;
+	enum m5mols_restype stype = __find_restype(mf->code);
+	int i = ARRAY_SIZE(m5mols_reg_res);
+	unsigned int min_err = ~0;
+
+	while (i--) {
+		int err;
+		if (stype == fsize->type) {
+			err = abs(fsize->width - mf->width)
+				+ abs(fsize->height - mf->height);
+
+			if (err < min_err) {
+				min_err = err;
+				match = fsize;
+			}
+		}
+		fsize++;
+	}
+	if (match) {
+		mf->width  = match->width;
+		mf->height = match->height;
+		*resolution = match->reg;
+		*type = stype;
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static struct v4l2_mbus_framefmt *__find_format(struct m5mols_info *info,
+				struct v4l2_subdev_fh *fh,
+				enum v4l2_subdev_format_whence which,
+				enum m5mols_restype type)
+{
+	if (which == V4L2_SUBDEV_FORMAT_TRY)
+		return fh ? v4l2_subdev_get_try_format(fh, 0) : NULL;
+
+	return &info->ffmt[type];
+}
+
+static int m5mols_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+			  struct v4l2_subdev_format *fmt)
+{
+	struct m5mols_info *info = to_m5mols(sd);
+	struct v4l2_mbus_framefmt *format;
+
+	if (fmt->pad != 0)
+		return -EINVAL;
+
+	format = __find_format(info, fh, fmt->which, info->res_type);
+	if (!format)
+		return -EINVAL;
+
+	fmt->format = *format;
+	return 0;
+}
+
+static int m5mols_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+			  struct v4l2_subdev_format *fmt)
+{
+	struct m5mols_info *info = to_m5mols(sd);
+	struct v4l2_mbus_framefmt *format = &fmt->format;
+	struct v4l2_mbus_framefmt *sfmt;
+	enum m5mols_restype type;
+	u32 resolution = 0;
+	int ret;
+
+	if (fmt->pad != 0)
+		return -EINVAL;
+
+	ret = __find_resolution(sd, format, &type, &resolution);
+	if (ret < 0)
+		return ret;
+
+	sfmt = __find_format(info, fh, fmt->which, type);
+	if (!sfmt)
+		return 0;
+
+	*sfmt		= m5mols_default_ffmt[type];
+	sfmt->width	= format->width;
+	sfmt->height	= format->height;
+
+	if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+		info->resolution = resolution;
+		info->code = format->code;
+		info->res_type = type;
+	}
+
+	return 0;
+}
+
+static int m5mols_enum_mbus_code(struct v4l2_subdev *sd,
+				 struct v4l2_subdev_fh *fh,
+				 struct v4l2_subdev_mbus_code_enum *code)
+{
+	if (!code || code->index >= SIZE_DEFAULT_FFMT)
+		return -EINVAL;
+
+	code->code = m5mols_default_ffmt[code->index].code;
+
+	return 0;
+}
+
+static struct v4l2_subdev_pad_ops m5mols_pad_ops = {
+	.enum_mbus_code	= m5mols_enum_mbus_code,
+	.get_fmt	= m5mols_get_fmt,
+	.set_fmt	= m5mols_set_fmt,
+};
+
+/**
+ * m5mols_sync_controls - Apply default scene mode and the current controls
+ *
+ * This is used only streaming for syncing between v4l2_ctrl framework and
+ * m5mols's controls. First, do the scenemode to the sensor, then call
+ * v4l2_ctrl_handler_setup. It can be same between some commands and
+ * the scenemode's in the default v4l2_ctrls. But, such commands of control
+ * should be prior to the scenemode's one.
+ */
+int m5mols_sync_controls(struct m5mols_info *info)
+{
+	int ret = -EINVAL;
+
+	if (!is_ctrl_synced(info)) {
+		ret = m5mols_do_scenemode(info, REG_SCENE_NORMAL);
+		if (ret)
+			return ret;
+
+		v4l2_ctrl_handler_setup(&info->handle);
+		info->ctrl_sync = true;
+	}
+
+	return ret;
+}
+
+/**
+ * m5mols_start_monitor - Start the monitor mode
+ *
+ * Before applying the controls setup the resolution and frame rate
+ * in PARAMETER mode, and then switch over to MONITOR mode.
+ */
+static int m5mols_start_monitor(struct m5mols_info *info)
+{
+	struct v4l2_subdev *sd = &info->sd;
+	int ret;
+
+	ret = m5mols_mode(info, REG_PARAMETER);
+	if (!ret)
+		ret = m5mols_write(sd, PARM_MON_SIZE, info->resolution);
+	if (!ret)
+		ret = m5mols_write(sd, PARM_MON_FPS, REG_FPS_30);
+	if (!ret)
+		ret = m5mols_mode(info, REG_MONITOR);
+	if (!ret)
+		ret = m5mols_sync_controls(info);
+
+	return ret;
+}
+
+static int m5mols_s_stream(struct v4l2_subdev *sd, int enable)
+{
+	struct m5mols_info *info = to_m5mols(sd);
+
+	if (enable) {
+		int ret = -EINVAL;
+
+		if (is_code(info->code, M5MOLS_RESTYPE_MONITOR))
+			ret = m5mols_start_monitor(info);
+		if (is_code(info->code, M5MOLS_RESTYPE_CAPTURE))
+			ret = m5mols_start_capture(info);
+
+		return ret;
+	}
+
+	return m5mols_mode(info, REG_PARAMETER);
+}
+
+static const struct v4l2_subdev_video_ops m5mols_video_ops = {
+	.s_stream	= m5mols_s_stream,
+};
+
+static int m5mols_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct v4l2_subdev *sd = to_sd(ctrl);
+	struct m5mols_info *info = to_m5mols(sd);
+	int ret;
+
+	info->mode_save = info->mode;
+
+	ret = m5mols_mode(info, REG_PARAMETER);
+	if (!ret)
+		ret = m5mols_set_ctrl(ctrl);
+	if (!ret)
+		ret = m5mols_mode(info, info->mode_save);
+
+	return ret;
+}
+
+static const struct v4l2_ctrl_ops m5mols_ctrl_ops = {
+	.s_ctrl	= m5mols_s_ctrl,
+};
+
+static int m5mols_sensor_power(struct m5mols_info *info, bool enable)
+{
+	struct v4l2_subdev *sd = &info->sd;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	const struct m5mols_platform_data *pdata = info->pdata;
+	int ret;
+
+	if (enable) {
+		if (is_powered(info))
+			return 0;
+
+		if (info->set_power) {
+			ret = info->set_power(&client->dev, 1);
+			if (ret)
+				return ret;
+		}
+
+		ret = regulator_bulk_enable(ARRAY_SIZE(supplies), supplies);
+		if (ret) {
+			info->set_power(&client->dev, 0);
+			return ret;
+		}
+
+		gpio_set_value(pdata->gpio_reset, !pdata->reset_polarity);
+		usleep_range(1000, 1000);
+		info->power = true;
+
+		return ret;
+	}
+
+	if (!is_powered(info))
+		return 0;
+
+	ret = regulator_bulk_disable(ARRAY_SIZE(supplies), supplies);
+	if (ret)
+		return ret;
+
+	if (info->set_power)
+		info->set_power(&client->dev, 0);
+
+	gpio_set_value(pdata->gpio_reset, pdata->reset_polarity);
+	usleep_range(1000, 1000);
+	info->power = false;
+
+	return ret;
+}
+
+/* m5mols_update_fw - optional firmware update routine */
+int __attribute__ ((weak)) m5mols_update_fw(struct v4l2_subdev *sd,
+		int (*set_power)(struct m5mols_info *, bool))
+{
+	return 0;
+}
+
+/**
+ * m5mols_sensor_armboot - Booting M-5MOLS internal ARM core.
+ *
+ * Booting internal ARM core makes the M-5MOLS is ready for getting commands
+ * with I2C. It's the first thing to be done after it powered up. It must wait
+ * at least 520ms recommended by M-5MOLS datasheet, after executing arm booting.
+ */
+static int m5mols_sensor_armboot(struct v4l2_subdev *sd)
+{
+	int ret;
+
+	ret = m5mols_write(sd, FLASH_CAM_START, REG_START_ARM_BOOT);
+	if (ret < 0)
+		return ret;
+
+	msleep(520);
+
+	ret = m5mols_get_version(sd);
+	if (!ret)
+		ret = m5mols_update_fw(sd, m5mols_sensor_power);
+	if (ret)
+		return ret;
+
+	v4l2_dbg(1, m5mols_debug, sd, "Success ARM Booting\n");
+
+	ret = m5mols_write(sd, PARM_INTERFACE, REG_INTERFACE_MIPI);
+	if (!ret)
+		ret = m5mols_enable_interrupt(sd, REG_INT_AF);
+
+	return ret;
+}
+
+static int m5mols_init_controls(struct m5mols_info *info)
+{
+	struct v4l2_subdev *sd = &info->sd;
+	u16 max_exposure;
+	u16 step_zoom;
+	int ret;
+
+	/* Determine value's range & step of controls for various FW version */
+	ret = m5mols_read(sd, AE_MAX_GAIN_MON, (u32 *)&max_exposure);
+	if (!ret)
+		step_zoom = is_manufacturer(info, REG_SAMSUNG_OPTICS) ? 31 : 1;
+	if (ret)
+		return ret;
+
+	v4l2_ctrl_handler_init(&info->handle, 6);
+	info->autowb = v4l2_ctrl_new_std(&info->handle,
+			&m5mols_ctrl_ops, V4L2_CID_AUTO_WHITE_BALANCE,
+			0, 1, 1, 0);
+	info->saturation = v4l2_ctrl_new_std(&info->handle,
+			&m5mols_ctrl_ops, V4L2_CID_SATURATION,
+			1, 5, 1, 3);
+	info->zoom = v4l2_ctrl_new_std(&info->handle,
+			&m5mols_ctrl_ops, V4L2_CID_ZOOM_ABSOLUTE,
+			1, 70, step_zoom, 1);
+	info->exposure = v4l2_ctrl_new_std(&info->handle,
+			&m5mols_ctrl_ops, V4L2_CID_EXPOSURE,
+			0, max_exposure, 1, (int)max_exposure/2);
+	info->colorfx = v4l2_ctrl_new_std_menu(&info->handle,
+			&m5mols_ctrl_ops, V4L2_CID_COLORFX,
+			4, (1 << V4L2_COLORFX_BW), V4L2_COLORFX_NONE);
+	info->autoexposure = v4l2_ctrl_new_std_menu(&info->handle,
+			&m5mols_ctrl_ops, V4L2_CID_EXPOSURE_AUTO,
+			1, 0, V4L2_EXPOSURE_MANUAL);
+
+	sd->ctrl_handler = &info->handle;
+	if (info->handle.error) {
+		v4l2_err(sd, "Failed to initialize controls: %d\n", ret);
+		v4l2_ctrl_handler_free(&info->handle);
+		return info->handle.error;
+	}
+
+	v4l2_ctrl_cluster(2, &info->autoexposure);
+
+	return 0;
+}
+
+/**
+ * m5mols_s_power - Main sensor power control function
+ *
+ * To prevent breaking the lens when the sensor is powered off the Soft-Landing
+ * algorithm is called where available. The Soft-Landing algorithm availability
+ * dependends on the firmware provider.
+ */
+static int m5mols_s_power(struct v4l2_subdev *sd, int on)
+{
+	struct m5mols_info *info = to_m5mols(sd);
+	int ret;
+
+	if (on) {
+		ret = m5mols_sensor_power(info, true);
+		if (!ret)
+			ret = m5mols_sensor_armboot(sd);
+		if (!ret)
+			ret = m5mols_init_controls(info);
+		if (ret)
+			return ret;
+
+		info->ffmt[M5MOLS_RESTYPE_MONITOR] =
+			m5mols_default_ffmt[M5MOLS_RESTYPE_MONITOR];
+		info->ffmt[M5MOLS_RESTYPE_CAPTURE] =
+			m5mols_default_ffmt[M5MOLS_RESTYPE_CAPTURE];
+		return ret;
+	}
+
+	if (is_manufacturer(info, REG_SAMSUNG_TECHWIN)) {
+		ret = m5mols_mode(info, REG_MONITOR);
+		if (!ret)
+			ret = m5mols_write(sd, AF_EXECUTE, REG_AF_STOP);
+		if (!ret)
+			ret = m5mols_write(sd, AF_MODE, REG_AF_POWEROFF);
+		if (!ret)
+			ret = m5mols_busy(sd, CAT_SYSTEM, CAT0_STATUS,
+					REG_AF_IDLE);
+		if (!ret)
+			v4l2_info(sd, "Success soft-landing lens\n");
+	}
+
+	ret = m5mols_sensor_power(info, false);
+	if (!ret) {
+		v4l2_ctrl_handler_free(&info->handle);
+		info->ctrl_sync = false;
+	}
+
+	return ret;
+}
+
+static int m5mols_log_status(struct v4l2_subdev *sd)
+{
+	struct m5mols_info *info = to_m5mols(sd);
+
+	v4l2_ctrl_handler_log_status(&info->handle, sd->name);
+
+	return 0;
+}
+
+static const struct v4l2_subdev_core_ops m5mols_core_ops = {
+	.s_power	= m5mols_s_power,
+	.g_ctrl		= v4l2_subdev_g_ctrl,
+	.s_ctrl		= v4l2_subdev_s_ctrl,
+	.queryctrl	= v4l2_subdev_queryctrl,
+	.querymenu	= v4l2_subdev_querymenu,
+	.g_ext_ctrls	= v4l2_subdev_g_ext_ctrls,
+	.try_ext_ctrls	= v4l2_subdev_try_ext_ctrls,
+	.s_ext_ctrls	= v4l2_subdev_s_ext_ctrls,
+	.log_status	= m5mols_log_status,
+};
+
+static const struct v4l2_subdev_ops m5mols_ops = {
+	.core		= &m5mols_core_ops,
+	.pad		= &m5mols_pad_ops,
+	.video		= &m5mols_video_ops,
+};
+
+static void m5mols_irq_work(struct work_struct *work)
+{
+	struct m5mols_info *info =
+		container_of(work, struct m5mols_info, work_irq);
+	struct v4l2_subdev *sd = &info->sd;
+	u32 reg;
+	int ret;
+
+	if (!is_powered(info) ||
+			m5mols_read(sd, SYSTEM_INT_FACTOR, &info->interrupt))
+		return;
+
+	switch (info->interrupt & REG_INT_MASK) {
+	case REG_INT_AF:
+		if (!is_available_af(info))
+			break;
+		ret = m5mols_read(sd, AF_STATUS, &reg);
+		v4l2_dbg(2, m5mols_debug, sd, "AF %s\n",
+			 reg == REG_AF_FAIL ? "Failed" :
+			 reg == REG_AF_SUCCESS ? "Success" :
+			 reg == REG_AF_IDLE ? "Idle" : "Busy");
+		break;
+	case REG_INT_CAPTURE:
+		if (!test_and_set_bit(ST_CAPT_IRQ, &info->flags))
+			wake_up_interruptible(&info->irq_waitq);
+
+		v4l2_dbg(2, m5mols_debug, sd, "CAPTURE\n");
+		break;
+	default:
+		v4l2_dbg(2, m5mols_debug, sd, "Undefined: %02x\n", reg);
+		break;
+	};
+}
+
+static irqreturn_t m5mols_irq_handler(int irq, void *data)
+{
+	struct v4l2_subdev *sd = data;
+	struct m5mols_info *info = to_m5mols(sd);
+
+	schedule_work(&info->work_irq);
+
+	return IRQ_HANDLED;
+}
+
+static int __devinit m5mols_probe(struct i2c_client *client,
+				  const struct i2c_device_id *id)
+{
+	const struct m5mols_platform_data *pdata = client->dev.platform_data;
+	struct m5mols_info *info;
+	struct v4l2_subdev *sd;
+	int ret;
+
+	if (pdata == NULL) {
+		dev_err(&client->dev, "No platform data\n");
+		return -EINVAL;
+	}
+
+	if (!gpio_is_valid(pdata->gpio_reset)) {
+		dev_err(&client->dev, "No valid RESET GPIO specified\n");
+		return -EINVAL;
+	}
+
+	if (!pdata->irq) {
+		dev_err(&client->dev, "Interrupt not assigned\n");
+		return -EINVAL;
+	}
+
+	info = kzalloc(sizeof(struct m5mols_info), GFP_KERNEL);
+	if (!info)
+		return -ENOMEM;
+
+	info->pdata = pdata;
+	info->set_power	= pdata->set_power;
+
+	ret = gpio_request(pdata->gpio_reset, "M5MOLS_NRST");
+	if (ret) {
+		dev_err(&client->dev, "Failed to request gpio: %d\n", ret);
+		goto out_free;
+	}
+	gpio_direction_output(pdata->gpio_reset, pdata->reset_polarity);
+
+	ret = regulator_bulk_get(&client->dev, ARRAY_SIZE(supplies), supplies);
+	if (ret) {
+		dev_err(&client->dev, "Failed to get regulators: %d\n", ret);
+		goto out_gpio;
+	}
+
+	sd = &info->sd;
+	strlcpy(sd->name, MODULE_NAME, sizeof(sd->name));
+	v4l2_i2c_subdev_init(sd, client, &m5mols_ops);
+
+	info->pad.flags = MEDIA_PAD_FL_SOURCE;
+	ret = media_entity_init(&sd->entity, 1, &info->pad, 0);
+	if (ret < 0)
+		goto out_reg;
+	sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR;
+
+	init_waitqueue_head(&info->irq_waitq);
+	INIT_WORK(&info->work_irq, m5mols_irq_work);
+	ret = request_irq(pdata->irq, m5mols_irq_handler,
+			  IRQF_TRIGGER_RISING, MODULE_NAME, sd);
+	if (ret) {
+		dev_err(&client->dev, "Interrupt request failed: %d\n", ret);
+		goto out_me;
+	}
+	info->res_type = M5MOLS_RESTYPE_MONITOR;
+	return 0;
+out_me:
+	media_entity_cleanup(&sd->entity);
+out_reg:
+	regulator_bulk_free(ARRAY_SIZE(supplies), supplies);
+out_gpio:
+	gpio_free(pdata->gpio_reset);
+out_free:
+	kfree(info);
+	return ret;
+}
+
+static int __devexit 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);
+	free_irq(info->pdata->irq, sd);
+
+	regulator_bulk_free(ARRAY_SIZE(supplies), supplies);
+	gpio_free(info->pdata->gpio_reset);
+	media_entity_cleanup(&sd->entity);
+	kfree(info);
+	return 0;
+}
+
+static const struct i2c_device_id m5mols_id[] = {
+	{ MODULE_NAME, 0 },
+	{ },
+};
+MODULE_DEVICE_TABLE(i2c, m5mols_id);
+
+static struct i2c_driver m5mols_i2c_driver = {
+	.driver = {
+		.name	= MODULE_NAME,
+	},
+	.probe		= m5mols_probe,
+	.remove		= __devexit_p(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@samsung.com>");
+MODULE_AUTHOR("Dongsoo Kim <dongsoo45.kim@samsung.com>");
+MODULE_DESCRIPTION("Fujitsu M-5MOLS 8M Pixel camera driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/m5mols/m5mols_reg.h b/drivers/media/video/m5mols/m5mols_reg.h
new file mode 100644
index 0000000..b83e36f
--- /dev/null
+++ b/drivers/media/video/m5mols/m5mols_reg.h
@@ -0,0 +1,399 @@
+/*
+ * Register map for M-5MOLS 8M Pixel camera sensor with ISP
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * Author: HeungJun Kim, riverful.kim@samsung.com
+ *
+ * Copyright (C) 2009 Samsung Electronics Co., Ltd.
+ * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com
+ *
+ * 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_REG_H
+#define M5MOLS_REG_H
+
+#define M5MOLS_I2C_MAX_SIZE	4
+#define M5MOLS_BYTE_READ	0x01
+#define M5MOLS_BYTE_WRITE	0x02
+
+#define I2C_CATEGORY(__cat)		((__cat >> 16) & 0xff)
+#define I2C_COMMAND(__comm)		((__comm >> 8) & 0xff)
+#define I2C_SIZE(__reg_s)		((__reg_s) & 0xff)
+#define I2C_REG(__cat, __cmd, __reg_s)	((__cat << 16) | (__cmd << 8) | __reg_s)
+
+/*
+ * Category section register
+ *
+ * The category means set including relevant command of M-5MOLS.
+ */
+#define CAT_SYSTEM		0x00
+#define CAT_PARAM		0x01
+#define CAT_MONITOR		0x02
+#define CAT_AE			0x03
+#define CAT_WB			0x06
+#define CAT_EXIF		0x07
+#define CAT_FD			0x09
+#define CAT_LENS		0x0a
+#define CAT_CAPT_PARM		0x0b
+#define CAT_CAPT_CTRL		0x0c
+#define CAT_FLASH		0x0f	/* related to FW, revisions, booting */
+
+/*
+ * Category 0 - SYSTEM mode
+ *
+ * The SYSTEM mode in the M-5MOLS means area available to handle with the whole
+ * & all-round system of sensor. It deals with version/interrupt/setting mode &
+ * even sensor's status. Especially, the M-5MOLS sensor with ISP varies by
+ * packaging & manufacturer, even the customer and project code. And the
+ * function details may vary among them. The version information helps to
+ * determine what methods shall be used in the driver.
+ *
+ * There is many registers between customer version address and awb one. For
+ * more specific contents, see definition if file m5mols.h.
+ */
+#define CAT0_VER_CUSTOMER	0x00	/* customer version */
+#define CAT0_VER_AWB		0x09	/* Auto WB version */
+#define CAT0_VER_STRING		0x0a	/* string including M-5MOLS */
+#define CAT0_SYSMODE		0x0b	/* SYSTEM mode register */
+#define CAT0_STATUS		0x0c	/* SYSTEM mode status register */
+#define CAT0_INT_FACTOR		0x10	/* interrupt pending register */
+#define CAT0_INT_ENABLE		0x11	/* interrupt enable register */
+
+#define SYSTEM_SYSMODE		I2C_REG(CAT_SYSTEM, CAT0_SYSMODE, 1)
+#define REG_SYSINIT		0x00	/* SYSTEM mode */
+#define REG_PARAMETER		0x01	/* PARAMETER mode */
+#define REG_MONITOR		0x02	/* MONITOR mode */
+#define REG_CAPTURE		0x03	/* CAPTURE mode */
+
+#define SYSTEM_CMD(__cmd)	I2C_REG(CAT_SYSTEM, cmd, 1)
+#define SYSTEM_VER_STRING	I2C_REG(CAT_SYSTEM, CAT0_VER_STRING, 1)
+#define REG_SAMSUNG_ELECTRO	"SE"	/* Samsung Electro-Mechanics */
+#define REG_SAMSUNG_OPTICS	"OP"	/* Samsung Fiber-Optics */
+#define REG_SAMSUNG_TECHWIN	"TB"	/* Samsung Techwin */
+
+#define SYSTEM_INT_FACTOR	I2C_REG(CAT_SYSTEM, CAT0_INT_FACTOR, 1)
+#define SYSTEM_INT_ENABLE	I2C_REG(CAT_SYSTEM, CAT0_INT_ENABLE, 1)
+#define REG_INT_MODE		(1 << 0)
+#define REG_INT_AF		(1 << 1)
+#define REG_INT_ZOOM		(1 << 2)
+#define REG_INT_CAPTURE		(1 << 3)
+#define REG_INT_FRAMESYNC	(1 << 4)
+#define REG_INT_FD		(1 << 5)
+#define REG_INT_LENS_INIT	(1 << 6)
+#define REG_INT_SOUND		(1 << 7)
+#define REG_INT_MASK		0x0f
+
+/*
+ * category 1 - PARAMETER mode
+ *
+ * This category supports function of camera features of M-5MOLS. It means we
+ * can handle with preview(MONITOR) resolution size/frame per second/interface
+ * between the sensor and the Application Processor/even the image effect.
+ */
+#define CAT1_DATA_INTERFACE	0x00	/* interface between sensor and AP */
+#define CAT1_MONITOR_SIZE	0x01	/* resolution at the MONITOR mode */
+#define CAT1_MONITOR_FPS	0x02	/* frame per second at this mode */
+#define CAT1_EFFECT		0x0b	/* image effects */
+
+#define PARM_MON_SIZE		I2C_REG(CAT_PARAM, CAT1_MONITOR_SIZE, 1)
+
+#define PARM_MON_FPS		I2C_REG(CAT_PARAM, CAT1_MONITOR_FPS, 1)
+#define REG_FPS_30		0x02
+
+#define PARM_INTERFACE		I2C_REG(CAT_PARAM, CAT1_DATA_INTERFACE, 1)
+#define REG_INTERFACE_MIPI	0x02
+
+#define PARM_EFFECT		I2C_REG(CAT_PARAM, CAT1_EFFECT, 1)
+#define REG_EFFECT_OFF		0x00
+#define REG_EFFECT_NEGA		0x01
+#define REG_EFFECT_EMBOSS	0x06
+#define REG_EFFECT_OUTLINE	0x07
+#define REG_EFFECT_WATERCOLOR	0x08
+
+/*
+ * Category 2 - MONITOR mode
+ *
+ * The MONITOR mode is same as preview mode as we said. The M-5MOLS has another
+ * mode named "Preview", but this preview mode is used at the case specific
+ * vider-recording mode. This mmode supports only YUYV format. On the other
+ * hand, the JPEG & RAW formats is supports by CAPTURE mode. And, there are
+ * another options like zoom/color effect(different with effect in PARAMETER
+ * mode)/anti hand shaking algorithm.
+ */
+#define CAT2_ZOOM		0x01	/* set the zoom position & execute */
+#define CAT2_ZOOM_STEP		0x03	/* set the zoom step */
+#define CAT2_CFIXB		0x09	/* CB value for color effect */
+#define CAT2_CFIXR		0x0a	/* CR value for color effect */
+#define CAT2_COLOR_EFFECT	0x0b	/* set on/off of color effect */
+#define CAT2_CHROMA_LVL		0x0f	/* set chroma level */
+#define CAT2_CHROMA_EN		0x10	/* set on/off of choroma */
+#define CAT2_EDGE_LVL		0x11	/* set sharpness level */
+#define CAT2_EDGE_EN		0x12	/* set on/off sharpness */
+#define CAT2_TONE_CTL		0x25	/* set tone color(contrast) */
+
+#define MON_ZOOM		I2C_REG(CAT_MONITOR, CAT2_ZOOM, 1)
+
+#define MON_CFIXR		I2C_REG(CAT_MONITOR, CAT2_CFIXR, 1)
+#define MON_CFIXB		I2C_REG(CAT_MONITOR, CAT2_CFIXB, 1)
+#define REG_CFIXB_SEPIA		0xd8
+#define REG_CFIXR_SEPIA		0x18
+
+#define MON_EFFECT		I2C_REG(CAT_MONITOR, CAT2_COLOR_EFFECT, 1)
+#define REG_COLOR_EFFECT_OFF	0x00
+#define REG_COLOR_EFFECT_ON	0x01
+
+#define MON_CHROMA_EN		I2C_REG(CAT_MONITOR, CAT2_CHROMA_EN, 1)
+#define MON_CHROMA_LVL		I2C_REG(CAT_MONITOR, CAT2_CHROMA_LVL, 1)
+#define REG_CHROMA_OFF		0x00
+#define REG_CHROMA_ON		0x01
+
+#define MON_EDGE_EN		I2C_REG(CAT_MONITOR, CAT2_EDGE_EN, 1)
+#define MON_EDGE_LVL		I2C_REG(CAT_MONITOR, CAT2_EDGE_LVL, 1)
+#define REG_EDGE_OFF		0x00
+#define REG_EDGE_ON		0x01
+
+#define MON_TONE_CTL		I2C_REG(CAT_MONITOR, CAT2_TONE_CTL, 1)
+
+/*
+ * Category 3 - Auto Exposure
+ *
+ * The M-5MOLS exposure capbility is detailed as which is similar to digital
+ * camera. This category supports AE locking/various AE mode(range of exposure)
+ * /ISO/flickering/EV bias/shutter/meteoring, and anything else. And the
+ * maximum/minimum exposure gain value depending on M-5MOLS firmware, may be
+ * different. So, this category also provide getting the max/min values. And,
+ * each MONITOR and CAPTURE mode has each gain/shutter/max exposure values.
+ */
+#define CAT3_AE_LOCK		0x00	/* locking Auto exposure */
+#define CAT3_AE_MODE		0x01	/* set AE mode, mode means range */
+#define CAT3_ISO		0x05	/* set ISO */
+#define CAT3_EV_PRESET_MONITOR	0x0a	/* EV(scenemode) preset for MONITOR */
+#define CAT3_EV_PRESET_CAPTURE	0x0b	/* EV(scenemode) preset for CAPTURE */
+#define CAT3_MANUAL_GAIN_MON	0x12	/* meteoring value for the MONITOR */
+#define CAT3_MAX_GAIN_MON	0x1a	/* max gain value for the MONITOR */
+#define CAT3_MANUAL_GAIN_CAP	0x26	/* meteoring value for the CAPTURE */
+#define CAT3_AE_INDEX		0x38	/* AE index */
+
+#define AE_LOCK			I2C_REG(CAT_AE, CAT3_AE_LOCK, 1)
+#define REG_AE_UNLOCK		0x00
+#define REG_AE_LOCK		0x01
+
+#define AE_MODE			I2C_REG(CAT_AE, CAT3_AE_MODE, 1)
+#define REG_AE_OFF		0x00	/* AE off */
+#define REG_AE_ALL		0x01	/* calc AE in all block integral */
+#define REG_AE_CENTER		0x03	/* calc AE in center weighted */
+#define REG_AE_SPOT		0x06	/* calc AE in specific spot */
+
+#define AE_ISO			I2C_REG(CAT_AE, CAT3_ISO, 1)
+#define REG_ISO_AUTO		0x00
+#define REG_ISO_50		0x01
+#define REG_ISO_100		0x02
+#define REG_ISO_200		0x03
+#define REG_ISO_400		0x04
+#define REG_ISO_800		0x05
+
+#define AE_EV_PRESET_MONITOR	I2C_REG(CAT_AE, CAT3_EV_PRESET_MONITOR, 1)
+#define AE_EV_PRESET_CAPTURE	I2C_REG(CAT_AE, CAT3_EV_PRESET_CAPTURE, 1)
+#define REG_SCENE_NORMAL	0x00
+#define REG_SCENE_PORTRAIT	0x01
+#define REG_SCENE_LANDSCAPE	0x02
+#define REG_SCENE_SPORTS	0x03
+#define REG_SCENE_PARTY_INDOOR	0x04
+#define REG_SCENE_BEACH_SNOW	0x05
+#define REG_SCENE_SUNSET	0x06
+#define REG_SCENE_DAWN_DUSK	0x07
+#define REG_SCENE_FALL		0x08
+#define REG_SCENE_NIGHT		0x09
+#define REG_SCENE_AGAINST_LIGHT	0x0a
+#define REG_SCENE_FIRE		0x0b
+#define REG_SCENE_TEXT		0x0c
+#define REG_SCENE_CANDLE	0x0d
+
+#define AE_MAN_GAIN_MON		I2C_REG(CAT_AE, CAT3_MANUAL_GAIN_MON, 2)
+#define AE_MAX_GAIN_MON		I2C_REG(CAT_AE, CAT3_MAX_GAIN_MON, 2)
+#define AE_MAN_GAIN_CAP		I2C_REG(CAT_AE, CAT3_MANUAL_GAIN_CAP, 2)
+
+#define AE_INDEX		I2C_REG(CAT_AE, CAT3_AE_INDEX, 1)
+#define REG_AE_INDEX_20_NEG	0x00
+#define REG_AE_INDEX_15_NEG	0x01
+#define REG_AE_INDEX_10_NEG	0x02
+#define REG_AE_INDEX_05_NEG	0x03
+#define REG_AE_INDEX_00		0x04
+#define REG_AE_INDEX_05_POS	0x05
+#define REG_AE_INDEX_10_POS	0x06
+#define REG_AE_INDEX_15_POS	0x07
+#define REG_AE_INDEX_20_POS	0x08
+
+/*
+ * Category 6 - White Balance
+ *
+ * This category provide AWB locking/mode/preset/speed/gain bias, etc.
+ */
+#define CAT6_AWB_LOCK		0x00	/* locking Auto Whitebalance */
+#define CAT6_AWB_MODE		0x02	/* set Auto or Manual */
+#define CAT6_AWB_MANUAL		0x03	/* set Manual(preset) value */
+
+#define AWB_LOCK		I2C_REG(CAT_WB, CAT6_AWB_LOCK, 1)
+#define REG_AWB_UNLOCK		0x00
+#define REG_AWB_LOCK		0x01
+
+#define AWB_MODE		I2C_REG(CAT_WB, CAT6_AWB_MODE, 1)
+#define REG_AWB_AUTO		0x01	/* AWB off */
+#define REG_AWB_PRESET		0x02	/* AWB preset */
+
+#define AWB_MANUAL		I2C_REG(CAT_WB, CAT6_AWB_MANUAL, 1)
+#define REG_AWB_INCANDESCENT	0x01
+#define REG_AWB_FLUORESCENT_1	0x02
+#define REG_AWB_FLUORESCENT_2	0x03
+#define REG_AWB_DAYLIGHT	0x04
+#define REG_AWB_CLOUDY		0x05
+#define REG_AWB_SHADE		0x06
+#define REG_AWB_HORIZON		0x07
+#define REG_AWB_LEDLIGHT	0x09
+
+/*
+ * Category 7 - EXIF information
+ */
+#define CAT7_INFO_EXPTIME_NU	0x00
+#define CAT7_INFO_EXPTIME_DE	0x04
+#define CAT7_INFO_TV_NU		0x08
+#define CAT7_INFO_TV_DE		0x0c
+#define CAT7_INFO_AV_NU		0x10
+#define CAT7_INFO_AV_DE		0x14
+#define CAT7_INFO_BV_NU		0x18
+#define CAT7_INFO_BV_DE		0x1c
+#define CAT7_INFO_EBV_NU	0x20
+#define CAT7_INFO_EBV_DE	0x24
+#define CAT7_INFO_ISO		0x28
+#define CAT7_INFO_FLASH		0x2a
+#define CAT7_INFO_SDR		0x2c
+#define CAT7_INFO_QVAL		0x2e
+
+#define EXIF_INFO_EXPTIME_NU	I2C_REG(CAT_EXIF, CAT7_INFO_EXPTIME_NU, 4)
+#define EXIF_INFO_EXPTIME_DE	I2C_REG(CAT_EXIF, CAT7_INFO_EXPTIME_DE, 4)
+#define EXIF_INFO_TV_NU		I2C_REG(CAT_EXIF, CAT7_INFO_TV_NU, 4)
+#define EXIF_INFO_TV_DE		I2C_REG(CAT_EXIF, CAT7_INFO_TV_DE, 4)
+#define EXIF_INFO_AV_NU		I2C_REG(CAT_EXIF, CAT7_INFO_AV_NU, 4)
+#define EXIF_INFO_AV_DE		I2C_REG(CAT_EXIF, CAT7_INFO_AV_DE, 4)
+#define EXIF_INFO_BV_NU		I2C_REG(CAT_EXIF, CAT7_INFO_BV_NU, 4)
+#define EXIF_INFO_BV_DE		I2C_REG(CAT_EXIF, CAT7_INFO_BV_DE, 4)
+#define EXIF_INFO_EBV_NU	I2C_REG(CAT_EXIF, CAT7_INFO_EBV_NU, 4)
+#define EXIF_INFO_EBV_DE	I2C_REG(CAT_EXIF, CAT7_INFO_EBV_DE, 4)
+#define EXIF_INFO_ISO		I2C_REG(CAT_EXIF, CAT7_INFO_ISO, 2)
+#define EXIF_INFO_FLASH		I2C_REG(CAT_EXIF, CAT7_INFO_FLASH, 2)
+#define EXIF_INFO_SDR		I2C_REG(CAT_EXIF, CAT7_INFO_SDR, 2)
+#define EXIF_INFO_QVAL		I2C_REG(CAT_EXIF, CAT7_INFO_QVAL, 2)
+
+/*
+ * Category 9 - Face Detection
+ */
+#define CAT9_FD_CTL		0x00
+
+#define FD_CTL			I2C_REG(CAT_FD, CAT9_FD_CTL, 1)
+#define BIT_FD_EN		0
+#define BIT_FD_DRAW_FACE_FRAME	4
+#define BIT_FD_DRAW_SMILE_LVL	6
+#define REG_FD(shift)		(1 << shift)
+#define REG_FD_OFF		0x0
+
+/*
+ * Category A - Lens Parameter
+ */
+#define CATA_AF_MODE		0x01
+#define CATA_AF_EXECUTE		0x02
+#define CATA_AF_STATUS		0x03
+#define CATA_AF_VERSION		0x0a
+
+#define AF_MODE			I2C_REG(CAT_LENS, CATA_AF_MODE, 1)
+#define REG_AF_NORMAL		0x00	/* Normal AF, one time */
+#define REG_AF_MACRO		0x01	/* Macro AF, one time */
+#define REG_AF_POWEROFF		0x07
+
+#define AF_EXECUTE		I2C_REG(CAT_LENS, CATA_AF_EXECUTE, 1)
+#define REG_AF_STOP		0x00
+#define REG_AF_EXE_AUTO		0x01
+#define REG_AF_EXE_CAF		0x02
+
+#define AF_STATUS		I2C_REG(CAT_LENS, CATA_AF_STATUS, 1)
+#define REG_AF_FAIL		0x00
+#define REG_AF_SUCCESS		0x02
+#define REG_AF_IDLE		0x04
+#define REG_AF_BUSY		0x05
+
+#define AF_VERSION		I2C_REG(CAT_LENS, CATA_AF_VERSION, 1)
+
+/*
+ * Category B - CAPTURE Parameter
+ */
+#define CATB_YUVOUT_MAIN	0x00
+#define CATB_MAIN_IMAGE_SIZE	0x01
+#define CATB_MCC_MODE		0x1d
+#define CATB_WDR_EN		0x2c
+#define CATB_LIGHT_CTRL		0x40
+#define CATB_FLASH_CTRL		0x41
+
+#define CAPP_YUVOUT_MAIN	I2C_REG(CAT_CAPT_PARM, CATB_YUVOUT_MAIN, 1)
+#define REG_YUV422		0x00
+#define REG_BAYER10		0x05
+#define REG_BAYER8		0x06
+#define REG_JPEG		0x10
+
+#define CAPP_MAIN_IMAGE_SIZE	I2C_REG(CAT_CAPT_PARM, CATB_MAIN_IMAGE_SIZE, 1)
+
+#define CAPP_MCC_MODE		I2C_REG(CAT_CAPT_PARM, CATB_MCC_MODE, 1)
+#define REG_MCC_OFF		0x00
+#define REG_MCC_NORMAL		0x01
+
+#define CAPP_WDR_EN		I2C_REG(CAT_CAPT_PARM, CATB_WDR_EN, 1)
+#define REG_WDR_OFF		0x00
+#define REG_WDR_ON		0x01
+#define REG_WDR_AUTO		0x02
+
+#define CAPP_LIGHT_CTRL		I2C_REG(CAT_CAPT_PARM, CATB_LIGHT_CTRL, 1)
+#define REG_LIGHT_OFF		0x00
+#define REG_LIGHT_ON		0x01
+#define REG_LIGHT_AUTO		0x02
+
+#define CAPP_FLASH_CTRL		I2C_REG(CAT_CAPT_PARM, CATB_FLASH_CTRL, 1)
+#define REG_FLASH_OFF		0x00
+#define REG_FLASH_ON		0x01
+#define REG_FLASH_AUTO		0x02
+
+/*
+ * Category C - CAPTURE Control
+ */
+#define CATC_CAP_MODE		0x00
+#define CATC_CAP_SEL_FRAME	0x06	/* It determines Single or Multi */
+#define CATC_CAP_START		0x09
+#define CATC_CAP_IMAGE_SIZE	0x0d
+#define CATC_CAP_THUMB_SIZE	0x11
+
+#define CAPC_MODE		I2C_REG(CAT_CAPT_CTRL, CATC_CAP_MODE, 1)
+#define REG_CAP_NONE		0x00
+#define REG_CAP_ANTI_SHAKE	0x02
+
+#define CAPC_SEL_FRAME		I2C_REG(CAT_CAPT_CTRL, CATC_CAP_SEL_FRAME, 1)
+
+#define CAPC_START		I2C_REG(CAT_CAPT_CTRL, CATC_CAP_START, 1)
+#define REG_CAP_START_MAIN	0x01
+#define REG_CAP_START_THUMB	0x03
+
+#define CAPC_IMAGE_SIZE		I2C_REG(CAT_CAPT_CTRL, CATC_CAP_IMAGE_SIZE, 1)
+#define CAPC_THUMB_SIZE		I2C_REG(CAT_CAPT_CTRL, CATC_CAP_THUMB_SIZE, 1)
+
+/*
+ * Category F - Flash
+ *
+ * This mode provides functions about internal flash stuff and system startup.
+ */
+#define CATF_CAM_START		0x12	/* It starts internal ARM core booting
+					 * after power-up */
+
+#define FLASH_CAM_START		I2C_REG(CAT_FLASH, CATF_CAM_START, 1)
+#define REG_START_ARM_BOOT	0x01
+
+#endif	/* M5MOLS_REG_H */
diff --git a/include/media/m5mols.h b/include/media/m5mols.h
new file mode 100644
index 0000000..2d7e7ca
--- /dev/null
+++ b/include/media/m5mols.h
@@ -0,0 +1,35 @@
+/*
+ * Driver header for M-5MOLS 8M Pixel camera sensor with ISP
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * Author: HeungJun Kim, riverful.kim@samsung.com
+ *
+ * Copyright (C) 2009 Samsung Electronics Co., Ltd.
+ * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com
+ *
+ * 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 MEDIA_M5MOLS_H
+#define MEDIA_M5MOLS_H
+
+/**
+ * struct m5mols_platform_data - platform data for M-5MOLS driver
+ * @irq:	GPIO getting the irq pin of M-5MOLS
+ * @gpio_reset:	GPIO driving the reset pin of M-5MOLS
+ * @reset_polarity: active state for gpio_rst pin, 0 or 1
+ * @set_power:	an additional callback to the board setup code
+ *		to be called after enabling and before disabling
+ *		the sensor's supply regulators
+ */
+struct m5mols_platform_data {
+	int irq;
+	int gpio_reset;
+	u8 reset_polarity;
+	int (*set_power)(struct device *dev, int on);
+};
+
+#endif	/* MEDIA_M5MOLS_H */
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* Re: [PATCH v9] Add support for M-5MOLS 8 Mega Pixel camera ISP
  2011-05-20  5:56       ` [PATCH v9] " HeungJun, Kim
@ 2011-05-25 13:54         ` Sakari Ailus
  2011-05-26  7:12           ` Kim, HeungJun
                             ` (6 more replies)
  0 siblings, 7 replies; 34+ messages in thread
From: Sakari Ailus @ 2011-05-25 13:54 UTC (permalink / raw)
  To: HeungJun, Kim
  Cc: linux-media, mchehab, hverkuil, laurent.pinchart,
	Sylwester Nawrocki, Kyungmin Park

Hi HeungJun,

Thanks for the patch!

I'm happy to see that Samsung is interested in getting such a driver to
mainline. :-) I suppose that theoretically nothing would prevent plugging
such a sensor to the OMAP 3 ISP, for example. It's just that the sensor
already does image processing and the ISP might not be that useful because
of this. But the interfaces would match, both in software and in hardware.

This is a subdev driver and uses the control framework. Good. I have
comments on the code below. 

On Fri, May 20, 2011 at 02:56:57PM +0900, HeungJun, Kim wrote:
> Add I2C/V4L2 subdev driver for M-5MOLS integrated image signal processor
> with 8 Mega Pixel sensor.
> 
> Signed-off-by: HeungJun, Kim <riverful.kim@samsung.com>
> Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
> ---
> 
> Hello everyone,
> 
> This is the ninth version of the subdev for M-5MOLS 8M Pixel camera sensor.
> If you see previous version, you can find at:
> http://www.spinics.net/lists/linux-media/msg32387.html
> 
> The major changes including points commented in the IRC is as follows.
> 1. remove a lot of inline I2C functions for being easy to read
>   : rename m5mols_read/write() to m5mols_read/write()
>   : add I2C_XXX() macro to combinine the arguments of the I2C raw functions
> 2. use atomic macro for checking capture interrupt
> 3. add comments for DocBook
> 
> Thanks to read, and any comments are welcome!
> 
> --
> Thanks and Regards,
> Heungjun Kim
> Samsung Electronics DMC R&D Center
> ---
>  drivers/media/video/Kconfig                  |    2 +
>  drivers/media/video/Makefile                 |    1 +
>  drivers/media/video/m5mols/Kconfig           |    5 +
>  drivers/media/video/m5mols/Makefile          |    3 +
>  drivers/media/video/m5mols/m5mols.h          |  296 ++++++++
>  drivers/media/video/m5mols/m5mols_capture.c  |  191 +++++
>  drivers/media/video/m5mols/m5mols_controls.c |  299 ++++++++
>  drivers/media/video/m5mols/m5mols_core.c     | 1004 ++++++++++++++++++++++++++
>  drivers/media/video/m5mols/m5mols_reg.h      |  399 ++++++++++
>  include/media/m5mols.h                       |   35 +
>  10 files changed, 2235 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/media/video/m5mols/Kconfig
>  create mode 100644 drivers/media/video/m5mols/Makefile
>  create mode 100644 drivers/media/video/m5mols/m5mols.h
>  create mode 100644 drivers/media/video/m5mols/m5mols_capture.c
>  create mode 100644 drivers/media/video/m5mols/m5mols_controls.c
>  create mode 100644 drivers/media/video/m5mols/m5mols_core.c
>  create mode 100644 drivers/media/video/m5mols/m5mols_reg.h
>  create mode 100644 include/media/m5mols.h
> 
> diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
> index d61414e..242c80c 100644
> --- a/drivers/media/video/Kconfig
> +++ b/drivers/media/video/Kconfig
> @@ -753,6 +753,8 @@ config VIDEO_NOON010PC30
>  	---help---
>  	  This driver supports NOON010PC30 CIF camera from Siliconfile
>  
> +source "drivers/media/video/m5mols/Kconfig"
> +
>  config VIDEO_OMAP3
>  	tristate "OMAP 3 Camera support (EXPERIMENTAL)"
>  	select OMAP_IOMMU
> diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
> index a10e4c3..d5d6de1 100644
> --- a/drivers/media/video/Makefile
> +++ b/drivers/media/video/Makefile
> @@ -69,6 +69,7 @@ obj-$(CONFIG_VIDEO_MT9V011) += mt9v011.o
>  obj-$(CONFIG_VIDEO_MT9V032) += mt9v032.o
>  obj-$(CONFIG_VIDEO_SR030PC30)	+= sr030pc30.o
>  obj-$(CONFIG_VIDEO_NOON010PC30)	+= noon010pc30.o
> +obj-$(CONFIG_VIDEO_M5MOLS)	+= m5mols/
>  
>  obj-$(CONFIG_SOC_CAMERA_IMX074)		+= imx074.o
>  obj-$(CONFIG_SOC_CAMERA_MT9M001)	+= mt9m001.o
> diff --git a/drivers/media/video/m5mols/Kconfig b/drivers/media/video/m5mols/Kconfig
> new file mode 100644
> index 0000000..302dc3d
> --- /dev/null
> +++ b/drivers/media/video/m5mols/Kconfig
> @@ -0,0 +1,5 @@
> +config VIDEO_M5MOLS
> +	tristate "Fujitsu M-5MOLS 8MP sensor support"
> +	depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
> +	---help---
> +	  This driver supports Fujitsu M-5MOLS camera sensor with ISP
> diff --git a/drivers/media/video/m5mols/Makefile b/drivers/media/video/m5mols/Makefile
> new file mode 100644
> index 0000000..0a44e02
> --- /dev/null
> +++ b/drivers/media/video/m5mols/Makefile
> @@ -0,0 +1,3 @@
> +m5mols-objs	:= m5mols_core.o m5mols_controls.o m5mols_capture.o
> +
> +obj-$(CONFIG_VIDEO_M5MOLS)		+= m5mols.o
> diff --git a/drivers/media/video/m5mols/m5mols.h b/drivers/media/video/m5mols/m5mols.h
> new file mode 100644
> index 0000000..10b55c8
> --- /dev/null
> +++ b/drivers/media/video/m5mols/m5mols.h
> @@ -0,0 +1,296 @@
> +/*
> + * Header for M-5MOLS 8M Pixel camera sensor with ISP
> + *
> + * Copyright (C) 2011 Samsung Electronics Co., Ltd.
> + * Author: HeungJun Kim, riverful.kim@samsung.com
> + *
> + * Copyright (C) 2009 Samsung Electronics Co., Ltd.
> + * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com
> + *
> + * 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-subdev.h>
> +#include "m5mols_reg.h"
> +
> +extern int m5mols_debug;
> +
> +#define to_m5mols(__sd)	container_of(__sd, struct m5mols_info, sd)
> +
> +#define to_sd(__ctrl) \
> +	(&container_of(__ctrl->handler, struct m5mols_info, handle)->sd)
> +
> +enum m5mols_restype {
> +	M5MOLS_RESTYPE_MONITOR,
> +	M5MOLS_RESTYPE_CAPTURE,
> +	M5MOLS_RESTYPE_MAX,
> +};
> +
> +/**
> + * struct m5mols_resolution - structure for the resolution
> + * @type: resolution type according to the pixel code
> + * @width: width of the resolution
> + * @height: height of the resolution
> + * @reg: resolution preset register value
> + */
> +struct m5mols_resolution {
> +	u8 reg;
> +	enum m5mols_restype type;
> +	u16 width;
> +	u16 height;
> +};
> +
> +/**
> + * struct m5mols_exif - structure for the EXIF information of M-5MOLS
> + * @exposure_time: exposure time register value
> + * @shutter_speed: speed of the shutter register value
> + * @aperture: aperture register value
> + * @exposure_bias: it calls also EV bias
> + * @iso_speed: ISO register value
> + * @flash: status register value of the flash
> + * @sdr: status register value of the Subject Distance Range
> + * @qval: not written exact meaning in document
> + */
> +struct m5mols_exif {
> +	u32 exposure_time;
> +	u32 shutter_speed;
> +	u32 aperture;
> +	u32 brightness;
> +	u32 exposure_bias;
> +	u16 iso_speed;
> +	u16 flash;
> +	u16 sdr;
> +	u16 qval;
> +};
> +
> +/**
> + * struct m5mols_capture - Structure for the capture capability
> + * @exif: EXIF information
> + * @main: size in bytes of the main image
> + * @thumb: size in bytes of the thumb image, if it was accompanied
> + * @total: total size in bytes of the produced image
> + */
> +struct m5mols_capture {
> +	struct m5mols_exif exif;
> +	u32 main;
> +	u32 thumb;
> +	u32 total;
> +};
> +
> +/**
> + * struct m5mols_scenemode - structure for the scenemode capability
> + * @metering: metering light register value
> + * @ev_bias: EV bias register value
> + * @wb_mode: mode which means the WhiteBalance is Auto or Manual
> + * @wb_preset: whitebalance preset register value in the Manual mode
> + * @chroma_en: register value whether the Chroma capability is enabled or not
> + * @chroma_lvl: chroma's level register value
> + * @edge_en: register value Whether the Edge capability is enabled or not
> + * @edge_lvl: edge's level register value
> + * @af_range: Auto Focus's range
> + * @fd_mode: Face Detection mode
> + * @mcc: Multi-axis Color Conversion which means emotion color
> + * @light: status of the Light
> + * @flash: status of the Flash
> + * @tone: Tone color which means Contrast
> + * @iso: ISO register value
> + * @capt_mode: Mode of the Image Stabilization while the camera capturing
> + * @wdr: Wide Dynamic Range register value
> + *
> + * The each value according to each scenemode is recommended in the documents.
> + */
> +struct m5mols_scenemode {
> +	u32 metering;
> +	u32 ev_bias;
> +	u32 wb_mode;
> +	u32 wb_preset;
> +	u32 chroma_en;
> +	u32 chroma_lvl;
> +	u32 edge_en;
> +	u32 edge_lvl;
> +	u32 af_range;
> +	u32 fd_mode;
> +	u32 mcc;
> +	u32 light;
> +	u32 flash;
> +	u32 tone;
> +	u32 iso;
> +	u32 capt_mode;
> +	u32 wdr;
> +};
> +
> +/**
> + * struct m5mols_version - firmware version information
> + * @customer:	customer information
> + * @project:	version of project information according to customer
> + * @fw:		firmware revision
> + * @hw:		hardware revision
> + * @param:	version of the parameter
> + * @awb:	Auto WhiteBalance algorithm version
> + * @str:	information about manufacturer and packaging vendor
> + * @af:		Auto Focus version
> + *
> + * The register offset starts the customer version at 0x0, and it ends
> + * the awb version at 0x09. The customer, project information occupies 1 bytes
> + * each. And also the fw, hw, param, awb each requires 2 bytes. The str is
> + * unique string associated with firmware's version. It includes information
> + * about manufacturer and the vendor of the sensor's packaging. The least
> + * significant 2 bytes of the string indicate packaging manufacturer.
> + */
> +#define VERSION_STRING_SIZE	22
> +struct m5mols_version {
> +	u8	customer;
> +	u8	project;
> +	u16	fw;
> +	u16	hw;
> +	u16	param;
> +	u16	awb;
> +	u8	str[VERSION_STRING_SIZE];
> +	u8	af;
> +};
> +#define VERSION_SIZE sizeof(struct m5mols_version)

You're using VERSION_SIZE in two places in one function. Is that worth
making it a macro? :-)

I think you should add attribute ((packed)) to the definition as well.

> +/**
> + * struct m5mols_info - M-5MOLS driver data structure
> + * @pdata: platform data
> + * @sd: v4l-subdev instance
> + * @pad: media pad
> + * @ffmt: current fmt according to resolution type
> + * @res_type: current resolution type
> + * @code: current code
> + * @irq_waitq: waitqueue for the capture
> + * @work_irq: workqueue for the IRQ
> + * @flags: state variable for the interrupt handler
> + * @handle: control handler
> + * @autoexposure: Auto Exposure control
> + * @exposure: Exposure control
> + * @autowb: Auto White Balance control
> + * @colorfx: Color effect control
> + * @saturation:	Saturation control
> + * @zoom: Zoom control
> + * @ver: information of the version
> + * @cap: the capture mode attributes
> + * @power: current sensor's power status
> + * @ctrl_sync: true means all controls of the sensor are initialized
> + * @int_capture: true means the capture interrupt is issued once
> + * @lock_ae: true means the Auto Exposure is locked
> + * @lock_awb: true means the Aut WhiteBalance is locked
> + * @resolution:	register value for current resolution
> + * @interrupt: register value for current interrupt status
> + * @mode: register value for current operation mode
> + * @mode_save: register value for current operation mode for saving
> + * @set_power: optional power callback to the board code
> + */
> +struct m5mols_info {
> +	const struct m5mols_platform_data *pdata;
> +	struct v4l2_subdev sd;
> +	struct media_pad pad;
> +	struct v4l2_mbus_framefmt ffmt[M5MOLS_RESTYPE_MAX];
> +	int res_type;
> +	enum v4l2_mbus_pixelcode code;
> +	wait_queue_head_t irq_waitq;
> +	struct work_struct work_irq;
> +	unsigned long flags;

You want to keep flags in the stack, not in a context independent location.
This will move to functions which actually use it.

> +
> +	struct v4l2_ctrl_handler handle;
> +	/* Autoexposure/exposure control cluster */
> +	struct {
> +		struct v4l2_ctrl *autoexposure;
> +		struct v4l2_ctrl *exposure;
> +	};

Would it be different without the anonymous struct?

> +	struct v4l2_ctrl *autowb;
> +	struct v4l2_ctrl *colorfx;
> +	struct v4l2_ctrl *saturation;
> +	struct v4l2_ctrl *zoom;
> +
> +	struct m5mols_version ver;
> +	struct m5mols_capture cap;
> +	bool power;
> +	bool ctrl_sync;
> +	bool lock_ae;
> +	bool lock_awb;
> +	u8 resolution;
> +	u32 interrupt;

What would be the reason to store interrupt status information here, and
again, not in a variable which is allocated from the stack?

> +	u32 mode;
> +	u32 mode_save;
> +	int (*set_power)(struct device *dev, int on);
> +};
> +
> +#define ST_CAPT_IRQ 0
> +
> +#define is_powered(__info) (__info->power)
> +#define is_ctrl_synced(__info) (__info->ctrl_sync)
> +#define is_available_af(__info)	(__info->ver.af)
> +#define is_code(__code, __type) (__code == m5mols_default_ffmt[__type].code)
> +#define is_manufacturer(__info, __manufacturer)	\
> +				(__info->ver.str[0] == __manufacturer[0] && \
> +				 __info->ver.str[1] == __manufacturer[1])
> +/*
> + * I2C operation of the M-5MOLS
> + *
> + * The I2C read operation of the M-5MOLS requires 2 messages. The first
> + * message sends the information about the command, command category, and total
> + * message size. The second message is used to retrieve the data specifed in
> + * the first message
> + *
> + *   1st message                                2nd message
> + *   +-------+---+----------+-----+-------+     +------+------+------+------+
> + *   | size1 | R | category | cmd | size2 |     | d[0] | d[1] | d[2] | d[3] |
> + *   +-------+---+----------+-----+-------+     +------+------+------+------+
> + *   - size1: message data size(5 in this case)
> + *   - size2: desired buffer size of the 2nd message
> + *   - d[0..3]: according to size2
> + *
> + * The I2C write operation needs just one message. The message includes
> + * category, command, total size, and desired data.
> + *
> + *   1st message
> + *   +-------+---+----------+-----+------+------+------+------+
> + *   | size1 | W | category | cmd | d[0] | d[1] | d[2] | d[3] |
> + *   +-------+---+----------+-----+------+------+------+------+
> + *   - d[0..3]: according to size1
> + */
> +int m5mols_read(struct v4l2_subdev *sd, u32 reg_comb, u32 *val);
> +int m5mols_write(struct v4l2_subdev *sd, u32 reg_comb, u32 val);
> +int m5mols_busy(struct v4l2_subdev *sd, u8 category, u8 cmd, u32 value);
> +
> +/*
> + * Mode operation of the M-5MOLS
> + *
> + * Changing the mode of the M-5MOLS is needed right executing order.
> + * There are three modes(PARAMETER, MONITOR, CAPTURE) which can be changed
> + * by user. There are various categories associated with each mode.
> + *
> + * +============================================================+
> + * | mode	| category					|
> + * +============================================================+
> + * | FLASH	| FLASH(only after Stand-by or Power-on)	|
> + * | SYSTEM	| SYSTEM(only after sensor arm-booting)		|
> + * | PARAMETER	| PARAMETER					|
> + * | MONITOR	| MONITOR(preview), Auto Focus, Face Detection	|
> + * | CAPTURE	| Single CAPTURE, Preview(recording)		|
> + * +============================================================+
> + *
> + * The available executing order between each modes are as follows:
> + *   PARAMETER <---> MONITOR <---> CAPTURE
> + */
> +int m5mols_mode(struct m5mols_info *info, u32 mode);
> +
> +int m5mols_enable_interrupt(struct v4l2_subdev *sd, u32 reg);
> +int m5mols_sync_controls(struct m5mols_info *info);
> +int m5mols_start_capture(struct m5mols_info *info);
> +int m5mols_do_scenemode(struct m5mols_info *info, u32 mode);
> +int m5mols_lock_3a(struct m5mols_info *info, bool lock);
> +int m5mols_set_ctrl(struct v4l2_ctrl *ctrl);
> +
> +/* The firmware function */
> +int m5mols_update_fw(struct v4l2_subdev *sd,
> +		     int (*set_power)(struct m5mols_info *, bool));
> +
> +#endif	/* M5MOLS_H */
> diff --git a/drivers/media/video/m5mols/m5mols_capture.c b/drivers/media/video/m5mols/m5mols_capture.c
> new file mode 100644
> index 0000000..d71a390
> --- /dev/null
> +++ b/drivers/media/video/m5mols/m5mols_capture.c
> @@ -0,0 +1,191 @@
> +/*
> + * The Capture code for Fujitsu M-5MOLS ISP
> + *
> + * Copyright (C) 2011 Samsung Electronics Co., Ltd.
> + * Author: HeungJun Kim, riverful.kim@samsung.com
> + *
> + * Copyright (C) 2009 Samsung Electronics Co., Ltd.
> + * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com
> + *
> + * 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/slab.h>
> +#include <linux/irq.h>
> +#include <linux/interrupt.h>
> +#include <linux/delay.h>
> +#include <linux/version.h>
> +#include <linux/gpio.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/videodev2.h>
> +#include <linux/version.h>
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-subdev.h>
> +#include <media/m5mols.h>
> +
> +#include "m5mols.h"
> +#include "m5mols_reg.h"
> +
> +static int m5mols_capture_error_handler(struct m5mols_info *info,
> +					int timeout)
> +{
> +	int ret;
> +
> +	/* Disable all interrupts and clear relevant interrupt staus bits */
> +	ret = m5mols_write(&info->sd, SYSTEM_INT_ENABLE,
> +			   info->interrupt & ~(REG_INT_CAPTURE));
> +	if (ret)
> +		return ret;
> +
> +	if (timeout == 0)
> +		return -ETIMEDOUT;
> +
> +	return 0;
> +}

Timeout should be handled outside this function. It does nothing else based
on it except return an error code.

> +/**
> + * m5mols_read_rational - I2C read of a rational number
> + *
> + * Read numerator and denominator from registers @addr_num and @addr_den
> + * respectively and return the division result in @val.
> + */
> +static int m5mols_read_rational(struct v4l2_subdev *sd, u32 addr_num,
> +				u32 addr_den, u32 *val)

Are there cases where addr_num + 4 != addr_den? If so, you could just drop
addr_den.

> +{
> +	u32 num, den;
> +
> +	int ret = m5mols_read(sd, addr_num, &num);
> +	if (!ret)
> +		ret = m5mols_read(sd, addr_den, &den);
> +	if (ret)
> +		return ret;
> +	*val = den == 0 ? 0 : num / den;
> +	return ret;
> +}
> +
> +/**
> + * m5mols_capture_info - Gather captured image information
> + *
> + * For now it gathers only EXIF information and file size.
> + */
> +static int m5mols_capture_info(struct m5mols_info *info)
> +{
> +	struct m5mols_exif *exif = &info->cap.exif;
> +	struct v4l2_subdev *sd = &info->sd;
> +	int ret;
> +
> +	ret = m5mols_read_rational(sd, EXIF_INFO_EXPTIME_NU,
> +				   EXIF_INFO_EXPTIME_DE, &exif->exposure_time);
> +	if (ret)
> +		return ret;
> +	ret = m5mols_read_rational(sd, EXIF_INFO_TV_NU, EXIF_INFO_TV_DE,
> +				   &exif->shutter_speed);
> +	if (ret)
> +		return ret;
> +	ret = m5mols_read_rational(sd, EXIF_INFO_AV_NU, EXIF_INFO_AV_DE,
> +				   &exif->aperture);
> +	if (ret)
> +		return ret;
> +	ret = m5mols_read_rational(sd, EXIF_INFO_BV_NU, EXIF_INFO_BV_DE,
> +				   &exif->brightness);
> +	if (ret)
> +		return ret;
> +	ret = m5mols_read_rational(sd, EXIF_INFO_EBV_NU, EXIF_INFO_EBV_DE,
> +				   &exif->exposure_bias);
> +	if (ret)
> +		return ret;

You could define register-memory address pairs in a structure and read the
registers in a loop to the memory. This would be cleaner than few tonnes of
function calls.

> +	ret = m5mols_read(sd, EXIF_INFO_ISO, (u32 *)&exif->iso_speed);
> +	if (!ret)
> +		ret = m5mols_read(sd, EXIF_INFO_FLASH, (u32 *)&exif->flash);
> +	if (!ret)
> +		ret = m5mols_read(sd, EXIF_INFO_SDR, (u32 *)&exif->sdr);
> +	if (!ret)
> +		ret = m5mols_read(sd, EXIF_INFO_QVAL, (u32 *)&exif->qval);
> +	if (ret)
> +		return ret;

Please don't do typecasting for pointers like this. You end up overwriting
other parts of memory. The final call will write to memory which isn't part
of the structure.

> +	if (!ret)
> +		ret = m5mols_read(sd, CAPC_IMAGE_SIZE, &info->cap.main);
> +	if (!ret)
> +		ret = m5mols_read(sd, CAPC_THUMB_SIZE, &info->cap.thumb);
> +	if (!ret)
> +		info->cap.total = info->cap.main + info->cap.thumb;
> +
> +	return ret;
> +}
> +
> +int m5mols_start_capture(struct m5mols_info *info)
> +{
> +	struct v4l2_subdev *sd = &info->sd;
> +	u32 resolution = info->resolution;
> +	int timeout;
> +	int ret;
> +
> +	/*
> +	 * Preparing capture. Setting control & interrupt before entering
> +	 * capture mode
> +	 *
> +	 * 1) change to MONITOR mode for operating control & interrupt
> +	 * 2) set controls (considering v4l2_control value & lock 3A)
> +	 * 3) set interrupt
> +	 * 4) change to CAPTURE mode
> +	 */
> +	ret = m5mols_mode(info, REG_MONITOR);
> +	if (!ret)
> +		ret = m5mols_sync_controls(info);
> +	if (!ret)
> +		ret = m5mols_lock_3a(info, true);
> +	if (!ret)
> +		ret = m5mols_enable_interrupt(sd, REG_INT_CAPTURE);
> +	if (!ret)
> +		ret = m5mols_mode(info, REG_CAPTURE);
> +	if (!ret) {
> +		/* Wait for capture interrupt, after changing capture mode */
> +		timeout = wait_event_interruptible_timeout(info->irq_waitq,
> +					   test_bit(ST_CAPT_IRQ, &info->flags),
> +					   msecs_to_jiffies(2000));
> +		if (test_and_clear_bit(ST_CAPT_IRQ, &info->flags))
> +			ret = m5mols_capture_error_handler(info, timeout);
> +	}
> +	if (!ret)
> +		ret = m5mols_lock_3a(info, false);
> +	if (ret)
> +		return ret;
> +	/*
> +	 * Starting capture. Setting capture frame count and resolution and
> +	 * the format(available format: JPEG, Bayer RAW, YUV).
> +	 *
> +	 * 1) select single or multi(enable to 25), format, size
> +	 * 2) set interrupt
> +	 * 3) start capture(for main image, now)
> +	 * 4) get information
> +	 * 5) notify file size to v4l2 device(e.g, to s5p-fimc v4l2 device)
> +	 */
> +	ret = m5mols_write(sd, CAPC_SEL_FRAME, 1);
> +	if (!ret)
> +		ret = m5mols_write(sd, CAPP_YUVOUT_MAIN, REG_JPEG);
> +	if (!ret)
> +		ret = m5mols_write(sd, CAPP_MAIN_IMAGE_SIZE, resolution);
> +	if (!ret)
> +		ret = m5mols_enable_interrupt(sd, REG_INT_CAPTURE);
> +	if (!ret)
> +		ret = m5mols_write(sd, CAPC_START, REG_CAP_START_MAIN);
> +	if (!ret) {
> +		/* Wait for the capture completion interrupt */
> +		timeout = wait_event_interruptible_timeout(info->irq_waitq,
> +					   test_bit(ST_CAPT_IRQ, &info->flags),
> +					   msecs_to_jiffies(2000));
> +		if (test_and_clear_bit(ST_CAPT_IRQ, &info->flags)) {
> +			ret = m5mols_capture_info(info);
> +			if (!ret)
> +				v4l2_subdev_notify(sd, 0, &info->cap.total);
> +		}
> +	}
> +
> +	return m5mols_capture_error_handler(info, timeout);
> +}
> diff --git a/drivers/media/video/m5mols/m5mols_controls.c b/drivers/media/video/m5mols/m5mols_controls.c
> new file mode 100644
> index 0000000..817c16f
> --- /dev/null
> +++ b/drivers/media/video/m5mols/m5mols_controls.c
> @@ -0,0 +1,299 @@
> +/*
> + * Controls for M-5MOLS 8M Pixel camera sensor with ISP
> + *
> + * Copyright (C) 2011 Samsung Electronics Co., Ltd.
> + * Author: HeungJun Kim, riverful.kim@samsung.com

<e-mail@address>

> + * Copyright (C) 2009 Samsung Electronics Co., Ltd.
> + * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com
> + *
> + * 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/videodev2.h>
> +#include <media/v4l2-ctrls.h>
> +
> +#include "m5mols.h"
> +#include "m5mols_reg.h"
> +
> +static struct m5mols_scenemode m5mols_default_scenemode[] = {
> +	[REG_SCENE_NORMAL] = {
> +		REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
> +		REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
> +		REG_AF_NORMAL, REG_FD_OFF,
> +		REG_MCC_NORMAL, REG_LIGHT_OFF, REG_FLASH_OFF,
> +		5, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF,
> +	},
> +	[REG_SCENE_PORTRAIT] = {
> +		REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
> +		REG_CHROMA_ON, 3, REG_EDGE_ON, 4,
> +		REG_AF_NORMAL, BIT_FD_EN | BIT_FD_DRAW_FACE_FRAME,
> +		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
> +		6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF,
> +	},
> +	[REG_SCENE_LANDSCAPE] = {
> +		REG_AE_ALL, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
> +		REG_CHROMA_ON, 4, REG_EDGE_ON, 6,
> +		REG_AF_NORMAL, REG_FD_OFF,
> +		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
> +		6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF,
> +	},
> +	[REG_SCENE_SPORTS] = {
> +		REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
> +		REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
> +		REG_AF_NORMAL, REG_FD_OFF,
> +		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
> +		6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF,
> +	},
> +	[REG_SCENE_PARTY_INDOOR] = {
> +		REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
> +		REG_CHROMA_ON, 4, REG_EDGE_ON, 5,
> +		REG_AF_NORMAL, REG_FD_OFF,
> +		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
> +		6, REG_ISO_200, REG_CAP_NONE, REG_WDR_OFF,
> +	},
> +	[REG_SCENE_BEACH_SNOW] = {
> +		REG_AE_CENTER, REG_AE_INDEX_10_POS, REG_AWB_AUTO, 0,
> +		REG_CHROMA_ON, 4, REG_EDGE_ON, 5,
> +		REG_AF_NORMAL, REG_FD_OFF,
> +		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
> +		6, REG_ISO_50, REG_CAP_NONE, REG_WDR_OFF,
> +	},
> +	[REG_SCENE_SUNSET] = {
> +		REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_PRESET,
> +		REG_AWB_DAYLIGHT,
> +		REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
> +		REG_AF_NORMAL, REG_FD_OFF,
> +		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
> +		6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF,
> +	},
> +	[REG_SCENE_DAWN_DUSK] = {
> +		REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_PRESET,
> +		REG_AWB_FLUORESCENT_1,
> +		REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
> +		REG_AF_NORMAL, REG_FD_OFF,
> +		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
> +		6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF,
> +	},
> +	[REG_SCENE_FALL] = {
> +		REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
> +		REG_CHROMA_ON, 5, REG_EDGE_ON, 5,
> +		REG_AF_NORMAL, REG_FD_OFF,
> +		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
> +		6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF,
> +	},
> +	[REG_SCENE_NIGHT] = {
> +		REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
> +		REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
> +		REG_AF_NORMAL, REG_FD_OFF,
> +		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
> +		6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF,
> +	},
> +	[REG_SCENE_AGAINST_LIGHT] = {
> +		REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
> +		REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
> +		REG_AF_NORMAL, REG_FD_OFF,
> +		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
> +		6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF,
> +	},
> +	[REG_SCENE_FIRE] = {
> +		REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
> +		REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
> +		REG_AF_NORMAL, REG_FD_OFF,
> +		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
> +		6, REG_ISO_50, REG_CAP_NONE, REG_WDR_OFF,
> +	},
> +	[REG_SCENE_TEXT] = {
> +		REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
> +		REG_CHROMA_ON, 3, REG_EDGE_ON, 7,
> +		REG_AF_MACRO, REG_FD_OFF,
> +		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
> +		6, REG_ISO_AUTO, REG_CAP_ANTI_SHAKE, REG_WDR_ON,
> +	},
> +	[REG_SCENE_CANDLE] = {
> +		REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
> +		REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
> +		REG_AF_NORMAL, REG_FD_OFF,
> +		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
> +		6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF,
> +	},

As it seems this functionality is dynamically configurable, and much of that
looks like something user space might want to choose independently,
shouldn't the underlying low level controls be exposed to user space as
such?

There definitely are different approaches to this; providing higher level
interface is restricting but on the other hand it may be better depending on
an application.

Some of these parameters would already have a V4L2 control for them.

> +};
> +
> +/**
> + * m5mols_do_scenemode() - Change current scenemode
> + * @mode:	Desired mode of the scenemode
> + *
> + * WARNING: The execution order is important. Do not change the order.
> + */
> +int m5mols_do_scenemode(struct m5mols_info *info, u32 mode)
> +{
> +	struct v4l2_subdev *sd = &info->sd;
> +	struct m5mols_scenemode scenemode = m5mols_default_scenemode[mode];
> +	int ret;
> +
> +	if (mode > REG_SCENE_CANDLE)
> +		return -EINVAL;
> +
> +	ret = m5mols_lock_3a(info, false);
> +	if (!ret)
> +		ret = m5mols_write(sd, AE_EV_PRESET_MONITOR, mode);
> +	if (!ret)
> +		ret = m5mols_write(sd, AE_EV_PRESET_CAPTURE, mode);
> +	if (!ret)
> +		ret = m5mols_write(sd, AE_MODE, scenemode.metering);
> +	if (!ret)
> +		ret = m5mols_write(sd, AE_INDEX, scenemode.ev_bias);
> +	if (!ret)
> +		ret = m5mols_write(sd, AWB_MODE, scenemode.wb_mode);
> +	if (!ret)
> +		ret = m5mols_write(sd, AWB_MANUAL, scenemode.wb_preset);
> +	if (!ret)
> +		ret = m5mols_write(sd, MON_CHROMA_EN, scenemode.chroma_en);
> +	if (!ret)
> +		ret = m5mols_write(sd, MON_CHROMA_LVL, scenemode.chroma_lvl);
> +	if (!ret)
> +		ret = m5mols_write(sd, MON_EDGE_EN, scenemode.edge_en);
> +	if (!ret)
> +		ret = m5mols_write(sd, MON_EDGE_LVL, scenemode.edge_lvl);
> +	if (!ret && is_available_af(info))
> +		ret = m5mols_write(sd, AF_MODE, scenemode.af_range);
> +	if (!ret && is_available_af(info))
> +		ret = m5mols_write(sd, FD_CTL, scenemode.fd_mode);
> +	if (!ret)
> +		ret = m5mols_write(sd, MON_TONE_CTL, scenemode.tone);
> +	if (!ret)
> +		ret = m5mols_write(sd, AE_ISO, scenemode.iso);
> +	if (!ret)
> +		ret = m5mols_mode(info, REG_CAPTURE);
> +	if (!ret)
> +		ret = m5mols_write(sd, CAPP_WDR_EN, scenemode.wdr);
> +	if (!ret)
> +		ret = m5mols_write(sd, CAPP_MCC_MODE, scenemode.mcc);
> +	if (!ret)
> +		ret = m5mols_write(sd, CAPP_LIGHT_CTRL, scenemode.light);
> +	if (!ret)
> +		ret = m5mols_write(sd, CAPP_FLASH_CTRL, scenemode.flash);
> +	if (!ret)
> +		ret = m5mols_write(sd, CAPC_MODE, scenemode.capt_mode);
> +	if (!ret)
> +		ret = m5mols_mode(info, REG_MONITOR);
> +
> +	return ret;
> +}
> +
> +static int m5mols_lock_ae(struct m5mols_info *info, bool lock)
> +{
> +	int ret = 0;
> +
> +	if (info->lock_ae != lock)
> +		ret = m5mols_write(&info->sd, AE_LOCK,
> +				lock ? REG_AE_LOCK : REG_AE_UNLOCK);
> +	if (!ret)
> +		info->lock_ae = lock;
> +
> +	return ret;
> +}
> +
> +static int m5mols_lock_awb(struct m5mols_info *info, bool lock)
> +{
> +	int ret = 0;
> +
> +	if (info->lock_awb != lock)
> +		ret = m5mols_write(&info->sd, AWB_LOCK,
> +				lock ? REG_AWB_LOCK : REG_AWB_UNLOCK);
> +	if (!ret)
> +		info->lock_awb = lock;
> +
> +	return ret;
> +}
> +
> +/* m5mols_lock_3a() - Lock 3A(Auto Exposure, Auto Whitebalance, Auto Focus) */
> +int m5mols_lock_3a(struct m5mols_info *info, bool lock)
> +{
> +	int ret;
> +
> +	ret = m5mols_lock_ae(info, lock);
> +	if (!ret)
> +		ret = m5mols_lock_awb(info, lock);
> +	/* Don't need to handle unlocking AF */
> +	if (!ret && is_available_af(info) && lock)
> +		ret = m5mols_write(&info->sd, AF_EXECUTE, REG_AF_STOP);
> +
> +	return ret;
> +}
> +
> +/* m5mols_set_ctrl() - The main s_ctrl function called by m5mols_set_ctrl() */
> +int m5mols_set_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +	struct v4l2_subdev *sd = to_sd(ctrl);
> +	struct m5mols_info *info = to_m5mols(sd);
> +	int ret;
> +
> +	switch (ctrl->id) {
> +	case V4L2_CID_ZOOM_ABSOLUTE:
> +		return m5mols_write(sd, MON_ZOOM, ctrl->val);
> +
> +	case V4L2_CID_EXPOSURE_AUTO:
> +		ret = m5mols_lock_ae(info,
> +			ctrl->val == V4L2_EXPOSURE_AUTO ? false : true);
> +		if (!ret && ctrl->val == V4L2_EXPOSURE_AUTO)
> +			ret = m5mols_write(sd, AE_MODE, REG_AE_ALL);
> +		if (!ret && ctrl->val == V4L2_EXPOSURE_MANUAL) {
> +			int val = info->exposure->val;
> +			ret = m5mols_write(sd, AE_MODE, REG_AE_OFF);
> +			if (!ret)
> +				ret = m5mols_write(sd, AE_MAN_GAIN_MON, val);
> +			if (!ret)
> +				ret = m5mols_write(sd, AE_MAN_GAIN_CAP, val);
> +		}
> +		return ret;
> +
> +	case V4L2_CID_AUTO_WHITE_BALANCE:
> +		ret = m5mols_lock_awb(info, ctrl->val ? false : true);
> +		if (!ret)
> +			ret = m5mols_write(sd, AWB_MODE, ctrl->val ?
> +				REG_AWB_AUTO : REG_AWB_PRESET);
> +		return ret;
> +
> +	case V4L2_CID_SATURATION:
> +		ret = m5mols_write(sd, MON_CHROMA_LVL, ctrl->val);
> +		if (!ret)
> +			ret = m5mols_write(sd, MON_CHROMA_EN, REG_CHROMA_ON);
> +		return ret;
> +
> +	case V4L2_CID_COLORFX:
> +		/*
> +		 * This control uses two kinds of registers: normal & color.
> +		 * The normal effect belongs to category 1, while the color
> +		 * one belongs to category 2.
> +		 *
> +		 * The normal effect uses one register: CAT1_EFFECT.
> +		 * The color effect uses three registers:
> +		 * CAT2_COLOR_EFFECT, CAT2_CFIXR, CAT2_CFIXB.
> +		 */
> +		ret = m5mols_write(sd, PARM_EFFECT,
> +			ctrl->val == V4L2_COLORFX_NEGATIVE ? REG_EFFECT_NEGA :
> +			ctrl->val == V4L2_COLORFX_EMBOSS ? REG_EFFECT_EMBOSS :
> +			REG_EFFECT_OFF);
> +		if (!ret)
> +			ret = m5mols_write(sd, MON_EFFECT,
> +				ctrl->val == V4L2_COLORFX_SEPIA ?
> +				REG_COLOR_EFFECT_ON : REG_COLOR_EFFECT_OFF);
> +		if (!ret)
> +			ret = m5mols_write(sd, MON_CFIXR,
> +				ctrl->val == V4L2_COLORFX_SEPIA ?
> +				REG_CFIXR_SEPIA : 0);
> +		if (!ret)
> +			ret = m5mols_write(sd, MON_CFIXB,
> +				ctrl->val == V4L2_COLORFX_SEPIA ?
> +				REG_CFIXB_SEPIA : 0);
> +		return ret;
> +	}
> +
> +	return -EINVAL;
> +}
> diff --git a/drivers/media/video/m5mols/m5mols_core.c b/drivers/media/video/m5mols/m5mols_core.c
> new file mode 100644
> index 0000000..76eac26
> --- /dev/null
> +++ b/drivers/media/video/m5mols/m5mols_core.c
> @@ -0,0 +1,1004 @@
> +/*
> + * Driver for M-5MOLS 8M Pixel camera sensor with ISP
> + *
> + * Copyright (C) 2011 Samsung Electronics Co., Ltd.
> + * Author: HeungJun Kim, riverful.kim@samsung.com
> + *
> + * Copyright (C) 2009 Samsung Electronics Co., Ltd.
> + * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com
> + *
> + * 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/slab.h>
> +#include <linux/irq.h>
> +#include <linux/interrupt.h>
> +#include <linux/delay.h>
> +#include <linux/version.h>
> +#include <linux/gpio.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/videodev2.h>
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-subdev.h>
> +#include <media/m5mols.h>
> +
> +#include "m5mols.h"
> +#include "m5mols_reg.h"
> +
> +int m5mols_debug;
> +module_param(m5mols_debug, int, 0644);
> +
> +#define MODULE_NAME		"M5MOLS"
> +#define M5MOLS_I2C_CHECK_RETRY	500
> +
> +/* The regulator consumer names for external voltage regulators */
> +static struct regulator_bulk_data supplies[] = {
> +	{
> +		.supply = "core",	/* ARM core power, 1.2V */
> +	}, {
> +		.supply	= "dig_18",	/* digital power 1, 1.8V */
> +	}, {
> +		.supply	= "d_sensor",	/* sensor power 1, 1.8V */
> +	}, {
> +		.supply	= "dig_28",	/* digital power 2, 2.8V */
> +	}, {
> +		.supply	= "a_sensor",	/* analog power */
> +	}, {
> +		.supply	= "dig_12",	/* digital power 3, 1.2V */
> +	},
> +};

This looks like something that belongs to board code, or perhaps in the near
future, to the device tree. The power supplies that are required by a device
is highly board dependent.

> +static struct v4l2_mbus_framefmt m5mols_default_ffmt[M5MOLS_RESTYPE_MAX] = {
> +	[M5MOLS_RESTYPE_MONITOR] = {
> +		.width		= 1920,
> +		.height		= 1080,
> +		.code		= V4L2_MBUS_FMT_VYUY8_2X8,
> +		.field		= V4L2_FIELD_NONE,
> +		.colorspace	= V4L2_COLORSPACE_JPEG,
> +	},
> +	[M5MOLS_RESTYPE_CAPTURE] = {
> +		.width		= 1920,
> +		.height		= 1080,
> +		.code		= V4L2_MBUS_FMT_JPEG_1X8,
> +		.field		= V4L2_FIELD_NONE,
> +		.colorspace	= V4L2_COLORSPACE_JPEG,
> +	},
> +};
> +#define SIZE_DEFAULT_FFMT	ARRAY_SIZE(m5mols_default_ffmt)
> +
> +static const struct m5mols_resolution m5mols_reg_res[] = {
> +	{ 0x01, M5MOLS_RESTYPE_MONITOR, 128, 96 },	/* SUB-QCIF */
> +	{ 0x03, M5MOLS_RESTYPE_MONITOR, 160, 120 },	/* QQVGA */
> +	{ 0x05, M5MOLS_RESTYPE_MONITOR, 176, 144 },	/* QCIF */
> +	{ 0x06, M5MOLS_RESTYPE_MONITOR, 176, 176 },
> +	{ 0x08, M5MOLS_RESTYPE_MONITOR, 240, 320 },	/* QVGA */
> +	{ 0x09, M5MOLS_RESTYPE_MONITOR, 320, 240 },	/* QVGA */
> +	{ 0x0c, M5MOLS_RESTYPE_MONITOR, 240, 400 },	/* WQVGA */
> +	{ 0x0d, M5MOLS_RESTYPE_MONITOR, 400, 240 },	/* WQVGA */
> +	{ 0x0e, M5MOLS_RESTYPE_MONITOR, 352, 288 },	/* CIF */
> +	{ 0x13, M5MOLS_RESTYPE_MONITOR, 480, 360 },
> +	{ 0x15, M5MOLS_RESTYPE_MONITOR, 640, 360 },	/* qHD */
> +	{ 0x17, M5MOLS_RESTYPE_MONITOR, 640, 480 },	/* VGA */
> +	{ 0x18, M5MOLS_RESTYPE_MONITOR, 720, 480 },
> +	{ 0x1a, M5MOLS_RESTYPE_MONITOR, 800, 480 },	/* WVGA */
> +	{ 0x1f, M5MOLS_RESTYPE_MONITOR, 800, 600 },	/* SVGA */
> +	{ 0x21, M5MOLS_RESTYPE_MONITOR, 1280, 720 },	/* HD */
> +	{ 0x25, M5MOLS_RESTYPE_MONITOR, 1920, 1080 },	/* 1080p */
> +	{ 0x29, M5MOLS_RESTYPE_MONITOR, 3264, 2448 },	/* 2.63fps 8M */
> +	{ 0x39, M5MOLS_RESTYPE_MONITOR, 800, 602 },	/* AHS_MON debug */
> +
> +	{ 0x02, M5MOLS_RESTYPE_CAPTURE, 320, 240 },	/* QVGA */
> +	{ 0x04, M5MOLS_RESTYPE_CAPTURE, 400, 240 },	/* WQVGA */
> +	{ 0x07, M5MOLS_RESTYPE_CAPTURE, 480, 360 },
> +	{ 0x08, M5MOLS_RESTYPE_CAPTURE, 640, 360 },	/* qHD */
> +	{ 0x09, M5MOLS_RESTYPE_CAPTURE, 640, 480 },	/* VGA */
> +	{ 0x0a, M5MOLS_RESTYPE_CAPTURE, 800, 480 },	/* WVGA */
> +	{ 0x10, M5MOLS_RESTYPE_CAPTURE, 1280, 720 },	/* HD */
> +	{ 0x14, M5MOLS_RESTYPE_CAPTURE, 1280, 960 },	/* 1M */
> +	{ 0x17, M5MOLS_RESTYPE_CAPTURE, 1600, 1200 },	/* 2M */
> +	{ 0x19, M5MOLS_RESTYPE_CAPTURE, 1920, 1080 },	/* Full-HD */
> +	{ 0x1a, M5MOLS_RESTYPE_CAPTURE, 2048, 1152 },	/* 3Mega */
> +	{ 0x1b, M5MOLS_RESTYPE_CAPTURE, 2048, 1536 },
> +	{ 0x1c, M5MOLS_RESTYPE_CAPTURE, 2560, 1440 },	/* 4Mega */
> +	{ 0x1d, M5MOLS_RESTYPE_CAPTURE, 2560, 1536 },
> +	{ 0x1f, M5MOLS_RESTYPE_CAPTURE, 2560, 1920 },	/* 5Mega */
> +	{ 0x21, M5MOLS_RESTYPE_CAPTURE, 3264, 1836 },	/* 6Mega */
> +	{ 0x22, M5MOLS_RESTYPE_CAPTURE, 3264, 1960 },
> +	{ 0x25, M5MOLS_RESTYPE_CAPTURE, 3264, 2448 },	/* 8Mega */
> +};
> +
> +/**
> + * m5mols_swap_byte - an byte array to integer conversion function
> + * @size: size in bytes of I2C packet defined in the M-5MOLS datasheet
> + *
> + * Convert I2C data byte array with performing any required byte
> + * reordering to assure proper values for each data type, regardless
> + * of the architecture endianness.
> + */
> +static u32 m5mols_swap_byte(u8 *data, u8 length)
> +{
> +	if (length == 1)
> +		return *data;
> +	else if (length == 2)
> +		return be16_to_cpu(*((u16 *)data));
> +	else
> +		return be32_to_cpu(*((u32 *)data));
> +}
> +
> +/**
> + * m5mols_read -  I2C read function
> + * @reg: combination of size, category and command for the I2C packet
> + * @val: read value
> + */
> +int m5mols_read(struct v4l2_subdev *sd, u32 reg, u32 *val)
> +{
> +	struct i2c_client *client = v4l2_get_subdevdata(sd);
> +	u8 rbuf[M5MOLS_I2C_MAX_SIZE + 1];
> +	u8 size = I2C_SIZE(reg);
> +	u8 category = I2C_CATEGORY(reg);
> +	u8 cmd = I2C_COMMAND(reg);
> +	struct i2c_msg msg[2];
> +	u8 wbuf[5];
> +	int ret;
> +
> +	if (!client->adapter)
> +		return -ENODEV;
> +
> +	if (size != 1 && size != 2 && size != 4) {
> +		v4l2_err(sd, "Wrong data size\n");
> +		return -EINVAL;
> +	}
> +
> +	msg[0].addr = client->addr;
> +	msg[0].flags = 0;
> +	msg[0].len = 5;
> +	msg[0].buf = wbuf;
> +	wbuf[0] = 5;
> +	wbuf[1] = M5MOLS_BYTE_READ;
> +	wbuf[2] = category;
> +	wbuf[3] = cmd;
> +	wbuf[4] = size;
> +
> +	msg[1].addr = client->addr;
> +	msg[1].flags = I2C_M_RD;
> +	msg[1].len = size + 1;
> +	msg[1].buf = rbuf;
> +
> +	/* minimum stabilization time */
> +	usleep_range(200, 200);
> +
> +	ret = i2c_transfer(client->adapter, msg, 2);
> +	if (ret < 0) {
> +		v4l2_err(sd, "read failed: size:%d cat:%02x cmd:%02x. %d\n",
> +			 size, category, cmd, ret);
> +		return ret;
> +	}
> +
> +	*val = m5mols_swap_byte(&rbuf[1], size);
> +
> +	return 0;
> +}
> +
> +/**
> + * m5mols_write - I2C command write function
> + * @reg: combination of size, category and command for the I2C packet
> + * @val: value to write
> + */
> +int m5mols_write(struct v4l2_subdev *sd, u32 reg, u32 val)
> +{
> +	struct i2c_client *client = v4l2_get_subdevdata(sd);
> +	u8 wbuf[M5MOLS_I2C_MAX_SIZE + 4];
> +	u8 category = I2C_CATEGORY(reg);
> +	u8 cmd = I2C_COMMAND(reg);
> +	u8 size	= I2C_SIZE(reg);
> +	u32 *buf = (u32 *)&wbuf[4];
> +	struct i2c_msg msg[1];
> +	int ret;
> +
> +	if (!client->adapter)
> +		return -ENODEV;
> +
> +	if (size != 1 && size != 2 && size != 4) {

You could define sizes for the types, e.g.

#define I2C_SIZE_U8	1

> +		v4l2_err(sd, "Wrong data size\n");
> +		return -EINVAL;
> +	}
> +
> +	msg->addr = client->addr;
> +	msg->flags = 0;
> +	msg->len = (u16)size + 4;
> +	msg->buf = wbuf;
> +	wbuf[0] = size + 4;
> +	wbuf[1] = M5MOLS_BYTE_WRITE;
> +	wbuf[2] = category;
> +	wbuf[3] = cmd;
> +
> +	*buf = m5mols_swap_byte((u8 *)&val, size);
> +
> +	usleep_range(200, 200);

Why to sleep always? Does the sensor require a delay between each I2C
access?

> +	ret = i2c_transfer(client->adapter, msg, 1);
> +	if (ret < 0) {
> +		v4l2_err(sd, "write failed: size:%d cat:%02x cmd:%02x. %d\n",
> +			size, category, cmd, ret);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +int m5mols_busy(struct v4l2_subdev *sd, u8 category, u8 cmd, u32 mask)
> +{
> +	u32 busy, i;
> +	int ret;
> +
> +	for (i = 0; i < M5MOLS_I2C_CHECK_RETRY; i++) {
> +		ret = m5mols_read(sd, I2C_REG(category, cmd, 1), &busy);
> +		if (ret < 0)
> +			return ret;
> +		if ((busy & mask) == mask)
> +			return 0;
> +	}
> +	return -EBUSY;
> +}
> +
> +/**
> + * m5mols_enable_interrupt - Clear interrupt pending bits and unmask interrupts
> + *
> + * Before writing desired interrupt value the INT_FACTOR register should
> + * be read to clear pending interrupts.
> + */
> +int m5mols_enable_interrupt(struct v4l2_subdev *sd, u32 reg)
> +{
> +	struct m5mols_info *info = to_m5mols(sd);
> +	u32 mask = is_available_af(info) ? REG_INT_AF : 0;
> +	u32 dummy;
> +	int ret;
> +
> +	ret = m5mols_read(sd, SYSTEM_INT_FACTOR, &dummy);
> +	if (!ret)
> +		ret = m5mols_write(sd, SYSTEM_INT_ENABLE, reg & ~mask);
> +	return ret;
> +}
> +
> +/**
> + * m5mols_reg_mode - Write the mode and check busy status
> + *
> + * It always accompanies a little delay changing the M-5MOLS mode, so it is
> + * needed checking current busy status to guarantee right mode.
> + */
> +static int m5mols_reg_mode(struct v4l2_subdev *sd, u32 mode)
> +{
> +	int ret = m5mols_write(sd, SYSTEM_SYSMODE, mode);
> +
> +	return ret ? ret : m5mols_busy(sd, CAT_SYSTEM, CAT0_SYSMODE, mode);
> +}
> +
> +/**
> + * m5mols_mode - manage the M-5MOLS's mode
> + * @mode: the required operation mode
> + *
> + * The commands of M-5MOLS are grouped into specific modes. Each functionality
> + * can be guaranteed only when the sensor is operating in mode which which
> + * a command belongs to.
> + */
> +int m5mols_mode(struct m5mols_info *info, u32 mode)
> +{
> +	struct v4l2_subdev *sd = &info->sd;
> +	int ret = -EINVAL;
> +	u32 reg;
> +
> +	if (mode < REG_PARAMETER && mode > REG_CAPTURE)
> +		return ret;
> +
> +	ret = m5mols_read(sd, SYSTEM_SYSMODE, &reg);
> +	if ((!ret && reg == mode) || ret)
> +		return ret;
> +
> +	switch (reg) {
> +	case REG_PARAMETER:
> +		ret = m5mols_reg_mode(sd, REG_MONITOR);
> +		if (!ret && mode == REG_MONITOR)
> +			break;
> +		if (!ret)
> +			ret = m5mols_reg_mode(sd, REG_CAPTURE);
> +		break;
> +
> +	case REG_MONITOR:
> +		if (mode == REG_PARAMETER) {
> +			ret = m5mols_reg_mode(sd, REG_PARAMETER);
> +			break;
> +		}
> +
> +		ret = m5mols_reg_mode(sd, REG_CAPTURE);
> +		break;
> +
> +	case REG_CAPTURE:
> +		ret = m5mols_reg_mode(sd, REG_MONITOR);
> +		if (!ret && mode == REG_MONITOR)
> +			break;
> +		if (!ret)
> +			ret = m5mols_reg_mode(sd, REG_PARAMETER);
> +		break;
> +
> +	default:
> +		v4l2_warn(sd, "Wrong mode: %d\n", mode);
> +	}
> +
> +	if (!ret)
> +		info->mode = mode;
> +
> +	return ret;
> +}
> +
> +/**
> + * m5mols_get_version - retrieve full revisions information of M-5MOLS
> + *
> + * The version information includes revisions of hardware and firmware,
> + * AutoFocus alghorithm version and the version string.
> + */
> +static int m5mols_get_version(struct v4l2_subdev *sd)
> +{
> +	struct m5mols_info *info = to_m5mols(sd);
> +	union {
> +		struct m5mols_version ver;
> +		u8 bytes[VERSION_SIZE];

You could even use u8 bytes[0] if you really need this union.

((char *)&ver)[cmd], for example.

> +	} version;
> +	u32 *value;
> +	u8 cmd = CAT0_VER_CUSTOMER;
> +	int ret;
> +
> +	do {
> +		value = (u32 *)&version.bytes[cmd];
> +		ret = m5mols_read(sd, SYSTEM_CMD(cmd), value);
> +		if (ret)
> +			return ret;
> +	} while (cmd++ != CAT0_VER_AWB);
> +
> +	do {
> +		value = (u32 *)&version.bytes[cmd];
> +		ret = m5mols_read(sd, SYSTEM_VER_STRING, value);
> +		if (ret)
> +			return ret;
> +		if (cmd >= VERSION_SIZE - 1)
> +			return -EINVAL;
> +	} while (version.bytes[cmd++]);


Please move cmd++ outside the condition.

I think you should have a different function to read and write different
types, e.g. m5mols_read_u8 and m5mols_read_u16.

You have the access width encoded in the register value. That would still be
checked by the function.

You also could subtract CAT0_VER_CUSTOMER from cmd when using it as index
to bytes[] above, as you now rely that it is actually zero.

> +
> +	value = (u32 *)&version.bytes[cmd];
> +	ret = m5mols_read(sd, AF_VERSION, value);

I think it'd be cleaner to refer to (u32 *)&version.bytes[cmd] directly
instead of using value.

Can you be sure that cmd points to the AF version here?

> +	if (ret)
> +		return ret;
> +
> +	/* store version information swapped for being readable */
> +	info->ver	= version.ver;

You could use info->ver straight away and drop version.ver.

> +	info->ver.fw	= be16_to_cpu(info->ver.fw);
> +	info->ver.hw	= be16_to_cpu(info->ver.hw);
> +	info->ver.param	= be16_to_cpu(info->ver.param);
> +	info->ver.awb	= be16_to_cpu(info->ver.awb);
> +
> +	v4l2_info(sd, "Manufacturer\t[%s]\n",
> +			is_manufacturer(info, REG_SAMSUNG_ELECTRO) ?
> +			"Samsung Electro-Machanics" :
> +			is_manufacturer(info, REG_SAMSUNG_OPTICS) ?
> +			"Samsung Fiber-Optics" :
> +			is_manufacturer(info, REG_SAMSUNG_TECHWIN) ?
> +			"Samsung Techwin" : "None");
> +	v4l2_info(sd, "Customer/Project\t[0x%02x/0x%02x]\n",
> +			info->ver.customer, info->ver.project);
> +
> +	if (!is_available_af(info))
> +		v4l2_info(sd, "No support Auto Focus on this firmware\n");
> +
> +	return ret;
> +}
> +
> +/**
> + * __find_restype - Lookup M-5MOLS resolution type according to pixel code
> + * @code: pixel code
> + */
> +static enum m5mols_restype __find_restype(enum v4l2_mbus_pixelcode code)
> +{
> +	enum m5mols_restype type = M5MOLS_RESTYPE_MONITOR;
> +
> +	do {
> +		if (code == m5mols_default_ffmt[type].code)
> +			return type;

type++ here, and ++ off of the condition below.

> +	} while (type++ != SIZE_DEFAULT_FFMT);
> +
> +	return 0;
> +}
> +
> +/**
> + * __find_resolution - Lookup preset and type of M-5MOLS's resolution
> + * @mf: pixel format to find/negotiate the resolution preset for
> + * @type: M-5MOLS resolution type
> + * @resolution:	M-5MOLS resolution preset register value
> + *
> + * Find nearest resolution matching resolution preset and adjust mf
> + * to supported values.
> + */
> +static int __find_resolution(struct v4l2_subdev *sd,
> +			     struct v4l2_mbus_framefmt *mf,
> +			     enum m5mols_restype *type,
> +			     u32 *resolution)
> +{
> +	const struct m5mols_resolution *fsize = &m5mols_reg_res[0];
> +	const struct m5mols_resolution *match = NULL;
> +	enum m5mols_restype stype = __find_restype(mf->code);
> +	int i = ARRAY_SIZE(m5mols_reg_res);
> +	unsigned int min_err = ~0;
> +
> +	while (i--) {
> +		int err;
> +		if (stype == fsize->type) {
> +			err = abs(fsize->width - mf->width)
> +				+ abs(fsize->height - mf->height);
> +
> +			if (err < min_err) {
> +				min_err = err;
> +				match = fsize;
> +			}
> +		}
> +		fsize++;
> +	}
> +	if (match) {
> +		mf->width  = match->width;
> +		mf->height = match->height;
> +		*resolution = match->reg;
> +		*type = stype;
> +		return 0;
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +static struct v4l2_mbus_framefmt *__find_format(struct m5mols_info *info,
> +				struct v4l2_subdev_fh *fh,
> +				enum v4l2_subdev_format_whence which,
> +				enum m5mols_restype type)
> +{
> +	if (which == V4L2_SUBDEV_FORMAT_TRY)
> +		return fh ? v4l2_subdev_get_try_format(fh, 0) : NULL;
> +
> +	return &info->ffmt[type];
> +}
> +
> +static int m5mols_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
> +			  struct v4l2_subdev_format *fmt)
> +{
> +	struct m5mols_info *info = to_m5mols(sd);
> +	struct v4l2_mbus_framefmt *format;
> +
> +	if (fmt->pad != 0)
> +		return -EINVAL;
> +
> +	format = __find_format(info, fh, fmt->which, info->res_type);
> +	if (!format)
> +		return -EINVAL;
> +
> +	fmt->format = *format;
> +	return 0;
> +}
> +
> +static int m5mols_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
> +			  struct v4l2_subdev_format *fmt)
> +{
> +	struct m5mols_info *info = to_m5mols(sd);
> +	struct v4l2_mbus_framefmt *format = &fmt->format;
> +	struct v4l2_mbus_framefmt *sfmt;
> +	enum m5mols_restype type;
> +	u32 resolution = 0;
> +	int ret;
> +
> +	if (fmt->pad != 0)
> +		return -EINVAL;
> +
> +	ret = __find_resolution(sd, format, &type, &resolution);
> +	if (ret < 0)
> +		return ret;
> +
> +	sfmt = __find_format(info, fh, fmt->which, type);
> +	if (!sfmt)
> +		return 0;
> +
> +	*sfmt		= m5mols_default_ffmt[type];
> +	sfmt->width	= format->width;
> +	sfmt->height	= format->height;
> +
> +	if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
> +		info->resolution = resolution;
> +		info->code = format->code;
> +		info->res_type = type;
> +	}
> +
> +	return 0;
> +}
> +
> +static int m5mols_enum_mbus_code(struct v4l2_subdev *sd,
> +				 struct v4l2_subdev_fh *fh,
> +				 struct v4l2_subdev_mbus_code_enum *code)
> +{
> +	ifv (!code || code->index >= SIZE_DEFAULT_FFMT)

Is it possible that code == NULL?

> +		return -EINVAL;
> +
> +	code->code = m5mols_default_ffmt[code->index].code;
> +
> +	return 0;
> +}
> +
> +static struct v4l2_subdev_pad_ops m5mols_pad_ops = {
> +	.enum_mbus_code	= m5mols_enum_mbus_code,
> +	.get_fmt	= m5mols_get_fmt,
> +	.set_fmt	= m5mols_set_fmt,
> +};
> +
> +/**
> + * m5mols_sync_controls - Apply default scene mode and the current controls
> + *
> + * This is used only streaming for syncing between v4l2_ctrl framework and
> + * m5mols's controls. First, do the scenemode to the sensor, then call
> + * v4l2_ctrl_handler_setup. It can be same between some commands and
> + * the scenemode's in the default v4l2_ctrls. But, such commands of control
> + * should be prior to the scenemode's one.
> + */
> +int m5mols_sync_controls(struct m5mols_info *info)
> +{
> +	int ret = -EINVAL;
> +
> +	if (!is_ctrl_synced(info)) {
> +		ret = m5mols_do_scenemode(info, REG_SCENE_NORMAL);
> +		if (ret)
> +			return ret;
> +
> +		v4l2_ctrl_handler_setup(&info->handle);
> +		info->ctrl_sync = true;
> +	}
> +
> +	return ret;
> +}
> +
> +/**
> + * m5mols_start_monitor - Start the monitor mode
> + *
> + * Before applying the controls setup the resolution and frame rate
> + * in PARAMETER mode, and then switch over to MONITOR mode.
> + */
> +static int m5mols_start_monitor(struct m5mols_info *info)
> +{
> +	struct v4l2_subdev *sd = &info->sd;
> +	int ret;
> +
> +	ret = m5mols_mode(info, REG_PARAMETER);
> +	if (!ret)
> +		ret = m5mols_write(sd, PARM_MON_SIZE, info->resolution);
> +	if (!ret)
> +		ret = m5mols_write(sd, PARM_MON_FPS, REG_FPS_30);
> +	if (!ret)
> +		ret = m5mols_mode(info, REG_MONITOR);
> +	if (!ret)
> +		ret = m5mols_sync_controls(info);
> +
> +	return ret;
> +}
> +
> +static int m5mols_s_stream(struct v4l2_subdev *sd, int enable)
> +{
> +	struct m5mols_info *info = to_m5mols(sd);
> +
> +	if (enable) {
> +		int ret = -EINVAL;
> +
> +		if (is_code(info->code, M5MOLS_RESTYPE_MONITOR))
> +			ret = m5mols_start_monitor(info);
> +		if (is_code(info->code, M5MOLS_RESTYPE_CAPTURE))
> +			ret = m5mols_start_capture(info);
> +
> +		return ret;
> +	}
> +
> +	return m5mols_mode(info, REG_PARAMETER);
> +}
> +
> +static const struct v4l2_subdev_video_ops m5mols_video_ops = {
> +	.s_stream	= m5mols_s_stream,
> +};
> +
> +static int m5mols_s_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +	struct v4l2_subdev *sd = to_sd(ctrl);
> +	struct m5mols_info *info = to_m5mols(sd);
> +	int ret;
> +
> +	info->mode_save = info->mode;
> +
> +	ret = m5mols_mode(info, REG_PARAMETER);
> +	if (!ret)
> +		ret = m5mols_set_ctrl(ctrl);
> +	if (!ret)
> +		ret = m5mols_mode(info, info->mode_save);
> +
> +	return ret;
> +}
> +
> +static const struct v4l2_ctrl_ops m5mols_ctrl_ops = {
> +	.s_ctrl	= m5mols_s_ctrl,
> +};
> +
> +static int m5mols_sensor_power(struct m5mols_info *info, bool enable)
> +{
> +	struct v4l2_subdev *sd = &info->sd;
> +	struct i2c_client *client = v4l2_get_subdevdata(sd);
> +	const struct m5mols_platform_data *pdata = info->pdata;
> +	int ret;
> +
> +	if (enable) {
> +		if (is_powered(info))
> +			return 0;
> +
> +		if (info->set_power) {
> +			ret = info->set_power(&client->dev, 1);
> +			if (ret)
> +				return ret;
> +		}
> +
> +		ret = regulator_bulk_enable(ARRAY_SIZE(supplies), supplies);
> +		if (ret) {
> +			info->set_power(&client->dev, 0);
> +			return ret;
> +		}
> +
> +		gpio_set_value(pdata->gpio_reset, !pdata->reset_polarity);
> +		usleep_range(1000, 1000);
> +		info->power = true;
> +
> +		return ret;
> +	}
> +
> +	if (!is_powered(info))
> +		return 0;
> +
> +	ret = regulator_bulk_disable(ARRAY_SIZE(supplies), supplies);
> +	if (ret)
> +		return ret;
> +
> +	if (info->set_power)
> +		info->set_power(&client->dev, 0);
> +
> +	gpio_set_value(pdata->gpio_reset, pdata->reset_polarity);
> +	usleep_range(1000, 1000);
> +	info->power = false;
> +
> +	return ret;
> +}
> +
> +/* m5mols_update_fw - optional firmware update routine */
> +int __attribute__ ((weak)) m5mols_update_fw(struct v4l2_subdev *sd,
> +		int (*set_power)(struct m5mols_info *, bool))
> +{
> +	return 0;
> +}
> +
> +/**
> + * m5mols_sensor_armboot - Booting M-5MOLS internal ARM core.
> + *
> + * Booting internal ARM core makes the M-5MOLS is ready for getting commands
> + * with I2C. It's the first thing to be done after it powered up. It must wait
> + * at least 520ms recommended by M-5MOLS datasheet, after executing arm booting.
> + */
> +static int m5mols_sensor_armboot(struct v4l2_subdev *sd)
> +{
> +	int ret;
> +
> +	ret = m5mols_write(sd, FLASH_CAM_START, REG_START_ARM_BOOT);
> +	if (ret < 0)
> +		return ret;
> +
> +	msleep(520);
> +
> +	ret = m5mols_get_version(sd);
> +	if (!ret)
> +		ret = m5mols_update_fw(sd, m5mols_sensor_power);
> +	if (ret)
> +		return ret;
> +
> +	v4l2_dbg(1, m5mols_debug, sd, "Success ARM Booting\n");
> +
> +	ret = m5mols_write(sd, PARM_INTERFACE, REG_INTERFACE_MIPI);
> +	if (!ret)
> +		ret = m5mols_enable_interrupt(sd, REG_INT_AF);
> +
> +	return ret;
> +}
> +
> +static int m5mols_init_controls(struct m5mols_info *info)
> +{
> +	struct v4l2_subdev *sd = &info->sd;
> +	u16 max_exposure;
> +	u16 step_zoom;
> +	int ret;
> +
> +	/* Determine value's range & step of controls for various FW version */
> +	ret = m5mols_read(sd, AE_MAX_GAIN_MON, (u32 *)&max_exposure);
> +	if (!ret)
> +		step_zoom = is_manufacturer(info, REG_SAMSUNG_OPTICS) ? 31 : 1;
> +	if (ret)
> +		return ret;
> +
> +	v4l2_ctrl_handler_init(&info->handle, 6);
> +	info->autowb = v4l2_ctrl_new_std(&info->handle,
> +			&m5mols_ctrl_ops, V4L2_CID_AUTO_WHITE_BALANCE,
> +			0, 1, 1, 0);
> +	info->saturation = v4l2_ctrl_new_std(&info->handle,
> +			&m5mols_ctrl_ops, V4L2_CID_SATURATION,
> +			1, 5, 1, 3);
> +	info->zoom = v4l2_ctrl_new_std(&info->handle,
> +			&m5mols_ctrl_ops, V4L2_CID_ZOOM_ABSOLUTE,
> +			1, 70, step_zoom, 1);
> +	info->exposure = v4l2_ctrl_new_std(&info->handle,
> +			&m5mols_ctrl_ops, V4L2_CID_EXPOSURE,
> +			0, max_exposure, 1, (int)max_exposure/2);
> +	info->colorfx = v4l2_ctrl_new_std_menu(&info->handle,
> +			&m5mols_ctrl_ops, V4L2_CID_COLORFX,
> +			4, (1 << V4L2_COLORFX_BW), V4L2_COLORFX_NONE);
> +	info->autoexposure = v4l2_ctrl_new_std_menu(&info->handle,
> +			&m5mols_ctrl_ops, V4L2_CID_EXPOSURE_AUTO,
> +			1, 0, V4L2_EXPOSURE_MANUAL);
> +
> +	sd->ctrl_handler = &info->handle;
> +	if (info->handle.error) {
> +		v4l2_err(sd, "Failed to initialize controls: %d\n", ret);
> +		v4l2_ctrl_handler_free(&info->handle);
> +		return info->handle.error;
> +	}
> +
> +	v4l2_ctrl_cluster(2, &info->autoexposure);
> +
> +	return 0;
> +}
> +
> +/**
> + * m5mols_s_power - Main sensor power control function
> + *
> + * To prevent breaking the lens when the sensor is powered off the Soft-Landing
> + * algorithm is called where available. The Soft-Landing algorithm availability
> + * dependends on the firmware provider.
> + */
> +static int m5mols_s_power(struct v4l2_subdev *sd, int on)
> +{
> +	struct m5mols_info *info = to_m5mols(sd);
> +	int ret;
> +
> +	if (on) {
> +		ret = m5mols_sensor_power(info, true);
> +		if (!ret)
> +			ret = m5mols_sensor_armboot(sd);
> +		if (!ret)
> +			ret = m5mols_init_controls(info);
> +		if (ret)
> +			return ret;
> +
> +		info->ffmt[M5MOLS_RESTYPE_MONITOR] =
> +			m5mols_default_ffmt[M5MOLS_RESTYPE_MONITOR];
> +		info->ffmt[M5MOLS_RESTYPE_CAPTURE] =
> +			m5mols_default_ffmt[M5MOLS_RESTYPE_CAPTURE];
> +		return ret;
> +	}
> +
> +	if (is_manufacturer(info, REG_SAMSUNG_TECHWIN)) {
> +		ret = m5mols_mode(info, REG_MONITOR);
> +		if (!ret)
> +			ret = m5mols_write(sd, AF_EXECUTE, REG_AF_STOP);
> +		if (!ret)
> +			ret = m5mols_write(sd, AF_MODE, REG_AF_POWEROFF);
> +		if (!ret)
> +			ret = m5mols_busy(sd, CAT_SYSTEM, CAT0_STATUS,
> +					REG_AF_IDLE);
> +		if (!ret)
> +			v4l2_info(sd, "Success soft-landing lens\n");
> +	}
> +
> +	ret = m5mols_sensor_power(info, false);
> +	if (!ret) {
> +		v4l2_ctrl_handler_free(&info->handle);
> +		info->ctrl_sync = false;
> +	}
> +
> +	return ret;
> +}
> +
> +static int m5mols_log_status(struct v4l2_subdev *sd)
> +{
> +	struct m5mols_info *info = to_m5mols(sd);
> +
> +	v4l2_ctrl_handler_log_status(&info->handle, sd->name);
> +
> +	return 0;
> +}
> +
> +static const struct v4l2_subdev_core_ops m5mols_core_ops = {
> +	.s_power	= m5mols_s_power,
> +	.g_ctrl		= v4l2_subdev_g_ctrl,
> +	.s_ctrl		= v4l2_subdev_s_ctrl,
> +	.queryctrl	= v4l2_subdev_queryctrl,
> +	.querymenu	= v4l2_subdev_querymenu,
> +	.g_ext_ctrls	= v4l2_subdev_g_ext_ctrls,
> +	.try_ext_ctrls	= v4l2_subdev_try_ext_ctrls,
> +	.s_ext_ctrls	= v4l2_subdev_s_ext_ctrls,
> +	.log_status	= m5mols_log_status,
> +};
> +
> +static const struct v4l2_subdev_ops m5mols_ops = {
> +	.core		= &m5mols_core_ops,
> +	.pad		= &m5mols_pad_ops,
> +	.video		= &m5mols_video_ops,
> +};
> +
> +static void m5mols_irq_work(struct work_struct *work)
> +{
> +	struct m5mols_info *info =
> +		container_of(work, struct m5mols_info, work_irq);
> +	struct v4l2_subdev *sd = &info->sd;
> +	u32 reg;
> +	int ret;
> +
> +	if (!is_powered(info) ||
> +			m5mols_read(sd, SYSTEM_INT_FACTOR, &info->interrupt))
> +		return;
> +
> +	switch (info->interrupt & REG_INT_MASK) {
> +	case REG_INT_AF:
> +		if (!is_available_af(info))
> +			break;
> +		ret = m5mols_read(sd, AF_STATUS, &reg);
> +		v4l2_dbg(2, m5mols_debug, sd, "AF %s\n",
> +			 reg == REG_AF_FAIL ? "Failed" :
> +			 reg == REG_AF_SUCCESS ? "Success" :
> +			 reg == REG_AF_IDLE ? "Idle" : "Busy");
> +		break;
> +	case REG_INT_CAPTURE:
> +		if (!test_and_set_bit(ST_CAPT_IRQ, &info->flags))
> +			wake_up_interruptible(&info->irq_waitq);
> +
> +		v4l2_dbg(2, m5mols_debug, sd, "CAPTURE\n");
> +		break;
> +	default:
> +		v4l2_dbg(2, m5mols_debug, sd, "Undefined: %02x\n", reg);
> +		break;
> +	};
> +}
> +
> +static irqreturn_t m5mols_irq_handler(int irq, void *data)
> +{
> +	struct v4l2_subdev *sd = data;
> +	struct m5mols_info *info = to_m5mols(sd);
> +
> +	schedule_work(&info->work_irq);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int __devinit m5mols_probe(struct i2c_client *client,
> +				  const struct i2c_device_id *id)
> +{
> +	const struct m5mols_platform_data *pdata = client->dev.platform_data;
> +	struct m5mols_info *info;
> +	struct v4l2_subdev *sd;
> +	int ret;
> +
> +	if (pdata == NULL) {
> +		dev_err(&client->dev, "No platform data\n");
> +		return -EINVAL;
> +	}
> +
> +	if (!gpio_is_valid(pdata->gpio_reset)) {
> +		dev_err(&client->dev, "No valid RESET GPIO specified\n");
> +		return -EINVAL;
> +	}
> +
> +	if (!pdata->irq) {
> +		dev_err(&client->dev, "Interrupt not assigned\n");
> +		return -EINVAL;
> +	}
> +
> +	info = kzalloc(sizeof(struct m5mols_info), GFP_KERNEL);
> +	if (!info)
> +		return -ENOMEM;
> +
> +	info->pdata = pdata;
> +	info->set_power	= pdata->set_power;
> +
> +	ret = gpio_request(pdata->gpio_reset, "M5MOLS_NRST");
> +	if (ret) {
> +		dev_err(&client->dev, "Failed to request gpio: %d\n", ret);
> +		goto out_free;
> +	}
> +	gpio_direction_output(pdata->gpio_reset, pdata->reset_polarity);
> +
> +	ret = regulator_bulk_get(&client->dev, ARRAY_SIZE(supplies), supplies);
> +	if (ret) {
> +		dev_err(&client->dev, "Failed to get regulators: %d\n", ret);
> +		goto out_gpio;
> +	}
> +
> +	sd = &info->sd;
> +	strlcpy(sd->name, MODULE_NAME, sizeof(sd->name));
> +	v4l2_i2c_subdev_init(sd, client, &m5mols_ops);
> +
> +	info->pad.flags = MEDIA_PAD_FL_SOURCE;
> +	ret = media_entity_init(&sd->entity, 1, &info->pad, 0);
> +	if (ret < 0)
> +		goto out_reg;
> +	sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR;
> +
> +	init_waitqueue_head(&info->irq_waitq);
> +	INIT_WORK(&info->work_irq, m5mols_irq_work);
> +	ret = request_irq(pdata->irq, m5mols_irq_handler,
> +			  IRQF_TRIGGER_RISING, MODULE_NAME, sd);
> +	if (ret) {
> +		dev_err(&client->dev, "Interrupt request failed: %d\n", ret);
> +		goto out_me;
> +	}
> +	info->res_type = M5MOLS_RESTYPE_MONITOR;
> +	return 0;
> +out_me:
> +	media_entity_cleanup(&sd->entity);
> +out_reg:
> +	regulator_bulk_free(ARRAY_SIZE(supplies), supplies);
> +out_gpio:
> +	gpio_free(pdata->gpio_reset);
> +out_free:
> +	kfree(info);
> +	return ret;
> +}
> +
> +static int __devexit 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);
> +	free_irq(info->pdata->irq, sd);
> +
> +	regulator_bulk_free(ARRAY_SIZE(supplies), supplies);
> +	gpio_free(info->pdata->gpio_reset);
> +	media_entity_cleanup(&sd->entity);
> +	kfree(info);
> +	return 0;
> +}
> +
> +static const struct i2c_device_id m5mols_id[] = {
> +	{ MODULE_NAME, 0 },
> +	{ },
> +};
> +MODULE_DEVICE_TABLE(i2c, m5mols_id);
> +
> +static struct i2c_driver m5mols_i2c_driver = {
> +	.driver = {
> +		.name	= MODULE_NAME,
> +	},
> +	.probe		= m5mols_probe,
> +	.remove		= __devexit_p(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@samsung.com>");
> +MODULE_AUTHOR("Dongsoo Kim <dongsoo45.kim@samsung.com>");
> +MODULE_DESCRIPTION("Fujitsu M-5MOLS 8M Pixel camera driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/media/video/m5mols/m5mols_reg.h b/drivers/media/video/m5mols/m5mols_reg.h
> new file mode 100644
> index 0000000..b83e36f
> --- /dev/null
> +++ b/drivers/media/video/m5mols/m5mols_reg.h
> @@ -0,0 +1,399 @@
> +/*
> + * Register map for M-5MOLS 8M Pixel camera sensor with ISP
> + *
> + * Copyright (C) 2011 Samsung Electronics Co., Ltd.
> + * Author: HeungJun Kim, riverful.kim@samsung.com
> + *
> + * Copyright (C) 2009 Samsung Electronics Co., Ltd.
> + * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com
> + *
> + * 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_REG_H
> +#define M5MOLS_REG_H
> +
> +#define M5MOLS_I2C_MAX_SIZE	4
> +#define M5MOLS_BYTE_READ	0x01
> +#define M5MOLS_BYTE_WRITE	0x02
> +
> +#define I2C_CATEGORY(__cat)		((__cat >> 16) & 0xff)
> +#define I2C_COMMAND(__comm)		((__comm >> 8) & 0xff)
> +#define I2C_SIZE(__reg_s)		((__reg_s) & 0xff)

I would put category and command to lower 16 bits and size in the upper 16
bits, but this is up to you. I think this would improve readability.

> +#define I2C_REG(__cat, __cmd, __reg_s)	((__cat << 16) | (__cmd << 8) | __reg_s)
> +
> +/*
> + * Category section register
> + *
> + * The category means set including relevant command of M-5MOLS.
> + */
> +#define CAT_SYSTEM		0x00
> +#define CAT_PARAM		0x01
> +#define CAT_MONITOR		0x02
> +#define CAT_AE			0x03
> +#define CAT_WB			0x06
> +#define CAT_EXIF		0x07
> +#define CAT_FD			0x09
> +#define CAT_LENS		0x0a
> +#define CAT_CAPT_PARM		0x0b
> +#define CAT_CAPT_CTRL		0x0c
> +#define CAT_FLASH		0x0f	/* related to FW, revisions, booting */

What about defining registers so that the category is part of the register
address? This is actually how it's in the address space as well.

> +
> +/*
> + * Category 0 - SYSTEM mode
> + *
> + * The SYSTEM mode in the M-5MOLS means area available to handle with the whole
> + * & all-round system of sensor. It deals with version/interrupt/setting mode &
> + * even sensor's status. Especially, the M-5MOLS sensor with ISP varies by
> + * packaging & manufacturer, even the customer and project code. And the
> + * function details may vary among them. The version information helps to
> + * determine what methods shall be used in the driver.
> + *
> + * There is many registers between customer version address and awb one. For
> + * more specific contents, see definition if file m5mols.h.
> + */
> +#define CAT0_VER_CUSTOMER	0x00	/* customer version */

If you keep the definitions as they are, I think "CAT0" should be replaced
by "SYSTEM". This would make it clear that the registers belong to that
category, instead of a numeric one.

> +#define CAT0_VER_AWB		0x09	/* Auto WB version */
> +#define CAT0_VER_STRING		0x0a	/* string including M-5MOLS */
> +#define CAT0_SYSMODE		0x0b	/* SYSTEM mode register */
> +#define CAT0_STATUS		0x0c	/* SYSTEM mode status register */
> +#define CAT0_INT_FACTOR		0x10	/* interrupt pending register */
> +#define CAT0_INT_ENABLE		0x11	/* interrupt enable register */
> +
> +#define SYSTEM_SYSMODE		I2C_REG(CAT_SYSTEM, CAT0_SYSMODE, 1)
> +#define REG_SYSINIT		0x00	/* SYSTEM mode */
> +#define REG_PARAMETER		0x01	/* PARAMETER mode */
> +#define REG_MONITOR		0x02	/* MONITOR mode */
> +#define REG_CAPTURE		0x03	/* CAPTURE mode */
> +
> +#define SYSTEM_CMD(__cmd)	I2C_REG(CAT_SYSTEM, cmd, 1)
> +#define SYSTEM_VER_STRING	I2C_REG(CAT_SYSTEM, CAT0_VER_STRING, 1)
> +#define REG_SAMSUNG_ELECTRO	"SE"	/* Samsung Electro-Mechanics */
> +#define REG_SAMSUNG_OPTICS	"OP"	/* Samsung Fiber-Optics */
> +#define REG_SAMSUNG_TECHWIN	"TB"	/* Samsung Techwin */
> +
> +#define SYSTEM_INT_FACTOR	I2C_REG(CAT_SYSTEM, CAT0_INT_FACTOR, 1)
> +#define SYSTEM_INT_ENABLE	I2C_REG(CAT_SYSTEM, CAT0_INT_ENABLE, 1)
> +#define REG_INT_MODE		(1 << 0)
> +#define REG_INT_AF		(1 << 1)
> +#define REG_INT_ZOOM		(1 << 2)
> +#define REG_INT_CAPTURE		(1 << 3)
> +#define REG_INT_FRAMESYNC	(1 << 4)
> +#define REG_INT_FD		(1 << 5)
> +#define REG_INT_LENS_INIT	(1 << 6)
> +#define REG_INT_SOUND		(1 << 7)
> +#define REG_INT_MASK		0x0f

I would prefix the register bit definitions with the name of the register.

E.g.

#define SYSTEM_INT_FACTOR_MODE	(1 << 0)

> +
> +/*
> + * category 1 - PARAMETER mode
> + *
> + * This category supports function of camera features of M-5MOLS. It means we
> + * can handle with preview(MONITOR) resolution size/frame per second/interface
> + * between the sensor and the Application Processor/even the image effect.
> + */
> +#define CAT1_DATA_INTERFACE	0x00	/* interface between sensor and AP */
> +#define CAT1_MONITOR_SIZE	0x01	/* resolution at the MONITOR mode */
> +#define CAT1_MONITOR_FPS	0x02	/* frame per second at this mode */
> +#define CAT1_EFFECT		0x0b	/* image effects */
> +
> +#define PARM_MON_SIZE		I2C_REG(CAT_PARAM, CAT1_MONITOR_SIZE, 1)
> +
> +#define PARM_MON_FPS		I2C_REG(CAT_PARAM, CAT1_MONITOR_FPS, 1)
> +#define REG_FPS_30		0x02
> +
> +#define PARM_INTERFACE		I2C_REG(CAT_PARAM, CAT1_DATA_INTERFACE, 1)
> +#define REG_INTERFACE_MIPI	0x02
> +
> +#define PARM_EFFECT		I2C_REG(CAT_PARAM, CAT1_EFFECT, 1)
> +#define REG_EFFECT_OFF		0x00
> +#define REG_EFFECT_NEGA		0x01
> +#define REG_EFFECT_EMBOSS	0x06
> +#define REG_EFFECT_OUTLINE	0x07
> +#define REG_EFFECT_WATERCOLOR	0x08
> +
> +/*
> + * Category 2 - MONITOR mode
> + *
> + * The MONITOR mode is same as preview mode as we said. The M-5MOLS has another
> + * mode named "Preview", but this preview mode is used at the case specific
> + * vider-recording mode. This mmode supports only YUYV format. On the other
> + * hand, the JPEG & RAW formats is supports by CAPTURE mode. And, there are
> + * another options like zoom/color effect(different with effect in PARAMETER
> + * mode)/anti hand shaking algorithm.
> + */
> +#define CAT2_ZOOM		0x01	/* set the zoom position & execute */
> +#define CAT2_ZOOM_STEP		0x03	/* set the zoom step */
> +#define CAT2_CFIXB		0x09	/* CB value for color effect */
> +#define CAT2_CFIXR		0x0a	/* CR value for color effect */
> +#define CAT2_COLOR_EFFECT	0x0b	/* set on/off of color effect */
> +#define CAT2_CHROMA_LVL		0x0f	/* set chroma level */
> +#define CAT2_CHROMA_EN		0x10	/* set on/off of choroma */
> +#define CAT2_EDGE_LVL		0x11	/* set sharpness level */
> +#define CAT2_EDGE_EN		0x12	/* set on/off sharpness */
> +#define CAT2_TONE_CTL		0x25	/* set tone color(contrast) */
> +
> +#define MON_ZOOM		I2C_REG(CAT_MONITOR, CAT2_ZOOM, 1)
> +
> +#define MON_CFIXR		I2C_REG(CAT_MONITOR, CAT2_CFIXR, 1)
> +#define MON_CFIXB		I2C_REG(CAT_MONITOR, CAT2_CFIXB, 1)
> +#define REG_CFIXB_SEPIA		0xd8
> +#define REG_CFIXR_SEPIA		0x18
> +
> +#define MON_EFFECT		I2C_REG(CAT_MONITOR, CAT2_COLOR_EFFECT, 1)
> +#define REG_COLOR_EFFECT_OFF	0x00
> +#define REG_COLOR_EFFECT_ON	0x01
> +
> +#define MON_CHROMA_EN		I2C_REG(CAT_MONITOR, CAT2_CHROMA_EN, 1)
> +#define MON_CHROMA_LVL		I2C_REG(CAT_MONITOR, CAT2_CHROMA_LVL, 1)
> +#define REG_CHROMA_OFF		0x00
> +#define REG_CHROMA_ON		0x01
> +
> +#define MON_EDGE_EN		I2C_REG(CAT_MONITOR, CAT2_EDGE_EN, 1)
> +#define MON_EDGE_LVL		I2C_REG(CAT_MONITOR, CAT2_EDGE_LVL, 1)
> +#define REG_EDGE_OFF		0x00
> +#define REG_EDGE_ON		0x01
> +
> +#define MON_TONE_CTL		I2C_REG(CAT_MONITOR, CAT2_TONE_CTL, 1)
> +
> +/*
> + * Category 3 - Auto Exposure
> + *
> + * The M-5MOLS exposure capbility is detailed as which is similar to digital
> + * camera. This category supports AE locking/various AE mode(range of exposure)
> + * /ISO/flickering/EV bias/shutter/meteoring, and anything else. And the
> + * maximum/minimum exposure gain value depending on M-5MOLS firmware, may be
> + * different. So, this category also provide getting the max/min values. And,
> + * each MONITOR and CAPTURE mode has each gain/shutter/max exposure values.
> + */
> +#define CAT3_AE_LOCK		0x00	/* locking Auto exposure */
> +#define CAT3_AE_MODE		0x01	/* set AE mode, mode means range */
> +#define CAT3_ISO		0x05	/* set ISO */
> +#define CAT3_EV_PRESET_MONITOR	0x0a	/* EV(scenemode) preset for MONITOR */
> +#define CAT3_EV_PRESET_CAPTURE	0x0b	/* EV(scenemode) preset for CAPTURE */
> +#define CAT3_MANUAL_GAIN_MON	0x12	/* meteoring value for the MONITOR */
> +#define CAT3_MAX_GAIN_MON	0x1a	/* max gain value for the MONITOR */
> +#define CAT3_MANUAL_GAIN_CAP	0x26	/* meteoring value for the CAPTURE */
> +#define CAT3_AE_INDEX		0x38	/* AE index */
> +
> +#define AE_LOCK			I2C_REG(CAT_AE, CAT3_AE_LOCK, 1)
> +#define REG_AE_UNLOCK		0x00
> +#define REG_AE_LOCK		0x01
> +
> +#define AE_MODE			I2C_REG(CAT_AE, CAT3_AE_MODE, 1)
> +#define REG_AE_OFF		0x00	/* AE off */
> +#define REG_AE_ALL		0x01	/* calc AE in all block integral */
> +#define REG_AE_CENTER		0x03	/* calc AE in center weighted */
> +#define REG_AE_SPOT		0x06	/* calc AE in specific spot */
> +
> +#define AE_ISO			I2C_REG(CAT_AE, CAT3_ISO, 1)
> +#define REG_ISO_AUTO		0x00
> +#define REG_ISO_50		0x01
> +#define REG_ISO_100		0x02
> +#define REG_ISO_200		0x03
> +#define REG_ISO_400		0x04
> +#define REG_ISO_800		0x05
> +
> +#define AE_EV_PRESET_MONITOR	I2C_REG(CAT_AE, CAT3_EV_PRESET_MONITOR, 1)
> +#define AE_EV_PRESET_CAPTURE	I2C_REG(CAT_AE, CAT3_EV_PRESET_CAPTURE, 1)
> +#define REG_SCENE_NORMAL	0x00
> +#define REG_SCENE_PORTRAIT	0x01
> +#define REG_SCENE_LANDSCAPE	0x02
> +#define REG_SCENE_SPORTS	0x03
> +#define REG_SCENE_PARTY_INDOOR	0x04
> +#define REG_SCENE_BEACH_SNOW	0x05
> +#define REG_SCENE_SUNSET	0x06
> +#define REG_SCENE_DAWN_DUSK	0x07
> +#define REG_SCENE_FALL		0x08
> +#define REG_SCENE_NIGHT		0x09
> +#define REG_SCENE_AGAINST_LIGHT	0x0a
> +#define REG_SCENE_FIRE		0x0b
> +#define REG_SCENE_TEXT		0x0c
> +#define REG_SCENE_CANDLE	0x0d
> +
> +#define AE_MAN_GAIN_MON		I2C_REG(CAT_AE, CAT3_MANUAL_GAIN_MON, 2)
> +#define AE_MAX_GAIN_MON		I2C_REG(CAT_AE, CAT3_MAX_GAIN_MON, 2)
> +#define AE_MAN_GAIN_CAP		I2C_REG(CAT_AE, CAT3_MANUAL_GAIN_CAP, 2)
> +
> +#define AE_INDEX		I2C_REG(CAT_AE, CAT3_AE_INDEX, 1)
> +#define REG_AE_INDEX_20_NEG	0x00
> +#define REG_AE_INDEX_15_NEG	0x01
> +#define REG_AE_INDEX_10_NEG	0x02
> +#define REG_AE_INDEX_05_NEG	0x03
> +#define REG_AE_INDEX_00		0x04
> +#define REG_AE_INDEX_05_POS	0x05
> +#define REG_AE_INDEX_10_POS	0x06
> +#define REG_AE_INDEX_15_POS	0x07
> +#define REG_AE_INDEX_20_POS	0x08
> +
> +/*
> + * Category 6 - White Balance
> + *
> + * This category provide AWB locking/mode/preset/speed/gain bias, etc.
> + */
> +#define CAT6_AWB_LOCK		0x00	/* locking Auto Whitebalance */
> +#define CAT6_AWB_MODE		0x02	/* set Auto or Manual */
> +#define CAT6_AWB_MANUAL		0x03	/* set Manual(preset) value */
> +
> +#define AWB_LOCK		I2C_REG(CAT_WB, CAT6_AWB_LOCK, 1)
> +#define REG_AWB_UNLOCK		0x00
> +#define REG_AWB_LOCK		0x01
> +
> +#define AWB_MODE		I2C_REG(CAT_WB, CAT6_AWB_MODE, 1)
> +#define REG_AWB_AUTO		0x01	/* AWB off */
> +#define REG_AWB_PRESET		0x02	/* AWB preset */
> +
> +#define AWB_MANUAL		I2C_REG(CAT_WB, CAT6_AWB_MANUAL, 1)
> +#define REG_AWB_INCANDESCENT	0x01
> +#define REG_AWB_FLUORESCENT_1	0x02
> +#define REG_AWB_FLUORESCENT_2	0x03
> +#define REG_AWB_DAYLIGHT	0x04
> +#define REG_AWB_CLOUDY		0x05
> +#define REG_AWB_SHADE		0x06
> +#define REG_AWB_HORIZON		0x07
> +#define REG_AWB_LEDLIGHT	0x09
> +
> +/*
> + * Category 7 - EXIF information
> + */
> +#define CAT7_INFO_EXPTIME_NU	0x00
> +#define CAT7_INFO_EXPTIME_DE	0x04
> +#define CAT7_INFO_TV_NU		0x08
> +#define CAT7_INFO_TV_DE		0x0c
> +#define CAT7_INFO_AV_NU		0x10
> +#define CAT7_INFO_AV_DE		0x14
> +#define CAT7_INFO_BV_NU		0x18
> +#define CAT7_INFO_BV_DE		0x1c
> +#define CAT7_INFO_EBV_NU	0x20
> +#define CAT7_INFO_EBV_DE	0x24
> +#define CAT7_INFO_ISO		0x28
> +#define CAT7_INFO_FLASH		0x2a
> +#define CAT7_INFO_SDR		0x2c
> +#define CAT7_INFO_QVAL		0x2e
> +
> +#define EXIF_INFO_EXPTIME_NU	I2C_REG(CAT_EXIF, CAT7_INFO_EXPTIME_NU, 4)
> +#define EXIF_INFO_EXPTIME_DE	I2C_REG(CAT_EXIF, CAT7_INFO_EXPTIME_DE, 4)
> +#define EXIF_INFO_TV_NU		I2C_REG(CAT_EXIF, CAT7_INFO_TV_NU, 4)
> +#define EXIF_INFO_TV_DE		I2C_REG(CAT_EXIF, CAT7_INFO_TV_DE, 4)
> +#define EXIF_INFO_AV_NU		I2C_REG(CAT_EXIF, CAT7_INFO_AV_NU, 4)
> +#define EXIF_INFO_AV_DE		I2C_REG(CAT_EXIF, CAT7_INFO_AV_DE, 4)
> +#define EXIF_INFO_BV_NU		I2C_REG(CAT_EXIF, CAT7_INFO_BV_NU, 4)
> +#define EXIF_INFO_BV_DE		I2C_REG(CAT_EXIF, CAT7_INFO_BV_DE, 4)
> +#define EXIF_INFO_EBV_NU	I2C_REG(CAT_EXIF, CAT7_INFO_EBV_NU, 4)
> +#define EXIF_INFO_EBV_DE	I2C_REG(CAT_EXIF, CAT7_INFO_EBV_DE, 4)
> +#define EXIF_INFO_ISO		I2C_REG(CAT_EXIF, CAT7_INFO_ISO, 2)
> +#define EXIF_INFO_FLASH		I2C_REG(CAT_EXIF, CAT7_INFO_FLASH, 2)
> +#define EXIF_INFO_SDR		I2C_REG(CAT_EXIF, CAT7_INFO_SDR, 2)
> +#define EXIF_INFO_QVAL		I2C_REG(CAT_EXIF, CAT7_INFO_QVAL, 2)
> +
> +/*
> + * Category 9 - Face Detection
> + */
> +#define CAT9_FD_CTL		0x00
> +
> +#define FD_CTL			I2C_REG(CAT_FD, CAT9_FD_CTL, 1)
> +#define BIT_FD_EN		0
> +#define BIT_FD_DRAW_FACE_FRAME	4
> +#define BIT_FD_DRAW_SMILE_LVL	6
> +#define REG_FD(shift)		(1 << shift)
> +#define REG_FD_OFF		0x0
> +
> +/*
> + * Category A - Lens Parameter
> + */
> +#define CATA_AF_MODE		0x01
> +#define CATA_AF_EXECUTE		0x02
> +#define CATA_AF_STATUS		0x03
> +#define CATA_AF_VERSION		0x0a
> +
> +#define AF_MODE			I2C_REG(CAT_LENS, CATA_AF_MODE, 1)
> +#define REG_AF_NORMAL		0x00	/* Normal AF, one time */
> +#define REG_AF_MACRO		0x01	/* Macro AF, one time */
> +#define REG_AF_POWEROFF		0x07
> +
> +#define AF_EXECUTE		I2C_REG(CAT_LENS, CATA_AF_EXECUTE, 1)
> +#define REG_AF_STOP		0x00
> +#define REG_AF_EXE_AUTO		0x01
> +#define REG_AF_EXE_CAF		0x02
> +
> +#define AF_STATUS		I2C_REG(CAT_LENS, CATA_AF_STATUS, 1)
> +#define REG_AF_FAIL		0x00
> +#define REG_AF_SUCCESS		0x02
> +#define REG_AF_IDLE		0x04
> +#define REG_AF_BUSY		0x05
> +
> +#define AF_VERSION		I2C_REG(CAT_LENS, CATA_AF_VERSION, 1)
> +
> +/*
> + * Category B - CAPTURE Parameter
> + */
> +#define CATB_YUVOUT_MAIN	0x00
> +#define CATB_MAIN_IMAGE_SIZE	0x01
> +#define CATB_MCC_MODE		0x1d
> +#define CATB_WDR_EN		0x2c
> +#define CATB_LIGHT_CTRL		0x40
> +#define CATB_FLASH_CTRL		0x41
> +
> +#define CAPP_YUVOUT_MAIN	I2C_REG(CAT_CAPT_PARM, CATB_YUVOUT_MAIN, 1)
> +#define REG_YUV422		0x00
> +#define REG_BAYER10		0x05
> +#define REG_BAYER8		0x06
> +#define REG_JPEG		0x10
> +
> +#define CAPP_MAIN_IMAGE_SIZE	I2C_REG(CAT_CAPT_PARM, CATB_MAIN_IMAGE_SIZE, 1)
> +
> +#define CAPP_MCC_MODE		I2C_REG(CAT_CAPT_PARM, CATB_MCC_MODE, 1)
> +#define REG_MCC_OFF		0x00
> +#define REG_MCC_NORMAL		0x01
> +
> +#define CAPP_WDR_EN		I2C_REG(CAT_CAPT_PARM, CATB_WDR_EN, 1)
> +#define REG_WDR_OFF		0x00
> +#define REG_WDR_ON		0x01
> +#define REG_WDR_AUTO		0x02
> +
> +#define CAPP_LIGHT_CTRL		I2C_REG(CAT_CAPT_PARM, CATB_LIGHT_CTRL, 1)
> +#define REG_LIGHT_OFF		0x00
> +#define REG_LIGHT_ON		0x01
> +#define REG_LIGHT_AUTO		0x02
> +
> +#define CAPP_FLASH_CTRL		I2C_REG(CAT_CAPT_PARM, CATB_FLASH_CTRL, 1)
> +#define REG_FLASH_OFF		0x00
> +#define REG_FLASH_ON		0x01
> +#define REG_FLASH_AUTO		0x02
> +
> +/*
> + * Category C - CAPTURE Control
> + */
> +#define CATC_CAP_MODE		0x00
> +#define CATC_CAP_SEL_FRAME	0x06	/* It determines Single or Multi */
> +#define CATC_CAP_START		0x09
> +#define CATC_CAP_IMAGE_SIZE	0x0d
> +#define CATC_CAP_THUMB_SIZE	0x11
> +
> +#define CAPC_MODE		I2C_REG(CAT_CAPT_CTRL, CATC_CAP_MODE, 1)
> +#define REG_CAP_NONE		0x00
> +#define REG_CAP_ANTI_SHAKE	0x02
> +
> +#define CAPC_SEL_FRAME		I2C_REG(CAT_CAPT_CTRL, CATC_CAP_SEL_FRAME, 1)
> +
> +#define CAPC_START		I2C_REG(CAT_CAPT_CTRL, CATC_CAP_START, 1)
> +#define REG_CAP_START_MAIN	0x01
> +#define REG_CAP_START_THUMB	0x03
> +
> +#define CAPC_IMAGE_SIZE		I2C_REG(CAT_CAPT_CTRL, CATC_CAP_IMAGE_SIZE, 1)
> +#define CAPC_THUMB_SIZE		I2C_REG(CAT_CAPT_CTRL, CATC_CAP_THUMB_SIZE, 1)
> +
> +/*
> + * Category F - Flash
> + *
> + * This mode provides functions about internal flash stuff and system startup.
> + */
> +#define CATF_CAM_START		0x12	/* It starts internal ARM core booting
> +					 * after power-up */
> +
> +#define FLASH_CAM_START		I2C_REG(CAT_FLASH, CATF_CAM_START, 1)
> +#define REG_START_ARM_BOOT	0x01
> +
> +#endif	/* M5MOLS_REG_H */
> diff --git a/include/media/m5mols.h b/include/media/m5mols.h
> new file mode 100644
> index 0000000..2d7e7ca
> --- /dev/null
> +++ b/include/media/m5mols.h
> @@ -0,0 +1,35 @@
> +/*
> + * Driver header for M-5MOLS 8M Pixel camera sensor with ISP
> + *
> + * Copyright (C) 2011 Samsung Electronics Co., Ltd.
> + * Author: HeungJun Kim, riverful.kim@samsung.com
> + *
> + * Copyright (C) 2009 Samsung Electronics Co., Ltd.
> + * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com
> + *
> + * 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 MEDIA_M5MOLS_H
> +#define MEDIA_M5MOLS_H
> +
> +/**
> + * struct m5mols_platform_data - platform data for M-5MOLS driver
> + * @irq:	GPIO getting the irq pin of M-5MOLS
> + * @gpio_reset:	GPIO driving the reset pin of M-5MOLS
> + * @reset_polarity: active state for gpio_rst pin, 0 or 1
> + * @set_power:	an additional callback to the board setup code
> + *		to be called after enabling and before disabling
> + *		the sensor's supply regulators
> + */
> +struct m5mols_platform_data {
> +	int irq;
> +	int gpio_reset;
> +	u8 reset_polarity;
> +	int (*set_power)(struct device *dev, int on);
> +};
> +
> +#endif	/* MEDIA_M5MOLS_H */
> -- 
> 1.7.0.4
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-media" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

-- 
Sakari Ailus
sakari dot ailus at iki dot fi

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH v9] Add support for M-5MOLS 8 Mega Pixel camera ISP
  2011-05-25 13:54         ` Sakari Ailus
@ 2011-05-26  7:12           ` Kim, HeungJun
  2011-06-05 11:55             ` Sakari Ailus
  2011-05-27 12:58           ` [PATCH 0/5] Fix micellaneous issues for M-5MOLS driver HeungJun, Kim
                             ` (5 subsequent siblings)
  6 siblings, 1 reply; 34+ messages in thread
From: Kim, HeungJun @ 2011-05-26  7:12 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: linux-media, mchehab, hverkuil, laurent.pinchart,
	Sylwester Nawrocki, Kyungmin Park

Hi Sakari,

2011-05-25 오후 10:54, Sakari Ailus 쓴 글:
> Hi HeungJun,
> 
> Thanks for the patch!
Also, thanks for the interests of this driver!

> 
> I'm happy to see that Samsung is interested in getting such a driver to
> mainline. :-) I suppose that theoretically nothing would prevent plugging
> such a sensor to the OMAP 3 ISP, for example. It's just that the sensor
> already does image processing and the ISP might not be that useful because
> of this. But the interfaces would match, both in software and in hardware.
This sensor(actually integrated ISP functionality as you know) is powerful,
and I think this driver can help to make the controls for digital camera.

But, TI OMAP 3 has also ISP independently, so I think having the ISP module
in the Processor is more better option cause of choice of various sensors,
although the driver's developer has more issues which should be handled.

I hope to handle ISP directly in the Samsung Processor.

> 
> This is a subdev driver and uses the control framework. Good. I have
> comments on the code below. 
Before that, this driver is already merged in Mauro's branch, and
I have spent a few months making this drivers for submitting this.
Some of your comments looks good and needed for this driver.
But, if I fix this and resend it and another comments happened,
this may be endless alone fight to reach "mergeing". :)

So, I want that I keep this comments in mind, and I'll guarantee the fixes
will be adapted the next time by type of the patch, after this driver patch
is merged fully in 3.0.

> 
> On Fri, May 20, 2011 at 02:56:57PM +0900, HeungJun, Kim wrote:
>> Add I2C/V4L2 subdev driver for M-5MOLS integrated image signal processor
>> with 8 Mega Pixel sensor.
>>
>> Signed-off-by: HeungJun, Kim <riverful.kim@samsung.com>
>> Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
>> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
>> ---
>>
>> Hello everyone,
>>
>> This is the ninth version of the subdev for M-5MOLS 8M Pixel camera sensor.
>> If you see previous version, you can find at:
>> http://www.spinics.net/lists/linux-media/msg32387.html
>>
>> The major changes including points commented in the IRC is as follows.
>> 1. remove a lot of inline I2C functions for being easy to read
>>   : rename m5mols_read/write() to m5mols_read/write()
>>   : add I2C_XXX() macro to combinine the arguments of the I2C raw functions
>> 2. use atomic macro for checking capture interrupt
>> 3. add comments for DocBook
>>
>> Thanks to read, and any comments are welcome!
>>
>> --
>> Thanks and Regards,
>> Heungjun Kim
>> Samsung Electronics DMC R&D Center
>> ---
>>  drivers/media/video/Kconfig                  |    2 +
>>  drivers/media/video/Makefile                 |    1 +
>>  drivers/media/video/m5mols/Kconfig           |    5 +
>>  drivers/media/video/m5mols/Makefile          |    3 +
>>  drivers/media/video/m5mols/m5mols.h          |  296 ++++++++
>>  drivers/media/video/m5mols/m5mols_capture.c  |  191 +++++
>>  drivers/media/video/m5mols/m5mols_controls.c |  299 ++++++++
>>  drivers/media/video/m5mols/m5mols_core.c     | 1004 ++++++++++++++++++++++++++
>>  drivers/media/video/m5mols/m5mols_reg.h      |  399 ++++++++++
>>  include/media/m5mols.h                       |   35 +
>>  10 files changed, 2235 insertions(+), 0 deletions(-)
>>  create mode 100644 drivers/media/video/m5mols/Kconfig
>>  create mode 100644 drivers/media/video/m5mols/Makefile
>>  create mode 100644 drivers/media/video/m5mols/m5mols.h
>>  create mode 100644 drivers/media/video/m5mols/m5mols_capture.c
>>  create mode 100644 drivers/media/video/m5mols/m5mols_controls.c
>>  create mode 100644 drivers/media/video/m5mols/m5mols_core.c
>>  create mode 100644 drivers/media/video/m5mols/m5mols_reg.h
>>  create mode 100644 include/media/m5mols.h
>>
>> diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
>> index d61414e..242c80c 100644
>> --- a/drivers/media/video/Kconfig
>> +++ b/drivers/media/video/Kconfig
>> @@ -753,6 +753,8 @@ config VIDEO_NOON010PC30
>>  	---help---
>>  	  This driver supports NOON010PC30 CIF camera from Siliconfile
>>  
>> +source "drivers/media/video/m5mols/Kconfig"
>> +
>>  config VIDEO_OMAP3
>>  	tristate "OMAP 3 Camera support (EXPERIMENTAL)"
>>  	select OMAP_IOMMU
>> diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
>> index a10e4c3..d5d6de1 100644
>> --- a/drivers/media/video/Makefile
>> +++ b/drivers/media/video/Makefile
>> @@ -69,6 +69,7 @@ obj-$(CONFIG_VIDEO_MT9V011) += mt9v011.o
>>  obj-$(CONFIG_VIDEO_MT9V032) += mt9v032.o
>>  obj-$(CONFIG_VIDEO_SR030PC30)	+= sr030pc30.o
>>  obj-$(CONFIG_VIDEO_NOON010PC30)	+= noon010pc30.o
>> +obj-$(CONFIG_VIDEO_M5MOLS)	+= m5mols/
>>  
>>  obj-$(CONFIG_SOC_CAMERA_IMX074)		+= imx074.o
>>  obj-$(CONFIG_SOC_CAMERA_MT9M001)	+= mt9m001.o
>> diff --git a/drivers/media/video/m5mols/Kconfig b/drivers/media/video/m5mols/Kconfig
>> new file mode 100644
>> index 0000000..302dc3d
>> --- /dev/null
>> +++ b/drivers/media/video/m5mols/Kconfig
>> @@ -0,0 +1,5 @@
>> +config VIDEO_M5MOLS
>> +	tristate "Fujitsu M-5MOLS 8MP sensor support"
>> +	depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
>> +	---help---
>> +	  This driver supports Fujitsu M-5MOLS camera sensor with ISP
>> diff --git a/drivers/media/video/m5mols/Makefile b/drivers/media/video/m5mols/Makefile
>> new file mode 100644
>> index 0000000..0a44e02
>> --- /dev/null
>> +++ b/drivers/media/video/m5mols/Makefile
>> @@ -0,0 +1,3 @@
>> +m5mols-objs	:= m5mols_core.o m5mols_controls.o m5mols_capture.o
>> +
>> +obj-$(CONFIG_VIDEO_M5MOLS)		+= m5mols.o
>> diff --git a/drivers/media/video/m5mols/m5mols.h b/drivers/media/video/m5mols/m5mols.h
>> new file mode 100644
>> index 0000000..10b55c8
>> --- /dev/null
>> +++ b/drivers/media/video/m5mols/m5mols.h
>> @@ -0,0 +1,296 @@
>> +/*
>> + * Header for M-5MOLS 8M Pixel camera sensor with ISP
>> + *
>> + * Copyright (C) 2011 Samsung Electronics Co., Ltd.
>> + * Author: HeungJun Kim, riverful.kim@samsung.com
>> + *
>> + * Copyright (C) 2009 Samsung Electronics Co., Ltd.
>> + * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com
>> + *
>> + * 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-subdev.h>
>> +#include "m5mols_reg.h"
>> +
>> +extern int m5mols_debug;
>> +
>> +#define to_m5mols(__sd)	container_of(__sd, struct m5mols_info, sd)
>> +
>> +#define to_sd(__ctrl) \
>> +	(&container_of(__ctrl->handler, struct m5mols_info, handle)->sd)
>> +
>> +enum m5mols_restype {
>> +	M5MOLS_RESTYPE_MONITOR,
>> +	M5MOLS_RESTYPE_CAPTURE,
>> +	M5MOLS_RESTYPE_MAX,
>> +};
>> +
>> +/**
>> + * struct m5mols_resolution - structure for the resolution
>> + * @type: resolution type according to the pixel code
>> + * @width: width of the resolution
>> + * @height: height of the resolution
>> + * @reg: resolution preset register value
>> + */
>> +struct m5mols_resolution {
>> +	u8 reg;
>> +	enum m5mols_restype type;
>> +	u16 width;
>> +	u16 height;
>> +};
>> +
>> +/**
>> + * struct m5mols_exif - structure for the EXIF information of M-5MOLS
>> + * @exposure_time: exposure time register value
>> + * @shutter_speed: speed of the shutter register value
>> + * @aperture: aperture register value
>> + * @exposure_bias: it calls also EV bias
>> + * @iso_speed: ISO register value
>> + * @flash: status register value of the flash
>> + * @sdr: status register value of the Subject Distance Range
>> + * @qval: not written exact meaning in document
>> + */
>> +struct m5mols_exif {
>> +	u32 exposure_time;
>> +	u32 shutter_speed;
>> +	u32 aperture;
>> +	u32 brightness;
>> +	u32 exposure_bias;
>> +	u16 iso_speed;
>> +	u16 flash;
>> +	u16 sdr;
>> +	u16 qval;
>> +};
>> +
>> +/**
>> + * struct m5mols_capture - Structure for the capture capability
>> + * @exif: EXIF information
>> + * @main: size in bytes of the main image
>> + * @thumb: size in bytes of the thumb image, if it was accompanied
>> + * @total: total size in bytes of the produced image
>> + */
>> +struct m5mols_capture {
>> +	struct m5mols_exif exif;
>> +	u32 main;
>> +	u32 thumb;
>> +	u32 total;
>> +};
>> +
>> +/**
>> + * struct m5mols_scenemode - structure for the scenemode capability
>> + * @metering: metering light register value
>> + * @ev_bias: EV bias register value
>> + * @wb_mode: mode which means the WhiteBalance is Auto or Manual
>> + * @wb_preset: whitebalance preset register value in the Manual mode
>> + * @chroma_en: register value whether the Chroma capability is enabled or not
>> + * @chroma_lvl: chroma's level register value
>> + * @edge_en: register value Whether the Edge capability is enabled or not
>> + * @edge_lvl: edge's level register value
>> + * @af_range: Auto Focus's range
>> + * @fd_mode: Face Detection mode
>> + * @mcc: Multi-axis Color Conversion which means emotion color
>> + * @light: status of the Light
>> + * @flash: status of the Flash
>> + * @tone: Tone color which means Contrast
>> + * @iso: ISO register value
>> + * @capt_mode: Mode of the Image Stabilization while the camera capturing
>> + * @wdr: Wide Dynamic Range register value
>> + *
>> + * The each value according to each scenemode is recommended in the documents.
>> + */
>> +struct m5mols_scenemode {
>> +	u32 metering;
>> +	u32 ev_bias;
>> +	u32 wb_mode;
>> +	u32 wb_preset;
>> +	u32 chroma_en;
>> +	u32 chroma_lvl;
>> +	u32 edge_en;
>> +	u32 edge_lvl;
>> +	u32 af_range;
>> +	u32 fd_mode;
>> +	u32 mcc;
>> +	u32 light;
>> +	u32 flash;
>> +	u32 tone;
>> +	u32 iso;
>> +	u32 capt_mode;
>> +	u32 wdr;
>> +};
>> +
>> +/**
>> + * struct m5mols_version - firmware version information
>> + * @customer:	customer information
>> + * @project:	version of project information according to customer
>> + * @fw:		firmware revision
>> + * @hw:		hardware revision
>> + * @param:	version of the parameter
>> + * @awb:	Auto WhiteBalance algorithm version
>> + * @str:	information about manufacturer and packaging vendor
>> + * @af:		Auto Focus version
>> + *
>> + * The register offset starts the customer version at 0x0, and it ends
>> + * the awb version at 0x09. The customer, project information occupies 1 bytes
>> + * each. And also the fw, hw, param, awb each requires 2 bytes. The str is
>> + * unique string associated with firmware's version. It includes information
>> + * about manufacturer and the vendor of the sensor's packaging. The least
>> + * significant 2 bytes of the string indicate packaging manufacturer.
>> + */
>> +#define VERSION_STRING_SIZE	22
>> +struct m5mols_version {
>> +	u8	customer;
>> +	u8	project;
>> +	u16	fw;
>> +	u16	hw;
>> +	u16	param;
>> +	u16	awb;
>> +	u8	str[VERSION_STRING_SIZE];
>> +	u8	af;
>> +};
>> +#define VERSION_SIZE sizeof(struct m5mols_version)
> 
> You're using VERSION_SIZE in two places in one function. Is that worth
> making it a macro? :-)
Just make for being readable. Until this 9th version of this patch,
I'm very taking care of over 80 character and for being readable.
Updating each version, any other word is not used MACRO, and the other word
need more space, on each line. At the next version, it happened that
the one not used MACRO should use MACRO. And switching like this
will be continued repedately.

So, I think it's no meaningless to use just twice. and want to fix.

And the other thing commented as belows, are most of same reason.

> 
> I think you should add attribute ((packed)) to the definition as well.
It looks needed. I added attibute packed for this struct for the next time.

> 
>> +/**
>> + * struct m5mols_info - M-5MOLS driver data structure
>> + * @pdata: platform data
>> + * @sd: v4l-subdev instance
>> + * @pad: media pad
>> + * @ffmt: current fmt according to resolution type
>> + * @res_type: current resolution type
>> + * @code: current code
>> + * @irq_waitq: waitqueue for the capture
>> + * @work_irq: workqueue for the IRQ
>> + * @flags: state variable for the interrupt handler
>> + * @handle: control handler
>> + * @autoexposure: Auto Exposure control
>> + * @exposure: Exposure control
>> + * @autowb: Auto White Balance control
>> + * @colorfx: Color effect control
>> + * @saturation:	Saturation control
>> + * @zoom: Zoom control
>> + * @ver: information of the version
>> + * @cap: the capture mode attributes
>> + * @power: current sensor's power status
>> + * @ctrl_sync: true means all controls of the sensor are initialized
>> + * @int_capture: true means the capture interrupt is issued once
>> + * @lock_ae: true means the Auto Exposure is locked
>> + * @lock_awb: true means the Aut WhiteBalance is locked
>> + * @resolution:	register value for current resolution
>> + * @interrupt: register value for current interrupt status
>> + * @mode: register value for current operation mode
>> + * @mode_save: register value for current operation mode for saving
>> + * @set_power: optional power callback to the board code
>> + */
>> +struct m5mols_info {
>> +	const struct m5mols_platform_data *pdata;
>> +	struct v4l2_subdev sd;
>> +	struct media_pad pad;
>> +	struct v4l2_mbus_framefmt ffmt[M5MOLS_RESTYPE_MAX];
>> +	int res_type;
>> +	enum v4l2_mbus_pixelcode code;
>> +	wait_queue_head_t irq_waitq;
>> +	struct work_struct work_irq;
>> +	unsigned long flags;
> 
> You want to keep flags in the stack, not in a context independent location.
> This will move to functions which actually use it.
I don't understand well what you mean "stack" and "context". Sorry for lack of
English skill. Tell me more specific detailed.

And as I tell about this variable "flags", this should be shared in the two files,
m5mols_capture.c and m5mols_core.c. 

> 
>> +
>> +	struct v4l2_ctrl_handler handle;
>> +	/* Autoexposure/exposure control cluster */
>> +	struct {
>> +		struct v4l2_ctrl *autoexposure;
>> +		struct v4l2_ctrl *exposure;
>> +	};
> 
> Would it be different without the anonymous struct?
These two v4l2_ctrl is clustered. So, anonymous struct should be used
for v4l2_ctrl_cluster().

> 
>> +	struct v4l2_ctrl *autowb;
>> +	struct v4l2_ctrl *colorfx;
>> +	struct v4l2_ctrl *saturation;
>> +	struct v4l2_ctrl *zoom;
>> +
>> +	struct m5mols_version ver;
>> +	struct m5mols_capture cap;
>> +	bool power;
>> +	bool ctrl_sync;
>> +	bool lock_ae;
>> +	bool lock_awb;
>> +	u8 resolution;
>> +	u32 interrupt;
> 
> What would be the reason to store interrupt status information here, and
> again, not in a variable which is allocated from the stack?
Ditto "stack". I need more detailed.

And this variable interrupt also should be shared in the two files.

> 
>> +	u32 mode;
>> +	u32 mode_save;
>> +	int (*set_power)(struct device *dev, int on);
>> +};
>> +
>> +#define ST_CAPT_IRQ 0
>> +
>> +#define is_powered(__info) (__info->power)
>> +#define is_ctrl_synced(__info) (__info->ctrl_sync)
>> +#define is_available_af(__info)	(__info->ver.af)
>> +#define is_code(__code, __type) (__code == m5mols_default_ffmt[__type].code)
>> +#define is_manufacturer(__info, __manufacturer)	\
>> +				(__info->ver.str[0] == __manufacturer[0] && \
>> +				 __info->ver.str[1] == __manufacturer[1])
>> +/*
>> + * I2C operation of the M-5MOLS
>> + *
>> + * The I2C read operation of the M-5MOLS requires 2 messages. The first
>> + * message sends the information about the command, command category, and total
>> + * message size. The second message is used to retrieve the data specifed in
>> + * the first message
>> + *
>> + *   1st message                                2nd message
>> + *   +-------+---+----------+-----+-------+     +------+------+------+------+
>> + *   | size1 | R | category | cmd | size2 |     | d[0] | d[1] | d[2] | d[3] |
>> + *   +-------+---+----------+-----+-------+     +------+------+------+------+
>> + *   - size1: message data size(5 in this case)
>> + *   - size2: desired buffer size of the 2nd message
>> + *   - d[0..3]: according to size2
>> + *
>> + * The I2C write operation needs just one message. The message includes
>> + * category, command, total size, and desired data.
>> + *
>> + *   1st message
>> + *   +-------+---+----------+-----+------+------+------+------+
>> + *   | size1 | W | category | cmd | d[0] | d[1] | d[2] | d[3] |
>> + *   +-------+---+----------+-----+------+------+------+------+
>> + *   - d[0..3]: according to size1
>> + */
>> +int m5mols_read(struct v4l2_subdev *sd, u32 reg_comb, u32 *val);
>> +int m5mols_write(struct v4l2_subdev *sd, u32 reg_comb, u32 val);
>> +int m5mols_busy(struct v4l2_subdev *sd, u8 category, u8 cmd, u32 value);
>> +
>> +/*
>> + * Mode operation of the M-5MOLS
>> + *
>> + * Changing the mode of the M-5MOLS is needed right executing order.
>> + * There are three modes(PARAMETER, MONITOR, CAPTURE) which can be changed
>> + * by user. There are various categories associated with each mode.
>> + *
>> + * +============================================================+
>> + * | mode	| category					|
>> + * +============================================================+
>> + * | FLASH	| FLASH(only after Stand-by or Power-on)	|
>> + * | SYSTEM	| SYSTEM(only after sensor arm-booting)		|
>> + * | PARAMETER	| PARAMETER					|
>> + * | MONITOR	| MONITOR(preview), Auto Focus, Face Detection	|
>> + * | CAPTURE	| Single CAPTURE, Preview(recording)		|
>> + * +============================================================+
>> + *
>> + * The available executing order between each modes are as follows:
>> + *   PARAMETER <---> MONITOR <---> CAPTURE
>> + */
>> +int m5mols_mode(struct m5mols_info *info, u32 mode);
>> +
>> +int m5mols_enable_interrupt(struct v4l2_subdev *sd, u32 reg);
>> +int m5mols_sync_controls(struct m5mols_info *info);
>> +int m5mols_start_capture(struct m5mols_info *info);
>> +int m5mols_do_scenemode(struct m5mols_info *info, u32 mode);
>> +int m5mols_lock_3a(struct m5mols_info *info, bool lock);
>> +int m5mols_set_ctrl(struct v4l2_ctrl *ctrl);
>> +
>> +/* The firmware function */
>> +int m5mols_update_fw(struct v4l2_subdev *sd,
>> +		     int (*set_power)(struct m5mols_info *, bool));
>> +
>> +#endif	/* M5MOLS_H */
>> diff --git a/drivers/media/video/m5mols/m5mols_capture.c b/drivers/media/video/m5mols/m5mols_capture.c
>> new file mode 100644
>> index 0000000..d71a390
>> --- /dev/null
>> +++ b/drivers/media/video/m5mols/m5mols_capture.c
>> @@ -0,0 +1,191 @@
>> +/*
>> + * The Capture code for Fujitsu M-5MOLS ISP
>> + *
>> + * Copyright (C) 2011 Samsung Electronics Co., Ltd.
>> + * Author: HeungJun Kim, riverful.kim@samsung.com
>> + *
>> + * Copyright (C) 2009 Samsung Electronics Co., Ltd.
>> + * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com
>> + *
>> + * 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/slab.h>
>> +#include <linux/irq.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/delay.h>
>> +#include <linux/version.h>
>> +#include <linux/gpio.h>
>> +#include <linux/regulator/consumer.h>
>> +#include <linux/videodev2.h>
>> +#include <linux/version.h>
>> +#include <media/v4l2-ctrls.h>
>> +#include <media/v4l2-device.h>
>> +#include <media/v4l2-subdev.h>
>> +#include <media/m5mols.h>
>> +
>> +#include "m5mols.h"
>> +#include "m5mols_reg.h"
>> +
>> +static int m5mols_capture_error_handler(struct m5mols_info *info,
>> +					int timeout)
>> +{
>> +	int ret;
>> +
>> +	/* Disable all interrupts and clear relevant interrupt staus bits */
>> +	ret = m5mols_write(&info->sd, SYSTEM_INT_ENABLE,
>> +			   info->interrupt & ~(REG_INT_CAPTURE));
>> +	if (ret)
>> +		return ret;
>> +
>> +	if (timeout == 0)
>> +		return -ETIMEDOUT;
>> +
>> +	return 0;
>> +}
> 
> Timeout should be handled outside this function. It does nothing else based
> on it except return an error code.
You're right. timeout is meaningless in this function .
I'll change this to another expression. Good catch.
 
> 
>> +/**
>> + * m5mols_read_rational - I2C read of a rational number
>> + *
>> + * Read numerator and denominator from registers @addr_num and @addr_den
>> + * respectively and return the division result in @val.
>> + */
>> +static int m5mols_read_rational(struct v4l2_subdev *sd, u32 addr_num,
>> +				u32 addr_den, u32 *val)
> 
> Are there cases where addr_num + 4 != addr_den? If so, you could just drop
> addr_den.
It's looks needed drop-checking although actually it's not happened.
This will be changed for the next time. 

> 
>> +{
>> +	u32 num, den;
>> +
>> +	int ret = m5mols_read(sd, addr_num, &num);
>> +	if (!ret)
>> +		ret = m5mols_read(sd, addr_den, &den);
>> +	if (ret)
>> +		return ret;
>> +	*val = den == 0 ? 0 : num / den;
>> +	return ret;
>> +}
>> +
>> +/**
>> + * m5mols_capture_info - Gather captured image information
>> + *
>> + * For now it gathers only EXIF information and file size.
>> + */
>> +static int m5mols_capture_info(struct m5mols_info *info)
>> +{
>> +	struct m5mols_exif *exif = &info->cap.exif;
>> +	struct v4l2_subdev *sd = &info->sd;
>> +	int ret;
>> +
>> +	ret = m5mols_read_rational(sd, EXIF_INFO_EXPTIME_NU,
>> +				   EXIF_INFO_EXPTIME_DE, &exif->exposure_time);
>> +	if (ret)
>> +		return ret;
>> +	ret = m5mols_read_rational(sd, EXIF_INFO_TV_NU, EXIF_INFO_TV_DE,
>> +				   &exif->shutter_speed);
>> +	if (ret)
>> +		return ret;
>> +	ret = m5mols_read_rational(sd, EXIF_INFO_AV_NU, EXIF_INFO_AV_DE,
>> +				   &exif->aperture);
>> +	if (ret)
>> +		return ret;
>> +	ret = m5mols_read_rational(sd, EXIF_INFO_BV_NU, EXIF_INFO_BV_DE,
>> +				   &exif->brightness);
>> +	if (ret)
>> +		return ret;
>> +	ret = m5mols_read_rational(sd, EXIF_INFO_EBV_NU, EXIF_INFO_EBV_DE,
>> +				   &exif->exposure_bias);
>> +	if (ret)
>> +		return ret;
> 
> You could define register-memory address pairs in a structure and read the
> registers in a loop to the memory. This would be cleaner than few tonnes of
> function calls.
Probably the combination with loop and register-memory may look good,
but another variables and macro(or definitions) is needed for loop.
So, the code is more complex I think. Then, the complexity
will be the same as current one.

Rather than this, it's better option to maintain just two function stack,
then it looks more intuitive I think.
It's not needed more tricky algorithm. It's just reading EXIF information.

> 
>> +	ret = m5mols_read(sd, EXIF_INFO_ISO, (u32 *)&exif->iso_speed);
>> +	if (!ret)
>> +		ret = m5mols_read(sd, EXIF_INFO_FLASH, (u32 *)&exif->flash);
>> +	if (!ret)
>> +		ret = m5mols_read(sd, EXIF_INFO_SDR, (u32 *)&exif->sdr);
>> +	if (!ret)
>> +		ret = m5mols_read(sd, EXIF_INFO_QVAL, (u32 *)&exif->qval);
>> +	if (ret)
>> +		return ret;
> 
> Please don't do typecasting for pointers like this. You end up overwriting
> other parts of memory. The final call will write to memory which isn't part
> of the structure.
It can be possible, and you're right. So, my next plan is making each function
of a few MACROS according to Bytes width, and you also recommended about that
as belows.

> 
>> +	if (!ret)
>> +		ret = m5mols_read(sd, CAPC_IMAGE_SIZE, &info->cap.main);
>> +	if (!ret)
>> +		ret = m5mols_read(sd, CAPC_THUMB_SIZE, &info->cap.thumb);
>> +	if (!ret)
>> +		info->cap.total = info->cap.main + info->cap.thumb;
>> +
>> +	return ret;
>> +}
>> +
>> +int m5mols_start_capture(struct m5mols_info *info)
>> +{
>> +	struct v4l2_subdev *sd = &info->sd;
>> +	u32 resolution = info->resolution;
>> +	int timeout;
>> +	int ret;
>> +
>> +	/*
>> +	 * Preparing capture. Setting control & interrupt before entering
>> +	 * capture mode
>> +	 *
>> +	 * 1) change to MONITOR mode for operating control & interrupt
>> +	 * 2) set controls (considering v4l2_control value & lock 3A)
>> +	 * 3) set interrupt
>> +	 * 4) change to CAPTURE mode
>> +	 */
>> +	ret = m5mols_mode(info, REG_MONITOR);
>> +	if (!ret)
>> +		ret = m5mols_sync_controls(info);
>> +	if (!ret)
>> +		ret = m5mols_lock_3a(info, true);
>> +	if (!ret)
>> +		ret = m5mols_enable_interrupt(sd, REG_INT_CAPTURE);
>> +	if (!ret)
>> +		ret = m5mols_mode(info, REG_CAPTURE);
>> +	if (!ret) {
>> +		/* Wait for capture interrupt, after changing capture mode */
>> +		timeout = wait_event_interruptible_timeout(info->irq_waitq,
>> +					   test_bit(ST_CAPT_IRQ, &info->flags),
>> +					   msecs_to_jiffies(2000));
>> +		if (test_and_clear_bit(ST_CAPT_IRQ, &info->flags))
>> +			ret = m5mols_capture_error_handler(info, timeout);
>> +	}
>> +	if (!ret)
>> +		ret = m5mols_lock_3a(info, false);
>> +	if (ret)
>> +		return ret;
>> +	/*
>> +	 * Starting capture. Setting capture frame count and resolution and
>> +	 * the format(available format: JPEG, Bayer RAW, YUV).
>> +	 *
>> +	 * 1) select single or multi(enable to 25), format, size
>> +	 * 2) set interrupt
>> +	 * 3) start capture(for main image, now)
>> +	 * 4) get information
>> +	 * 5) notify file size to v4l2 device(e.g, to s5p-fimc v4l2 device)
>> +	 */
>> +	ret = m5mols_write(sd, CAPC_SEL_FRAME, 1);
>> +	if (!ret)
>> +		ret = m5mols_write(sd, CAPP_YUVOUT_MAIN, REG_JPEG);
>> +	if (!ret)
>> +		ret = m5mols_write(sd, CAPP_MAIN_IMAGE_SIZE, resolution);
>> +	if (!ret)
>> +		ret = m5mols_enable_interrupt(sd, REG_INT_CAPTURE);
>> +	if (!ret)
>> +		ret = m5mols_write(sd, CAPC_START, REG_CAP_START_MAIN);
>> +	if (!ret) {
>> +		/* Wait for the capture completion interrupt */
>> +		timeout = wait_event_interruptible_timeout(info->irq_waitq,
>> +					   test_bit(ST_CAPT_IRQ, &info->flags),
>> +					   msecs_to_jiffies(2000));
>> +		if (test_and_clear_bit(ST_CAPT_IRQ, &info->flags)) {
>> +			ret = m5mols_capture_info(info);
>> +			if (!ret)
>> +				v4l2_subdev_notify(sd, 0, &info->cap.total);
>> +		}
>> +	}
>> +
>> +	return m5mols_capture_error_handler(info, timeout);
>> +}
>> diff --git a/drivers/media/video/m5mols/m5mols_controls.c b/drivers/media/video/m5mols/m5mols_controls.c
>> new file mode 100644
>> index 0000000..817c16f
>> --- /dev/null
>> +++ b/drivers/media/video/m5mols/m5mols_controls.c
>> @@ -0,0 +1,299 @@
>> +/*
>> + * Controls for M-5MOLS 8M Pixel camera sensor with ISP
>> + *
>> + * Copyright (C) 2011 Samsung Electronics Co., Ltd.
>> + * Author: HeungJun Kim, riverful.kim@samsung.com
> 
> <e-mail@address>
Ok. I would remember this. Thanks let me know :) 

> 
>> + * Copyright (C) 2009 Samsung Electronics Co., Ltd.
>> + * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com
>> + *
>> + * 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/videodev2.h>
>> +#include <media/v4l2-ctrls.h>
>> +
>> +#include "m5mols.h"
>> +#include "m5mols_reg.h"
>> +
>> +static struct m5mols_scenemode m5mols_default_scenemode[] = {
>> +	[REG_SCENE_NORMAL] = {
>> +		REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
>> +		REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
>> +		REG_AF_NORMAL, REG_FD_OFF,
>> +		REG_MCC_NORMAL, REG_LIGHT_OFF, REG_FLASH_OFF,
>> +		5, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF,
>> +	},
>> +	[REG_SCENE_PORTRAIT] = {
>> +		REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
>> +		REG_CHROMA_ON, 3, REG_EDGE_ON, 4,
>> +		REG_AF_NORMAL, BIT_FD_EN | BIT_FD_DRAW_FACE_FRAME,
>> +		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
>> +		6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF,
>> +	},
>> +	[REG_SCENE_LANDSCAPE] = {
>> +		REG_AE_ALL, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
>> +		REG_CHROMA_ON, 4, REG_EDGE_ON, 6,
>> +		REG_AF_NORMAL, REG_FD_OFF,
>> +		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
>> +		6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF,
>> +	},
>> +	[REG_SCENE_SPORTS] = {
>> +		REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
>> +		REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
>> +		REG_AF_NORMAL, REG_FD_OFF,
>> +		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
>> +		6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF,
>> +	},
>> +	[REG_SCENE_PARTY_INDOOR] = {
>> +		REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
>> +		REG_CHROMA_ON, 4, REG_EDGE_ON, 5,
>> +		REG_AF_NORMAL, REG_FD_OFF,
>> +		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
>> +		6, REG_ISO_200, REG_CAP_NONE, REG_WDR_OFF,
>> +	},
>> +	[REG_SCENE_BEACH_SNOW] = {
>> +		REG_AE_CENTER, REG_AE_INDEX_10_POS, REG_AWB_AUTO, 0,
>> +		REG_CHROMA_ON, 4, REG_EDGE_ON, 5,
>> +		REG_AF_NORMAL, REG_FD_OFF,
>> +		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
>> +		6, REG_ISO_50, REG_CAP_NONE, REG_WDR_OFF,
>> +	},
>> +	[REG_SCENE_SUNSET] = {
>> +		REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_PRESET,
>> +		REG_AWB_DAYLIGHT,
>> +		REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
>> +		REG_AF_NORMAL, REG_FD_OFF,
>> +		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
>> +		6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF,
>> +	},
>> +	[REG_SCENE_DAWN_DUSK] = {
>> +		REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_PRESET,
>> +		REG_AWB_FLUORESCENT_1,
>> +		REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
>> +		REG_AF_NORMAL, REG_FD_OFF,
>> +		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
>> +		6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF,
>> +	},
>> +	[REG_SCENE_FALL] = {
>> +		REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
>> +		REG_CHROMA_ON, 5, REG_EDGE_ON, 5,
>> +		REG_AF_NORMAL, REG_FD_OFF,
>> +		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
>> +		6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF,
>> +	},
>> +	[REG_SCENE_NIGHT] = {
>> +		REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
>> +		REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
>> +		REG_AF_NORMAL, REG_FD_OFF,
>> +		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
>> +		6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF,
>> +	},
>> +	[REG_SCENE_AGAINST_LIGHT] = {
>> +		REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
>> +		REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
>> +		REG_AF_NORMAL, REG_FD_OFF,
>> +		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
>> +		6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF,
>> +	},
>> +	[REG_SCENE_FIRE] = {
>> +		REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
>> +		REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
>> +		REG_AF_NORMAL, REG_FD_OFF,
>> +		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
>> +		6, REG_ISO_50, REG_CAP_NONE, REG_WDR_OFF,
>> +	},
>> +	[REG_SCENE_TEXT] = {
>> +		REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
>> +		REG_CHROMA_ON, 3, REG_EDGE_ON, 7,
>> +		REG_AF_MACRO, REG_FD_OFF,
>> +		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
>> +		6, REG_ISO_AUTO, REG_CAP_ANTI_SHAKE, REG_WDR_ON,
>> +	},
>> +	[REG_SCENE_CANDLE] = {
>> +		REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
>> +		REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
>> +		REG_AF_NORMAL, REG_FD_OFF,
>> +		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
>> +		6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF,
>> +	},
> 
> As it seems this functionality is dynamically configurable, and much of that
> looks like something user space might want to choose independently,
It's not. The preset itself can be user space APIs, but the order and procedure
should be handled in the driver or kernel, at least in this case.

The wrong order and missing command are not completed the one of scenemode.

> shouldn't the underlying low level controls be exposed to user space as
> such?
> 
> There definitely are different approaches to this; providing higher level
> interface is restricting but on the other hand it may be better depending on
> an application.
> 
> Some of these parameters would already have a V4L2 control for them.
Actually, I have a plan to prepare RFC to expose some controls and
things related with control timing. :)

Briefly saying that, the current control framework can handle independently,
but it's possible to handle any control attached the other control(s).
So, some controls to supposed to be attached is combined with final specific control,
or IOCTL, and if any specific control is called by user, the other controls
is doing by orderly.

The benefit of such thing, is easy to handle controls in the user defined
circumstance like camera, more user-friendly. And, at the kernel or driver side,
it's supported only things which the device proivde.
The driver's code will be simple and the user is happy.

Anyway, this is not part of this story, so let's talk about this later.

> 
>> +};
>> +
>> +/**
>> + * m5mols_do_scenemode() - Change current scenemode
>> + * @mode:	Desired mode of the scenemode
>> + *
>> + * WARNING: The execution order is important. Do not change the order.
>> + */
>> +int m5mols_do_scenemode(struct m5mols_info *info, u32 mode)
>> +{
>> +	struct v4l2_subdev *sd = &info->sd;
>> +	struct m5mols_scenemode scenemode = m5mols_default_scenemode[mode];
>> +	int ret;
>> +
>> +	if (mode > REG_SCENE_CANDLE)
>> +		return -EINVAL;
>> +
>> +	ret = m5mols_lock_3a(info, false);
>> +	if (!ret)
>> +		ret = m5mols_write(sd, AE_EV_PRESET_MONITOR, mode);
>> +	if (!ret)
>> +		ret = m5mols_write(sd, AE_EV_PRESET_CAPTURE, mode);
>> +	if (!ret)
>> +		ret = m5mols_write(sd, AE_MODE, scenemode.metering);
>> +	if (!ret)
>> +		ret = m5mols_write(sd, AE_INDEX, scenemode.ev_bias);
>> +	if (!ret)
>> +		ret = m5mols_write(sd, AWB_MODE, scenemode.wb_mode);
>> +	if (!ret)
>> +		ret = m5mols_write(sd, AWB_MANUAL, scenemode.wb_preset);
>> +	if (!ret)
>> +		ret = m5mols_write(sd, MON_CHROMA_EN, scenemode.chroma_en);
>> +	if (!ret)
>> +		ret = m5mols_write(sd, MON_CHROMA_LVL, scenemode.chroma_lvl);
>> +	if (!ret)
>> +		ret = m5mols_write(sd, MON_EDGE_EN, scenemode.edge_en);
>> +	if (!ret)
>> +		ret = m5mols_write(sd, MON_EDGE_LVL, scenemode.edge_lvl);
>> +	if (!ret && is_available_af(info))
>> +		ret = m5mols_write(sd, AF_MODE, scenemode.af_range);
>> +	if (!ret && is_available_af(info))
>> +		ret = m5mols_write(sd, FD_CTL, scenemode.fd_mode);
>> +	if (!ret)
>> +		ret = m5mols_write(sd, MON_TONE_CTL, scenemode.tone);
>> +	if (!ret)
>> +		ret = m5mols_write(sd, AE_ISO, scenemode.iso);
>> +	if (!ret)
>> +		ret = m5mols_mode(info, REG_CAPTURE);
>> +	if (!ret)
>> +		ret = m5mols_write(sd, CAPP_WDR_EN, scenemode.wdr);
>> +	if (!ret)
>> +		ret = m5mols_write(sd, CAPP_MCC_MODE, scenemode.mcc);
>> +	if (!ret)
>> +		ret = m5mols_write(sd, CAPP_LIGHT_CTRL, scenemode.light);
>> +	if (!ret)
>> +		ret = m5mols_write(sd, CAPP_FLASH_CTRL, scenemode.flash);
>> +	if (!ret)
>> +		ret = m5mols_write(sd, CAPC_MODE, scenemode.capt_mode);
>> +	if (!ret)
>> +		ret = m5mols_mode(info, REG_MONITOR);
>> +
>> +	return ret;
>> +}
>> +
>> +static int m5mols_lock_ae(struct m5mols_info *info, bool lock)
>> +{
>> +	int ret = 0;
>> +
>> +	if (info->lock_ae != lock)
>> +		ret = m5mols_write(&info->sd, AE_LOCK,
>> +				lock ? REG_AE_LOCK : REG_AE_UNLOCK);
>> +	if (!ret)
>> +		info->lock_ae = lock;
>> +
>> +	return ret;
>> +}
>> +
>> +static int m5mols_lock_awb(struct m5mols_info *info, bool lock)
>> +{
>> +	int ret = 0;
>> +
>> +	if (info->lock_awb != lock)
>> +		ret = m5mols_write(&info->sd, AWB_LOCK,
>> +				lock ? REG_AWB_LOCK : REG_AWB_UNLOCK);
>> +	if (!ret)
>> +		info->lock_awb = lock;
>> +
>> +	return ret;
>> +}
>> +
>> +/* m5mols_lock_3a() - Lock 3A(Auto Exposure, Auto Whitebalance, Auto Focus) */
>> +int m5mols_lock_3a(struct m5mols_info *info, bool lock)
>> +{
>> +	int ret;
>> +
>> +	ret = m5mols_lock_ae(info, lock);
>> +	if (!ret)
>> +		ret = m5mols_lock_awb(info, lock);
>> +	/* Don't need to handle unlocking AF */
>> +	if (!ret && is_available_af(info) && lock)
>> +		ret = m5mols_write(&info->sd, AF_EXECUTE, REG_AF_STOP);
>> +
>> +	return ret;
>> +}
>> +
>> +/* m5mols_set_ctrl() - The main s_ctrl function called by m5mols_set_ctrl() */
>> +int m5mols_set_ctrl(struct v4l2_ctrl *ctrl)
>> +{
>> +	struct v4l2_subdev *sd = to_sd(ctrl);
>> +	struct m5mols_info *info = to_m5mols(sd);
>> +	int ret;
>> +
>> +	switch (ctrl->id) {
>> +	case V4L2_CID_ZOOM_ABSOLUTE:
>> +		return m5mols_write(sd, MON_ZOOM, ctrl->val);
>> +
>> +	case V4L2_CID_EXPOSURE_AUTO:
>> +		ret = m5mols_lock_ae(info,
>> +			ctrl->val == V4L2_EXPOSURE_AUTO ? false : true);
>> +		if (!ret && ctrl->val == V4L2_EXPOSURE_AUTO)
>> +			ret = m5mols_write(sd, AE_MODE, REG_AE_ALL);
>> +		if (!ret && ctrl->val == V4L2_EXPOSURE_MANUAL) {
>> +			int val = info->exposure->val;
>> +			ret = m5mols_write(sd, AE_MODE, REG_AE_OFF);
>> +			if (!ret)
>> +				ret = m5mols_write(sd, AE_MAN_GAIN_MON, val);
>> +			if (!ret)
>> +				ret = m5mols_write(sd, AE_MAN_GAIN_CAP, val);
>> +		}
>> +		return ret;
>> +
>> +	case V4L2_CID_AUTO_WHITE_BALANCE:
>> +		ret = m5mols_lock_awb(info, ctrl->val ? false : true);
>> +		if (!ret)
>> +			ret = m5mols_write(sd, AWB_MODE, ctrl->val ?
>> +				REG_AWB_AUTO : REG_AWB_PRESET);
>> +		return ret;
>> +
>> +	case V4L2_CID_SATURATION:
>> +		ret = m5mols_write(sd, MON_CHROMA_LVL, ctrl->val);
>> +		if (!ret)
>> +			ret = m5mols_write(sd, MON_CHROMA_EN, REG_CHROMA_ON);
>> +		return ret;
>> +
>> +	case V4L2_CID_COLORFX:
>> +		/*
>> +		 * This control uses two kinds of registers: normal & color.
>> +		 * The normal effect belongs to category 1, while the color
>> +		 * one belongs to category 2.
>> +		 *
>> +		 * The normal effect uses one register: CAT1_EFFECT.
>> +		 * The color effect uses three registers:
>> +		 * CAT2_COLOR_EFFECT, CAT2_CFIXR, CAT2_CFIXB.
>> +		 */
>> +		ret = m5mols_write(sd, PARM_EFFECT,
>> +			ctrl->val == V4L2_COLORFX_NEGATIVE ? REG_EFFECT_NEGA :
>> +			ctrl->val == V4L2_COLORFX_EMBOSS ? REG_EFFECT_EMBOSS :
>> +			REG_EFFECT_OFF);
>> +		if (!ret)
>> +			ret = m5mols_write(sd, MON_EFFECT,
>> +				ctrl->val == V4L2_COLORFX_SEPIA ?
>> +				REG_COLOR_EFFECT_ON : REG_COLOR_EFFECT_OFF);
>> +		if (!ret)
>> +			ret = m5mols_write(sd, MON_CFIXR,
>> +				ctrl->val == V4L2_COLORFX_SEPIA ?
>> +				REG_CFIXR_SEPIA : 0);
>> +		if (!ret)
>> +			ret = m5mols_write(sd, MON_CFIXB,
>> +				ctrl->val == V4L2_COLORFX_SEPIA ?
>> +				REG_CFIXB_SEPIA : 0);
>> +		return ret;
>> +	}
>> +
>> +	return -EINVAL;
>> +}
>> diff --git a/drivers/media/video/m5mols/m5mols_core.c b/drivers/media/video/m5mols/m5mols_core.c
>> new file mode 100644
>> index 0000000..76eac26
>> --- /dev/null
>> +++ b/drivers/media/video/m5mols/m5mols_core.c
>> @@ -0,0 +1,1004 @@
>> +/*
>> + * Driver for M-5MOLS 8M Pixel camera sensor with ISP
>> + *
>> + * Copyright (C) 2011 Samsung Electronics Co., Ltd.
>> + * Author: HeungJun Kim, riverful.kim@samsung.com
>> + *
>> + * Copyright (C) 2009 Samsung Electronics Co., Ltd.
>> + * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com
>> + *
>> + * 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/slab.h>
>> +#include <linux/irq.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/delay.h>
>> +#include <linux/version.h>
>> +#include <linux/gpio.h>
>> +#include <linux/regulator/consumer.h>
>> +#include <linux/videodev2.h>
>> +#include <media/v4l2-ctrls.h>
>> +#include <media/v4l2-device.h>
>> +#include <media/v4l2-subdev.h>
>> +#include <media/m5mols.h>
>> +
>> +#include "m5mols.h"
>> +#include "m5mols_reg.h"
>> +
>> +int m5mols_debug;
>> +module_param(m5mols_debug, int, 0644);
>> +
>> +#define MODULE_NAME		"M5MOLS"
>> +#define M5MOLS_I2C_CHECK_RETRY	500
>> +
>> +/* The regulator consumer names for external voltage regulators */
>> +static struct regulator_bulk_data supplies[] = {
>> +	{
>> +		.supply = "core",	/* ARM core power, 1.2V */
>> +	}, {
>> +		.supply	= "dig_18",	/* digital power 1, 1.8V */
>> +	}, {
>> +		.supply	= "d_sensor",	/* sensor power 1, 1.8V */
>> +	}, {
>> +		.supply	= "dig_28",	/* digital power 2, 2.8V */
>> +	}, {
>> +		.supply	= "a_sensor",	/* analog power */
>> +	}, {
>> +		.supply	= "dig_12",	/* digital power 3, 1.2V */
>> +	},
>> +};
> 
> This looks like something that belongs to board code, or perhaps in the near
> future, to the device tree. The power supplies that are required by a device
> is highly board dependent.
If the regulator name is not common all M-5MOLS, You're right.
But the regulator name of M-5MOLS is fixed.

The benefit fixed in the driver helps that the developer can attach the driver,
if he/she knows the names of regulators.

Commonly, you're right, but in this case(documented accurately with M-5MOLS
datasheet), it's better.
 
> 
>> +static struct v4l2_mbus_framefmt m5mols_default_ffmt[M5MOLS_RESTYPE_MAX] = {
>> +	[M5MOLS_RESTYPE_MONITOR] = {
>> +		.width		= 1920,
>> +		.height		= 1080,
>> +		.code		= V4L2_MBUS_FMT_VYUY8_2X8,
>> +		.field		= V4L2_FIELD_NONE,
>> +		.colorspace	= V4L2_COLORSPACE_JPEG,
>> +	},
>> +	[M5MOLS_RESTYPE_CAPTURE] = {
>> +		.width		= 1920,
>> +		.height		= 1080,
>> +		.code		= V4L2_MBUS_FMT_JPEG_1X8,
>> +		.field		= V4L2_FIELD_NONE,
>> +		.colorspace	= V4L2_COLORSPACE_JPEG,
>> +	},
>> +};
>> +#define SIZE_DEFAULT_FFMT	ARRAY_SIZE(m5mols_default_ffmt)
>> +
>> +static const struct m5mols_resolution m5mols_reg_res[] = {
>> +	{ 0x01, M5MOLS_RESTYPE_MONITOR, 128, 96 },	/* SUB-QCIF */
>> +	{ 0x03, M5MOLS_RESTYPE_MONITOR, 160, 120 },	/* QQVGA */
>> +	{ 0x05, M5MOLS_RESTYPE_MONITOR, 176, 144 },	/* QCIF */
>> +	{ 0x06, M5MOLS_RESTYPE_MONITOR, 176, 176 },
>> +	{ 0x08, M5MOLS_RESTYPE_MONITOR, 240, 320 },	/* QVGA */
>> +	{ 0x09, M5MOLS_RESTYPE_MONITOR, 320, 240 },	/* QVGA */
>> +	{ 0x0c, M5MOLS_RESTYPE_MONITOR, 240, 400 },	/* WQVGA */
>> +	{ 0x0d, M5MOLS_RESTYPE_MONITOR, 400, 240 },	/* WQVGA */
>> +	{ 0x0e, M5MOLS_RESTYPE_MONITOR, 352, 288 },	/* CIF */
>> +	{ 0x13, M5MOLS_RESTYPE_MONITOR, 480, 360 },
>> +	{ 0x15, M5MOLS_RESTYPE_MONITOR, 640, 360 },	/* qHD */
>> +	{ 0x17, M5MOLS_RESTYPE_MONITOR, 640, 480 },	/* VGA */
>> +	{ 0x18, M5MOLS_RESTYPE_MONITOR, 720, 480 },
>> +	{ 0x1a, M5MOLS_RESTYPE_MONITOR, 800, 480 },	/* WVGA */
>> +	{ 0x1f, M5MOLS_RESTYPE_MONITOR, 800, 600 },	/* SVGA */
>> +	{ 0x21, M5MOLS_RESTYPE_MONITOR, 1280, 720 },	/* HD */
>> +	{ 0x25, M5MOLS_RESTYPE_MONITOR, 1920, 1080 },	/* 1080p */
>> +	{ 0x29, M5MOLS_RESTYPE_MONITOR, 3264, 2448 },	/* 2.63fps 8M */
>> +	{ 0x39, M5MOLS_RESTYPE_MONITOR, 800, 602 },	/* AHS_MON debug */
>> +
>> +	{ 0x02, M5MOLS_RESTYPE_CAPTURE, 320, 240 },	/* QVGA */
>> +	{ 0x04, M5MOLS_RESTYPE_CAPTURE, 400, 240 },	/* WQVGA */
>> +	{ 0x07, M5MOLS_RESTYPE_CAPTURE, 480, 360 },
>> +	{ 0x08, M5MOLS_RESTYPE_CAPTURE, 640, 360 },	/* qHD */
>> +	{ 0x09, M5MOLS_RESTYPE_CAPTURE, 640, 480 },	/* VGA */
>> +	{ 0x0a, M5MOLS_RESTYPE_CAPTURE, 800, 480 },	/* WVGA */
>> +	{ 0x10, M5MOLS_RESTYPE_CAPTURE, 1280, 720 },	/* HD */
>> +	{ 0x14, M5MOLS_RESTYPE_CAPTURE, 1280, 960 },	/* 1M */
>> +	{ 0x17, M5MOLS_RESTYPE_CAPTURE, 1600, 1200 },	/* 2M */
>> +	{ 0x19, M5MOLS_RESTYPE_CAPTURE, 1920, 1080 },	/* Full-HD */
>> +	{ 0x1a, M5MOLS_RESTYPE_CAPTURE, 2048, 1152 },	/* 3Mega */
>> +	{ 0x1b, M5MOLS_RESTYPE_CAPTURE, 2048, 1536 },
>> +	{ 0x1c, M5MOLS_RESTYPE_CAPTURE, 2560, 1440 },	/* 4Mega */
>> +	{ 0x1d, M5MOLS_RESTYPE_CAPTURE, 2560, 1536 },
>> +	{ 0x1f, M5MOLS_RESTYPE_CAPTURE, 2560, 1920 },	/* 5Mega */
>> +	{ 0x21, M5MOLS_RESTYPE_CAPTURE, 3264, 1836 },	/* 6Mega */
>> +	{ 0x22, M5MOLS_RESTYPE_CAPTURE, 3264, 1960 },
>> +	{ 0x25, M5MOLS_RESTYPE_CAPTURE, 3264, 2448 },	/* 8Mega */
>> +};
>> +
>> +/**
>> + * m5mols_swap_byte - an byte array to integer conversion function
>> + * @size: size in bytes of I2C packet defined in the M-5MOLS datasheet
>> + *
>> + * Convert I2C data byte array with performing any required byte
>> + * reordering to assure proper values for each data type, regardless
>> + * of the architecture endianness.
>> + */
>> +static u32 m5mols_swap_byte(u8 *data, u8 length)
>> +{
>> +	if (length == 1)
>> +		return *data;
>> +	else if (length == 2)
>> +		return be16_to_cpu(*((u16 *)data));
>> +	else
>> +		return be32_to_cpu(*((u32 *)data));
>> +}
>> +
>> +/**
>> + * m5mols_read -  I2C read function
>> + * @reg: combination of size, category and command for the I2C packet
>> + * @val: read value
>> + */
>> +int m5mols_read(struct v4l2_subdev *sd, u32 reg, u32 *val)
>> +{
>> +	struct i2c_client *client = v4l2_get_subdevdata(sd);
>> +	u8 rbuf[M5MOLS_I2C_MAX_SIZE + 1];
>> +	u8 size = I2C_SIZE(reg);
>> +	u8 category = I2C_CATEGORY(reg);
>> +	u8 cmd = I2C_COMMAND(reg);
>> +	struct i2c_msg msg[2];
>> +	u8 wbuf[5];
>> +	int ret;
>> +
>> +	if (!client->adapter)
>> +		return -ENODEV;
>> +
>> +	if (size != 1 && size != 2 && size != 4) {
>> +		v4l2_err(sd, "Wrong data size\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	msg[0].addr = client->addr;
>> +	msg[0].flags = 0;
>> +	msg[0].len = 5;
>> +	msg[0].buf = wbuf;
>> +	wbuf[0] = 5;
>> +	wbuf[1] = M5MOLS_BYTE_READ;
>> +	wbuf[2] = category;
>> +	wbuf[3] = cmd;
>> +	wbuf[4] = size;
>> +
>> +	msg[1].addr = client->addr;
>> +	msg[1].flags = I2C_M_RD;
>> +	msg[1].len = size + 1;
>> +	msg[1].buf = rbuf;
>> +
>> +	/* minimum stabilization time */
>> +	usleep_range(200, 200);
>> +
>> +	ret = i2c_transfer(client->adapter, msg, 2);
>> +	if (ret < 0) {
>> +		v4l2_err(sd, "read failed: size:%d cat:%02x cmd:%02x. %d\n",
>> +			 size, category, cmd, ret);
>> +		return ret;
>> +	}
>> +
>> +	*val = m5mols_swap_byte(&rbuf[1], size);
>> +
>> +	return 0;
>> +}
>> +
>> +/**
>> + * m5mols_write - I2C command write function
>> + * @reg: combination of size, category and command for the I2C packet
>> + * @val: value to write
>> + */
>> +int m5mols_write(struct v4l2_subdev *sd, u32 reg, u32 val)
>> +{
>> +	struct i2c_client *client = v4l2_get_subdevdata(sd);
>> +	u8 wbuf[M5MOLS_I2C_MAX_SIZE + 4];
>> +	u8 category = I2C_CATEGORY(reg);
>> +	u8 cmd = I2C_COMMAND(reg);
>> +	u8 size	= I2C_SIZE(reg);
>> +	u32 *buf = (u32 *)&wbuf[4];
>> +	struct i2c_msg msg[1];
>> +	int ret;
>> +
>> +	if (!client->adapter)
>> +		return -ENODEV;
>> +
>> +	if (size != 1 && size != 2 && size != 4) {
> 
> You could define sizes for the types, e.g.
> 
> #define I2C_SIZE_U8	1
I already try to do like this, using width as definition.
But it makes many lines over 80 characters. Moreover, using just number
is more simple in this case.

Ditto - over 80 characters.

> 
>> +		v4l2_err(sd, "Wrong data size\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	msg->addr = client->addr;
>> +	msg->flags = 0;
>> +	msg->len = (u16)size + 4;
>> +	msg->buf = wbuf;
>> +	wbuf[0] = size + 4;
>> +	wbuf[1] = M5MOLS_BYTE_WRITE;
>> +	wbuf[2] = category;
>> +	wbuf[3] = cmd;
>> +
>> +	*buf = m5mols_swap_byte((u8 *)&val, size);
>> +
>> +	usleep_range(200, 200);
> 
> Why to sleep always? Does the sensor require a delay between each I2C
> access?
It's experimental values. The M-5MOLS I2C communication is a litte sensitive,
and I expect that this sensor is integrated with another ARM-core and internal
Firmware, and ARM-core's performance is not good. So, the dealy should be needed.

> 
>> +	ret = i2c_transfer(client->adapter, msg, 1);
>> +	if (ret < 0) {
>> +		v4l2_err(sd, "write failed: size:%d cat:%02x cmd:%02x. %d\n",
>> +			size, category, cmd, ret);
>> +		return ret;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +int m5mols_busy(struct v4l2_subdev *sd, u8 category, u8 cmd, u32 mask)
>> +{
>> +	u32 busy, i;
>> +	int ret;
>> +
>> +	for (i = 0; i < M5MOLS_I2C_CHECK_RETRY; i++) {
>> +		ret = m5mols_read(sd, I2C_REG(category, cmd, 1), &busy);
>> +		if (ret < 0)
>> +			return ret;
>> +		if ((busy & mask) == mask)
>> +			return 0;
>> +	}
>> +	return -EBUSY;
>> +}
>> +
>> +/**
>> + * m5mols_enable_interrupt - Clear interrupt pending bits and unmask interrupts
>> + *
>> + * Before writing desired interrupt value the INT_FACTOR register should
>> + * be read to clear pending interrupts.
>> + */
>> +int m5mols_enable_interrupt(struct v4l2_subdev *sd, u32 reg)
>> +{
>> +	struct m5mols_info *info = to_m5mols(sd);
>> +	u32 mask = is_available_af(info) ? REG_INT_AF : 0;
>> +	u32 dummy;
>> +	int ret;
>> +
>> +	ret = m5mols_read(sd, SYSTEM_INT_FACTOR, &dummy);
>> +	if (!ret)
>> +		ret = m5mols_write(sd, SYSTEM_INT_ENABLE, reg & ~mask);
>> +	return ret;
>> +}
>> +
>> +/**
>> + * m5mols_reg_mode - Write the mode and check busy status
>> + *
>> + * It always accompanies a little delay changing the M-5MOLS mode, so it is
>> + * needed checking current busy status to guarantee right mode.
>> + */
>> +static int m5mols_reg_mode(struct v4l2_subdev *sd, u32 mode)
>> +{
>> +	int ret = m5mols_write(sd, SYSTEM_SYSMODE, mode);
>> +
>> +	return ret ? ret : m5mols_busy(sd, CAT_SYSTEM, CAT0_SYSMODE, mode);
>> +}
>> +
>> +/**
>> + * m5mols_mode - manage the M-5MOLS's mode
>> + * @mode: the required operation mode
>> + *
>> + * The commands of M-5MOLS are grouped into specific modes. Each functionality
>> + * can be guaranteed only when the sensor is operating in mode which which
>> + * a command belongs to.
>> + */
>> +int m5mols_mode(struct m5mols_info *info, u32 mode)
>> +{
>> +	struct v4l2_subdev *sd = &info->sd;
>> +	int ret = -EINVAL;
>> +	u32 reg;
>> +
>> +	if (mode < REG_PARAMETER && mode > REG_CAPTURE)
>> +		return ret;
>> +
>> +	ret = m5mols_read(sd, SYSTEM_SYSMODE, &reg);
>> +	if ((!ret && reg == mode) || ret)
>> +		return ret;
>> +
>> +	switch (reg) {
>> +	case REG_PARAMETER:
>> +		ret = m5mols_reg_mode(sd, REG_MONITOR);
>> +		if (!ret && mode == REG_MONITOR)
>> +			break;
>> +		if (!ret)
>> +			ret = m5mols_reg_mode(sd, REG_CAPTURE);
>> +		break;
>> +
>> +	case REG_MONITOR:
>> +		if (mode == REG_PARAMETER) {
>> +			ret = m5mols_reg_mode(sd, REG_PARAMETER);
>> +			break;
>> +		}
>> +
>> +		ret = m5mols_reg_mode(sd, REG_CAPTURE);
>> +		break;
>> +
>> +	case REG_CAPTURE:
>> +		ret = m5mols_reg_mode(sd, REG_MONITOR);
>> +		if (!ret && mode == REG_MONITOR)
>> +			break;
>> +		if (!ret)
>> +			ret = m5mols_reg_mode(sd, REG_PARAMETER);
>> +		break;
>> +
>> +	default:
>> +		v4l2_warn(sd, "Wrong mode: %d\n", mode);
>> +	}
>> +
>> +	if (!ret)
>> +		info->mode = mode;
>> +
>> +	return ret;
>> +}
>> +
>> +/**
>> + * m5mols_get_version - retrieve full revisions information of M-5MOLS
>> + *
>> + * The version information includes revisions of hardware and firmware,
>> + * AutoFocus alghorithm version and the version string.
>> + */
>> +static int m5mols_get_version(struct v4l2_subdev *sd)
>> +{
>> +	struct m5mols_info *info = to_m5mols(sd);
>> +	union {
>> +		struct m5mols_version ver;
>> +		u8 bytes[VERSION_SIZE];
> 
> You could even use u8 bytes[0] if you really need this union.
> 
> ((char *)&ver)[cmd], for example.
> 
>> +	} version;
>> +	u32 *value;
>> +	u8 cmd = CAT0_VER_CUSTOMER;
>> +	int ret;
>> +
>> +	do {
>> +		value = (u32 *)&version.bytes[cmd];
>> +		ret = m5mols_read(sd, SYSTEM_CMD(cmd), value);
>> +		if (ret)
>> +			return ret;
>> +	} while (cmd++ != CAT0_VER_AWB);
>> +
>> +	do {
>> +		value = (u32 *)&version.bytes[cmd];
>> +		ret = m5mols_read(sd, SYSTEM_VER_STRING, value);
>> +		if (ret)
>> +			return ret;
>> +		if (cmd >= VERSION_SIZE - 1)
>> +			return -EINVAL;
>> +	} while (version.bytes[cmd++]);
> 
> 
> Please move cmd++ outside the condition.
Ok, I'll keep that.

> 
> I think you should have a different function to read and write different
> types, e.g. m5mols_read_u8 and m5mols_read_u16.
As I said, this is good option and it will be changed like your recommendation.
I very consider this usage at the next time.

> 
> You have the access width encoded in the register value. That would still be
> checked by the function.
> 
> You also could subtract CAT0_VER_CUSTOMER from cmd when using it as index
> to bytes[] above, as you now rely that it is actually zero.
> 
>> +
>> +	value = (u32 *)&version.bytes[cmd];
>> +	ret = m5mols_read(sd, AF_VERSION, value);
> 
> I think it'd be cleaner to refer to (u32 *)&version.bytes[cmd] directly
> instead of using value.
> 
> Can you be sure that cmd points to the AF version here?
> 
>> +	if (ret)
>> +		return ret;
>> +
>> +	/* store version information swapped for being readable */
>> +	info->ver	= version.ver;
> 
> You could use info->ver straight away and drop version.ver.
Ok, but this is also making me hard. It's been always stucked by over 80 character.
And, I choose this the best.
But, another option is appeared, I'll consider this again.

> 
>> +	info->ver.fw	= be16_to_cpu(info->ver.fw);
>> +	info->ver.hw	= be16_to_cpu(info->ver.hw);
>> +	info->ver.param	= be16_to_cpu(info->ver.param);
>> +	info->ver.awb	= be16_to_cpu(info->ver.awb);
>> +
>> +	v4l2_info(sd, "Manufacturer\t[%s]\n",
>> +			is_manufacturer(info, REG_SAMSUNG_ELECTRO) ?
>> +			"Samsung Electro-Machanics" :
>> +			is_manufacturer(info, REG_SAMSUNG_OPTICS) ?
>> +			"Samsung Fiber-Optics" :
>> +			is_manufacturer(info, REG_SAMSUNG_TECHWIN) ?
>> +			"Samsung Techwin" : "None");
>> +	v4l2_info(sd, "Customer/Project\t[0x%02x/0x%02x]\n",
>> +			info->ver.customer, info->ver.project);
>> +
>> +	if (!is_available_af(info))
>> +		v4l2_info(sd, "No support Auto Focus on this firmware\n");
>> +
>> +	return ret;
>> +}
>> +
>> +/**
>> + * __find_restype - Lookup M-5MOLS resolution type according to pixel code
>> + * @code: pixel code
>> + */
>> +static enum m5mols_restype __find_restype(enum v4l2_mbus_pixelcode code)
>> +{
>> +	enum m5mols_restype type = M5MOLS_RESTYPE_MONITOR;
>> +
>> +	do {
>> +		if (code == m5mols_default_ffmt[type].code)
>> +			return type;
> 
> type++ here, and ++ off of the condition below.
Ok, I'll adapt this for the next time.

> 
>> +	} while (type++ != SIZE_DEFAULT_FFMT);
>> +
>> +	return 0;
>> +}
>> +
>> +/**
>> + * __find_resolution - Lookup preset and type of M-5MOLS's resolution
>> + * @mf: pixel format to find/negotiate the resolution preset for
>> + * @type: M-5MOLS resolution type
>> + * @resolution:	M-5MOLS resolution preset register value
>> + *
>> + * Find nearest resolution matching resolution preset and adjust mf
>> + * to supported values.
>> + */
>> +static int __find_resolution(struct v4l2_subdev *sd,
>> +			     struct v4l2_mbus_framefmt *mf,
>> +			     enum m5mols_restype *type,
>> +			     u32 *resolution)
>> +{
>> +	const struct m5mols_resolution *fsize = &m5mols_reg_res[0];
>> +	const struct m5mols_resolution *match = NULL;
>> +	enum m5mols_restype stype = __find_restype(mf->code);
>> +	int i = ARRAY_SIZE(m5mols_reg_res);
>> +	unsigned int min_err = ~0;
>> +
>> +	while (i--) {
>> +		int err;
>> +		if (stype == fsize->type) {
>> +			err = abs(fsize->width - mf->width)
>> +				+ abs(fsize->height - mf->height);
>> +
>> +			if (err < min_err) {
>> +				min_err = err;
>> +				match = fsize;
>> +			}
>> +		}
>> +		fsize++;
>> +	}
>> +	if (match) {
>> +		mf->width  = match->width;
>> +		mf->height = match->height;
>> +		*resolution = match->reg;
>> +		*type = stype;
>> +		return 0;
>> +	}
>> +
>> +	return -EINVAL;
>> +}
>> +
>> +static struct v4l2_mbus_framefmt *__find_format(struct m5mols_info *info,
>> +				struct v4l2_subdev_fh *fh,
>> +				enum v4l2_subdev_format_whence which,
>> +				enum m5mols_restype type)
>> +{
>> +	if (which == V4L2_SUBDEV_FORMAT_TRY)
>> +		return fh ? v4l2_subdev_get_try_format(fh, 0) : NULL;
>> +
>> +	return &info->ffmt[type];
>> +}
>> +
>> +static int m5mols_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
>> +			  struct v4l2_subdev_format *fmt)
>> +{
>> +	struct m5mols_info *info = to_m5mols(sd);
>> +	struct v4l2_mbus_framefmt *format;
>> +
>> +	if (fmt->pad != 0)
>> +		return -EINVAL;
>> +
>> +	format = __find_format(info, fh, fmt->which, info->res_type);
>> +	if (!format)
>> +		return -EINVAL;
>> +
>> +	fmt->format = *format;
>> +	return 0;
>> +}
>> +
>> +static int m5mols_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
>> +			  struct v4l2_subdev_format *fmt)
>> +{
>> +	struct m5mols_info *info = to_m5mols(sd);
>> +	struct v4l2_mbus_framefmt *format = &fmt->format;
>> +	struct v4l2_mbus_framefmt *sfmt;
>> +	enum m5mols_restype type;
>> +	u32 resolution = 0;
>> +	int ret;
>> +
>> +	if (fmt->pad != 0)
>> +		return -EINVAL;
>> +
>> +	ret = __find_resolution(sd, format, &type, &resolution);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	sfmt = __find_format(info, fh, fmt->which, type);
>> +	if (!sfmt)
>> +		return 0;
>> +
>> +	*sfmt		= m5mols_default_ffmt[type];
>> +	sfmt->width	= format->width;
>> +	sfmt->height	= format->height;
>> +
>> +	if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
>> +		info->resolution = resolution;
>> +		info->code = format->code;
>> +		info->res_type = type;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static int m5mols_enum_mbus_code(struct v4l2_subdev *sd,
>> +				 struct v4l2_subdev_fh *fh,
>> +				 struct v4l2_subdev_mbus_code_enum *code)
>> +{
>> +	ifv (!code || code->index >= SIZE_DEFAULT_FFMT)
> 
> Is it possible that code == NULL?
It depends on the driver using this subdev driver. 
The test have done on the s5p-fimc driver for now, but I don't have
any trouble cause of code == NULL.

But, probably the code can get through by userspace, so it should be
needed I guess.

> 
>> +		return -EINVAL;
>> +
>> +	code->code = m5mols_default_ffmt[code->index].code;
>> +
>> +	return 0;
>> +}
>> +
>> +static struct v4l2_subdev_pad_ops m5mols_pad_ops = {
>> +	.enum_mbus_code	= m5mols_enum_mbus_code,
>> +	.get_fmt	= m5mols_get_fmt,
>> +	.set_fmt	= m5mols_set_fmt,
>> +};
>> +
>> +/**
>> + * m5mols_sync_controls - Apply default scene mode and the current controls
>> + *
>> + * This is used only streaming for syncing between v4l2_ctrl framework and
>> + * m5mols's controls. First, do the scenemode to the sensor, then call
>> + * v4l2_ctrl_handler_setup. It can be same between some commands and
>> + * the scenemode's in the default v4l2_ctrls. But, such commands of control
>> + * should be prior to the scenemode's one.
>> + */
>> +int m5mols_sync_controls(struct m5mols_info *info)
>> +{
>> +	int ret = -EINVAL;
>> +
>> +	if (!is_ctrl_synced(info)) {
>> +		ret = m5mols_do_scenemode(info, REG_SCENE_NORMAL);
>> +		if (ret)
>> +			return ret;
>> +
>> +		v4l2_ctrl_handler_setup(&info->handle);
>> +		info->ctrl_sync = true;
>> +	}
>> +
>> +	return ret;
>> +}
>> +
>> +/**
>> + * m5mols_start_monitor - Start the monitor mode
>> + *
>> + * Before applying the controls setup the resolution and frame rate
>> + * in PARAMETER mode, and then switch over to MONITOR mode.
>> + */
>> +static int m5mols_start_monitor(struct m5mols_info *info)
>> +{
>> +	struct v4l2_subdev *sd = &info->sd;
>> +	int ret;
>> +
>> +	ret = m5mols_mode(info, REG_PARAMETER);
>> +	if (!ret)
>> +		ret = m5mols_write(sd, PARM_MON_SIZE, info->resolution);
>> +	if (!ret)
>> +		ret = m5mols_write(sd, PARM_MON_FPS, REG_FPS_30);
>> +	if (!ret)
>> +		ret = m5mols_mode(info, REG_MONITOR);
>> +	if (!ret)
>> +		ret = m5mols_sync_controls(info);
>> +
>> +	return ret;
>> +}
>> +
>> +static int m5mols_s_stream(struct v4l2_subdev *sd, int enable)
>> +{
>> +	struct m5mols_info *info = to_m5mols(sd);
>> +
>> +	if (enable) {
>> +		int ret = -EINVAL;
>> +
>> +		if (is_code(info->code, M5MOLS_RESTYPE_MONITOR))
>> +			ret = m5mols_start_monitor(info);
>> +		if (is_code(info->code, M5MOLS_RESTYPE_CAPTURE))
>> +			ret = m5mols_start_capture(info);
>> +
>> +		return ret;
>> +	}
>> +
>> +	return m5mols_mode(info, REG_PARAMETER);
>> +}
>> +
>> +static const struct v4l2_subdev_video_ops m5mols_video_ops = {
>> +	.s_stream	= m5mols_s_stream,
>> +};
>> +
>> +static int m5mols_s_ctrl(struct v4l2_ctrl *ctrl)
>> +{
>> +	struct v4l2_subdev *sd = to_sd(ctrl);
>> +	struct m5mols_info *info = to_m5mols(sd);
>> +	int ret;
>> +
>> +	info->mode_save = info->mode;
>> +
>> +	ret = m5mols_mode(info, REG_PARAMETER);
>> +	if (!ret)
>> +		ret = m5mols_set_ctrl(ctrl);
>> +	if (!ret)
>> +		ret = m5mols_mode(info, info->mode_save);
>> +
>> +	return ret;
>> +}
>> +
>> +static const struct v4l2_ctrl_ops m5mols_ctrl_ops = {
>> +	.s_ctrl	= m5mols_s_ctrl,
>> +};
>> +
>> +static int m5mols_sensor_power(struct m5mols_info *info, bool enable)
>> +{
>> +	struct v4l2_subdev *sd = &info->sd;
>> +	struct i2c_client *client = v4l2_get_subdevdata(sd);
>> +	const struct m5mols_platform_data *pdata = info->pdata;
>> +	int ret;
>> +
>> +	if (enable) {
>> +		if (is_powered(info))
>> +			return 0;
>> +
>> +		if (info->set_power) {
>> +			ret = info->set_power(&client->dev, 1);
>> +			if (ret)
>> +				return ret;
>> +		}
>> +
>> +		ret = regulator_bulk_enable(ARRAY_SIZE(supplies), supplies);
>> +		if (ret) {
>> +			info->set_power(&client->dev, 0);
>> +			return ret;
>> +		}
>> +
>> +		gpio_set_value(pdata->gpio_reset, !pdata->reset_polarity);
>> +		usleep_range(1000, 1000);
>> +		info->power = true;
>> +
>> +		return ret;
>> +	}
>> +
>> +	if (!is_powered(info))
>> +		return 0;
>> +
>> +	ret = regulator_bulk_disable(ARRAY_SIZE(supplies), supplies);
>> +	if (ret)
>> +		return ret;
>> +
>> +	if (info->set_power)
>> +		info->set_power(&client->dev, 0);
>> +
>> +	gpio_set_value(pdata->gpio_reset, pdata->reset_polarity);
>> +	usleep_range(1000, 1000);
>> +	info->power = false;
>> +
>> +	return ret;
>> +}
>> +
>> +/* m5mols_update_fw - optional firmware update routine */
>> +int __attribute__ ((weak)) m5mols_update_fw(struct v4l2_subdev *sd,
>> +		int (*set_power)(struct m5mols_info *, bool))
>> +{
>> +	return 0;
>> +}
>> +
>> +/**
>> + * m5mols_sensor_armboot - Booting M-5MOLS internal ARM core.
>> + *
>> + * Booting internal ARM core makes the M-5MOLS is ready for getting commands
>> + * with I2C. It's the first thing to be done after it powered up. It must wait
>> + * at least 520ms recommended by M-5MOLS datasheet, after executing arm booting.
>> + */
>> +static int m5mols_sensor_armboot(struct v4l2_subdev *sd)
>> +{
>> +	int ret;
>> +
>> +	ret = m5mols_write(sd, FLASH_CAM_START, REG_START_ARM_BOOT);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	msleep(520);
>> +
>> +	ret = m5mols_get_version(sd);
>> +	if (!ret)
>> +		ret = m5mols_update_fw(sd, m5mols_sensor_power);
>> +	if (ret)
>> +		return ret;
>> +
>> +	v4l2_dbg(1, m5mols_debug, sd, "Success ARM Booting\n");
>> +
>> +	ret = m5mols_write(sd, PARM_INTERFACE, REG_INTERFACE_MIPI);
>> +	if (!ret)
>> +		ret = m5mols_enable_interrupt(sd, REG_INT_AF);
>> +
>> +	return ret;
>> +}
>> +
>> +static int m5mols_init_controls(struct m5mols_info *info)
>> +{
>> +	struct v4l2_subdev *sd = &info->sd;
>> +	u16 max_exposure;
>> +	u16 step_zoom;
>> +	int ret;
>> +
>> +	/* Determine value's range & step of controls for various FW version */
>> +	ret = m5mols_read(sd, AE_MAX_GAIN_MON, (u32 *)&max_exposure);
>> +	if (!ret)
>> +		step_zoom = is_manufacturer(info, REG_SAMSUNG_OPTICS) ? 31 : 1;
>> +	if (ret)
>> +		return ret;
>> +
>> +	v4l2_ctrl_handler_init(&info->handle, 6);
>> +	info->autowb = v4l2_ctrl_new_std(&info->handle,
>> +			&m5mols_ctrl_ops, V4L2_CID_AUTO_WHITE_BALANCE,
>> +			0, 1, 1, 0);
>> +	info->saturation = v4l2_ctrl_new_std(&info->handle,
>> +			&m5mols_ctrl_ops, V4L2_CID_SATURATION,
>> +			1, 5, 1, 3);
>> +	info->zoom = v4l2_ctrl_new_std(&info->handle,
>> +			&m5mols_ctrl_ops, V4L2_CID_ZOOM_ABSOLUTE,
>> +			1, 70, step_zoom, 1);
>> +	info->exposure = v4l2_ctrl_new_std(&info->handle,
>> +			&m5mols_ctrl_ops, V4L2_CID_EXPOSURE,
>> +			0, max_exposure, 1, (int)max_exposure/2);
>> +	info->colorfx = v4l2_ctrl_new_std_menu(&info->handle,
>> +			&m5mols_ctrl_ops, V4L2_CID_COLORFX,
>> +			4, (1 << V4L2_COLORFX_BW), V4L2_COLORFX_NONE);
>> +	info->autoexposure = v4l2_ctrl_new_std_menu(&info->handle,
>> +			&m5mols_ctrl_ops, V4L2_CID_EXPOSURE_AUTO,
>> +			1, 0, V4L2_EXPOSURE_MANUAL);
>> +
>> +	sd->ctrl_handler = &info->handle;
>> +	if (info->handle.error) {
>> +		v4l2_err(sd, "Failed to initialize controls: %d\n", ret);
>> +		v4l2_ctrl_handler_free(&info->handle);
>> +		return info->handle.error;
>> +	}
>> +
>> +	v4l2_ctrl_cluster(2, &info->autoexposure);
>> +
>> +	return 0;
>> +}
>> +
>> +/**
>> + * m5mols_s_power - Main sensor power control function
>> + *
>> + * To prevent breaking the lens when the sensor is powered off the Soft-Landing
>> + * algorithm is called where available. The Soft-Landing algorithm availability
>> + * dependends on the firmware provider.
>> + */
>> +static int m5mols_s_power(struct v4l2_subdev *sd, int on)
>> +{
>> +	struct m5mols_info *info = to_m5mols(sd);
>> +	int ret;
>> +
>> +	if (on) {
>> +		ret = m5mols_sensor_power(info, true);
>> +		if (!ret)
>> +			ret = m5mols_sensor_armboot(sd);
>> +		if (!ret)
>> +			ret = m5mols_init_controls(info);
>> +		if (ret)
>> +			return ret;
>> +
>> +		info->ffmt[M5MOLS_RESTYPE_MONITOR] =
>> +			m5mols_default_ffmt[M5MOLS_RESTYPE_MONITOR];
>> +		info->ffmt[M5MOLS_RESTYPE_CAPTURE] =
>> +			m5mols_default_ffmt[M5MOLS_RESTYPE_CAPTURE];
>> +		return ret;
>> +	}
>> +
>> +	if (is_manufacturer(info, REG_SAMSUNG_TECHWIN)) {
>> +		ret = m5mols_mode(info, REG_MONITOR);
>> +		if (!ret)
>> +			ret = m5mols_write(sd, AF_EXECUTE, REG_AF_STOP);
>> +		if (!ret)
>> +			ret = m5mols_write(sd, AF_MODE, REG_AF_POWEROFF);
>> +		if (!ret)
>> +			ret = m5mols_busy(sd, CAT_SYSTEM, CAT0_STATUS,
>> +					REG_AF_IDLE);
>> +		if (!ret)
>> +			v4l2_info(sd, "Success soft-landing lens\n");
>> +	}
>> +
>> +	ret = m5mols_sensor_power(info, false);
>> +	if (!ret) {
>> +		v4l2_ctrl_handler_free(&info->handle);
>> +		info->ctrl_sync = false;
>> +	}
>> +
>> +	return ret;
>> +}
>> +
>> +static int m5mols_log_status(struct v4l2_subdev *sd)
>> +{
>> +	struct m5mols_info *info = to_m5mols(sd);
>> +
>> +	v4l2_ctrl_handler_log_status(&info->handle, sd->name);
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct v4l2_subdev_core_ops m5mols_core_ops = {
>> +	.s_power	= m5mols_s_power,
>> +	.g_ctrl		= v4l2_subdev_g_ctrl,
>> +	.s_ctrl		= v4l2_subdev_s_ctrl,
>> +	.queryctrl	= v4l2_subdev_queryctrl,
>> +	.querymenu	= v4l2_subdev_querymenu,
>> +	.g_ext_ctrls	= v4l2_subdev_g_ext_ctrls,
>> +	.try_ext_ctrls	= v4l2_subdev_try_ext_ctrls,
>> +	.s_ext_ctrls	= v4l2_subdev_s_ext_ctrls,
>> +	.log_status	= m5mols_log_status,
>> +};
>> +
>> +static const struct v4l2_subdev_ops m5mols_ops = {
>> +	.core		= &m5mols_core_ops,
>> +	.pad		= &m5mols_pad_ops,
>> +	.video		= &m5mols_video_ops,
>> +};
>> +
>> +static void m5mols_irq_work(struct work_struct *work)
>> +{
>> +	struct m5mols_info *info =
>> +		container_of(work, struct m5mols_info, work_irq);
>> +	struct v4l2_subdev *sd = &info->sd;
>> +	u32 reg;
>> +	int ret;
>> +
>> +	if (!is_powered(info) ||
>> +			m5mols_read(sd, SYSTEM_INT_FACTOR, &info->interrupt))
>> +		return;
>> +
>> +	switch (info->interrupt & REG_INT_MASK) {
>> +	case REG_INT_AF:
>> +		if (!is_available_af(info))
>> +			break;
>> +		ret = m5mols_read(sd, AF_STATUS, &reg);
>> +		v4l2_dbg(2, m5mols_debug, sd, "AF %s\n",
>> +			 reg == REG_AF_FAIL ? "Failed" :
>> +			 reg == REG_AF_SUCCESS ? "Success" :
>> +			 reg == REG_AF_IDLE ? "Idle" : "Busy");
>> +		break;
>> +	case REG_INT_CAPTURE:
>> +		if (!test_and_set_bit(ST_CAPT_IRQ, &info->flags))
>> +			wake_up_interruptible(&info->irq_waitq);
>> +
>> +		v4l2_dbg(2, m5mols_debug, sd, "CAPTURE\n");
>> +		break;
>> +	default:
>> +		v4l2_dbg(2, m5mols_debug, sd, "Undefined: %02x\n", reg);
>> +		break;
>> +	};
>> +}
>> +
>> +static irqreturn_t m5mols_irq_handler(int irq, void *data)
>> +{
>> +	struct v4l2_subdev *sd = data;
>> +	struct m5mols_info *info = to_m5mols(sd);
>> +
>> +	schedule_work(&info->work_irq);
>> +
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +static int __devinit m5mols_probe(struct i2c_client *client,
>> +				  const struct i2c_device_id *id)
>> +{
>> +	const struct m5mols_platform_data *pdata = client->dev.platform_data;
>> +	struct m5mols_info *info;
>> +	struct v4l2_subdev *sd;
>> +	int ret;
>> +
>> +	if (pdata == NULL) {
>> +		dev_err(&client->dev, "No platform data\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	if (!gpio_is_valid(pdata->gpio_reset)) {
>> +		dev_err(&client->dev, "No valid RESET GPIO specified\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	if (!pdata->irq) {
>> +		dev_err(&client->dev, "Interrupt not assigned\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	info = kzalloc(sizeof(struct m5mols_info), GFP_KERNEL);
>> +	if (!info)
>> +		return -ENOMEM;
>> +
>> +	info->pdata = pdata;
>> +	info->set_power	= pdata->set_power;
>> +
>> +	ret = gpio_request(pdata->gpio_reset, "M5MOLS_NRST");
>> +	if (ret) {
>> +		dev_err(&client->dev, "Failed to request gpio: %d\n", ret);
>> +		goto out_free;
>> +	}
>> +	gpio_direction_output(pdata->gpio_reset, pdata->reset_polarity);
>> +
>> +	ret = regulator_bulk_get(&client->dev, ARRAY_SIZE(supplies), supplies);
>> +	if (ret) {
>> +		dev_err(&client->dev, "Failed to get regulators: %d\n", ret);
>> +		goto out_gpio;
>> +	}
>> +
>> +	sd = &info->sd;
>> +	strlcpy(sd->name, MODULE_NAME, sizeof(sd->name));
>> +	v4l2_i2c_subdev_init(sd, client, &m5mols_ops);
>> +
>> +	info->pad.flags = MEDIA_PAD_FL_SOURCE;
>> +	ret = media_entity_init(&sd->entity, 1, &info->pad, 0);
>> +	if (ret < 0)
>> +		goto out_reg;
>> +	sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR;
>> +
>> +	init_waitqueue_head(&info->irq_waitq);
>> +	INIT_WORK(&info->work_irq, m5mols_irq_work);
>> +	ret = request_irq(pdata->irq, m5mols_irq_handler,
>> +			  IRQF_TRIGGER_RISING, MODULE_NAME, sd);
>> +	if (ret) {
>> +		dev_err(&client->dev, "Interrupt request failed: %d\n", ret);
>> +		goto out_me;
>> +	}
>> +	info->res_type = M5MOLS_RESTYPE_MONITOR;
>> +	return 0;
>> +out_me:
>> +	media_entity_cleanup(&sd->entity);
>> +out_reg:
>> +	regulator_bulk_free(ARRAY_SIZE(supplies), supplies);
>> +out_gpio:
>> +	gpio_free(pdata->gpio_reset);
>> +out_free:
>> +	kfree(info);
>> +	return ret;
>> +}
>> +
>> +static int __devexit 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);
>> +	free_irq(info->pdata->irq, sd);
>> +
>> +	regulator_bulk_free(ARRAY_SIZE(supplies), supplies);
>> +	gpio_free(info->pdata->gpio_reset);
>> +	media_entity_cleanup(&sd->entity);
>> +	kfree(info);
>> +	return 0;
>> +}
>> +
>> +static const struct i2c_device_id m5mols_id[] = {
>> +	{ MODULE_NAME, 0 },
>> +	{ },
>> +};
>> +MODULE_DEVICE_TABLE(i2c, m5mols_id);
>> +
>> +static struct i2c_driver m5mols_i2c_driver = {
>> +	.driver = {
>> +		.name	= MODULE_NAME,
>> +	},
>> +	.probe		= m5mols_probe,
>> +	.remove		= __devexit_p(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@samsung.com>");
>> +MODULE_AUTHOR("Dongsoo Kim <dongsoo45.kim@samsung.com>");
>> +MODULE_DESCRIPTION("Fujitsu M-5MOLS 8M Pixel camera driver");
>> +MODULE_LICENSE("GPL");
>> diff --git a/drivers/media/video/m5mols/m5mols_reg.h b/drivers/media/video/m5mols/m5mols_reg.h
>> new file mode 100644
>> index 0000000..b83e36f
>> --- /dev/null
>> +++ b/drivers/media/video/m5mols/m5mols_reg.h
>> @@ -0,0 +1,399 @@
>> +/*
>> + * Register map for M-5MOLS 8M Pixel camera sensor with ISP
>> + *
>> + * Copyright (C) 2011 Samsung Electronics Co., Ltd.
>> + * Author: HeungJun Kim, riverful.kim@samsung.com
>> + *
>> + * Copyright (C) 2009 Samsung Electronics Co., Ltd.
>> + * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com
>> + *
>> + * 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_REG_H
>> +#define M5MOLS_REG_H
>> +
>> +#define M5MOLS_I2C_MAX_SIZE	4
>> +#define M5MOLS_BYTE_READ	0x01
>> +#define M5MOLS_BYTE_WRITE	0x02
>> +
>> +#define I2C_CATEGORY(__cat)		((__cat >> 16) & 0xff)
>> +#define I2C_COMMAND(__comm)		((__comm >> 8) & 0xff)
>> +#define I2C_SIZE(__reg_s)		((__reg_s) & 0xff)
> 
> I would put category and command to lower 16 bits and size in the upper 16
> bits, but this is up to you. I think this would improve readability.
OK. I would deeply consider that :-)

> 
>> +#define I2C_REG(__cat, __cmd, __reg_s)	((__cat << 16) | (__cmd << 8) | __reg_s)
>> +
>> +/*
>> + * Category section register
>> + *
>> + * The category means set including relevant command of M-5MOLS.
>> + */
>> +#define CAT_SYSTEM		0x00
>> +#define CAT_PARAM		0x01
>> +#define CAT_MONITOR		0x02
>> +#define CAT_AE			0x03
>> +#define CAT_WB			0x06
>> +#define CAT_EXIF		0x07
>> +#define CAT_FD			0x09
>> +#define CAT_LENS		0x0a
>> +#define CAT_CAPT_PARM		0x0b
>> +#define CAT_CAPT_CTRL		0x0c
>> +#define CAT_FLASH		0x0f	/* related to FW, revisions, booting */
> 
> What about defining registers so that the category is part of the register
> address? This is actually how it's in the address space as well.
> 
>> +
>> +/*
>> + * Category 0 - SYSTEM mode
>> + *
>> + * The SYSTEM mode in the M-5MOLS means area available to handle with the whole
>> + * & all-round system of sensor. It deals with version/interrupt/setting mode &
>> + * even sensor's status. Especially, the M-5MOLS sensor with ISP varies by
>> + * packaging & manufacturer, even the customer and project code. And the
>> + * function details may vary among them. The version information helps to
>> + * determine what methods shall be used in the driver.
>> + *
>> + * There is many registers between customer version address and awb one. For
>> + * more specific contents, see definition if file m5mols.h.
>> + */
>> +#define CAT0_VER_CUSTOMER	0x00	/* customer version */
> 
> If you keep the definitions as they are, I think "CAT0" should be replaced
> by "SYSTEM". This would make it clear that the registers belong to that
> category, instead of a numeric one.
Actually, this register named on Document of M-5MOLS. I wanted to satisfy
to keep that name and to looks readable.
So, I choose only the name pointing the register value, is followed by document,
and the prefix like SYSTEM_ is used in the code.

> 
>> +#define CAT0_VER_AWB		0x09	/* Auto WB version */
>> +#define CAT0_VER_STRING		0x0a	/* string including M-5MOLS */
>> +#define CAT0_SYSMODE		0x0b	/* SYSTEM mode register */
>> +#define CAT0_STATUS		0x0c	/* SYSTEM mode status register */
>> +#define CAT0_INT_FACTOR		0x10	/* interrupt pending register */
>> +#define CAT0_INT_ENABLE		0x11	/* interrupt enable register */
>> +
>> +#define SYSTEM_SYSMODE		I2C_REG(CAT_SYSTEM, CAT0_SYSMODE, 1)
>> +#define REG_SYSINIT		0x00	/* SYSTEM mode */
>> +#define REG_PARAMETER		0x01	/* PARAMETER mode */
>> +#define REG_MONITOR		0x02	/* MONITOR mode */
>> +#define REG_CAPTURE		0x03	/* CAPTURE mode */
>> +
>> +#define SYSTEM_CMD(__cmd)	I2C_REG(CAT_SYSTEM, cmd, 1)
>> +#define SYSTEM_VER_STRING	I2C_REG(CAT_SYSTEM, CAT0_VER_STRING, 1)
>> +#define REG_SAMSUNG_ELECTRO	"SE"	/* Samsung Electro-Mechanics */
>> +#define REG_SAMSUNG_OPTICS	"OP"	/* Samsung Fiber-Optics */
>> +#define REG_SAMSUNG_TECHWIN	"TB"	/* Samsung Techwin */
>> +
>> +#define SYSTEM_INT_FACTOR	I2C_REG(CAT_SYSTEM, CAT0_INT_FACTOR, 1)
>> +#define SYSTEM_INT_ENABLE	I2C_REG(CAT_SYSTEM, CAT0_INT_ENABLE, 1)
>> +#define REG_INT_MODE		(1 << 0)
>> +#define REG_INT_AF		(1 << 1)
>> +#define REG_INT_ZOOM		(1 << 2)
>> +#define REG_INT_CAPTURE		(1 << 3)
>> +#define REG_INT_FRAMESYNC	(1 << 4)
>> +#define REG_INT_FD		(1 << 5)
>> +#define REG_INT_LENS_INIT	(1 << 6)
>> +#define REG_INT_SOUND		(1 << 7)
>> +#define REG_INT_MASK		0x0f
> 
> I would prefix the register bit definitions with the name of the register.
> 
> E.g.
> 
> #define SYSTEM_INT_FACTOR_MODE	(1 << 0)
It's Ditto "over 80 character"

If already used once like this, it should use in another case, so
length of the SYSTEM_INT_FACTOR_MODE does not matter itself, but the other
case can be troubled.
 
> 
>> +
>> +/*
>> + * category 1 - PARAMETER mode
>> + *
>> + * This category supports function of camera features of M-5MOLS. It means we
>> + * can handle with preview(MONITOR) resolution size/frame per second/interface
>> + * between the sensor and the Application Processor/even the image effect.
>> + */
>> +#define CAT1_DATA_INTERFACE	0x00	/* interface between sensor and AP */
>> +#define CAT1_MONITOR_SIZE	0x01	/* resolution at the MONITOR mode */
>> +#define CAT1_MONITOR_FPS	0x02	/* frame per second at this mode */
>> +#define CAT1_EFFECT		0x0b	/* image effects */
>> +
>> +#define PARM_MON_SIZE		I2C_REG(CAT_PARAM, CAT1_MONITOR_SIZE, 1)
>> +
>> +#define PARM_MON_FPS		I2C_REG(CAT_PARAM, CAT1_MONITOR_FPS, 1)
>> +#define REG_FPS_30		0x02
>> +
>> +#define PARM_INTERFACE		I2C_REG(CAT_PARAM, CAT1_DATA_INTERFACE, 1)
>> +#define REG_INTERFACE_MIPI	0x02
>> +
>> +#define PARM_EFFECT		I2C_REG(CAT_PARAM, CAT1_EFFECT, 1)
>> +#define REG_EFFECT_OFF		0x00
>> +#define REG_EFFECT_NEGA		0x01
>> +#define REG_EFFECT_EMBOSS	0x06
>> +#define REG_EFFECT_OUTLINE	0x07
>> +#define REG_EFFECT_WATERCOLOR	0x08
>> +
>> +/*
>> + * Category 2 - MONITOR mode
>> + *
>> + * The MONITOR mode is same as preview mode as we said. The M-5MOLS has another
>> + * mode named "Preview", but this preview mode is used at the case specific
>> + * vider-recording mode. This mmode supports only YUYV format. On the other
>> + * hand, the JPEG & RAW formats is supports by CAPTURE mode. And, there are
>> + * another options like zoom/color effect(different with effect in PARAMETER
>> + * mode)/anti hand shaking algorithm.
>> + */
>> +#define CAT2_ZOOM		0x01	/* set the zoom position & execute */
>> +#define CAT2_ZOOM_STEP		0x03	/* set the zoom step */
>> +#define CAT2_CFIXB		0x09	/* CB value for color effect */
>> +#define CAT2_CFIXR		0x0a	/* CR value for color effect */
>> +#define CAT2_COLOR_EFFECT	0x0b	/* set on/off of color effect */
>> +#define CAT2_CHROMA_LVL		0x0f	/* set chroma level */
>> +#define CAT2_CHROMA_EN		0x10	/* set on/off of choroma */
>> +#define CAT2_EDGE_LVL		0x11	/* set sharpness level */
>> +#define CAT2_EDGE_EN		0x12	/* set on/off sharpness */
>> +#define CAT2_TONE_CTL		0x25	/* set tone color(contrast) */
>> +
>> +#define MON_ZOOM		I2C_REG(CAT_MONITOR, CAT2_ZOOM, 1)
>> +
>> +#define MON_CFIXR		I2C_REG(CAT_MONITOR, CAT2_CFIXR, 1)
>> +#define MON_CFIXB		I2C_REG(CAT_MONITOR, CAT2_CFIXB, 1)
>> +#define REG_CFIXB_SEPIA		0xd8
>> +#define REG_CFIXR_SEPIA		0x18
>> +
>> +#define MON_EFFECT		I2C_REG(CAT_MONITOR, CAT2_COLOR_EFFECT, 1)
>> +#define REG_COLOR_EFFECT_OFF	0x00
>> +#define REG_COLOR_EFFECT_ON	0x01
>> +
>> +#define MON_CHROMA_EN		I2C_REG(CAT_MONITOR, CAT2_CHROMA_EN, 1)
>> +#define MON_CHROMA_LVL		I2C_REG(CAT_MONITOR, CAT2_CHROMA_LVL, 1)
>> +#define REG_CHROMA_OFF		0x00
>> +#define REG_CHROMA_ON		0x01
>> +
>> +#define MON_EDGE_EN		I2C_REG(CAT_MONITOR, CAT2_EDGE_EN, 1)
>> +#define MON_EDGE_LVL		I2C_REG(CAT_MONITOR, CAT2_EDGE_LVL, 1)
>> +#define REG_EDGE_OFF		0x00
>> +#define REG_EDGE_ON		0x01
>> +
>> +#define MON_TONE_CTL		I2C_REG(CAT_MONITOR, CAT2_TONE_CTL, 1)
>> +
>> +/*
>> + * Category 3 - Auto Exposure
>> + *
>> + * The M-5MOLS exposure capbility is detailed as which is similar to digital
>> + * camera. This category supports AE locking/various AE mode(range of exposure)
>> + * /ISO/flickering/EV bias/shutter/meteoring, and anything else. And the
>> + * maximum/minimum exposure gain value depending on M-5MOLS firmware, may be
>> + * different. So, this category also provide getting the max/min values. And,
>> + * each MONITOR and CAPTURE mode has each gain/shutter/max exposure values.
>> + */
>> +#define CAT3_AE_LOCK		0x00	/* locking Auto exposure */
>> +#define CAT3_AE_MODE		0x01	/* set AE mode, mode means range */
>> +#define CAT3_ISO		0x05	/* set ISO */
>> +#define CAT3_EV_PRESET_MONITOR	0x0a	/* EV(scenemode) preset for MONITOR */
>> +#define CAT3_EV_PRESET_CAPTURE	0x0b	/* EV(scenemode) preset for CAPTURE */
>> +#define CAT3_MANUAL_GAIN_MON	0x12	/* meteoring value for the MONITOR */
>> +#define CAT3_MAX_GAIN_MON	0x1a	/* max gain value for the MONITOR */
>> +#define CAT3_MANUAL_GAIN_CAP	0x26	/* meteoring value for the CAPTURE */
>> +#define CAT3_AE_INDEX		0x38	/* AE index */
>> +
>> +#define AE_LOCK			I2C_REG(CAT_AE, CAT3_AE_LOCK, 1)
>> +#define REG_AE_UNLOCK		0x00
>> +#define REG_AE_LOCK		0x01
>> +
>> +#define AE_MODE			I2C_REG(CAT_AE, CAT3_AE_MODE, 1)
>> +#define REG_AE_OFF		0x00	/* AE off */
>> +#define REG_AE_ALL		0x01	/* calc AE in all block integral */
>> +#define REG_AE_CENTER		0x03	/* calc AE in center weighted */
>> +#define REG_AE_SPOT		0x06	/* calc AE in specific spot */
>> +
>> +#define AE_ISO			I2C_REG(CAT_AE, CAT3_ISO, 1)
>> +#define REG_ISO_AUTO		0x00
>> +#define REG_ISO_50		0x01
>> +#define REG_ISO_100		0x02
>> +#define REG_ISO_200		0x03
>> +#define REG_ISO_400		0x04
>> +#define REG_ISO_800		0x05
>> +
>> +#define AE_EV_PRESET_MONITOR	I2C_REG(CAT_AE, CAT3_EV_PRESET_MONITOR, 1)
>> +#define AE_EV_PRESET_CAPTURE	I2C_REG(CAT_AE, CAT3_EV_PRESET_CAPTURE, 1)
>> +#define REG_SCENE_NORMAL	0x00
>> +#define REG_SCENE_PORTRAIT	0x01
>> +#define REG_SCENE_LANDSCAPE	0x02
>> +#define REG_SCENE_SPORTS	0x03
>> +#define REG_SCENE_PARTY_INDOOR	0x04
>> +#define REG_SCENE_BEACH_SNOW	0x05
>> +#define REG_SCENE_SUNSET	0x06
>> +#define REG_SCENE_DAWN_DUSK	0x07
>> +#define REG_SCENE_FALL		0x08
>> +#define REG_SCENE_NIGHT		0x09
>> +#define REG_SCENE_AGAINST_LIGHT	0x0a
>> +#define REG_SCENE_FIRE		0x0b
>> +#define REG_SCENE_TEXT		0x0c
>> +#define REG_SCENE_CANDLE	0x0d
>> +
>> +#define AE_MAN_GAIN_MON		I2C_REG(CAT_AE, CAT3_MANUAL_GAIN_MON, 2)
>> +#define AE_MAX_GAIN_MON		I2C_REG(CAT_AE, CAT3_MAX_GAIN_MON, 2)
>> +#define AE_MAN_GAIN_CAP		I2C_REG(CAT_AE, CAT3_MANUAL_GAIN_CAP, 2)
>> +
>> +#define AE_INDEX		I2C_REG(CAT_AE, CAT3_AE_INDEX, 1)
>> +#define REG_AE_INDEX_20_NEG	0x00
>> +#define REG_AE_INDEX_15_NEG	0x01
>> +#define REG_AE_INDEX_10_NEG	0x02
>> +#define REG_AE_INDEX_05_NEG	0x03
>> +#define REG_AE_INDEX_00		0x04
>> +#define REG_AE_INDEX_05_POS	0x05
>> +#define REG_AE_INDEX_10_POS	0x06
>> +#define REG_AE_INDEX_15_POS	0x07
>> +#define REG_AE_INDEX_20_POS	0x08
>> +
>> +/*
>> + * Category 6 - White Balance
>> + *
>> + * This category provide AWB locking/mode/preset/speed/gain bias, etc.
>> + */
>> +#define CAT6_AWB_LOCK		0x00	/* locking Auto Whitebalance */
>> +#define CAT6_AWB_MODE		0x02	/* set Auto or Manual */
>> +#define CAT6_AWB_MANUAL		0x03	/* set Manual(preset) value */
>> +
>> +#define AWB_LOCK		I2C_REG(CAT_WB, CAT6_AWB_LOCK, 1)
>> +#define REG_AWB_UNLOCK		0x00
>> +#define REG_AWB_LOCK		0x01
>> +
>> +#define AWB_MODE		I2C_REG(CAT_WB, CAT6_AWB_MODE, 1)
>> +#define REG_AWB_AUTO		0x01	/* AWB off */
>> +#define REG_AWB_PRESET		0x02	/* AWB preset */
>> +
>> +#define AWB_MANUAL		I2C_REG(CAT_WB, CAT6_AWB_MANUAL, 1)
>> +#define REG_AWB_INCANDESCENT	0x01
>> +#define REG_AWB_FLUORESCENT_1	0x02
>> +#define REG_AWB_FLUORESCENT_2	0x03
>> +#define REG_AWB_DAYLIGHT	0x04
>> +#define REG_AWB_CLOUDY		0x05
>> +#define REG_AWB_SHADE		0x06
>> +#define REG_AWB_HORIZON		0x07
>> +#define REG_AWB_LEDLIGHT	0x09
>> +
>> +/*
>> + * Category 7 - EXIF information
>> + */
>> +#define CAT7_INFO_EXPTIME_NU	0x00
>> +#define CAT7_INFO_EXPTIME_DE	0x04
>> +#define CAT7_INFO_TV_NU		0x08
>> +#define CAT7_INFO_TV_DE		0x0c
>> +#define CAT7_INFO_AV_NU		0x10
>> +#define CAT7_INFO_AV_DE		0x14
>> +#define CAT7_INFO_BV_NU		0x18
>> +#define CAT7_INFO_BV_DE		0x1c
>> +#define CAT7_INFO_EBV_NU	0x20
>> +#define CAT7_INFO_EBV_DE	0x24
>> +#define CAT7_INFO_ISO		0x28
>> +#define CAT7_INFO_FLASH		0x2a
>> +#define CAT7_INFO_SDR		0x2c
>> +#define CAT7_INFO_QVAL		0x2e
>> +
>> +#define EXIF_INFO_EXPTIME_NU	I2C_REG(CAT_EXIF, CAT7_INFO_EXPTIME_NU, 4)
>> +#define EXIF_INFO_EXPTIME_DE	I2C_REG(CAT_EXIF, CAT7_INFO_EXPTIME_DE, 4)
>> +#define EXIF_INFO_TV_NU		I2C_REG(CAT_EXIF, CAT7_INFO_TV_NU, 4)
>> +#define EXIF_INFO_TV_DE		I2C_REG(CAT_EXIF, CAT7_INFO_TV_DE, 4)
>> +#define EXIF_INFO_AV_NU		I2C_REG(CAT_EXIF, CAT7_INFO_AV_NU, 4)
>> +#define EXIF_INFO_AV_DE		I2C_REG(CAT_EXIF, CAT7_INFO_AV_DE, 4)
>> +#define EXIF_INFO_BV_NU		I2C_REG(CAT_EXIF, CAT7_INFO_BV_NU, 4)
>> +#define EXIF_INFO_BV_DE		I2C_REG(CAT_EXIF, CAT7_INFO_BV_DE, 4)
>> +#define EXIF_INFO_EBV_NU	I2C_REG(CAT_EXIF, CAT7_INFO_EBV_NU, 4)
>> +#define EXIF_INFO_EBV_DE	I2C_REG(CAT_EXIF, CAT7_INFO_EBV_DE, 4)
>> +#define EXIF_INFO_ISO		I2C_REG(CAT_EXIF, CAT7_INFO_ISO, 2)
>> +#define EXIF_INFO_FLASH		I2C_REG(CAT_EXIF, CAT7_INFO_FLASH, 2)
>> +#define EXIF_INFO_SDR		I2C_REG(CAT_EXIF, CAT7_INFO_SDR, 2)
>> +#define EXIF_INFO_QVAL		I2C_REG(CAT_EXIF, CAT7_INFO_QVAL, 2)
>> +
>> +/*
>> + * Category 9 - Face Detection
>> + */
>> +#define CAT9_FD_CTL		0x00
>> +
>> +#define FD_CTL			I2C_REG(CAT_FD, CAT9_FD_CTL, 1)
>> +#define BIT_FD_EN		0
>> +#define BIT_FD_DRAW_FACE_FRAME	4
>> +#define BIT_FD_DRAW_SMILE_LVL	6
>> +#define REG_FD(shift)		(1 << shift)
>> +#define REG_FD_OFF		0x0
>> +
>> +/*
>> + * Category A - Lens Parameter
>> + */
>> +#define CATA_AF_MODE		0x01
>> +#define CATA_AF_EXECUTE		0x02
>> +#define CATA_AF_STATUS		0x03
>> +#define CATA_AF_VERSION		0x0a
>> +
>> +#define AF_MODE			I2C_REG(CAT_LENS, CATA_AF_MODE, 1)
>> +#define REG_AF_NORMAL		0x00	/* Normal AF, one time */
>> +#define REG_AF_MACRO		0x01	/* Macro AF, one time */
>> +#define REG_AF_POWEROFF		0x07
>> +
>> +#define AF_EXECUTE		I2C_REG(CAT_LENS, CATA_AF_EXECUTE, 1)
>> +#define REG_AF_STOP		0x00
>> +#define REG_AF_EXE_AUTO		0x01
>> +#define REG_AF_EXE_CAF		0x02
>> +
>> +#define AF_STATUS		I2C_REG(CAT_LENS, CATA_AF_STATUS, 1)
>> +#define REG_AF_FAIL		0x00
>> +#define REG_AF_SUCCESS		0x02
>> +#define REG_AF_IDLE		0x04
>> +#define REG_AF_BUSY		0x05
>> +
>> +#define AF_VERSION		I2C_REG(CAT_LENS, CATA_AF_VERSION, 1)
>> +
>> +/*
>> + * Category B - CAPTURE Parameter
>> + */
>> +#define CATB_YUVOUT_MAIN	0x00
>> +#define CATB_MAIN_IMAGE_SIZE	0x01
>> +#define CATB_MCC_MODE		0x1d
>> +#define CATB_WDR_EN		0x2c
>> +#define CATB_LIGHT_CTRL		0x40
>> +#define CATB_FLASH_CTRL		0x41
>> +
>> +#define CAPP_YUVOUT_MAIN	I2C_REG(CAT_CAPT_PARM, CATB_YUVOUT_MAIN, 1)
>> +#define REG_YUV422		0x00
>> +#define REG_BAYER10		0x05
>> +#define REG_BAYER8		0x06
>> +#define REG_JPEG		0x10
>> +
>> +#define CAPP_MAIN_IMAGE_SIZE	I2C_REG(CAT_CAPT_PARM, CATB_MAIN_IMAGE_SIZE, 1)
>> +
>> +#define CAPP_MCC_MODE		I2C_REG(CAT_CAPT_PARM, CATB_MCC_MODE, 1)
>> +#define REG_MCC_OFF		0x00
>> +#define REG_MCC_NORMAL		0x01
>> +
>> +#define CAPP_WDR_EN		I2C_REG(CAT_CAPT_PARM, CATB_WDR_EN, 1)
>> +#define REG_WDR_OFF		0x00
>> +#define REG_WDR_ON		0x01
>> +#define REG_WDR_AUTO		0x02
>> +
>> +#define CAPP_LIGHT_CTRL		I2C_REG(CAT_CAPT_PARM, CATB_LIGHT_CTRL, 1)
>> +#define REG_LIGHT_OFF		0x00
>> +#define REG_LIGHT_ON		0x01
>> +#define REG_LIGHT_AUTO		0x02
>> +
>> +#define CAPP_FLASH_CTRL		I2C_REG(CAT_CAPT_PARM, CATB_FLASH_CTRL, 1)
>> +#define REG_FLASH_OFF		0x00
>> +#define REG_FLASH_ON		0x01
>> +#define REG_FLASH_AUTO		0x02
>> +
>> +/*
>> + * Category C - CAPTURE Control
>> + */
>> +#define CATC_CAP_MODE		0x00
>> +#define CATC_CAP_SEL_FRAME	0x06	/* It determines Single or Multi */
>> +#define CATC_CAP_START		0x09
>> +#define CATC_CAP_IMAGE_SIZE	0x0d
>> +#define CATC_CAP_THUMB_SIZE	0x11
>> +
>> +#define CAPC_MODE		I2C_REG(CAT_CAPT_CTRL, CATC_CAP_MODE, 1)
>> +#define REG_CAP_NONE		0x00
>> +#define REG_CAP_ANTI_SHAKE	0x02
>> +
>> +#define CAPC_SEL_FRAME		I2C_REG(CAT_CAPT_CTRL, CATC_CAP_SEL_FRAME, 1)
>> +
>> +#define CAPC_START		I2C_REG(CAT_CAPT_CTRL, CATC_CAP_START, 1)
>> +#define REG_CAP_START_MAIN	0x01
>> +#define REG_CAP_START_THUMB	0x03
>> +
>> +#define CAPC_IMAGE_SIZE		I2C_REG(CAT_CAPT_CTRL, CATC_CAP_IMAGE_SIZE, 1)
>> +#define CAPC_THUMB_SIZE		I2C_REG(CAT_CAPT_CTRL, CATC_CAP_THUMB_SIZE, 1)
>> +
>> +/*
>> + * Category F - Flash
>> + *
>> + * This mode provides functions about internal flash stuff and system startup.
>> + */
>> +#define CATF_CAM_START		0x12	/* It starts internal ARM core booting
>> +					 * after power-up */
>> +
>> +#define FLASH_CAM_START		I2C_REG(CAT_FLASH, CATF_CAM_START, 1)
>> +#define REG_START_ARM_BOOT	0x01
>> +
>> +#endif	/* M5MOLS_REG_H */
>> diff --git a/include/media/m5mols.h b/include/media/m5mols.h
>> new file mode 100644
>> index 0000000..2d7e7ca
>> --- /dev/null
>> +++ b/include/media/m5mols.h
>> @@ -0,0 +1,35 @@
>> +/*
>> + * Driver header for M-5MOLS 8M Pixel camera sensor with ISP
>> + *
>> + * Copyright (C) 2011 Samsung Electronics Co., Ltd.
>> + * Author: HeungJun Kim, riverful.kim@samsung.com
>> + *
>> + * Copyright (C) 2009 Samsung Electronics Co., Ltd.
>> + * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com
>> + *
>> + * 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 MEDIA_M5MOLS_H
>> +#define MEDIA_M5MOLS_H
>> +
>> +/**
>> + * struct m5mols_platform_data - platform data for M-5MOLS driver
>> + * @irq:	GPIO getting the irq pin of M-5MOLS
>> + * @gpio_reset:	GPIO driving the reset pin of M-5MOLS
>> + * @reset_polarity: active state for gpio_rst pin, 0 or 1
>> + * @set_power:	an additional callback to the board setup code
>> + *		to be called after enabling and before disabling
>> + *		the sensor's supply regulators
>> + */
>> +struct m5mols_platform_data {
>> +	int irq;
>> +	int gpio_reset;
>> +	u8 reset_polarity;
>> +	int (*set_power)(struct device *dev, int on);
>> +};
>> +
>> +#endif	/* MEDIA_M5MOLS_H */
>> -- 
>> 1.7.0.4
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-media" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 
The things you pointed out, will be reflected by a submission of patch style.

And, very thanks once again!!

We can talk more about the digital camera's functionality later :)

Regards,
Heungjun Kim

^ permalink raw reply	[flat|nested] 34+ messages in thread

* [PATCH 0/5] Fix micellaneous issues for M-5MOLS driver
  2011-05-25 13:54         ` Sakari Ailus
  2011-05-26  7:12           ` Kim, HeungJun
@ 2011-05-27 12:58           ` HeungJun, Kim
  2011-05-31  7:35             ` [PATCH v2 0/4] " HeungJun, Kim
                               ` (4 more replies)
  2011-05-27 12:58           ` [PATCH 1/5] m5mols: fix reading wrong size of captured main/thumb image HeungJun, Kim
                             ` (4 subsequent siblings)
  6 siblings, 5 replies; 34+ messages in thread
From: HeungJun, Kim @ 2011-05-27 12:58 UTC (permalink / raw)
  To: linux-media; +Cc: mchehab, s.nawrocki, sakari.ailus

This patch series is to handle some issues about M-5MOLS driver.

Except for first patch, most of these issues comes from to Sakari,
And I very appreciate the comments and reviews about this driver. Thanks.

The first change is fixing to read wrong capture image size.

The second change is preventing overwriting part of memory by u32 value
argument of m5mols_read(). So, add exclusive functions according to byte
width of value argument.

The third change is removing using union by reading version information,
and choose reading directly.

The fourth change is renmaing m5mols_capture_error_handler() to proper name -
m5mols_capture_post_work(). This function's object is to proceed all post works
in this function. And, so, I add more comments and rename this function
for preventing to confuse.

The fifth change is missign <, > for the email address.

Thanks, and any comments welcome.

Regards,
Heungjun Kim


^ permalink raw reply	[flat|nested] 34+ messages in thread

* [PATCH 1/5] m5mols: fix reading wrong size of captured main/thumb image
  2011-05-25 13:54         ` Sakari Ailus
  2011-05-26  7:12           ` Kim, HeungJun
  2011-05-27 12:58           ` [PATCH 0/5] Fix micellaneous issues for M-5MOLS driver HeungJun, Kim
@ 2011-05-27 12:58           ` HeungJun, Kim
  2011-05-27 12:58           ` [PATCH 2/5] m5mols: add m5mols_read_u8/u16/u32() according to I2C byte width HeungJun, Kim
                             ` (3 subsequent siblings)
  6 siblings, 0 replies; 34+ messages in thread
From: HeungJun, Kim @ 2011-05-27 12:58 UTC (permalink / raw)
  To: linux-media
  Cc: mchehab, s.nawrocki, sakari.ailus, HeungJun, Kim, Kyungmin Park

Signed-off-by: HeungJun, Kim <riverful.kim@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 drivers/media/video/m5mols/m5mols_reg.h |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/media/video/m5mols/m5mols_reg.h b/drivers/media/video/m5mols/m5mols_reg.h
index b83e36f..8260f50 100644
--- a/drivers/media/video/m5mols/m5mols_reg.h
+++ b/drivers/media/video/m5mols/m5mols_reg.h
@@ -382,8 +382,8 @@
 #define REG_CAP_START_MAIN	0x01
 #define REG_CAP_START_THUMB	0x03
 
-#define CAPC_IMAGE_SIZE		I2C_REG(CAT_CAPT_CTRL, CATC_CAP_IMAGE_SIZE, 1)
-#define CAPC_THUMB_SIZE		I2C_REG(CAT_CAPT_CTRL, CATC_CAP_THUMB_SIZE, 1)
+#define CAPC_IMAGE_SIZE		I2C_REG(CAT_CAPT_CTRL, CATC_CAP_IMAGE_SIZE, 4)
+#define CAPC_THUMB_SIZE		I2C_REG(CAT_CAPT_CTRL, CATC_CAP_THUMB_SIZE, 4)
 
 /*
  * Category F - Flash
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 2/5] m5mols: add m5mols_read_u8/u16/u32() according to I2C byte width
  2011-05-25 13:54         ` Sakari Ailus
                             ` (2 preceding siblings ...)
  2011-05-27 12:58           ` [PATCH 1/5] m5mols: fix reading wrong size of captured main/thumb image HeungJun, Kim
@ 2011-05-27 12:58           ` HeungJun, Kim
  2011-05-27 12:58           ` [PATCH 3/5] m5mols: remove union in the m5mols_get_version(), and VERSION_SIZE HeungJun, Kim
                             ` (2 subsequent siblings)
  6 siblings, 0 replies; 34+ messages in thread
From: HeungJun, Kim @ 2011-05-27 12:58 UTC (permalink / raw)
  To: linux-media
  Cc: mchehab, s.nawrocki, sakari.ailus, HeungJun, Kim, Kyungmin Park

For now, the m5mols_read() share in case of I2C packet 1, 2, 4 byte(2) width.
So, this commit adds 3 functions - m5mols_read_u8/u16/u32() according to byte
width of I2C packet. And, the u32 variables in spite of u8 or u16 for fitting
to m5mols_read() having no choice, is replaced to have original byte width
like u8, u16, u32 as same reason.

Signed-off-by: HeungJun, Kim <riverful.kim@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 drivers/media/video/m5mols/m5mols.h          |   52 +++++++-------
 drivers/media/video/m5mols/m5mols_capture.c  |   18 +++---
 drivers/media/video/m5mols/m5mols_controls.c |    2 +-
 drivers/media/video/m5mols/m5mols_core.c     |   96 ++++++++++++++++++--------
 4 files changed, 104 insertions(+), 64 deletions(-)

diff --git a/drivers/media/video/m5mols/m5mols.h b/drivers/media/video/m5mols/m5mols.h
index 10b55c8..dbe8928 100644
--- a/drivers/media/video/m5mols/m5mols.h
+++ b/drivers/media/video/m5mols/m5mols.h
@@ -106,23 +106,23 @@ struct m5mols_capture {
  * The each value according to each scenemode is recommended in the documents.
  */
 struct m5mols_scenemode {
-	u32 metering;
-	u32 ev_bias;
-	u32 wb_mode;
-	u32 wb_preset;
-	u32 chroma_en;
-	u32 chroma_lvl;
-	u32 edge_en;
-	u32 edge_lvl;
-	u32 af_range;
-	u32 fd_mode;
-	u32 mcc;
-	u32 light;
-	u32 flash;
-	u32 tone;
-	u32 iso;
-	u32 capt_mode;
-	u32 wdr;
+	u8 metering;
+	u8 ev_bias;
+	u8 wb_mode;
+	u8 wb_preset;
+	u8 chroma_en;
+	u8 chroma_lvl;
+	u8 edge_en;
+	u8 edge_lvl;
+	u8 af_range;
+	u8 fd_mode;
+	u8 mcc;
+	u8 light;
+	u8 flash;
+	u8 tone;
+	u8 iso;
+	u8 capt_mode;
+	u8 wdr;
 };
 
 /**
@@ -216,9 +216,9 @@ struct m5mols_info {
 	bool lock_ae;
 	bool lock_awb;
 	u8 resolution;
-	u32 interrupt;
-	u32 mode;
-	u32 mode_save;
+	u8 interrupt;
+	u8 mode;
+	u8 mode_save;
 	int (*set_power)(struct device *dev, int on);
 };
 
@@ -256,9 +256,11 @@ struct m5mols_info {
  *   +-------+---+----------+-----+------+------+------+------+
  *   - d[0..3]: according to size1
  */
-int m5mols_read(struct v4l2_subdev *sd, u32 reg_comb, u32 *val);
+int m5mols_read_u8(struct v4l2_subdev *sd, u32 reg_comb, u8 *val);
+int m5mols_read_u16(struct v4l2_subdev *sd, u32 reg_comb, u16 *val);
+int m5mols_read_u32(struct v4l2_subdev *sd, u32 reg_comb, u32 *val);
 int m5mols_write(struct v4l2_subdev *sd, u32 reg_comb, u32 val);
-int m5mols_busy(struct v4l2_subdev *sd, u8 category, u8 cmd, u32 value);
+int m5mols_busy(struct v4l2_subdev *sd, u8 category, u8 cmd, u8 value);
 
 /*
  * Mode operation of the M-5MOLS
@@ -280,12 +282,12 @@ int m5mols_busy(struct v4l2_subdev *sd, u8 category, u8 cmd, u32 value);
  * The available executing order between each modes are as follows:
  *   PARAMETER <---> MONITOR <---> CAPTURE
  */
-int m5mols_mode(struct m5mols_info *info, u32 mode);
+int m5mols_mode(struct m5mols_info *info, u8 mode);
 
-int m5mols_enable_interrupt(struct v4l2_subdev *sd, u32 reg);
+int m5mols_enable_interrupt(struct v4l2_subdev *sd, u8 reg);
 int m5mols_sync_controls(struct m5mols_info *info);
 int m5mols_start_capture(struct m5mols_info *info);
-int m5mols_do_scenemode(struct m5mols_info *info, u32 mode);
+int m5mols_do_scenemode(struct m5mols_info *info, u8 mode);
 int m5mols_lock_3a(struct m5mols_info *info, bool lock);
 int m5mols_set_ctrl(struct v4l2_ctrl *ctrl);
 
diff --git a/drivers/media/video/m5mols/m5mols_capture.c b/drivers/media/video/m5mols/m5mols_capture.c
index d71a390..751f459 100644
--- a/drivers/media/video/m5mols/m5mols_capture.c
+++ b/drivers/media/video/m5mols/m5mols_capture.c
@@ -58,9 +58,9 @@ static int m5mols_read_rational(struct v4l2_subdev *sd, u32 addr_num,
 {
 	u32 num, den;
 
-	int ret = m5mols_read(sd, addr_num, &num);
+	int ret = m5mols_read_u32(sd, addr_num, &num);
 	if (!ret)
-		ret = m5mols_read(sd, addr_den, &den);
+		ret = m5mols_read_u32(sd, addr_den, &den);
 	if (ret)
 		return ret;
 	*val = den == 0 ? 0 : num / den;
@@ -99,20 +99,20 @@ static int m5mols_capture_info(struct m5mols_info *info)
 	if (ret)
 		return ret;
 
-	ret = m5mols_read(sd, EXIF_INFO_ISO, (u32 *)&exif->iso_speed);
+	ret = m5mols_read_u16(sd, EXIF_INFO_ISO, &exif->iso_speed);
 	if (!ret)
-		ret = m5mols_read(sd, EXIF_INFO_FLASH, (u32 *)&exif->flash);
+		ret = m5mols_read_u16(sd, EXIF_INFO_FLASH, &exif->flash);
 	if (!ret)
-		ret = m5mols_read(sd, EXIF_INFO_SDR, (u32 *)&exif->sdr);
+		ret = m5mols_read_u16(sd, EXIF_INFO_SDR, &exif->sdr);
 	if (!ret)
-		ret = m5mols_read(sd, EXIF_INFO_QVAL, (u32 *)&exif->qval);
+		ret = m5mols_read_u16(sd, EXIF_INFO_QVAL, &exif->qval);
 	if (ret)
 		return ret;
 
 	if (!ret)
-		ret = m5mols_read(sd, CAPC_IMAGE_SIZE, &info->cap.main);
+		ret = m5mols_read_u32(sd, CAPC_IMAGE_SIZE, &info->cap.main);
 	if (!ret)
-		ret = m5mols_read(sd, CAPC_THUMB_SIZE, &info->cap.thumb);
+		ret = m5mols_read_u32(sd, CAPC_THUMB_SIZE, &info->cap.thumb);
 	if (!ret)
 		info->cap.total = info->cap.main + info->cap.thumb;
 
@@ -122,7 +122,7 @@ static int m5mols_capture_info(struct m5mols_info *info)
 int m5mols_start_capture(struct m5mols_info *info)
 {
 	struct v4l2_subdev *sd = &info->sd;
-	u32 resolution = info->resolution;
+	u8 resolution = info->resolution;
 	int timeout;
 	int ret;
 
diff --git a/drivers/media/video/m5mols/m5mols_controls.c b/drivers/media/video/m5mols/m5mols_controls.c
index 817c16f..d392c83 100644
--- a/drivers/media/video/m5mols/m5mols_controls.c
+++ b/drivers/media/video/m5mols/m5mols_controls.c
@@ -130,7 +130,7 @@ static struct m5mols_scenemode m5mols_default_scenemode[] = {
  *
  * WARNING: The execution order is important. Do not change the order.
  */
-int m5mols_do_scenemode(struct m5mols_info *info, u32 mode)
+int m5mols_do_scenemode(struct m5mols_info *info, u8 mode)
 {
 	struct v4l2_subdev *sd = &info->sd;
 	struct m5mols_scenemode scenemode = m5mols_default_scenemode[mode];
diff --git a/drivers/media/video/m5mols/m5mols_core.c b/drivers/media/video/m5mols/m5mols_core.c
index 76eac26..2b1f23f 100644
--- a/drivers/media/video/m5mols/m5mols_core.c
+++ b/drivers/media/video/m5mols/m5mols_core.c
@@ -133,13 +133,13 @@ static u32 m5mols_swap_byte(u8 *data, u8 length)
 /**
  * m5mols_read -  I2C read function
  * @reg: combination of size, category and command for the I2C packet
+ * @size: desired size of I2C packet
  * @val: read value
  */
-int m5mols_read(struct v4l2_subdev *sd, u32 reg, u32 *val)
+static int m5mols_read(struct v4l2_subdev *sd, u32 size, u32 reg, u32 *val)
 {
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	u8 rbuf[M5MOLS_I2C_MAX_SIZE + 1];
-	u8 size = I2C_SIZE(reg);
 	u8 category = I2C_CATEGORY(reg);
 	u8 cmd = I2C_COMMAND(reg);
 	struct i2c_msg msg[2];
@@ -149,11 +149,6 @@ int m5mols_read(struct v4l2_subdev *sd, u32 reg, u32 *val)
 	if (!client->adapter)
 		return -ENODEV;
 
-	if (size != 1 && size != 2 && size != 4) {
-		v4l2_err(sd, "Wrong data size\n");
-		return -EINVAL;
-	}
-
 	msg[0].addr = client->addr;
 	msg[0].flags = 0;
 	msg[0].len = 5;
@@ -184,6 +179,52 @@ int m5mols_read(struct v4l2_subdev *sd, u32 reg, u32 *val)
 	return 0;
 }
 
+int m5mols_read_u8(struct v4l2_subdev *sd, u32 reg, u8 *val)
+{
+	u32 val_32;
+	int ret;
+
+	if (I2C_SIZE(reg) != 1) {
+		v4l2_err(sd, "Wrong data size\n");
+		return -EINVAL;
+	}
+
+	ret = m5mols_read(sd, I2C_SIZE(reg), reg, &val_32);
+	if (ret)
+		return ret;
+
+	*val = (u8)val_32;
+	return ret;
+}
+
+int m5mols_read_u16(struct v4l2_subdev *sd, u32 reg, u16 *val)
+{
+	u32 val_32;
+	int ret;
+
+	if (I2C_SIZE(reg) != 2) {
+		v4l2_err(sd, "Wrong data size\n");
+		return -EINVAL;
+	}
+
+	ret = m5mols_read(sd, I2C_SIZE(reg), reg, &val_32);
+	if (ret)
+		return ret;
+
+	*val = (u16)val_32;
+	return ret;
+}
+
+int m5mols_read_u32(struct v4l2_subdev *sd, u32 reg, u32 *val)
+{
+	if (I2C_SIZE(reg) != 4) {
+		v4l2_err(sd, "Wrong data size\n");
+		return -EINVAL;
+	}
+
+	return m5mols_read(sd, I2C_SIZE(reg), reg, val);
+}
+
 /**
  * m5mols_write - I2C command write function
  * @reg: combination of size, category and command for the I2C packet
@@ -231,13 +272,14 @@ int m5mols_write(struct v4l2_subdev *sd, u32 reg, u32 val)
 	return 0;
 }
 
-int m5mols_busy(struct v4l2_subdev *sd, u8 category, u8 cmd, u32 mask)
+int m5mols_busy(struct v4l2_subdev *sd, u8 category, u8 cmd, u8 mask)
 {
-	u32 busy, i;
+	u8 busy;
+	int i;
 	int ret;
 
 	for (i = 0; i < M5MOLS_I2C_CHECK_RETRY; i++) {
-		ret = m5mols_read(sd, I2C_REG(category, cmd, 1), &busy);
+		ret = m5mols_read_u8(sd, I2C_REG(category, cmd, 1), &busy);
 		if (ret < 0)
 			return ret;
 		if ((busy & mask) == mask)
@@ -252,14 +294,14 @@ int m5mols_busy(struct v4l2_subdev *sd, u8 category, u8 cmd, u32 mask)
  * Before writing desired interrupt value the INT_FACTOR register should
  * be read to clear pending interrupts.
  */
-int m5mols_enable_interrupt(struct v4l2_subdev *sd, u32 reg)
+int m5mols_enable_interrupt(struct v4l2_subdev *sd, u8 reg)
 {
 	struct m5mols_info *info = to_m5mols(sd);
-	u32 mask = is_available_af(info) ? REG_INT_AF : 0;
-	u32 dummy;
+	u8 mask = is_available_af(info) ? REG_INT_AF : 0;
+	u8 dummy;
 	int ret;
 
-	ret = m5mols_read(sd, SYSTEM_INT_FACTOR, &dummy);
+	ret = m5mols_read_u8(sd, SYSTEM_INT_FACTOR, &dummy);
 	if (!ret)
 		ret = m5mols_write(sd, SYSTEM_INT_ENABLE, reg & ~mask);
 	return ret;
@@ -271,7 +313,7 @@ int m5mols_enable_interrupt(struct v4l2_subdev *sd, u32 reg)
  * It always accompanies a little delay changing the M-5MOLS mode, so it is
  * needed checking current busy status to guarantee right mode.
  */
-static int m5mols_reg_mode(struct v4l2_subdev *sd, u32 mode)
+static int m5mols_reg_mode(struct v4l2_subdev *sd, u8 mode)
 {
 	int ret = m5mols_write(sd, SYSTEM_SYSMODE, mode);
 
@@ -286,16 +328,16 @@ static int m5mols_reg_mode(struct v4l2_subdev *sd, u32 mode)
  * can be guaranteed only when the sensor is operating in mode which which
  * a command belongs to.
  */
-int m5mols_mode(struct m5mols_info *info, u32 mode)
+int m5mols_mode(struct m5mols_info *info, u8 mode)
 {
 	struct v4l2_subdev *sd = &info->sd;
 	int ret = -EINVAL;
-	u32 reg;
+	u8 reg;
 
 	if (mode < REG_PARAMETER && mode > REG_CAPTURE)
 		return ret;
 
-	ret = m5mols_read(sd, SYSTEM_SYSMODE, &reg);
+	ret = m5mols_read_u8(sd, SYSTEM_SYSMODE, &reg);
 	if ((!ret && reg == mode) || ret)
 		return ret;
 
@@ -348,28 +390,24 @@ static int m5mols_get_version(struct v4l2_subdev *sd)
 		struct m5mols_version ver;
 		u8 bytes[VERSION_SIZE];
 	} version;
-	u32 *value;
 	u8 cmd = CAT0_VER_CUSTOMER;
 	int ret;
 
 	do {
-		value = (u32 *)&version.bytes[cmd];
-		ret = m5mols_read(sd, SYSTEM_CMD(cmd), value);
+		ret = m5mols_read_u8(sd, SYSTEM_CMD(cmd), &version.bytes[cmd]);
 		if (ret)
 			return ret;
 	} while (cmd++ != CAT0_VER_AWB);
 
 	do {
-		value = (u32 *)&version.bytes[cmd];
-		ret = m5mols_read(sd, SYSTEM_VER_STRING, value);
+		ret = m5mols_read_u8(sd, SYSTEM_VER_STRING, &version.bytes[cmd]);
 		if (ret)
 			return ret;
 		if (cmd >= VERSION_SIZE - 1)
 			return -EINVAL;
 	} while (version.bytes[cmd++]);
 
-	value = (u32 *)&version.bytes[cmd];
-	ret = m5mols_read(sd, AF_VERSION, value);
+	ret = m5mols_read_u8(sd, AF_VERSION, &version.bytes[cmd]);
 	if (ret)
 		return ret;
 
@@ -722,7 +760,7 @@ static int m5mols_init_controls(struct m5mols_info *info)
 	int ret;
 
 	/* Determine value's range & step of controls for various FW version */
-	ret = m5mols_read(sd, AE_MAX_GAIN_MON, (u32 *)&max_exposure);
+	ret = m5mols_read_u16(sd, AE_MAX_GAIN_MON, &max_exposure);
 	if (!ret)
 		step_zoom = is_manufacturer(info, REG_SAMSUNG_OPTICS) ? 31 : 1;
 	if (ret)
@@ -842,18 +880,18 @@ static void m5mols_irq_work(struct work_struct *work)
 	struct m5mols_info *info =
 		container_of(work, struct m5mols_info, work_irq);
 	struct v4l2_subdev *sd = &info->sd;
-	u32 reg;
+	u8 reg;
 	int ret;
 
 	if (!is_powered(info) ||
-			m5mols_read(sd, SYSTEM_INT_FACTOR, &info->interrupt))
+			m5mols_read_u8(sd, SYSTEM_INT_FACTOR, &info->interrupt))
 		return;
 
 	switch (info->interrupt & REG_INT_MASK) {
 	case REG_INT_AF:
 		if (!is_available_af(info))
 			break;
-		ret = m5mols_read(sd, AF_STATUS, &reg);
+		ret = m5mols_read_u8(sd, AF_STATUS, &reg);
 		v4l2_dbg(2, m5mols_debug, sd, "AF %s\n",
 			 reg == REG_AF_FAIL ? "Failed" :
 			 reg == REG_AF_SUCCESS ? "Success" :
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 3/5] m5mols: remove union in the m5mols_get_version(), and VERSION_SIZE
  2011-05-25 13:54         ` Sakari Ailus
                             ` (3 preceding siblings ...)
  2011-05-27 12:58           ` [PATCH 2/5] m5mols: add m5mols_read_u8/u16/u32() according to I2C byte width HeungJun, Kim
@ 2011-05-27 12:58           ` HeungJun, Kim
  2011-05-27 12:58           ` [PATCH 4/5] m5mols: rename m5mols_capture_error_handler() to proper name HeungJun, Kim
  2011-05-27 12:58           ` [PATCH 5/5] m5mols: add parenthesis <> for the head and back of email address HeungJun, Kim
  6 siblings, 0 replies; 34+ messages in thread
From: HeungJun, Kim @ 2011-05-27 12:58 UTC (permalink / raw)
  To: linux-media
  Cc: mchehab, s.nawrocki, sakari.ailus, HeungJun, Kim, Kyungmin Park

Remove union version in the m5mols_get_version(), and read version information
directly. Also remove VERSION_SIZE.

Signed-off-by: HeungJun, Kim <riverful.kim@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 drivers/media/video/m5mols/m5mols.h      |    1 -
 drivers/media/video/m5mols/m5mols_core.c |   42 +++++++++++++++---------------
 drivers/media/video/m5mols/m5mols_reg.h  |   13 ++++++++-
 3 files changed, 33 insertions(+), 23 deletions(-)

diff --git a/drivers/media/video/m5mols/m5mols.h b/drivers/media/video/m5mols/m5mols.h
index dbe8928..9ae1709 100644
--- a/drivers/media/video/m5mols/m5mols.h
+++ b/drivers/media/video/m5mols/m5mols.h
@@ -154,7 +154,6 @@ struct m5mols_version {
 	u8	str[VERSION_STRING_SIZE];
 	u8	af;
 };
-#define VERSION_SIZE sizeof(struct m5mols_version)
 
 /**
  * struct m5mols_info - M-5MOLS driver data structure
diff --git a/drivers/media/video/m5mols/m5mols_core.c b/drivers/media/video/m5mols/m5mols_core.c
index 2b1f23f..8ccab95 100644
--- a/drivers/media/video/m5mols/m5mols_core.c
+++ b/drivers/media/video/m5mols/m5mols_core.c
@@ -386,33 +386,33 @@ int m5mols_mode(struct m5mols_info *info, u8 mode)
 static int m5mols_get_version(struct v4l2_subdev *sd)
 {
 	struct m5mols_info *info = to_m5mols(sd);
-	union {
-		struct m5mols_version ver;
-		u8 bytes[VERSION_SIZE];
-	} version;
-	u8 cmd = CAT0_VER_CUSTOMER;
+	struct m5mols_version *ver = &info->ver;
+	u8 *str = ver->str;
+	int i;
 	int ret;
 
-	do {
-		ret = m5mols_read_u8(sd, SYSTEM_CMD(cmd), &version.bytes[cmd]);
-		if (ret)
-			return ret;
-	} while (cmd++ != CAT0_VER_AWB);
+	ret = m5mols_read_u8(sd, SYSTEM_VER_CUSTOMER, &ver->customer);
+	if (!ret)
+		ret = m5mols_read_u8(sd, SYSTEM_VER_PROJECT, &ver->project);
+	if (!ret)
+		ret = m5mols_read_u16(sd, SYSTEM_VER_FIRMWARE, &ver->fw);
+	if (!ret)
+		ret = m5mols_read_u16(sd, SYSTEM_VER_HARDWARE, &ver->hw);
+	if (!ret)
+		ret = m5mols_read_u16(sd, SYSTEM_VER_PARAMETER, &ver->param);
+	if (!ret)
+		ret = m5mols_read_u16(sd, SYSTEM_VER_AWB, &ver->awb);
+	if (!ret)
+		ret = m5mols_read_u8(sd, AF_VERSION, &ver->af);
+	if (ret)
+		return ret;
 
-	do {
-		ret = m5mols_read_u8(sd, SYSTEM_VER_STRING, &version.bytes[cmd]);
+	for (i = 0; i < VERSION_STRING_SIZE; i++) {
+		ret = m5mols_read_u8(sd, SYSTEM_VER_STRING, &str[i]);
 		if (ret)
 			return ret;
-		if (cmd >= VERSION_SIZE - 1)
-			return -EINVAL;
-	} while (version.bytes[cmd++]);
-
-	ret = m5mols_read_u8(sd, AF_VERSION, &version.bytes[cmd]);
-	if (ret)
-		return ret;
+	}
 
-	/* store version information swapped for being readable */
-	info->ver	= version.ver;
 	info->ver.fw	= be16_to_cpu(info->ver.fw);
 	info->ver.hw	= be16_to_cpu(info->ver.hw);
 	info->ver.param	= be16_to_cpu(info->ver.param);
diff --git a/drivers/media/video/m5mols/m5mols_reg.h b/drivers/media/video/m5mols/m5mols_reg.h
index 8260f50..5f5bdcf 100644
--- a/drivers/media/video/m5mols/m5mols_reg.h
+++ b/drivers/media/video/m5mols/m5mols_reg.h
@@ -56,13 +56,24 @@
  * more specific contents, see definition if file m5mols.h.
  */
 #define CAT0_VER_CUSTOMER	0x00	/* customer version */
-#define CAT0_VER_AWB		0x09	/* Auto WB version */
+#define CAT0_VER_PROJECT	0x01	/* project version */
+#define CAT0_VER_FIRMWARE	0x02	/* Firmware version */
+#define CAT0_VER_HARDWARE	0x04	/* Hardware version */
+#define CAT0_VER_PARAMETER	0x06	/* Parameter version */
+#define CAT0_VER_AWB		0x08	/* Auto WB version */
 #define CAT0_VER_STRING		0x0a	/* string including M-5MOLS */
 #define CAT0_SYSMODE		0x0b	/* SYSTEM mode register */
 #define CAT0_STATUS		0x0c	/* SYSTEM mode status register */
 #define CAT0_INT_FACTOR		0x10	/* interrupt pending register */
 #define CAT0_INT_ENABLE		0x11	/* interrupt enable register */
 
+#define SYSTEM_VER_CUSTOMER	I2C_REG(CAT_SYSTEM, CAT0_VER_CUSTOMER, 1)
+#define SYSTEM_VER_PROJECT	I2C_REG(CAT_SYSTEM, CAT0_VER_PROJECT, 1)
+#define SYSTEM_VER_FIRMWARE	I2C_REG(CAT_SYSTEM, CAT0_VER_FIRMWARE, 2)
+#define SYSTEM_VER_HARDWARE	I2C_REG(CAT_SYSTEM, CAT0_VER_HARDWARE, 2)
+#define SYSTEM_VER_PARAMETER	I2C_REG(CAT_SYSTEM, CAT0_VER_PARAMETER, 2)
+#define SYSTEM_VER_AWB		I2C_REG(CAT_SYSTEM, CAT0_VER_AWB, 2)
+
 #define SYSTEM_SYSMODE		I2C_REG(CAT_SYSTEM, CAT0_SYSMODE, 1)
 #define REG_SYSINIT		0x00	/* SYSTEM mode */
 #define REG_PARAMETER		0x01	/* PARAMETER mode */
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 4/5] m5mols: rename m5mols_capture_error_handler() to proper name
  2011-05-25 13:54         ` Sakari Ailus
                             ` (4 preceding siblings ...)
  2011-05-27 12:58           ` [PATCH 3/5] m5mols: remove union in the m5mols_get_version(), and VERSION_SIZE HeungJun, Kim
@ 2011-05-27 12:58           ` HeungJun, Kim
  2011-05-27 12:58           ` [PATCH 5/5] m5mols: add parenthesis <> for the head and back of email address HeungJun, Kim
  6 siblings, 0 replies; 34+ messages in thread
From: HeungJun, Kim @ 2011-05-27 12:58 UTC (permalink / raw)
  To: linux-media
  Cc: mchehab, s.nawrocki, sakari.ailus, HeungJun, Kim, Kyungmin Park

The m5mols_capture_post_work() is collecting works after capture. The order should
be kept, and it's safe to say success of capture when all this conditions are fine.

Signed-off-by: HeungJun, Kim <riverful.kim@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 drivers/media/video/m5mols/m5mols_capture.c |   17 ++++++++++++-----
 1 files changed, 12 insertions(+), 5 deletions(-)

diff --git a/drivers/media/video/m5mols/m5mols_capture.c b/drivers/media/video/m5mols/m5mols_capture.c
index 751f459..8436105 100644
--- a/drivers/media/video/m5mols/m5mols_capture.c
+++ b/drivers/media/video/m5mols/m5mols_capture.c
@@ -31,22 +31,29 @@
 #include "m5mols.h"
 #include "m5mols_reg.h"
 
-static int m5mols_capture_error_handler(struct m5mols_info *info,
-					int timeout)
+/**
+ * m5mols_capture_post_work - Handle post work after capture interrupt occur
+ *
+ * Return 0 if all condition associated with capture is fine.
+ */
+static int m5mols_capture_post_work(struct m5mols_info *info, int timeout)
 {
 	int ret;
 
-	/* Disable all interrupts and clear relevant interrupt staus bits */
+	/* First, disable capture interrupt */
 	ret = m5mols_write(&info->sd, SYSTEM_INT_ENABLE,
 			   info->interrupt & ~(REG_INT_CAPTURE));
 	if (ret)
 		return ret;
 
+	/* Then, if timeout is exhasted, return ETIMEDOUT */
 	if (timeout == 0)
 		return -ETIMEDOUT;
 
+	/* All condition is satisfied, return 0 */
 	return 0;
 }
+
 /**
  * m5mols_read_rational - I2C read of a rational number
  *
@@ -150,7 +157,7 @@ int m5mols_start_capture(struct m5mols_info *info)
 					   test_bit(ST_CAPT_IRQ, &info->flags),
 					   msecs_to_jiffies(2000));
 		if (test_and_clear_bit(ST_CAPT_IRQ, &info->flags))
-			ret = m5mols_capture_error_handler(info, timeout);
+			ret = m5mols_capture_post_work(info, timeout);
 	}
 	if (!ret)
 		ret = m5mols_lock_3a(info, false);
@@ -187,5 +194,5 @@ int m5mols_start_capture(struct m5mols_info *info)
 		}
 	}
 
-	return m5mols_capture_error_handler(info, timeout);
+	return m5mols_capture_post_work(info, timeout);
 }
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 5/5] m5mols: add parenthesis <> for the head and back of email address
  2011-05-25 13:54         ` Sakari Ailus
                             ` (5 preceding siblings ...)
  2011-05-27 12:58           ` [PATCH 4/5] m5mols: rename m5mols_capture_error_handler() to proper name HeungJun, Kim
@ 2011-05-27 12:58           ` HeungJun, Kim
  6 siblings, 0 replies; 34+ messages in thread
From: HeungJun, Kim @ 2011-05-27 12:58 UTC (permalink / raw)
  To: linux-media
  Cc: mchehab, s.nawrocki, sakari.ailus, HeungJun, Kim, Kyungmin Park

Signed-off-by: HeungJun, Kim <riverful.kim@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 drivers/media/video/m5mols/m5mols.h          |    4 ++--
 drivers/media/video/m5mols/m5mols_capture.c  |    4 ++--
 drivers/media/video/m5mols/m5mols_controls.c |    4 ++--
 drivers/media/video/m5mols/m5mols_core.c     |    4 ++--
 drivers/media/video/m5mols/m5mols_reg.h      |    4 ++--
 include/media/m5mols.h                       |    4 ++--
 6 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/drivers/media/video/m5mols/m5mols.h b/drivers/media/video/m5mols/m5mols.h
index 9ae1709..89d09a8 100644
--- a/drivers/media/video/m5mols/m5mols.h
+++ b/drivers/media/video/m5mols/m5mols.h
@@ -2,10 +2,10 @@
  * Header for M-5MOLS 8M Pixel camera sensor with ISP
  *
  * Copyright (C) 2011 Samsung Electronics Co., Ltd.
- * Author: HeungJun Kim, riverful.kim@samsung.com
+ * Author: HeungJun Kim <riverful.kim@samsung.com>
  *
  * Copyright (C) 2009 Samsung Electronics Co., Ltd.
- * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com
+ * Author: Dongsoo Nathaniel Kim <dongsoo45.kim@samsung.com>
  *
  * 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
diff --git a/drivers/media/video/m5mols/m5mols_capture.c b/drivers/media/video/m5mols/m5mols_capture.c
index 8436105..d91a67e 100644
--- a/drivers/media/video/m5mols/m5mols_capture.c
+++ b/drivers/media/video/m5mols/m5mols_capture.c
@@ -2,10 +2,10 @@
  * The Capture code for Fujitsu M-5MOLS ISP
  *
  * Copyright (C) 2011 Samsung Electronics Co., Ltd.
- * Author: HeungJun Kim, riverful.kim@samsung.com
+ * Author: HeungJun Kim <riverful.kim@samsung.com>
  *
  * Copyright (C) 2009 Samsung Electronics Co., Ltd.
- * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com
+ * Author: Dongsoo Nathaniel Kim <dongsoo45.kim@samsung.com>
  *
  * 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
diff --git a/drivers/media/video/m5mols/m5mols_controls.c b/drivers/media/video/m5mols/m5mols_controls.c
index d392c83..d135d20 100644
--- a/drivers/media/video/m5mols/m5mols_controls.c
+++ b/drivers/media/video/m5mols/m5mols_controls.c
@@ -2,10 +2,10 @@
  * Controls for M-5MOLS 8M Pixel camera sensor with ISP
  *
  * Copyright (C) 2011 Samsung Electronics Co., Ltd.
- * Author: HeungJun Kim, riverful.kim@samsung.com
+ * Author: HeungJun Kim <riverful.kim@samsung.com>
  *
  * Copyright (C) 2009 Samsung Electronics Co., Ltd.
- * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com
+ * Author: Dongsoo Nathaniel Kim <dongsoo45.kim@samsung.com>
  *
  * 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
diff --git a/drivers/media/video/m5mols/m5mols_core.c b/drivers/media/video/m5mols/m5mols_core.c
index 8ccab95..e2c8a10 100644
--- a/drivers/media/video/m5mols/m5mols_core.c
+++ b/drivers/media/video/m5mols/m5mols_core.c
@@ -2,10 +2,10 @@
  * Driver for M-5MOLS 8M Pixel camera sensor with ISP
  *
  * Copyright (C) 2011 Samsung Electronics Co., Ltd.
- * Author: HeungJun Kim, riverful.kim@samsung.com
+ * Author: HeungJun Kim <riverful.kim@samsung.com>
  *
  * Copyright (C) 2009 Samsung Electronics Co., Ltd.
- * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com
+ * Author: Dongsoo Nathaniel Kim <dongsoo45.kim@samsung.com>
  *
  * 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
diff --git a/drivers/media/video/m5mols/m5mols_reg.h b/drivers/media/video/m5mols/m5mols_reg.h
index 5f5bdcf..c755bd6 100644
--- a/drivers/media/video/m5mols/m5mols_reg.h
+++ b/drivers/media/video/m5mols/m5mols_reg.h
@@ -2,10 +2,10 @@
  * Register map for M-5MOLS 8M Pixel camera sensor with ISP
  *
  * Copyright (C) 2011 Samsung Electronics Co., Ltd.
- * Author: HeungJun Kim, riverful.kim@samsung.com
+ * Author: HeungJun Kim <riverful.kim@samsung.com>
  *
  * Copyright (C) 2009 Samsung Electronics Co., Ltd.
- * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com
+ * Author: Dongsoo Nathaniel Kim <dongsoo45.kim@samsung.com>
  *
  * 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
diff --git a/include/media/m5mols.h b/include/media/m5mols.h
index 2d7e7ca..aac2c0e 100644
--- a/include/media/m5mols.h
+++ b/include/media/m5mols.h
@@ -2,10 +2,10 @@
  * Driver header for M-5MOLS 8M Pixel camera sensor with ISP
  *
  * Copyright (C) 2011 Samsung Electronics Co., Ltd.
- * Author: HeungJun Kim, riverful.kim@samsung.com
+ * Author: HeungJun Kim <riverful.kim@samsung.com>
  *
  * Copyright (C) 2009 Samsung Electronics Co., Ltd.
- * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com
+ * Author: Dongsoo Nathaniel Kim <dongsoo45.kim@samsung.com>
  *
  * 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
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH v2 0/4] Fix micellaneous issues for M-5MOLS driver
  2011-05-27 12:58           ` [PATCH 0/5] Fix micellaneous issues for M-5MOLS driver HeungJun, Kim
@ 2011-05-31  7:35             ` HeungJun, Kim
  2011-05-31  7:35             ` [PATCH v2 1/4] m5mols: Fix capture image size register definition HeungJun, Kim
                               ` (3 subsequent siblings)
  4 siblings, 0 replies; 34+ messages in thread
From: HeungJun, Kim @ 2011-05-31  7:35 UTC (permalink / raw)
  To: linux-media; +Cc: mchehab, s.nawrocki, sakari.ailus

Hello,

This is second verion of patch series to handle some issues about M-5MOLS
driver.

The difference against first patch series is as follows:

1) Add contents for 1/5.
	It should be the contents in the each patches, but I've missed it.
	So, I added the contents in the patch.

2) Discard 4/5 about changing m5mols_capture_error_handler()'s name.
	When I saw the comments about timeout variable, I agreed to Sakari's
	comments, and I would remove this. But, after thiking about that,
	It's better not to remove the timeout, and to add more comments
	about this functions's role for making more clearly.

	But, it occurs more confuseness and looks like inconsistent and
	impolite. If this patch gives confuseness to you, I apologize for
	that. It was not my inttention.

	So, my conclusion is to discard 4/5 patch for keeping the previous one.

Thanks, and any comments welcome.

Regards,
Heungjun Kim


^ permalink raw reply	[flat|nested] 34+ messages in thread

* [PATCH v2 1/4] m5mols: Fix capture image size register definition
  2011-05-27 12:58           ` [PATCH 0/5] Fix micellaneous issues for M-5MOLS driver HeungJun, Kim
  2011-05-31  7:35             ` [PATCH v2 0/4] " HeungJun, Kim
@ 2011-05-31  7:35             ` HeungJun, Kim
  2011-05-31  7:36             ` [PATCH v2 2/4] m5mols: add m5mols_read_u8/u16/u32() according to I2C byte width HeungJun, Kim
                               ` (2 subsequent siblings)
  4 siblings, 0 replies; 34+ messages in thread
From: HeungJun, Kim @ 2011-05-31  7:35 UTC (permalink / raw)
  To: linux-media
  Cc: mchehab, s.nawrocki, sakari.ailus, HeungJun, Kim, Kyungmin Park

The main capture and the thumbnail image size registers were
erroneously defined to have 1 byte width, resulting in wrong
reported image size. Fix this by changing the registers width
to correct value.

Reported-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
Signed-off-by: HeungJun, Kim <riverful.kim@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 drivers/media/video/m5mols/m5mols_reg.h |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/media/video/m5mols/m5mols_reg.h b/drivers/media/video/m5mols/m5mols_reg.h
index b83e36f..8260f50 100644
--- a/drivers/media/video/m5mols/m5mols_reg.h
+++ b/drivers/media/video/m5mols/m5mols_reg.h
@@ -382,8 +382,8 @@
 #define REG_CAP_START_MAIN	0x01
 #define REG_CAP_START_THUMB	0x03
 
-#define CAPC_IMAGE_SIZE		I2C_REG(CAT_CAPT_CTRL, CATC_CAP_IMAGE_SIZE, 1)
-#define CAPC_THUMB_SIZE		I2C_REG(CAT_CAPT_CTRL, CATC_CAP_THUMB_SIZE, 1)
+#define CAPC_IMAGE_SIZE		I2C_REG(CAT_CAPT_CTRL, CATC_CAP_IMAGE_SIZE, 4)
+#define CAPC_THUMB_SIZE		I2C_REG(CAT_CAPT_CTRL, CATC_CAP_THUMB_SIZE, 4)
 
 /*
  * Category F - Flash
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH v2 2/4] m5mols: add m5mols_read_u8/u16/u32() according to I2C byte width
  2011-05-27 12:58           ` [PATCH 0/5] Fix micellaneous issues for M-5MOLS driver HeungJun, Kim
  2011-05-31  7:35             ` [PATCH v2 0/4] " HeungJun, Kim
  2011-05-31  7:35             ` [PATCH v2 1/4] m5mols: Fix capture image size register definition HeungJun, Kim
@ 2011-05-31  7:36             ` HeungJun, Kim
  2011-05-31  7:36             ` [PATCH v2 3/4] m5mols: remove union in the m5mols_get_version(), and VERSION_SIZE HeungJun, Kim
  2011-05-31  7:36             ` [PATCH v2 4/4] m5mols: add parenthesis <> for the head and back of email address HeungJun, Kim
  4 siblings, 0 replies; 34+ messages in thread
From: HeungJun, Kim @ 2011-05-31  7:36 UTC (permalink / raw)
  To: linux-media
  Cc: mchehab, s.nawrocki, sakari.ailus, HeungJun, Kim, Kyungmin Park

For now, the m5mols_read() share in case of I2C packet 1, 2, 4 byte(s) width.
So, this commit adds 3 functions - m5mols_read_u8/u16/u32() according to byte
width of I2C packet. And, the u32 variables in spite of u8 or u16 for fitting
to m5mols_read() having no choice, is replaced to have original byte width
like u8, u16, u32 as same reason.

Signed-off-by: HeungJun, Kim <riverful.kim@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 drivers/media/video/m5mols/m5mols.h          |   52 +++++++-------
 drivers/media/video/m5mols/m5mols_capture.c  |   18 +++---
 drivers/media/video/m5mols/m5mols_controls.c |    2 +-
 drivers/media/video/m5mols/m5mols_core.c     |   96 ++++++++++++++++++--------
 4 files changed, 104 insertions(+), 64 deletions(-)

diff --git a/drivers/media/video/m5mols/m5mols.h b/drivers/media/video/m5mols/m5mols.h
index 10b55c8..dbe8928 100644
--- a/drivers/media/video/m5mols/m5mols.h
+++ b/drivers/media/video/m5mols/m5mols.h
@@ -106,23 +106,23 @@ struct m5mols_capture {
  * The each value according to each scenemode is recommended in the documents.
  */
 struct m5mols_scenemode {
-	u32 metering;
-	u32 ev_bias;
-	u32 wb_mode;
-	u32 wb_preset;
-	u32 chroma_en;
-	u32 chroma_lvl;
-	u32 edge_en;
-	u32 edge_lvl;
-	u32 af_range;
-	u32 fd_mode;
-	u32 mcc;
-	u32 light;
-	u32 flash;
-	u32 tone;
-	u32 iso;
-	u32 capt_mode;
-	u32 wdr;
+	u8 metering;
+	u8 ev_bias;
+	u8 wb_mode;
+	u8 wb_preset;
+	u8 chroma_en;
+	u8 chroma_lvl;
+	u8 edge_en;
+	u8 edge_lvl;
+	u8 af_range;
+	u8 fd_mode;
+	u8 mcc;
+	u8 light;
+	u8 flash;
+	u8 tone;
+	u8 iso;
+	u8 capt_mode;
+	u8 wdr;
 };
 
 /**
@@ -216,9 +216,9 @@ struct m5mols_info {
 	bool lock_ae;
 	bool lock_awb;
 	u8 resolution;
-	u32 interrupt;
-	u32 mode;
-	u32 mode_save;
+	u8 interrupt;
+	u8 mode;
+	u8 mode_save;
 	int (*set_power)(struct device *dev, int on);
 };
 
@@ -256,9 +256,11 @@ struct m5mols_info {
  *   +-------+---+----------+-----+------+------+------+------+
  *   - d[0..3]: according to size1
  */
-int m5mols_read(struct v4l2_subdev *sd, u32 reg_comb, u32 *val);
+int m5mols_read_u8(struct v4l2_subdev *sd, u32 reg_comb, u8 *val);
+int m5mols_read_u16(struct v4l2_subdev *sd, u32 reg_comb, u16 *val);
+int m5mols_read_u32(struct v4l2_subdev *sd, u32 reg_comb, u32 *val);
 int m5mols_write(struct v4l2_subdev *sd, u32 reg_comb, u32 val);
-int m5mols_busy(struct v4l2_subdev *sd, u8 category, u8 cmd, u32 value);
+int m5mols_busy(struct v4l2_subdev *sd, u8 category, u8 cmd, u8 value);
 
 /*
  * Mode operation of the M-5MOLS
@@ -280,12 +282,12 @@ int m5mols_busy(struct v4l2_subdev *sd, u8 category, u8 cmd, u32 value);
  * The available executing order between each modes are as follows:
  *   PARAMETER <---> MONITOR <---> CAPTURE
  */
-int m5mols_mode(struct m5mols_info *info, u32 mode);
+int m5mols_mode(struct m5mols_info *info, u8 mode);
 
-int m5mols_enable_interrupt(struct v4l2_subdev *sd, u32 reg);
+int m5mols_enable_interrupt(struct v4l2_subdev *sd, u8 reg);
 int m5mols_sync_controls(struct m5mols_info *info);
 int m5mols_start_capture(struct m5mols_info *info);
-int m5mols_do_scenemode(struct m5mols_info *info, u32 mode);
+int m5mols_do_scenemode(struct m5mols_info *info, u8 mode);
 int m5mols_lock_3a(struct m5mols_info *info, bool lock);
 int m5mols_set_ctrl(struct v4l2_ctrl *ctrl);
 
diff --git a/drivers/media/video/m5mols/m5mols_capture.c b/drivers/media/video/m5mols/m5mols_capture.c
index d71a390..751f459 100644
--- a/drivers/media/video/m5mols/m5mols_capture.c
+++ b/drivers/media/video/m5mols/m5mols_capture.c
@@ -58,9 +58,9 @@ static int m5mols_read_rational(struct v4l2_subdev *sd, u32 addr_num,
 {
 	u32 num, den;
 
-	int ret = m5mols_read(sd, addr_num, &num);
+	int ret = m5mols_read_u32(sd, addr_num, &num);
 	if (!ret)
-		ret = m5mols_read(sd, addr_den, &den);
+		ret = m5mols_read_u32(sd, addr_den, &den);
 	if (ret)
 		return ret;
 	*val = den == 0 ? 0 : num / den;
@@ -99,20 +99,20 @@ static int m5mols_capture_info(struct m5mols_info *info)
 	if (ret)
 		return ret;
 
-	ret = m5mols_read(sd, EXIF_INFO_ISO, (u32 *)&exif->iso_speed);
+	ret = m5mols_read_u16(sd, EXIF_INFO_ISO, &exif->iso_speed);
 	if (!ret)
-		ret = m5mols_read(sd, EXIF_INFO_FLASH, (u32 *)&exif->flash);
+		ret = m5mols_read_u16(sd, EXIF_INFO_FLASH, &exif->flash);
 	if (!ret)
-		ret = m5mols_read(sd, EXIF_INFO_SDR, (u32 *)&exif->sdr);
+		ret = m5mols_read_u16(sd, EXIF_INFO_SDR, &exif->sdr);
 	if (!ret)
-		ret = m5mols_read(sd, EXIF_INFO_QVAL, (u32 *)&exif->qval);
+		ret = m5mols_read_u16(sd, EXIF_INFO_QVAL, &exif->qval);
 	if (ret)
 		return ret;
 
 	if (!ret)
-		ret = m5mols_read(sd, CAPC_IMAGE_SIZE, &info->cap.main);
+		ret = m5mols_read_u32(sd, CAPC_IMAGE_SIZE, &info->cap.main);
 	if (!ret)
-		ret = m5mols_read(sd, CAPC_THUMB_SIZE, &info->cap.thumb);
+		ret = m5mols_read_u32(sd, CAPC_THUMB_SIZE, &info->cap.thumb);
 	if (!ret)
 		info->cap.total = info->cap.main + info->cap.thumb;
 
@@ -122,7 +122,7 @@ static int m5mols_capture_info(struct m5mols_info *info)
 int m5mols_start_capture(struct m5mols_info *info)
 {
 	struct v4l2_subdev *sd = &info->sd;
-	u32 resolution = info->resolution;
+	u8 resolution = info->resolution;
 	int timeout;
 	int ret;
 
diff --git a/drivers/media/video/m5mols/m5mols_controls.c b/drivers/media/video/m5mols/m5mols_controls.c
index 817c16f..d392c83 100644
--- a/drivers/media/video/m5mols/m5mols_controls.c
+++ b/drivers/media/video/m5mols/m5mols_controls.c
@@ -130,7 +130,7 @@ static struct m5mols_scenemode m5mols_default_scenemode[] = {
  *
  * WARNING: The execution order is important. Do not change the order.
  */
-int m5mols_do_scenemode(struct m5mols_info *info, u32 mode)
+int m5mols_do_scenemode(struct m5mols_info *info, u8 mode)
 {
 	struct v4l2_subdev *sd = &info->sd;
 	struct m5mols_scenemode scenemode = m5mols_default_scenemode[mode];
diff --git a/drivers/media/video/m5mols/m5mols_core.c b/drivers/media/video/m5mols/m5mols_core.c
index 76eac26..2b1f23f 100644
--- a/drivers/media/video/m5mols/m5mols_core.c
+++ b/drivers/media/video/m5mols/m5mols_core.c
@@ -133,13 +133,13 @@ static u32 m5mols_swap_byte(u8 *data, u8 length)
 /**
  * m5mols_read -  I2C read function
  * @reg: combination of size, category and command for the I2C packet
+ * @size: desired size of I2C packet
  * @val: read value
  */
-int m5mols_read(struct v4l2_subdev *sd, u32 reg, u32 *val)
+static int m5mols_read(struct v4l2_subdev *sd, u32 size, u32 reg, u32 *val)
 {
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
 	u8 rbuf[M5MOLS_I2C_MAX_SIZE + 1];
-	u8 size = I2C_SIZE(reg);
 	u8 category = I2C_CATEGORY(reg);
 	u8 cmd = I2C_COMMAND(reg);
 	struct i2c_msg msg[2];
@@ -149,11 +149,6 @@ int m5mols_read(struct v4l2_subdev *sd, u32 reg, u32 *val)
 	if (!client->adapter)
 		return -ENODEV;
 
-	if (size != 1 && size != 2 && size != 4) {
-		v4l2_err(sd, "Wrong data size\n");
-		return -EINVAL;
-	}
-
 	msg[0].addr = client->addr;
 	msg[0].flags = 0;
 	msg[0].len = 5;
@@ -184,6 +179,52 @@ int m5mols_read(struct v4l2_subdev *sd, u32 reg, u32 *val)
 	return 0;
 }
 
+int m5mols_read_u8(struct v4l2_subdev *sd, u32 reg, u8 *val)
+{
+	u32 val_32;
+	int ret;
+
+	if (I2C_SIZE(reg) != 1) {
+		v4l2_err(sd, "Wrong data size\n");
+		return -EINVAL;
+	}
+
+	ret = m5mols_read(sd, I2C_SIZE(reg), reg, &val_32);
+	if (ret)
+		return ret;
+
+	*val = (u8)val_32;
+	return ret;
+}
+
+int m5mols_read_u16(struct v4l2_subdev *sd, u32 reg, u16 *val)
+{
+	u32 val_32;
+	int ret;
+
+	if (I2C_SIZE(reg) != 2) {
+		v4l2_err(sd, "Wrong data size\n");
+		return -EINVAL;
+	}
+
+	ret = m5mols_read(sd, I2C_SIZE(reg), reg, &val_32);
+	if (ret)
+		return ret;
+
+	*val = (u16)val_32;
+	return ret;
+}
+
+int m5mols_read_u32(struct v4l2_subdev *sd, u32 reg, u32 *val)
+{
+	if (I2C_SIZE(reg) != 4) {
+		v4l2_err(sd, "Wrong data size\n");
+		return -EINVAL;
+	}
+
+	return m5mols_read(sd, I2C_SIZE(reg), reg, val);
+}
+
 /**
  * m5mols_write - I2C command write function
  * @reg: combination of size, category and command for the I2C packet
@@ -231,13 +272,14 @@ int m5mols_write(struct v4l2_subdev *sd, u32 reg, u32 val)
 	return 0;
 }
 
-int m5mols_busy(struct v4l2_subdev *sd, u8 category, u8 cmd, u32 mask)
+int m5mols_busy(struct v4l2_subdev *sd, u8 category, u8 cmd, u8 mask)
 {
-	u32 busy, i;
+	u8 busy;
+	int i;
 	int ret;
 
 	for (i = 0; i < M5MOLS_I2C_CHECK_RETRY; i++) {
-		ret = m5mols_read(sd, I2C_REG(category, cmd, 1), &busy);
+		ret = m5mols_read_u8(sd, I2C_REG(category, cmd, 1), &busy);
 		if (ret < 0)
 			return ret;
 		if ((busy & mask) == mask)
@@ -252,14 +294,14 @@ int m5mols_busy(struct v4l2_subdev *sd, u8 category, u8 cmd, u32 mask)
  * Before writing desired interrupt value the INT_FACTOR register should
  * be read to clear pending interrupts.
  */
-int m5mols_enable_interrupt(struct v4l2_subdev *sd, u32 reg)
+int m5mols_enable_interrupt(struct v4l2_subdev *sd, u8 reg)
 {
 	struct m5mols_info *info = to_m5mols(sd);
-	u32 mask = is_available_af(info) ? REG_INT_AF : 0;
-	u32 dummy;
+	u8 mask = is_available_af(info) ? REG_INT_AF : 0;
+	u8 dummy;
 	int ret;
 
-	ret = m5mols_read(sd, SYSTEM_INT_FACTOR, &dummy);
+	ret = m5mols_read_u8(sd, SYSTEM_INT_FACTOR, &dummy);
 	if (!ret)
 		ret = m5mols_write(sd, SYSTEM_INT_ENABLE, reg & ~mask);
 	return ret;
@@ -271,7 +313,7 @@ int m5mols_enable_interrupt(struct v4l2_subdev *sd, u32 reg)
  * It always accompanies a little delay changing the M-5MOLS mode, so it is
  * needed checking current busy status to guarantee right mode.
  */
-static int m5mols_reg_mode(struct v4l2_subdev *sd, u32 mode)
+static int m5mols_reg_mode(struct v4l2_subdev *sd, u8 mode)
 {
 	int ret = m5mols_write(sd, SYSTEM_SYSMODE, mode);
 
@@ -286,16 +328,16 @@ static int m5mols_reg_mode(struct v4l2_subdev *sd, u32 mode)
  * can be guaranteed only when the sensor is operating in mode which which
  * a command belongs to.
  */
-int m5mols_mode(struct m5mols_info *info, u32 mode)
+int m5mols_mode(struct m5mols_info *info, u8 mode)
 {
 	struct v4l2_subdev *sd = &info->sd;
 	int ret = -EINVAL;
-	u32 reg;
+	u8 reg;
 
 	if (mode < REG_PARAMETER && mode > REG_CAPTURE)
 		return ret;
 
-	ret = m5mols_read(sd, SYSTEM_SYSMODE, &reg);
+	ret = m5mols_read_u8(sd, SYSTEM_SYSMODE, &reg);
 	if ((!ret && reg == mode) || ret)
 		return ret;
 
@@ -348,28 +390,24 @@ static int m5mols_get_version(struct v4l2_subdev *sd)
 		struct m5mols_version ver;
 		u8 bytes[VERSION_SIZE];
 	} version;
-	u32 *value;
 	u8 cmd = CAT0_VER_CUSTOMER;
 	int ret;
 
 	do {
-		value = (u32 *)&version.bytes[cmd];
-		ret = m5mols_read(sd, SYSTEM_CMD(cmd), value);
+		ret = m5mols_read_u8(sd, SYSTEM_CMD(cmd), &version.bytes[cmd]);
 		if (ret)
 			return ret;
 	} while (cmd++ != CAT0_VER_AWB);
 
 	do {
-		value = (u32 *)&version.bytes[cmd];
-		ret = m5mols_read(sd, SYSTEM_VER_STRING, value);
+		ret = m5mols_read_u8(sd, SYSTEM_VER_STRING, &version.bytes[cmd]);
 		if (ret)
 			return ret;
 		if (cmd >= VERSION_SIZE - 1)
 			return -EINVAL;
 	} while (version.bytes[cmd++]);
 
-	value = (u32 *)&version.bytes[cmd];
-	ret = m5mols_read(sd, AF_VERSION, value);
+	ret = m5mols_read_u8(sd, AF_VERSION, &version.bytes[cmd]);
 	if (ret)
 		return ret;
 
@@ -722,7 +760,7 @@ static int m5mols_init_controls(struct m5mols_info *info)
 	int ret;
 
 	/* Determine value's range & step of controls for various FW version */
-	ret = m5mols_read(sd, AE_MAX_GAIN_MON, (u32 *)&max_exposure);
+	ret = m5mols_read_u16(sd, AE_MAX_GAIN_MON, &max_exposure);
 	if (!ret)
 		step_zoom = is_manufacturer(info, REG_SAMSUNG_OPTICS) ? 31 : 1;
 	if (ret)
@@ -842,18 +880,18 @@ static void m5mols_irq_work(struct work_struct *work)
 	struct m5mols_info *info =
 		container_of(work, struct m5mols_info, work_irq);
 	struct v4l2_subdev *sd = &info->sd;
-	u32 reg;
+	u8 reg;
 	int ret;
 
 	if (!is_powered(info) ||
-			m5mols_read(sd, SYSTEM_INT_FACTOR, &info->interrupt))
+			m5mols_read_u8(sd, SYSTEM_INT_FACTOR, &info->interrupt))
 		return;
 
 	switch (info->interrupt & REG_INT_MASK) {
 	case REG_INT_AF:
 		if (!is_available_af(info))
 			break;
-		ret = m5mols_read(sd, AF_STATUS, &reg);
+		ret = m5mols_read_u8(sd, AF_STATUS, &reg);
 		v4l2_dbg(2, m5mols_debug, sd, "AF %s\n",
 			 reg == REG_AF_FAIL ? "Failed" :
 			 reg == REG_AF_SUCCESS ? "Success" :
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH v2 3/4] m5mols: remove union in the m5mols_get_version(), and VERSION_SIZE
  2011-05-27 12:58           ` [PATCH 0/5] Fix micellaneous issues for M-5MOLS driver HeungJun, Kim
                               ` (2 preceding siblings ...)
  2011-05-31  7:36             ` [PATCH v2 2/4] m5mols: add m5mols_read_u8/u16/u32() according to I2C byte width HeungJun, Kim
@ 2011-05-31  7:36             ` HeungJun, Kim
  2011-06-05 12:03               ` Sakari Ailus
  2011-05-31  7:36             ` [PATCH v2 4/4] m5mols: add parenthesis <> for the head and back of email address HeungJun, Kim
  4 siblings, 1 reply; 34+ messages in thread
From: HeungJun, Kim @ 2011-05-31  7:36 UTC (permalink / raw)
  To: linux-media
  Cc: mchehab, s.nawrocki, sakari.ailus, HeungJun, Kim, Kyungmin Park

Remove union version in the m5mols_get_version(), and read version information
directly. Also remove VERSION_SIZE.

Signed-off-by: HeungJun, Kim <riverful.kim@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 drivers/media/video/m5mols/m5mols.h      |    1 -
 drivers/media/video/m5mols/m5mols_core.c |   42 +++++++++++++++---------------
 drivers/media/video/m5mols/m5mols_reg.h  |   13 ++++++++-
 3 files changed, 33 insertions(+), 23 deletions(-)

diff --git a/drivers/media/video/m5mols/m5mols.h b/drivers/media/video/m5mols/m5mols.h
index dbe8928..9ae1709 100644
--- a/drivers/media/video/m5mols/m5mols.h
+++ b/drivers/media/video/m5mols/m5mols.h
@@ -154,7 +154,6 @@ struct m5mols_version {
 	u8	str[VERSION_STRING_SIZE];
 	u8	af;
 };
-#define VERSION_SIZE sizeof(struct m5mols_version)
 
 /**
  * struct m5mols_info - M-5MOLS driver data structure
diff --git a/drivers/media/video/m5mols/m5mols_core.c b/drivers/media/video/m5mols/m5mols_core.c
index 2b1f23f..8ccab95 100644
--- a/drivers/media/video/m5mols/m5mols_core.c
+++ b/drivers/media/video/m5mols/m5mols_core.c
@@ -386,33 +386,33 @@ int m5mols_mode(struct m5mols_info *info, u8 mode)
 static int m5mols_get_version(struct v4l2_subdev *sd)
 {
 	struct m5mols_info *info = to_m5mols(sd);
-	union {
-		struct m5mols_version ver;
-		u8 bytes[VERSION_SIZE];
-	} version;
-	u8 cmd = CAT0_VER_CUSTOMER;
+	struct m5mols_version *ver = &info->ver;
+	u8 *str = ver->str;
+	int i;
 	int ret;
 
-	do {
-		ret = m5mols_read_u8(sd, SYSTEM_CMD(cmd), &version.bytes[cmd]);
-		if (ret)
-			return ret;
-	} while (cmd++ != CAT0_VER_AWB);
+	ret = m5mols_read_u8(sd, SYSTEM_VER_CUSTOMER, &ver->customer);
+	if (!ret)
+		ret = m5mols_read_u8(sd, SYSTEM_VER_PROJECT, &ver->project);
+	if (!ret)
+		ret = m5mols_read_u16(sd, SYSTEM_VER_FIRMWARE, &ver->fw);
+	if (!ret)
+		ret = m5mols_read_u16(sd, SYSTEM_VER_HARDWARE, &ver->hw);
+	if (!ret)
+		ret = m5mols_read_u16(sd, SYSTEM_VER_PARAMETER, &ver->param);
+	if (!ret)
+		ret = m5mols_read_u16(sd, SYSTEM_VER_AWB, &ver->awb);
+	if (!ret)
+		ret = m5mols_read_u8(sd, AF_VERSION, &ver->af);
+	if (ret)
+		return ret;
 
-	do {
-		ret = m5mols_read_u8(sd, SYSTEM_VER_STRING, &version.bytes[cmd]);
+	for (i = 0; i < VERSION_STRING_SIZE; i++) {
+		ret = m5mols_read_u8(sd, SYSTEM_VER_STRING, &str[i]);
 		if (ret)
 			return ret;
-		if (cmd >= VERSION_SIZE - 1)
-			return -EINVAL;
-	} while (version.bytes[cmd++]);
-
-	ret = m5mols_read_u8(sd, AF_VERSION, &version.bytes[cmd]);
-	if (ret)
-		return ret;
+	}
 
-	/* store version information swapped for being readable */
-	info->ver	= version.ver;
 	info->ver.fw	= be16_to_cpu(info->ver.fw);
 	info->ver.hw	= be16_to_cpu(info->ver.hw);
 	info->ver.param	= be16_to_cpu(info->ver.param);
diff --git a/drivers/media/video/m5mols/m5mols_reg.h b/drivers/media/video/m5mols/m5mols_reg.h
index 8260f50..5f5bdcf 100644
--- a/drivers/media/video/m5mols/m5mols_reg.h
+++ b/drivers/media/video/m5mols/m5mols_reg.h
@@ -56,13 +56,24 @@
  * more specific contents, see definition if file m5mols.h.
  */
 #define CAT0_VER_CUSTOMER	0x00	/* customer version */
-#define CAT0_VER_AWB		0x09	/* Auto WB version */
+#define CAT0_VER_PROJECT	0x01	/* project version */
+#define CAT0_VER_FIRMWARE	0x02	/* Firmware version */
+#define CAT0_VER_HARDWARE	0x04	/* Hardware version */
+#define CAT0_VER_PARAMETER	0x06	/* Parameter version */
+#define CAT0_VER_AWB		0x08	/* Auto WB version */
 #define CAT0_VER_STRING		0x0a	/* string including M-5MOLS */
 #define CAT0_SYSMODE		0x0b	/* SYSTEM mode register */
 #define CAT0_STATUS		0x0c	/* SYSTEM mode status register */
 #define CAT0_INT_FACTOR		0x10	/* interrupt pending register */
 #define CAT0_INT_ENABLE		0x11	/* interrupt enable register */
 
+#define SYSTEM_VER_CUSTOMER	I2C_REG(CAT_SYSTEM, CAT0_VER_CUSTOMER, 1)
+#define SYSTEM_VER_PROJECT	I2C_REG(CAT_SYSTEM, CAT0_VER_PROJECT, 1)
+#define SYSTEM_VER_FIRMWARE	I2C_REG(CAT_SYSTEM, CAT0_VER_FIRMWARE, 2)
+#define SYSTEM_VER_HARDWARE	I2C_REG(CAT_SYSTEM, CAT0_VER_HARDWARE, 2)
+#define SYSTEM_VER_PARAMETER	I2C_REG(CAT_SYSTEM, CAT0_VER_PARAMETER, 2)
+#define SYSTEM_VER_AWB		I2C_REG(CAT_SYSTEM, CAT0_VER_AWB, 2)
+
 #define SYSTEM_SYSMODE		I2C_REG(CAT_SYSTEM, CAT0_SYSMODE, 1)
 #define REG_SYSINIT		0x00	/* SYSTEM mode */
 #define REG_PARAMETER		0x01	/* PARAMETER mode */
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH v2 4/4] m5mols: add parenthesis <> for the head and back of email address
  2011-05-27 12:58           ` [PATCH 0/5] Fix micellaneous issues for M-5MOLS driver HeungJun, Kim
                               ` (3 preceding siblings ...)
  2011-05-31  7:36             ` [PATCH v2 3/4] m5mols: remove union in the m5mols_get_version(), and VERSION_SIZE HeungJun, Kim
@ 2011-05-31  7:36             ` HeungJun, Kim
  4 siblings, 0 replies; 34+ messages in thread
From: HeungJun, Kim @ 2011-05-31  7:36 UTC (permalink / raw)
  To: linux-media
  Cc: mchehab, s.nawrocki, sakari.ailus, HeungJun, Kim, Kyungmin Park

Signed-off-by: HeungJun, Kim <riverful.kim@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 drivers/media/video/m5mols/m5mols.h          |    4 ++--
 drivers/media/video/m5mols/m5mols_capture.c  |    4 ++--
 drivers/media/video/m5mols/m5mols_controls.c |    4 ++--
 drivers/media/video/m5mols/m5mols_core.c     |    4 ++--
 drivers/media/video/m5mols/m5mols_reg.h      |    4 ++--
 include/media/m5mols.h                       |    4 ++--
 6 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/drivers/media/video/m5mols/m5mols.h b/drivers/media/video/m5mols/m5mols.h
index 9ae1709..89d09a8 100644
--- a/drivers/media/video/m5mols/m5mols.h
+++ b/drivers/media/video/m5mols/m5mols.h
@@ -2,10 +2,10 @@
  * Header for M-5MOLS 8M Pixel camera sensor with ISP
  *
  * Copyright (C) 2011 Samsung Electronics Co., Ltd.
- * Author: HeungJun Kim, riverful.kim@samsung.com
+ * Author: HeungJun Kim <riverful.kim@samsung.com>
  *
  * Copyright (C) 2009 Samsung Electronics Co., Ltd.
- * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com
+ * Author: Dongsoo Nathaniel Kim <dongsoo45.kim@samsung.com>
  *
  * 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
diff --git a/drivers/media/video/m5mols/m5mols_capture.c b/drivers/media/video/m5mols/m5mols_capture.c
index 751f459..d947192 100644
--- a/drivers/media/video/m5mols/m5mols_capture.c
+++ b/drivers/media/video/m5mols/m5mols_capture.c
@@ -2,10 +2,10 @@
  * The Capture code for Fujitsu M-5MOLS ISP
  *
  * Copyright (C) 2011 Samsung Electronics Co., Ltd.
- * Author: HeungJun Kim, riverful.kim@samsung.com
+ * Author: HeungJun Kim <riverful.kim@samsung.com>
  *
  * Copyright (C) 2009 Samsung Electronics Co., Ltd.
- * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com
+ * Author: Dongsoo Nathaniel Kim <dongsoo45.kim@samsung.com>
  *
  * 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
diff --git a/drivers/media/video/m5mols/m5mols_controls.c b/drivers/media/video/m5mols/m5mols_controls.c
index d392c83..d135d20 100644
--- a/drivers/media/video/m5mols/m5mols_controls.c
+++ b/drivers/media/video/m5mols/m5mols_controls.c
@@ -2,10 +2,10 @@
  * Controls for M-5MOLS 8M Pixel camera sensor with ISP
  *
  * Copyright (C) 2011 Samsung Electronics Co., Ltd.
- * Author: HeungJun Kim, riverful.kim@samsung.com
+ * Author: HeungJun Kim <riverful.kim@samsung.com>
  *
  * Copyright (C) 2009 Samsung Electronics Co., Ltd.
- * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com
+ * Author: Dongsoo Nathaniel Kim <dongsoo45.kim@samsung.com>
  *
  * 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
diff --git a/drivers/media/video/m5mols/m5mols_core.c b/drivers/media/video/m5mols/m5mols_core.c
index 8ccab95..e2c8a10 100644
--- a/drivers/media/video/m5mols/m5mols_core.c
+++ b/drivers/media/video/m5mols/m5mols_core.c
@@ -2,10 +2,10 @@
  * Driver for M-5MOLS 8M Pixel camera sensor with ISP
  *
  * Copyright (C) 2011 Samsung Electronics Co., Ltd.
- * Author: HeungJun Kim, riverful.kim@samsung.com
+ * Author: HeungJun Kim <riverful.kim@samsung.com>
  *
  * Copyright (C) 2009 Samsung Electronics Co., Ltd.
- * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com
+ * Author: Dongsoo Nathaniel Kim <dongsoo45.kim@samsung.com>
  *
  * 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
diff --git a/drivers/media/video/m5mols/m5mols_reg.h b/drivers/media/video/m5mols/m5mols_reg.h
index 5f5bdcf..c755bd6 100644
--- a/drivers/media/video/m5mols/m5mols_reg.h
+++ b/drivers/media/video/m5mols/m5mols_reg.h
@@ -2,10 +2,10 @@
  * Register map for M-5MOLS 8M Pixel camera sensor with ISP
  *
  * Copyright (C) 2011 Samsung Electronics Co., Ltd.
- * Author: HeungJun Kim, riverful.kim@samsung.com
+ * Author: HeungJun Kim <riverful.kim@samsung.com>
  *
  * Copyright (C) 2009 Samsung Electronics Co., Ltd.
- * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com
+ * Author: Dongsoo Nathaniel Kim <dongsoo45.kim@samsung.com>
  *
  * 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
diff --git a/include/media/m5mols.h b/include/media/m5mols.h
index 2d7e7ca..aac2c0e 100644
--- a/include/media/m5mols.h
+++ b/include/media/m5mols.h
@@ -2,10 +2,10 @@
  * Driver header for M-5MOLS 8M Pixel camera sensor with ISP
  *
  * Copyright (C) 2011 Samsung Electronics Co., Ltd.
- * Author: HeungJun Kim, riverful.kim@samsung.com
+ * Author: HeungJun Kim <riverful.kim@samsung.com>
  *
  * Copyright (C) 2009 Samsung Electronics Co., Ltd.
- * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com
+ * Author: Dongsoo Nathaniel Kim <dongsoo45.kim@samsung.com>
  *
  * 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
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* Re: [PATCH v9] Add support for M-5MOLS 8 Mega Pixel camera ISP
  2011-05-26  7:12           ` Kim, HeungJun
@ 2011-06-05 11:55             ` Sakari Ailus
  2011-06-05 12:11               ` Hans Verkuil
                                 ` (2 more replies)
  0 siblings, 3 replies; 34+ messages in thread
From: Sakari Ailus @ 2011-06-05 11:55 UTC (permalink / raw)
  To: Kim, HeungJun
  Cc: linux-media, mchehab, hverkuil, laurent.pinchart,
	Sylwester Nawrocki, Kyungmin Park

On Thu, May 26, 2011 at 04:12:47PM +0900, Kim, HeungJun wrote:
> Hi Sakari,

Hi HeungJun,

> 2011-05-25 ?????? 10:54, Sakari Ailus ??? ???:
> > Hi HeungJun,
> > 
> > Thanks for the patch!
> Also, thanks for the interests of this driver!

You'we welcome! :-)

> > 
> > I'm happy to see that Samsung is interested in getting such a driver to
> > mainline. :-) I suppose that theoretically nothing would prevent plugging
> > such a sensor to the OMAP 3 ISP, for example. It's just that the sensor
> > already does image processing and the ISP might not be that useful because
> > of this. But the interfaces would match, both in software and in hardware.
> This sensor(actually integrated ISP functionality as you know) is powerful,
> and I think this driver can help to make the controls for digital camera.
> 
> But, TI OMAP 3 has also ISP independently, so I think having the ISP module
> in the Processor is more better option cause of choice of various sensors,
> although the driver's developer has more issues which should be handled.
> 
> I hope to handle ISP directly in the Samsung Processor.
> 
> > 
> > This is a subdev driver and uses the control framework. Good. I have
> > comments on the code below. 
> Before that, this driver is already merged in Mauro's branch, and
> I have spent a few months making this drivers for submitting this.
> Some of your comments looks good and needed for this driver.
> But, if I fix this and resend it and another comments happened,
> this may be endless alone fight to reach "mergeing". :)

Sounds good to me. I have a few additional comments.

> So, I want that I keep this comments in mind, and I'll guarantee the fixes
> will be adapted the next time by type of the patch, after this driver patch
> is merged fully in 3.0.
[clip]

> >> +/**
> >> + * struct m5mols_version - firmware version information
> >> + * @customer:	customer information
> >> + * @project:	version of project information according to customer
> >> + * @fw:		firmware revision
> >> + * @hw:		hardware revision
> >> + * @param:	version of the parameter
> >> + * @awb:	Auto WhiteBalance algorithm version
> >> + * @str:	information about manufacturer and packaging vendor
> >> + * @af:		Auto Focus version
> >> + *
> >> + * The register offset starts the customer version at 0x0, and it ends
> >> + * the awb version at 0x09. The customer, project information occupies 1 bytes
> >> + * each. And also the fw, hw, param, awb each requires 2 bytes. The str is
> >> + * unique string associated with firmware's version. It includes information
> >> + * about manufacturer and the vendor of the sensor's packaging. The least
> >> + * significant 2 bytes of the string indicate packaging manufacturer.
> >> + */
> >> +#define VERSION_STRING_SIZE	22
> >> +struct m5mols_version {
> >> +	u8	customer;
> >> +	u8	project;
> >> +	u16	fw;
> >> +	u16	hw;
> >> +	u16	param;
> >> +	u16	awb;
> >> +	u8	str[VERSION_STRING_SIZE];
> >> +	u8	af;
> >> +};
> >> +#define VERSION_SIZE sizeof(struct m5mols_version)
> > 
> > You're using VERSION_SIZE in two places in one function. Is that worth
> > making it a macro? :-)
> Just make for being readable. Until this 9th version of this patch,
> I'm very taking care of over 80 character and for being readable.
> Updating each version, any other word is not used MACRO, and the other word
> need more space, on each line. At the next version, it happened that
> the one not used MACRO should use MACRO. And switching like this
> will be continued repedately.
> 
> So, I think it's no meaningless to use just twice. and want to fix.
> 
> And the other thing commented as belows, are most of same reason.

I'm fine with this.

> > 
> > I think you should add attribute ((packed)) to the definition as well.
> It looks needed. I added attibute packed for this struct for the next time.
> 
> > 
> >> +/**
> >> + * struct m5mols_info - M-5MOLS driver data structure
> >> + * @pdata: platform data
> >> + * @sd: v4l-subdev instance
> >> + * @pad: media pad
> >> + * @ffmt: current fmt according to resolution type
> >> + * @res_type: current resolution type
> >> + * @code: current code
> >> + * @irq_waitq: waitqueue for the capture
> >> + * @work_irq: workqueue for the IRQ
> >> + * @flags: state variable for the interrupt handler
> >> + * @handle: control handler
> >> + * @autoexposure: Auto Exposure control
> >> + * @exposure: Exposure control
> >> + * @autowb: Auto White Balance control
> >> + * @colorfx: Color effect control
> >> + * @saturation:	Saturation control
> >> + * @zoom: Zoom control
> >> + * @ver: information of the version
> >> + * @cap: the capture mode attributes
> >> + * @power: current sensor's power status
> >> + * @ctrl_sync: true means all controls of the sensor are initialized
> >> + * @int_capture: true means the capture interrupt is issued once
> >> + * @lock_ae: true means the Auto Exposure is locked
> >> + * @lock_awb: true means the Aut WhiteBalance is locked
> >> + * @resolution:	register value for current resolution
> >> + * @interrupt: register value for current interrupt status
> >> + * @mode: register value for current operation mode
> >> + * @mode_save: register value for current operation mode for saving
> >> + * @set_power: optional power callback to the board code
> >> + */
> >> +struct m5mols_info {
> >> +	const struct m5mols_platform_data *pdata;
> >> +	struct v4l2_subdev sd;
> >> +	struct media_pad pad;
> >> +	struct v4l2_mbus_framefmt ffmt[M5MOLS_RESTYPE_MAX];
> >> +	int res_type;
> >> +	enum v4l2_mbus_pixelcode code;
> >> +	wait_queue_head_t irq_waitq;
> >> +	struct work_struct work_irq;
> >> +	unsigned long flags;
> > 
> > You want to keep flags in the stack, not in a context independent location.
> > This will move to functions which actually use it.
> I don't understand well what you mean "stack" and "context". Sorry for lack of
> English skill. Tell me more specific detailed.

Please ignore my previous comment on this. In confused this with something
else.

> And as I tell about this variable "flags", this should be shared in the two files,
> m5mols_capture.c and m5mols_core.c. 
> 
> > 
> >> +
> >> +	struct v4l2_ctrl_handler handle;
> >> +	/* Autoexposure/exposure control cluster */
> >> +	struct {
> >> +		struct v4l2_ctrl *autoexposure;
> >> +		struct v4l2_ctrl *exposure;
> >> +	};
> > 
> > Would it be different without the anonymous struct?
> These two v4l2_ctrl is clustered. So, anonymous struct should be used
> for v4l2_ctrl_cluster().

It makes no difference in how the pointers are arranged in the memory.

[clip]

> >> +	[REG_SCENE_FIRE] = {
> >> +		REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
> >> +		REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
> >> +		REG_AF_NORMAL, REG_FD_OFF,
> >> +		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
> >> +		6, REG_ISO_50, REG_CAP_NONE, REG_WDR_OFF,
> >> +	},
> >> +	[REG_SCENE_TEXT] = {
> >> +		REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
> >> +		REG_CHROMA_ON, 3, REG_EDGE_ON, 7,
> >> +		REG_AF_MACRO, REG_FD_OFF,
> >> +		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
> >> +		6, REG_ISO_AUTO, REG_CAP_ANTI_SHAKE, REG_WDR_ON,
> >> +	},
> >> +	[REG_SCENE_CANDLE] = {
> >> +		REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0,
> >> +		REG_CHROMA_ON, 3, REG_EDGE_ON, 5,
> >> +		REG_AF_NORMAL, REG_FD_OFF,
> >> +		REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF,
> >> +		6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF,
> >> +	},
> > 
> > As it seems this functionality is dynamically configurable, and much of that
> > looks like something user space might want to choose independently,
> It's not. The preset itself can be user space APIs, but the order and procedure
> should be handled in the driver or kernel, at least in this case.
> 
> The wrong order and missing command are not completed the one of scenemode.
> 
> > shouldn't the underlying low level controls be exposed to user space as
> > such?
> > 
> > There definitely are different approaches to this; providing higher level
> > interface is restricting but on the other hand it may be better depending on
> > an application.
> > 
> > Some of these parameters would already have a V4L2 control for them.
> Actually, I have a plan to prepare RFC to expose some controls and
> things related with control timing. :)

I'm looking forward to this. Control timing is also something I'm interested
in.

> Briefly saying that, the current control framework can handle independently,
> but it's possible to handle any control attached the other control(s).
> So, some controls to supposed to be attached is combined with final specific control,
> or IOCTL, and if any specific control is called by user, the other controls
> is doing by orderly.
> 
> The benefit of such thing, is easy to handle controls in the user defined
> circumstance like camera, more user-friendly. And, at the kernel or driver side,
> it's supported only things which the device proivde.
> The driver's code will be simple and the user is happy.
> 
> Anyway, this is not part of this story, so let's talk about this later.

[clip]

> >> diff --git a/drivers/media/video/m5mols/m5mols_core.c b/drivers/media/video/m5mols/m5mols_core.c
> >> new file mode 100644
> >> index 0000000..76eac26
> >> --- /dev/null
> >> +++ b/drivers/media/video/m5mols/m5mols_core.c
> >> @@ -0,0 +1,1004 @@
> >> +/*
> >> + * Driver for M-5MOLS 8M Pixel camera sensor with ISP
> >> + *
> >> + * Copyright (C) 2011 Samsung Electronics Co., Ltd.
> >> + * Author: HeungJun Kim, riverful.kim@samsung.com
> >> + *
> >> + * Copyright (C) 2009 Samsung Electronics Co., Ltd.
> >> + * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com
> >> + *
> >> + * 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/slab.h>
> >> +#include <linux/irq.h>
> >> +#include <linux/interrupt.h>
> >> +#include <linux/delay.h>
> >> +#include <linux/version.h>
> >> +#include <linux/gpio.h>
> >> +#include <linux/regulator/consumer.h>
> >> +#include <linux/videodev2.h>
> >> +#include <media/v4l2-ctrls.h>
> >> +#include <media/v4l2-device.h>
> >> +#include <media/v4l2-subdev.h>
> >> +#include <media/m5mols.h>
> >> +
> >> +#include "m5mols.h"
> >> +#include "m5mols_reg.h"
> >> +
> >> +int m5mols_debug;
> >> +module_param(m5mols_debug, int, 0644);
> >> +
> >> +#define MODULE_NAME		"M5MOLS"
> >> +#define M5MOLS_I2C_CHECK_RETRY	500
> >> +
> >> +/* The regulator consumer names for external voltage regulators */
> >> +static struct regulator_bulk_data supplies[] = {
> >> +	{
> >> +		.supply = "core",	/* ARM core power, 1.2V */
> >> +	}, {
> >> +		.supply	= "dig_18",	/* digital power 1, 1.8V */
> >> +	}, {
> >> +		.supply	= "d_sensor",	/* sensor power 1, 1.8V */
> >> +	}, {
> >> +		.supply	= "dig_28",	/* digital power 2, 2.8V */
> >> +	}, {
> >> +		.supply	= "a_sensor",	/* analog power */
> >> +	}, {
> >> +		.supply	= "dig_12",	/* digital power 3, 1.2V */
> >> +	},
> >> +};
> > 
> > This looks like something that belongs to board code, or perhaps in the near
> > future, to the device tree. The power supplies that are required by a device
> > is highly board dependent.
> If the regulator name is not common all M-5MOLS, You're right.
> But the regulator name of M-5MOLS is fixed.

As far as I understand, M-5MOLS is a sensor which you can, in principle,
attach to more or less random hardware. The regulators are not part of the
sensor. If someone adds a board which has regulators names or otherwise
arranged differently, this change must be done at that time.

> The benefit fixed in the driver helps that the developer can attach the driver,
> if he/she knows the names of regulators.
> 
> Commonly, you're right, but in this case(documented accurately with M-5MOLS
> datasheet), it's better.
>  
[clip]

> >> +int m5mols_write(struct v4l2_subdev *sd, u32 reg, u32 val)
> >> +{
> >> +	struct i2c_client *client = v4l2_get_subdevdata(sd);
> >> +	u8 wbuf[M5MOLS_I2C_MAX_SIZE + 4];
> >> +	u8 category = I2C_CATEGORY(reg);
> >> +	u8 cmd = I2C_COMMAND(reg);
> >> +	u8 size	= I2C_SIZE(reg);
> >> +	u32 *buf = (u32 *)&wbuf[4];
> >> +	struct i2c_msg msg[1];
> >> +	int ret;
> >> +
> >> +	if (!client->adapter)
> >> +		return -ENODEV;
> >> +
> >> +	if (size != 1 && size != 2 && size != 4) {
> > 
> > You could define sizes for the types, e.g.
> > 
> > #define I2C_SIZE_U8	1
> I already try to do like this, using width as definition.
> But it makes many lines over 80 characters. Moreover, using just number
> is more simple in this case.

Wrapping lines is also possible but sometimes it's just better not to.

> Ditto - over 80 characters.
> 
> > 
> >> +		v4l2_err(sd, "Wrong data size\n");
> >> +		return -EINVAL;
> >> +	}
> >> +
> >> +	msg->addr = client->addr;
> >> +	msg->flags = 0;
> >> +	msg->len = (u16)size + 4;
> >> +	msg->buf = wbuf;
> >> +	wbuf[0] = size + 4;
> >> +	wbuf[1] = M5MOLS_BYTE_WRITE;
> >> +	wbuf[2] = category;
> >> +	wbuf[3] = cmd;
> >> +
> >> +	*buf = m5mols_swap_byte((u8 *)&val, size);
> >> +
> >> +	usleep_range(200, 200);
> > 
> > Why to sleep always? Does the sensor require a delay between each I2C
> > access?
> It's experimental values. The M-5MOLS I2C communication is a litte sensitive,
> and I expect that this sensor is integrated with another ARM-core and internal
> Firmware, and ARM-core's performance is not good. So, the dealy should be needed.

Perhaps a comment telling this would be good?

> 
> > 
> >> +	ret = i2c_transfer(client->adapter, msg, 1);
> >> +	if (ret < 0) {
> >> +		v4l2_err(sd, "write failed: size:%d cat:%02x cmd:%02x. %d\n",
> >> +			size, category, cmd, ret);
> >> +		return ret;
> >> +	}
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +int m5mols_busy(struct v4l2_subdev *sd, u8 category, u8 cmd, u32 mask)
> >> +{
> >> +	u32 busy, i;
> >> +	int ret;
> >> +
> >> +	for (i = 0; i < M5MOLS_I2C_CHECK_RETRY; i++) {
> >> +		ret = m5mols_read(sd, I2C_REG(category, cmd, 1), &busy);
> >> +		if (ret < 0)
> >> +			return ret;
> >> +		if ((busy & mask) == mask)
> >> +			return 0;
> >> +	}
> >> +	return -EBUSY;
> >> +}
> >> +
> >> +/**
> >> + * m5mols_enable_interrupt - Clear interrupt pending bits and unmask interrupts
> >> + *
> >> + * Before writing desired interrupt value the INT_FACTOR register should
> >> + * be read to clear pending interrupts.
> >> + */
> >> +int m5mols_enable_interrupt(struct v4l2_subdev *sd, u32 reg)
> >> +{
> >> +	struct m5mols_info *info = to_m5mols(sd);
> >> +	u32 mask = is_available_af(info) ? REG_INT_AF : 0;
> >> +	u32 dummy;
> >> +	int ret;
> >> +
> >> +	ret = m5mols_read(sd, SYSTEM_INT_FACTOR, &dummy);
> >> +	if (!ret)
> >> +		ret = m5mols_write(sd, SYSTEM_INT_ENABLE, reg & ~mask);
> >> +	return ret;
> >> +}
> >> +
> >> +/**
> >> + * m5mols_reg_mode - Write the mode and check busy status
> >> + *
> >> + * It always accompanies a little delay changing the M-5MOLS mode, so it is
> >> + * needed checking current busy status to guarantee right mode.
> >> + */
> >> +static int m5mols_reg_mode(struct v4l2_subdev *sd, u32 mode)
> >> +{
> >> +	int ret = m5mols_write(sd, SYSTEM_SYSMODE, mode);
> >> +
> >> +	return ret ? ret : m5mols_busy(sd, CAT_SYSTEM, CAT0_SYSMODE, mode);
> >> +}
> >> +
> >> +/**
> >> + * m5mols_mode - manage the M-5MOLS's mode
> >> + * @mode: the required operation mode
> >> + *
> >> + * The commands of M-5MOLS are grouped into specific modes. Each functionality
> >> + * can be guaranteed only when the sensor is operating in mode which which
> >> + * a command belongs to.
> >> + */
> >> +int m5mols_mode(struct m5mols_info *info, u32 mode)
> >> +{
> >> +	struct v4l2_subdev *sd = &info->sd;
> >> +	int ret = -EINVAL;
> >> +	u32 reg;
> >> +
> >> +	if (mode < REG_PARAMETER && mode > REG_CAPTURE)
> >> +		return ret;
> >> +
> >> +	ret = m5mols_read(sd, SYSTEM_SYSMODE, &reg);
> >> +	if ((!ret && reg == mode) || ret)
> >> +		return ret;
> >> +
> >> +	switch (reg) {
> >> +	case REG_PARAMETER:
> >> +		ret = m5mols_reg_mode(sd, REG_MONITOR);
> >> +		if (!ret && mode == REG_MONITOR)
> >> +			break;
> >> +		if (!ret)
> >> +			ret = m5mols_reg_mode(sd, REG_CAPTURE);
> >> +		break;
> >> +
> >> +	case REG_MONITOR:
> >> +		if (mode == REG_PARAMETER) {
> >> +			ret = m5mols_reg_mode(sd, REG_PARAMETER);
> >> +			break;
> >> +		}
> >> +
> >> +		ret = m5mols_reg_mode(sd, REG_CAPTURE);
> >> +		break;
> >> +
> >> +	case REG_CAPTURE:
> >> +		ret = m5mols_reg_mode(sd, REG_MONITOR);
> >> +		if (!ret && mode == REG_MONITOR)
> >> +			break;
> >> +		if (!ret)
> >> +			ret = m5mols_reg_mode(sd, REG_PARAMETER);
> >> +		break;
> >> +
> >> +	default:
> >> +		v4l2_warn(sd, "Wrong mode: %d\n", mode);
> >> +	}
> >> +
> >> +	if (!ret)
> >> +		info->mode = mode;
> >> +
> >> +	return ret;
> >> +}
> >> +
> >> +/**
> >> + * m5mols_get_version - retrieve full revisions information of M-5MOLS
> >> + *
> >> + * The version information includes revisions of hardware and firmware,
> >> + * AutoFocus alghorithm version and the version string.
> >> + */
> >> +static int m5mols_get_version(struct v4l2_subdev *sd)
> >> +{
> >> +	struct m5mols_info *info = to_m5mols(sd);
> >> +	union {
> >> +		struct m5mols_version ver;
> >> +		u8 bytes[VERSION_SIZE];
> > 
> > You could even use u8 bytes[0] if you really need this union.
> > 
> > ((char *)&ver)[cmd], for example.
> > 
> >> +	} version;
> >> +	u32 *value;
> >> +	u8 cmd = CAT0_VER_CUSTOMER;
> >> +	int ret;
> >> +
> >> +	do {
> >> +		value = (u32 *)&version.bytes[cmd];
> >> +		ret = m5mols_read(sd, SYSTEM_CMD(cmd), value);
> >> +		if (ret)
> >> +			return ret;
> >> +	} while (cmd++ != CAT0_VER_AWB);
> >> +
> >> +	do {
> >> +		value = (u32 *)&version.bytes[cmd];
> >> +		ret = m5mols_read(sd, SYSTEM_VER_STRING, value);
> >> +		if (ret)
> >> +			return ret;
> >> +		if (cmd >= VERSION_SIZE - 1)
> >> +			return -EINVAL;
> >> +	} while (version.bytes[cmd++]);
> > 
> > 
> > Please move cmd++ outside the condition.
> Ok, I'll keep that.
> 
> > 
> > I think you should have a different function to read and write different
> > types, e.g. m5mols_read_u8 and m5mols_read_u16.
> As I said, this is good option and it will be changed like your recommendation.
> I very consider this usage at the next time.
> 
> > 
> > You have the access width encoded in the register value. That would still be
> > checked by the function.
> > 
> > You also could subtract CAT0_VER_CUSTOMER from cmd when using it as index
> > to bytes[] above, as you now rely that it is actually zero.
> > 
> >> +
> >> +	value = (u32 *)&version.bytes[cmd];
> >> +	ret = m5mols_read(sd, AF_VERSION, value);
> > 
> > I think it'd be cleaner to refer to (u32 *)&version.bytes[cmd] directly
> > instead of using value.
> > 
> > Can you be sure that cmd points to the AF version here?
> > 
> >> +	if (ret)
> >> +		return ret;
> >> +
> >> +	/* store version information swapped for being readable */
> >> +	info->ver	= version.ver;
> > 
> > You could use info->ver straight away and drop version.ver.
> Ok, but this is also making me hard. It's been always stucked by over 80 character.
> And, I choose this the best.
> But, another option is appeared, I'll consider this again.
> 
> > 
> >> +	info->ver.fw	= be16_to_cpu(info->ver.fw);
> >> +	info->ver.hw	= be16_to_cpu(info->ver.hw);
> >> +	info->ver.param	= be16_to_cpu(info->ver.param);
> >> +	info->ver.awb	= be16_to_cpu(info->ver.awb);
> >> +
> >> +	v4l2_info(sd, "Manufacturer\t[%s]\n",
> >> +			is_manufacturer(info, REG_SAMSUNG_ELECTRO) ?
> >> +			"Samsung Electro-Machanics" :
> >> +			is_manufacturer(info, REG_SAMSUNG_OPTICS) ?
> >> +			"Samsung Fiber-Optics" :
> >> +			is_manufacturer(info, REG_SAMSUNG_TECHWIN) ?
> >> +			"Samsung Techwin" : "None");
> >> +	v4l2_info(sd, "Customer/Project\t[0x%02x/0x%02x]\n",
> >> +			info->ver.customer, info->ver.project);
> >> +
> >> +	if (!is_available_af(info))
> >> +		v4l2_info(sd, "No support Auto Focus on this firmware\n");
> >> +
> >> +	return ret;
> >> +}
> >> +
> >> +/**
> >> + * __find_restype - Lookup M-5MOLS resolution type according to pixel code
> >> + * @code: pixel code
> >> + */
> >> +static enum m5mols_restype __find_restype(enum v4l2_mbus_pixelcode code)
> >> +{
> >> +	enum m5mols_restype type = M5MOLS_RESTYPE_MONITOR;
> >> +
> >> +	do {
> >> +		if (code == m5mols_default_ffmt[type].code)
> >> +			return type;
> > 
> > type++ here, and ++ off of the condition below.
> Ok, I'll adapt this for the next time.
> 
> > 
> >> +	} while (type++ != SIZE_DEFAULT_FFMT);
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +/**
> >> + * __find_resolution - Lookup preset and type of M-5MOLS's resolution
> >> + * @mf: pixel format to find/negotiate the resolution preset for
> >> + * @type: M-5MOLS resolution type
> >> + * @resolution:	M-5MOLS resolution preset register value
> >> + *
> >> + * Find nearest resolution matching resolution preset and adjust mf
> >> + * to supported values.
> >> + */
> >> +static int __find_resolution(struct v4l2_subdev *sd,
> >> +			     struct v4l2_mbus_framefmt *mf,
> >> +			     enum m5mols_restype *type,
> >> +			     u32 *resolution)
> >> +{
> >> +	const struct m5mols_resolution *fsize = &m5mols_reg_res[0];
> >> +	const struct m5mols_resolution *match = NULL;
> >> +	enum m5mols_restype stype = __find_restype(mf->code);
> >> +	int i = ARRAY_SIZE(m5mols_reg_res);
> >> +	unsigned int min_err = ~0;
> >> +
> >> +	while (i--) {
> >> +		int err;
> >> +		if (stype == fsize->type) {
> >> +			err = abs(fsize->width - mf->width)
> >> +				+ abs(fsize->height - mf->height);
> >> +
> >> +			if (err < min_err) {
> >> +				min_err = err;
> >> +				match = fsize;
> >> +			}
> >> +		}
> >> +		fsize++;
> >> +	}
> >> +	if (match) {
> >> +		mf->width  = match->width;
> >> +		mf->height = match->height;
> >> +		*resolution = match->reg;
> >> +		*type = stype;
> >> +		return 0;
> >> +	}
> >> +
> >> +	return -EINVAL;
> >> +}
> >> +
> >> +static struct v4l2_mbus_framefmt *__find_format(struct m5mols_info *info,
> >> +				struct v4l2_subdev_fh *fh,
> >> +				enum v4l2_subdev_format_whence which,
> >> +				enum m5mols_restype type)
> >> +{
> >> +	if (which == V4L2_SUBDEV_FORMAT_TRY)
> >> +		return fh ? v4l2_subdev_get_try_format(fh, 0) : NULL;
> >> +
> >> +	return &info->ffmt[type];
> >> +}
> >> +
> >> +static int m5mols_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
> >> +			  struct v4l2_subdev_format *fmt)
> >> +{
> >> +	struct m5mols_info *info = to_m5mols(sd);
> >> +	struct v4l2_mbus_framefmt *format;
> >> +
> >> +	if (fmt->pad != 0)
> >> +		return -EINVAL;
> >> +
> >> +	format = __find_format(info, fh, fmt->which, info->res_type);
> >> +	if (!format)
> >> +		return -EINVAL;
> >> +
> >> +	fmt->format = *format;
> >> +	return 0;
> >> +}
> >> +
> >> +static int m5mols_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
> >> +			  struct v4l2_subdev_format *fmt)
> >> +{
> >> +	struct m5mols_info *info = to_m5mols(sd);
> >> +	struct v4l2_mbus_framefmt *format = &fmt->format;
> >> +	struct v4l2_mbus_framefmt *sfmt;
> >> +	enum m5mols_restype type;
> >> +	u32 resolution = 0;
> >> +	int ret;
> >> +
> >> +	if (fmt->pad != 0)
> >> +		return -EINVAL;
> >> +
> >> +	ret = __find_resolution(sd, format, &type, &resolution);
> >> +	if (ret < 0)
> >> +		return ret;
> >> +
> >> +	sfmt = __find_format(info, fh, fmt->which, type);
> >> +	if (!sfmt)
> >> +		return 0;
> >> +
> >> +	*sfmt		= m5mols_default_ffmt[type];
> >> +	sfmt->width	= format->width;
> >> +	sfmt->height	= format->height;
> >> +
> >> +	if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
> >> +		info->resolution = resolution;
> >> +		info->code = format->code;
> >> +		info->res_type = type;
> >> +	}
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static int m5mols_enum_mbus_code(struct v4l2_subdev *sd,
> >> +				 struct v4l2_subdev_fh *fh,
> >> +				 struct v4l2_subdev_mbus_code_enum *code)
> >> +{
> >> +	ifv (!code || code->index >= SIZE_DEFAULT_FFMT)
> > 
> > Is it possible that code == NULL?
> It depends on the driver using this subdev driver. 
> The test have done on the s5p-fimc driver for now, but I don't have
> any trouble cause of code == NULL.
> 
> But, probably the code can get through by userspace, so it should be
> needed I guess.

The contents are from user space but the pointer is not. No need to check
for NULL. See subdev_do_ioctl() in v4l2-subdev.c.

[clip]
> > 
> >> +		return -EINVAL;
> >> +
> >> +	code->code = m5mols_default_ffmt[code->index].code;
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static struct v4l2_subdev_pad_ops m5mols_pad_ops = {
> >> +	.enum_mbus_code	= m5mols_enum_mbus_code,
> >> +	.get_fmt	= m5mols_get_fmt,
> >> +	.set_fmt	= m5mols_set_fmt,
> >> +};
> >> +
> >> +/**
> >> + * m5mols_sync_controls - Apply default scene mode and the current controls
> >> + *
> >> + * This is used only streaming for syncing between v4l2_ctrl framework and
> >> + * m5mols's controls. First, do the scenemode to the sensor, then call
> >> + * v4l2_ctrl_handler_setup. It can be same between some commands and
> >> + * the scenemode's in the default v4l2_ctrls. But, such commands of control
> >> + * should be prior to the scenemode's one.
> >> + */
> >> +int m5mols_sync_controls(struct m5mols_info *info)
> >> +{
> >> +	int ret = -EINVAL;
> >> +
> >> +	if (!is_ctrl_synced(info)) {
> >> +		ret = m5mols_do_scenemode(info, REG_SCENE_NORMAL);
> >> +		if (ret)
> >> +			return ret;
> >> +
> >> +		v4l2_ctrl_handler_setup(&info->handle);
> >> +		info->ctrl_sync = true;
> >> +	}
> >> +
> >> +	return ret;
> >> +}
> >> +
> >> +/**
> >> + * m5mols_start_monitor - Start the monitor mode
> >> + *
> >> + * Before applying the controls setup the resolution and frame rate
> >> + * in PARAMETER mode, and then switch over to MONITOR mode.
> >> + */
> >> +static int m5mols_start_monitor(struct m5mols_info *info)
> >> +{
> >> +	struct v4l2_subdev *sd = &info->sd;
> >> +	int ret;
> >> +
> >> +	ret = m5mols_mode(info, REG_PARAMETER);
> >> +	if (!ret)
> >> +		ret = m5mols_write(sd, PARM_MON_SIZE, info->resolution);
> >> +	if (!ret)
> >> +		ret = m5mols_write(sd, PARM_MON_FPS, REG_FPS_30);
> >> +	if (!ret)
> >> +		ret = m5mols_mode(info, REG_MONITOR);
> >> +	if (!ret)
> >> +		ret = m5mols_sync_controls(info);
> >> +
> >> +	return ret;
> >> +}
> >> +
> >> +static int m5mols_s_stream(struct v4l2_subdev *sd, int enable)
> >> +{
> >> +	struct m5mols_info *info = to_m5mols(sd);
> >> +
> >> +	if (enable) {
> >> +		int ret = -EINVAL;
> >> +
> >> +		if (is_code(info->code, M5MOLS_RESTYPE_MONITOR))
> >> +			ret = m5mols_start_monitor(info);
> >> +		if (is_code(info->code, M5MOLS_RESTYPE_CAPTURE))
> >> +			ret = m5mols_start_capture(info);
> >> +
> >> +		return ret;
> >> +	}
> >> +
> >> +	return m5mols_mode(info, REG_PARAMETER);
> >> +}
> >> +
> >> +static const struct v4l2_subdev_video_ops m5mols_video_ops = {
> >> +	.s_stream	= m5mols_s_stream,
> >> +};
> >> +
> >> +static int m5mols_s_ctrl(struct v4l2_ctrl *ctrl)
> >> +{
> >> +	struct v4l2_subdev *sd = to_sd(ctrl);
> >> +	struct m5mols_info *info = to_m5mols(sd);
> >> +	int ret;
> >> +
> >> +	info->mode_save = info->mode;
> >> +
> >> +	ret = m5mols_mode(info, REG_PARAMETER);
> >> +	if (!ret)
> >> +		ret = m5mols_set_ctrl(ctrl);
> >> +	if (!ret)
> >> +		ret = m5mols_mode(info, info->mode_save);
> >> +
> >> +	return ret;
> >> +}
> >> +
> >> +static const struct v4l2_ctrl_ops m5mols_ctrl_ops = {
> >> +	.s_ctrl	= m5mols_s_ctrl,
> >> +};
> >> +
> >> +static int m5mols_sensor_power(struct m5mols_info *info, bool enable)
> >> +{
> >> +	struct v4l2_subdev *sd = &info->sd;
> >> +	struct i2c_client *client = v4l2_get_subdevdata(sd);
> >> +	const struct m5mols_platform_data *pdata = info->pdata;
> >> +	int ret;
> >> +
> >> +	if (enable) {
> >> +		if (is_powered(info))
> >> +			return 0;
> >> +
> >> +		if (info->set_power) {
> >> +			ret = info->set_power(&client->dev, 1);
> >> +			if (ret)
> >> +				return ret;
> >> +		}
> >> +
> >> +		ret = regulator_bulk_enable(ARRAY_SIZE(supplies), supplies);
> >> +		if (ret) {
> >> +			info->set_power(&client->dev, 0);
> >> +			return ret;
> >> +		}
> >> +
> >> +		gpio_set_value(pdata->gpio_reset, !pdata->reset_polarity);
> >> +		usleep_range(1000, 1000);
> >> +		info->power = true;
> >> +
> >> +		return ret;
> >> +	}
> >> +
> >> +	if (!is_powered(info))
> >> +		return 0;
> >> +
> >> +	ret = regulator_bulk_disable(ARRAY_SIZE(supplies), supplies);
> >> +	if (ret)
> >> +		return ret;
> >> +
> >> +	if (info->set_power)
> >> +		info->set_power(&client->dev, 0);
> >> +
> >> +	gpio_set_value(pdata->gpio_reset, pdata->reset_polarity);
> >> +	usleep_range(1000, 1000);
> >> +	info->power = false;
> >> +
> >> +	return ret;
> >> +}
> >> +
> >> +/* m5mols_update_fw - optional firmware update routine */
> >> +int __attribute__ ((weak)) m5mols_update_fw(struct v4l2_subdev *sd,
> >> +		int (*set_power)(struct m5mols_info *, bool))
> >> +{
> >> +	return 0;
> >> +}
> >> +
> >> +/**
> >> + * m5mols_sensor_armboot - Booting M-5MOLS internal ARM core.
> >> + *
> >> + * Booting internal ARM core makes the M-5MOLS is ready for getting commands
> >> + * with I2C. It's the first thing to be done after it powered up. It must wait
> >> + * at least 520ms recommended by M-5MOLS datasheet, after executing arm booting.
> >> + */
> >> +static int m5mols_sensor_armboot(struct v4l2_subdev *sd)
> >> +{
> >> +	int ret;
> >> +
> >> +	ret = m5mols_write(sd, FLASH_CAM_START, REG_START_ARM_BOOT);
> >> +	if (ret < 0)
> >> +		return ret;
> >> +
> >> +	msleep(520);
> >> +
> >> +	ret = m5mols_get_version(sd);
> >> +	if (!ret)
> >> +		ret = m5mols_update_fw(sd, m5mols_sensor_power);
> >> +	if (ret)
> >> +		return ret;
> >> +
> >> +	v4l2_dbg(1, m5mols_debug, sd, "Success ARM Booting\n");
> >> +
> >> +	ret = m5mols_write(sd, PARM_INTERFACE, REG_INTERFACE_MIPI);
> >> +	if (!ret)
> >> +		ret = m5mols_enable_interrupt(sd, REG_INT_AF);
> >> +
> >> +	return ret;
> >> +}
> >> +
> >> +static int m5mols_init_controls(struct m5mols_info *info)
> >> +{
> >> +	struct v4l2_subdev *sd = &info->sd;
> >> +	u16 max_exposure;
> >> +	u16 step_zoom;
> >> +	int ret;
> >> +
> >> +	/* Determine value's range & step of controls for various FW version */
> >> +	ret = m5mols_read(sd, AE_MAX_GAIN_MON, (u32 *)&max_exposure);
> >> +	if (!ret)
> >> +		step_zoom = is_manufacturer(info, REG_SAMSUNG_OPTICS) ? 31 : 1;
> >> +	if (ret)
> >> +		return ret;
> >> +
> >> +	v4l2_ctrl_handler_init(&info->handle, 6);
> >> +	info->autowb = v4l2_ctrl_new_std(&info->handle,
> >> +			&m5mols_ctrl_ops, V4L2_CID_AUTO_WHITE_BALANCE,
> >> +			0, 1, 1, 0);
> >> +	info->saturation = v4l2_ctrl_new_std(&info->handle,
> >> +			&m5mols_ctrl_ops, V4L2_CID_SATURATION,
> >> +			1, 5, 1, 3);
> >> +	info->zoom = v4l2_ctrl_new_std(&info->handle,
> >> +			&m5mols_ctrl_ops, V4L2_CID_ZOOM_ABSOLUTE,
> >> +			1, 70, step_zoom, 1);
> >> +	info->exposure = v4l2_ctrl_new_std(&info->handle,
> >> +			&m5mols_ctrl_ops, V4L2_CID_EXPOSURE,
> >> +			0, max_exposure, 1, (int)max_exposure/2);
> >> +	info->colorfx = v4l2_ctrl_new_std_menu(&info->handle,
> >> +			&m5mols_ctrl_ops, V4L2_CID_COLORFX,
> >> +			4, (1 << V4L2_COLORFX_BW), V4L2_COLORFX_NONE);
> >> +	info->autoexposure = v4l2_ctrl_new_std_menu(&info->handle,
> >> +			&m5mols_ctrl_ops, V4L2_CID_EXPOSURE_AUTO,
> >> +			1, 0, V4L2_EXPOSURE_MANUAL);
> >> +
> >> +	sd->ctrl_handler = &info->handle;
> >> +	if (info->handle.error) {
> >> +		v4l2_err(sd, "Failed to initialize controls: %d\n", ret);
> >> +		v4l2_ctrl_handler_free(&info->handle);
> >> +		return info->handle.error;
> >> +	}
> >> +
> >> +	v4l2_ctrl_cluster(2, &info->autoexposure);
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +/**
> >> + * m5mols_s_power - Main sensor power control function
> >> + *
> >> + * To prevent breaking the lens when the sensor is powered off the Soft-Landing
> >> + * algorithm is called where available. The Soft-Landing algorithm availability
> >> + * dependends on the firmware provider.
> >> + */
> >> +static int m5mols_s_power(struct v4l2_subdev *sd, int on)
> >> +{
> >> +	struct m5mols_info *info = to_m5mols(sd);
> >> +	int ret;
> >> +
> >> +	if (on) {
> >> +		ret = m5mols_sensor_power(info, true);
> >> +		if (!ret)
> >> +			ret = m5mols_sensor_armboot(sd);
> >> +		if (!ret)
> >> +			ret = m5mols_init_controls(info);
> >> +		if (ret)
> >> +			return ret;
> >> +
> >> +		info->ffmt[M5MOLS_RESTYPE_MONITOR] =
> >> +			m5mols_default_ffmt[M5MOLS_RESTYPE_MONITOR];
> >> +		info->ffmt[M5MOLS_RESTYPE_CAPTURE] =
> >> +			m5mols_default_ffmt[M5MOLS_RESTYPE_CAPTURE];
> >> +		return ret;
> >> +	}
> >> +
> >> +	if (is_manufacturer(info, REG_SAMSUNG_TECHWIN)) {
> >> +		ret = m5mols_mode(info, REG_MONITOR);
> >> +		if (!ret)
> >> +			ret = m5mols_write(sd, AF_EXECUTE, REG_AF_STOP);
> >> +		if (!ret)
> >> +			ret = m5mols_write(sd, AF_MODE, REG_AF_POWEROFF);
> >> +		if (!ret)
> >> +			ret = m5mols_busy(sd, CAT_SYSTEM, CAT0_STATUS,
> >> +					REG_AF_IDLE);
> >> +		if (!ret)
> >> +			v4l2_info(sd, "Success soft-landing lens\n");
> >> +	}
> >> +
> >> +	ret = m5mols_sensor_power(info, false);
> >> +	if (!ret) {
> >> +		v4l2_ctrl_handler_free(&info->handle);
> >> +		info->ctrl_sync = false;
> >> +	}
> >> +
> >> +	return ret;
> >> +}
> >> +
> >> +static int m5mols_log_status(struct v4l2_subdev *sd)
> >> +{
> >> +	struct m5mols_info *info = to_m5mols(sd);
> >> +
> >> +	v4l2_ctrl_handler_log_status(&info->handle, sd->name);
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static const struct v4l2_subdev_core_ops m5mols_core_ops = {
> >> +	.s_power	= m5mols_s_power,
> >> +	.g_ctrl		= v4l2_subdev_g_ctrl,
> >> +	.s_ctrl		= v4l2_subdev_s_ctrl,
> >> +	.queryctrl	= v4l2_subdev_queryctrl,
> >> +	.querymenu	= v4l2_subdev_querymenu,
> >> +	.g_ext_ctrls	= v4l2_subdev_g_ext_ctrls,
> >> +	.try_ext_ctrls	= v4l2_subdev_try_ext_ctrls,
> >> +	.s_ext_ctrls	= v4l2_subdev_s_ext_ctrls,
> >> +	.log_status	= m5mols_log_status,
> >> +};
> >> +
> >> +static const struct v4l2_subdev_ops m5mols_ops = {
> >> +	.core		= &m5mols_core_ops,
> >> +	.pad		= &m5mols_pad_ops,
> >> +	.video		= &m5mols_video_ops,
> >> +};
> >> +
> >> +static void m5mols_irq_work(struct work_struct *work)
> >> +{
> >> +	struct m5mols_info *info =
> >> +		container_of(work, struct m5mols_info, work_irq);
> >> +	struct v4l2_subdev *sd = &info->sd;
> >> +	u32 reg;
> >> +	int ret;
> >> +
> >> +	if (!is_powered(info) ||
> >> +			m5mols_read(sd, SYSTEM_INT_FACTOR, &info->interrupt))
> >> +		return;
> >> +
> >> +	switch (info->interrupt & REG_INT_MASK) {
> >> +	case REG_INT_AF:
> >> +		if (!is_available_af(info))
> >> +			break;
> >> +		ret = m5mols_read(sd, AF_STATUS, &reg);
> >> +		v4l2_dbg(2, m5mols_debug, sd, "AF %s\n",
> >> +			 reg == REG_AF_FAIL ? "Failed" :
> >> +			 reg == REG_AF_SUCCESS ? "Success" :
> >> +			 reg == REG_AF_IDLE ? "Idle" : "Busy");
> >> +		break;
> >> +	case REG_INT_CAPTURE:
> >> +		if (!test_and_set_bit(ST_CAPT_IRQ, &info->flags))
> >> +			wake_up_interruptible(&info->irq_waitq);
> >> +
> >> +		v4l2_dbg(2, m5mols_debug, sd, "CAPTURE\n");
> >> +		break;
> >> +	default:
> >> +		v4l2_dbg(2, m5mols_debug, sd, "Undefined: %02x\n", reg);
> >> +		break;
> >> +	};
> >> +}
> >> +
> >> +static irqreturn_t m5mols_irq_handler(int irq, void *data)
> >> +{
> >> +	struct v4l2_subdev *sd = data;
> >> +	struct m5mols_info *info = to_m5mols(sd);
> >> +
> >> +	schedule_work(&info->work_irq);
> >> +
> >> +	return IRQ_HANDLED;
> >> +}
> >> +
> >> +static int __devinit m5mols_probe(struct i2c_client *client,
> >> +				  const struct i2c_device_id *id)
> >> +{
> >> +	const struct m5mols_platform_data *pdata = client->dev.platform_data;
> >> +	struct m5mols_info *info;
> >> +	struct v4l2_subdev *sd;
> >> +	int ret;
> >> +
> >> +	if (pdata == NULL) {
> >> +		dev_err(&client->dev, "No platform data\n");
> >> +		return -EINVAL;
> >> +	}
> >> +
> >> +	if (!gpio_is_valid(pdata->gpio_reset)) {
> >> +		dev_err(&client->dev, "No valid RESET GPIO specified\n");
> >> +		return -EINVAL;
> >> +	}
> >> +
> >> +	if (!pdata->irq) {
> >> +		dev_err(&client->dev, "Interrupt not assigned\n");
> >> +		return -EINVAL;
> >> +	}
> >> +
> >> +	info = kzalloc(sizeof(struct m5mols_info), GFP_KERNEL);
> >> +	if (!info)
> >> +		return -ENOMEM;
> >> +
> >> +	info->pdata = pdata;
> >> +	info->set_power	= pdata->set_power;
> >> +
> >> +	ret = gpio_request(pdata->gpio_reset, "M5MOLS_NRST");
> >> +	if (ret) {
> >> +		dev_err(&client->dev, "Failed to request gpio: %d\n", ret);
> >> +		goto out_free;
> >> +	}
> >> +	gpio_direction_output(pdata->gpio_reset, pdata->reset_polarity);
> >> +
> >> +	ret = regulator_bulk_get(&client->dev, ARRAY_SIZE(supplies), supplies);
> >> +	if (ret) {
> >> +		dev_err(&client->dev, "Failed to get regulators: %d\n", ret);
> >> +		goto out_gpio;
> >> +	}
> >> +
> >> +	sd = &info->sd;
> >> +	strlcpy(sd->name, MODULE_NAME, sizeof(sd->name));
> >> +	v4l2_i2c_subdev_init(sd, client, &m5mols_ops);
> >> +
> >> +	info->pad.flags = MEDIA_PAD_FL_SOURCE;
> >> +	ret = media_entity_init(&sd->entity, 1, &info->pad, 0);
> >> +	if (ret < 0)
> >> +		goto out_reg;
> >> +	sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR;
> >> +
> >> +	init_waitqueue_head(&info->irq_waitq);
> >> +	INIT_WORK(&info->work_irq, m5mols_irq_work);
> >> +	ret = request_irq(pdata->irq, m5mols_irq_handler,
> >> +			  IRQF_TRIGGER_RISING, MODULE_NAME, sd);
> >> +	if (ret) {
> >> +		dev_err(&client->dev, "Interrupt request failed: %d\n", ret);
> >> +		goto out_me;
> >> +	}
> >> +	info->res_type = M5MOLS_RESTYPE_MONITOR;
> >> +	return 0;
> >> +out_me:
> >> +	media_entity_cleanup(&sd->entity);
> >> +out_reg:
> >> +	regulator_bulk_free(ARRAY_SIZE(supplies), supplies);
> >> +out_gpio:
> >> +	gpio_free(pdata->gpio_reset);
> >> +out_free:
> >> +	kfree(info);
> >> +	return ret;
> >> +}
> >> +
> >> +static int __devexit 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);
> >> +	free_irq(info->pdata->irq, sd);
> >> +
> >> +	regulator_bulk_free(ARRAY_SIZE(supplies), supplies);
> >> +	gpio_free(info->pdata->gpio_reset);
> >> +	media_entity_cleanup(&sd->entity);
> >> +	kfree(info);
> >> +	return 0;
> >> +}
> >> +
> >> +static const struct i2c_device_id m5mols_id[] = {
> >> +	{ MODULE_NAME, 0 },
> >> +	{ },
> >> +};
> >> +MODULE_DEVICE_TABLE(i2c, m5mols_id);
> >> +
> >> +static struct i2c_driver m5mols_i2c_driver = {
> >> +	.driver = {
> >> +		.name	= MODULE_NAME,
> >> +	},
> >> +	.probe		= m5mols_probe,
> >> +	.remove		= __devexit_p(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@samsung.com>");
> >> +MODULE_AUTHOR("Dongsoo Kim <dongsoo45.kim@samsung.com>");
> >> +MODULE_DESCRIPTION("Fujitsu M-5MOLS 8M Pixel camera driver");
> >> +MODULE_LICENSE("GPL");
> >> diff --git a/drivers/media/video/m5mols/m5mols_reg.h b/drivers/media/video/m5mols/m5mols_reg.h
> >> new file mode 100644
> >> index 0000000..b83e36f
> >> --- /dev/null
> >> +++ b/drivers/media/video/m5mols/m5mols_reg.h
> >> @@ -0,0 +1,399 @@
> >> +/*
> >> + * Register map for M-5MOLS 8M Pixel camera sensor with ISP
> >> + *
> >> + * Copyright (C) 2011 Samsung Electronics Co., Ltd.
> >> + * Author: HeungJun Kim, riverful.kim@samsung.com
> >> + *
> >> + * Copyright (C) 2009 Samsung Electronics Co., Ltd.
> >> + * Author: Dongsoo Nathaniel Kim, dongsoo45.kim@samsung.com
> >> + *
> >> + * 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_REG_H
> >> +#define M5MOLS_REG_H
> >> +
> >> +#define M5MOLS_I2C_MAX_SIZE	4
> >> +#define M5MOLS_BYTE_READ	0x01
> >> +#define M5MOLS_BYTE_WRITE	0x02
> >> +
> >> +#define I2C_CATEGORY(__cat)		((__cat >> 16) & 0xff)
> >> +#define I2C_COMMAND(__comm)		((__comm >> 8) & 0xff)
> >> +#define I2C_SIZE(__reg_s)		((__reg_s) & 0xff)
> > 
> > I would put category and command to lower 16 bits and size in the upper 16
> > bits, but this is up to you. I think this would improve readability.
> OK. I would deeply consider that :-)
> 
> > 
> >> +#define I2C_REG(__cat, __cmd, __reg_s)	((__cat << 16) | (__cmd << 8) | __reg_s)
> >> +
> >> +/*
> >> + * Category section register
> >> + *
> >> + * The category means set including relevant command of M-5MOLS.
> >> + */
> >> +#define CAT_SYSTEM		0x00
> >> +#define CAT_PARAM		0x01
> >> +#define CAT_MONITOR		0x02
> >> +#define CAT_AE			0x03
> >> +#define CAT_WB			0x06
> >> +#define CAT_EXIF		0x07
> >> +#define CAT_FD			0x09
> >> +#define CAT_LENS		0x0a
> >> +#define CAT_CAPT_PARM		0x0b
> >> +#define CAT_CAPT_CTRL		0x0c
> >> +#define CAT_FLASH		0x0f	/* related to FW, revisions, booting */
> > 
> > What about defining registers so that the category is part of the register
> > address? This is actually how it's in the address space as well.
> > 
> >> +
> >> +/*
> >> + * Category 0 - SYSTEM mode
> >> + *
> >> + * The SYSTEM mode in the M-5MOLS means area available to handle with the whole
> >> + * & all-round system of sensor. It deals with version/interrupt/setting mode &
> >> + * even sensor's status. Especially, the M-5MOLS sensor with ISP varies by
> >> + * packaging & manufacturer, even the customer and project code. And the
> >> + * function details may vary among them. The version information helps to
> >> + * determine what methods shall be used in the driver.
> >> + *
> >> + * There is many registers between customer version address and awb one. For
> >> + * more specific contents, see definition if file m5mols.h.
> >> + */
> >> +#define CAT0_VER_CUSTOMER	0x00	/* customer version */
> > 
> > If you keep the definitions as they are, I think "CAT0" should be replaced
> > by "SYSTEM". This would make it clear that the registers belong to that
> > category, instead of a numeric one.
> Actually, this register named on Document of M-5MOLS. I wanted to satisfy
> to keep that name and to looks readable.
> So, I choose only the name pointing the register value, is followed by document,
> and the prefix like SYSTEM_ is used in the code.
> 
> > 
> >> +#define CAT0_VER_AWB		0x09	/* Auto WB version */
> >> +#define CAT0_VER_STRING		0x0a	/* string including M-5MOLS */
> >> +#define CAT0_SYSMODE		0x0b	/* SYSTEM mode register */
> >> +#define CAT0_STATUS		0x0c	/* SYSTEM mode status register */
> >> +#define CAT0_INT_FACTOR		0x10	/* interrupt pending register */
> >> +#define CAT0_INT_ENABLE		0x11	/* interrupt enable register */
> >> +
> >> +#define SYSTEM_SYSMODE		I2C_REG(CAT_SYSTEM, CAT0_SYSMODE, 1)
> >> +#define REG_SYSINIT		0x00	/* SYSTEM mode */
> >> +#define REG_PARAMETER		0x01	/* PARAMETER mode */
> >> +#define REG_MONITOR		0x02	/* MONITOR mode */
> >> +#define REG_CAPTURE		0x03	/* CAPTURE mode */
> >> +
> >> +#define SYSTEM_CMD(__cmd)	I2C_REG(CAT_SYSTEM, cmd, 1)
> >> +#define SYSTEM_VER_STRING	I2C_REG(CAT_SYSTEM, CAT0_VER_STRING, 1)
> >> +#define REG_SAMSUNG_ELECTRO	"SE"	/* Samsung Electro-Mechanics */
> >> +#define REG_SAMSUNG_OPTICS	"OP"	/* Samsung Fiber-Optics */
> >> +#define REG_SAMSUNG_TECHWIN	"TB"	/* Samsung Techwin */
> >> +
> >> +#define SYSTEM_INT_FACTOR	I2C_REG(CAT_SYSTEM, CAT0_INT_FACTOR, 1)
> >> +#define SYSTEM_INT_ENABLE	I2C_REG(CAT_SYSTEM, CAT0_INT_ENABLE, 1)
> >> +#define REG_INT_MODE		(1 << 0)
> >> +#define REG_INT_AF		(1 << 1)
> >> +#define REG_INT_ZOOM		(1 << 2)
> >> +#define REG_INT_CAPTURE		(1 << 3)
> >> +#define REG_INT_FRAMESYNC	(1 << 4)
> >> +#define REG_INT_FD		(1 << 5)
> >> +#define REG_INT_LENS_INIT	(1 << 6)
> >> +#define REG_INT_SOUND		(1 << 7)
> >> +#define REG_INT_MASK		0x0f
> > 
> > I would prefix the register bit definitions with the name of the register.
> > 
> > E.g.
> > 
> > #define SYSTEM_INT_FACTOR_MODE	(1 << 0)
> It's Ditto "over 80 character"
> 
> If already used once like this, it should use in another case, so
> length of the SYSTEM_INT_FACTOR_MODE does not matter itself, but the other
> case can be troubled.

It's sometimes acceptable to have over 80 characters per line. Yes, it's an
exception but sometimes it's the case. See e.g. videodev2.h, for example.

Regards,

-- 
Sakari Ailus
sakari dot ailus at iki dot fi

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH v2 3/4] m5mols: remove union in the m5mols_get_version(), and VERSION_SIZE
  2011-05-31  7:36             ` [PATCH v2 3/4] m5mols: remove union in the m5mols_get_version(), and VERSION_SIZE HeungJun, Kim
@ 2011-06-05 12:03               ` Sakari Ailus
  2011-06-05 12:11                 ` Sakari Ailus
  0 siblings, 1 reply; 34+ messages in thread
From: Sakari Ailus @ 2011-06-05 12:03 UTC (permalink / raw)
  To: HeungJun, Kim; +Cc: linux-media, mchehab, s.nawrocki, Kyungmin Park

Hi HeungJun,

Thanks for the patch.

On Tue, May 31, 2011 at 04:36:01PM +0900, HeungJun, Kim wrote:
> Remove union version in the m5mols_get_version(), and read version information
> directly. Also remove VERSION_SIZE.
> 
> Signed-off-by: HeungJun, Kim <riverful.kim@samsung.com>
> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
> ---
>  drivers/media/video/m5mols/m5mols.h      |    1 -
>  drivers/media/video/m5mols/m5mols_core.c |   42 +++++++++++++++---------------
>  drivers/media/video/m5mols/m5mols_reg.h  |   13 ++++++++-
>  3 files changed, 33 insertions(+), 23 deletions(-)
> 
> diff --git a/drivers/media/video/m5mols/m5mols.h b/drivers/media/video/m5mols/m5mols.h
> index dbe8928..9ae1709 100644
> --- a/drivers/media/video/m5mols/m5mols.h
> +++ b/drivers/media/video/m5mols/m5mols.h
> @@ -154,7 +154,6 @@ struct m5mols_version {
>  	u8	str[VERSION_STRING_SIZE];
>  	u8	af;
>  };
> -#define VERSION_SIZE sizeof(struct m5mols_version)
>  
>  /**
>   * struct m5mols_info - M-5MOLS driver data structure
> diff --git a/drivers/media/video/m5mols/m5mols_core.c b/drivers/media/video/m5mols/m5mols_core.c
> index 2b1f23f..8ccab95 100644
> --- a/drivers/media/video/m5mols/m5mols_core.c
> +++ b/drivers/media/video/m5mols/m5mols_core.c
> @@ -386,33 +386,33 @@ int m5mols_mode(struct m5mols_info *info, u8 mode)
>  static int m5mols_get_version(struct v4l2_subdev *sd)
>  {
>  	struct m5mols_info *info = to_m5mols(sd);
> -	union {
> -		struct m5mols_version ver;
> -		u8 bytes[VERSION_SIZE];
> -	} version;
> -	u8 cmd = CAT0_VER_CUSTOMER;
> +	struct m5mols_version *ver = &info->ver;
> +	u8 *str = ver->str;
> +	int i;
>  	int ret;
>  
> -	do {
> -		ret = m5mols_read_u8(sd, SYSTEM_CMD(cmd), &version.bytes[cmd]);
> -		if (ret)
> -			return ret;
> -	} while (cmd++ != CAT0_VER_AWB);
> +	ret = m5mols_read_u8(sd, SYSTEM_VER_CUSTOMER, &ver->customer);
> +	if (!ret)
> +		ret = m5mols_read_u8(sd, SYSTEM_VER_PROJECT, &ver->project);
> +	if (!ret)
> +		ret = m5mols_read_u16(sd, SYSTEM_VER_FIRMWARE, &ver->fw);
> +	if (!ret)
> +		ret = m5mols_read_u16(sd, SYSTEM_VER_HARDWARE, &ver->hw);
> +	if (!ret)
> +		ret = m5mols_read_u16(sd, SYSTEM_VER_PARAMETER, &ver->param);
> +	if (!ret)
> +		ret = m5mols_read_u16(sd, SYSTEM_VER_AWB, &ver->awb);
> +	if (!ret)
> +		ret = m5mols_read_u8(sd, AF_VERSION, &ver->af);
> +	if (ret)
> +		return ret;
>  
> -	do {
> -		ret = m5mols_read_u8(sd, SYSTEM_VER_STRING, &version.bytes[cmd]);
> +	for (i = 0; i < VERSION_STRING_SIZE; i++) {
> +		ret = m5mols_read_u8(sd, SYSTEM_VER_STRING, &str[i]);
>  		if (ret)
>  			return ret;
> -		if (cmd >= VERSION_SIZE - 1)
> -			return -EINVAL;
> -	} while (version.bytes[cmd++]);
> -
> -	ret = m5mols_read_u8(sd, AF_VERSION, &version.bytes[cmd]);
> -	if (ret)
> -		return ret;
> +	}
>  
> -	/* store version information swapped for being readable */
> -	info->ver	= version.ver;
>  	info->ver.fw	= be16_to_cpu(info->ver.fw);
>  	info->ver.hw	= be16_to_cpu(info->ver.hw);
>  	info->ver.param	= be16_to_cpu(info->ver.param);

As you have a local variable ver pointing to info->ver, you should also use
it here.

> diff --git a/drivers/media/video/m5mols/m5mols_reg.h b/drivers/media/video/m5mols/m5mols_reg.h
> index 8260f50..5f5bdcf 100644
> --- a/drivers/media/video/m5mols/m5mols_reg.h
> +++ b/drivers/media/video/m5mols/m5mols_reg.h
> @@ -56,13 +56,24 @@
>   * more specific contents, see definition if file m5mols.h.
>   */
>  #define CAT0_VER_CUSTOMER	0x00	/* customer version */
> -#define CAT0_VER_AWB		0x09	/* Auto WB version */
> +#define CAT0_VER_PROJECT	0x01	/* project version */
> +#define CAT0_VER_FIRMWARE	0x02	/* Firmware version */
> +#define CAT0_VER_HARDWARE	0x04	/* Hardware version */
> +#define CAT0_VER_PARAMETER	0x06	/* Parameter version */
> +#define CAT0_VER_AWB		0x08	/* Auto WB version */
>  #define CAT0_VER_STRING		0x0a	/* string including M-5MOLS */
>  #define CAT0_SYSMODE		0x0b	/* SYSTEM mode register */
>  #define CAT0_STATUS		0x0c	/* SYSTEM mode status register */
>  #define CAT0_INT_FACTOR		0x10	/* interrupt pending register */
>  #define CAT0_INT_ENABLE		0x11	/* interrupt enable register */
>  
> +#define SYSTEM_VER_CUSTOMER	I2C_REG(CAT_SYSTEM, CAT0_VER_CUSTOMER, 1)
> +#define SYSTEM_VER_PROJECT	I2C_REG(CAT_SYSTEM, CAT0_VER_PROJECT, 1)
> +#define SYSTEM_VER_FIRMWARE	I2C_REG(CAT_SYSTEM, CAT0_VER_FIRMWARE, 2)
> +#define SYSTEM_VER_HARDWARE	I2C_REG(CAT_SYSTEM, CAT0_VER_HARDWARE, 2)
> +#define SYSTEM_VER_PARAMETER	I2C_REG(CAT_SYSTEM, CAT0_VER_PARAMETER, 2)
> +#define SYSTEM_VER_AWB		I2C_REG(CAT_SYSTEM, CAT0_VER_AWB, 2)
> +
>  #define SYSTEM_SYSMODE		I2C_REG(CAT_SYSTEM, CAT0_SYSMODE, 1)
>  #define REG_SYSINIT		0x00	/* SYSTEM mode */
>  #define REG_PARAMETER		0x01	/* PARAMETER mode */
> -- 
> 1.7.0.4
> 

Cheers,

-- 
Sakari Ailus
sakari dot ailus at iki dot fi

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH v9] Add support for M-5MOLS 8 Mega Pixel camera ISP
  2011-06-05 11:55             ` Sakari Ailus
@ 2011-06-05 12:11               ` Hans Verkuil
  2011-06-05 13:08                 ` Sakari Ailus
  2011-06-05 13:13               ` Sylwester Nawrocki
  2011-06-06  9:14               ` Kim HeungJun
  2 siblings, 1 reply; 34+ messages in thread
From: Hans Verkuil @ 2011-06-05 12:11 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: Kim, HeungJun, linux-media, mchehab, laurent.pinchart,
	Sylwester Nawrocki, Kyungmin Park

On Sunday, June 05, 2011 13:55:30 Sakari Ailus wrote:
> On Thu, May 26, 2011 at 04:12:47PM +0900, Kim, HeungJun wrote:
> > >> +
> > >> +	struct v4l2_ctrl_handler handle;
> > >> +	/* Autoexposure/exposure control cluster */
> > >> +	struct {
> > >> +		struct v4l2_ctrl *autoexposure;
> > >> +		struct v4l2_ctrl *exposure;
> > >> +	};
> > > 
> > > Would it be different without the anonymous struct?
> > These two v4l2_ctrl is clustered. So, anonymous struct should be used
> > for v4l2_ctrl_cluster().
> 
> It makes no difference in how the pointers are arranged in the memory.

The reason I use an anonymous struct for control clusters is that they
nicely highlight that these two pointers belong together.

You don't need to do this, of course, but in that case you have to clearly
group such pointers using comment(s). Alternatively, you can use a pointer
array, but that's a pain in the ass to use in practice.

So the sole purpose of the anonymous struct is to visually group the pointers
without making it harder to access them.

It is debatable whether it is needed if you have only one cluster. But when
you have more than one cluster this approach is very effective.

Regards,

	Hans

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH v2 3/4] m5mols: remove union in the m5mols_get_version(), and VERSION_SIZE
  2011-06-05 12:03               ` Sakari Ailus
@ 2011-06-05 12:11                 ` Sakari Ailus
  2011-06-06  9:20                   ` Kim HeungJun
  0 siblings, 1 reply; 34+ messages in thread
From: Sakari Ailus @ 2011-06-05 12:11 UTC (permalink / raw)
  To: HeungJun, Kim; +Cc: linux-media, mchehab, s.nawrocki, Kyungmin Park

On Sun, Jun 05, 2011 at 03:03:47PM +0300, Sakari Ailus wrote:
[clip]
> > -	/* store version information swapped for being readable */
> > -	info->ver	= version.ver;
> >  	info->ver.fw	= be16_to_cpu(info->ver.fw);
> >  	info->ver.hw	= be16_to_cpu(info->ver.hw);
> >  	info->ver.param	= be16_to_cpu(info->ver.param);
> 
> As you have a local variable ver pointing to info->ver, you should also use
> it here.

With this change,

Acked-by: Sakari Ailus <sakari.ailus@iki.fi>

-- 
Sakari Ailus
sakari dot ailus at iki dot fi

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH v9] Add support for M-5MOLS 8 Mega Pixel camera ISP
  2011-06-05 12:11               ` Hans Verkuil
@ 2011-06-05 13:08                 ` Sakari Ailus
  0 siblings, 0 replies; 34+ messages in thread
From: Sakari Ailus @ 2011-06-05 13:08 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: Kim, HeungJun, linux-media, mchehab, laurent.pinchart,
	Sylwester Nawrocki, Kyungmin Park

Hi Hans,

On Sun, Jun 05, 2011 at 02:11:14PM +0200, Hans Verkuil wrote:
> On Sunday, June 05, 2011 13:55:30 Sakari Ailus wrote:
> > On Thu, May 26, 2011 at 04:12:47PM +0900, Kim, HeungJun wrote:
> > > >> +
> > > >> +	struct v4l2_ctrl_handler handle;
> > > >> +	/* Autoexposure/exposure control cluster */
> > > >> +	struct {
> > > >> +		struct v4l2_ctrl *autoexposure;
> > > >> +		struct v4l2_ctrl *exposure;
> > > >> +	};
> > > > 
> > > > Would it be different without the anonymous struct?
> > > These two v4l2_ctrl is clustered. So, anonymous struct should be used
> > > for v4l2_ctrl_cluster().
> > 
> > It makes no difference in how the pointers are arranged in the memory.
> 
> The reason I use an anonymous struct for control clusters is that they
> nicely highlight that these two pointers belong together.
> 
> You don't need to do this, of course, but in that case you have to clearly
> group such pointers using comment(s). Alternatively, you can use a pointer
> array, but that's a pain in the ass to use in practice.
> 
> So the sole purpose of the anonymous struct is to visually group the pointers
> without making it harder to access them.
> 
> It is debatable whether it is needed if you have only one cluster. But when
> you have more than one cluster this approach is very effective.

This is fine for me as well.

-- 
Sakari Ailus
sakari dot ailus at iki dot fi

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH v9] Add support for M-5MOLS 8 Mega Pixel camera ISP
  2011-06-05 11:55             ` Sakari Ailus
  2011-06-05 12:11               ` Hans Verkuil
@ 2011-06-05 13:13               ` Sylwester Nawrocki
  2011-06-06 10:17                 ` Sakari Ailus
  2011-06-06  9:14               ` Kim HeungJun
  2 siblings, 1 reply; 34+ messages in thread
From: Sylwester Nawrocki @ 2011-06-05 13:13 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: Kim, HeungJun, linux-media, mchehab, hverkuil, laurent.pinchart,
	Sylwester Nawrocki, Kyungmin Park, Mark Brown

Hi Sakari,

On 06/05/2011 01:55 PM, Sakari Ailus wrote:
> On Thu, May 26, 2011 at 04:12:47PM +0900, Kim, HeungJun wrote:
>> Hi Sakari,
> 
> Hi HeungJun,
> 
>> 2011-05-25 ?????? 10:54, Sakari Ailus ??? ???:
>>> Hi HeungJun,
>>>
>>> Thanks for the patch!
>> Also, thanks for the interests of this driver!
> 
> You'we welcome! :-)
> 
>>>
>>> I'm happy to see that Samsung is interested in getting such a driver to
>>> mainline. :-) I suppose that theoretically nothing would prevent plugging
>>> such a sensor to the OMAP 3 ISP, for example. It's just that the sensor
>>> already does image processing and the ISP might not be that useful because
>>> of this. But the interfaces would match, both in software and in hardware.
>> This sensor(actually integrated ISP functionality as you know) is powerful,
>> and I think this driver can help to make the controls for digital camera.
>>
>> But, TI OMAP 3 has also ISP independently, so I think having the ISP module
>> in the Processor is more better option cause of choice of various sensors,
>> although the driver's developer has more issues which should be handled.
>>
>> I hope to handle ISP directly in the Samsung Processor.
>>
>>>
>>> This is a subdev driver and uses the control framework. Good. I have
>>> comments on the code below.
>> Before that, this driver is already merged in Mauro's branch, and
>> I have spent a few months making this drivers for submitting this.
>> Some of your comments looks good and needed for this driver.
>> But, if I fix this and resend it and another comments happened,
>> this may be endless alone fight to reach "mergeing". :)
> 
> Sounds good to me. I have a few additional comments.
> 
>> So, I want that I keep this comments in mind, and I'll guarantee the fixes
>> will be adapted the next time by type of the patch, after this driver patch
>> is merged fully in 3.0.
> [clip]
> 
...

>>>> +
>>>> +/* The regulator consumer names for external voltage regulators */
>>>> +static struct regulator_bulk_data supplies[] = {
>>>> +	{
>>>> +		.supply = "core",	/* ARM core power, 1.2V */
>>>> +	}, {
>>>> +		.supply	= "dig_18",	/* digital power 1, 1.8V */
>>>> +	}, {
>>>> +		.supply	= "d_sensor",	/* sensor power 1, 1.8V */
>>>> +	}, {
>>>> +		.supply	= "dig_28",	/* digital power 2, 2.8V */
>>>> +	}, {
>>>> +		.supply	= "a_sensor",	/* analog power */
>>>> +	}, {
>>>> +		.supply	= "dig_12",	/* digital power 3, 1.2V */
>>>> +	},
>>>> +};
>>>
>>> This looks like something that belongs to board code, or perhaps in the near
>>> future, to the device tree. The power supplies that are required by a device
>>> is highly board dependent.
>> If the regulator name is not common all M-5MOLS, You're right.
>> But the regulator name of M-5MOLS is fixed.
> 
> As far as I understand, M-5MOLS is a sensor which you can, in principle,
> attach to more or less random hardware. The regulators are not part of the
> sensor. If someone adds a board which has regulators names or otherwise
> arranged differently, this change must be done at that time.

As you may know the above names are _regulator supply names_, not names
of the actual regulators. One voltage regulator can have multiple 
"regulator supply names", i.e. the "pads" to which particular loads
can be connected).

In particular all the above regulator supplies could be assigned to single
regulator in the board code, if there is, for example, only one GPIO enabling
all required voltage regulators.

The regulator API allows you to map the regulators and regulator loads 
without passing any information as platform data. Also the regulators'
hierarchy can be modelled if some of the regulators require other regulator
to be enabled in advance.

IMHO as long as the driver handles each voltage required by the device there
should be no need to abuse the platform data.
It's a different story though when some of the sensor revisions have voltage 
supplies arranged differently.

Thanks,
Sylwester
> 
>> The benefit fixed in the driver helps that the developer can attach the driver,
>> if he/she knows the names of regulators.
>>
>> Commonly, you're right, but in this case(documented accurately with M-5MOLS
>> datasheet), it's better.
>>
> [clip]
> 

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH v9] Add support for M-5MOLS 8 Mega Pixel camera ISP
  2011-06-05 11:55             ` Sakari Ailus
  2011-06-05 12:11               ` Hans Verkuil
  2011-06-05 13:13               ` Sylwester Nawrocki
@ 2011-06-06  9:14               ` Kim HeungJun
  2 siblings, 0 replies; 34+ messages in thread
From: Kim HeungJun @ 2011-06-06  9:14 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: Kim HeungJun, Kim, HeungJun, linux-media, mchehab, hverkuil,
	laurent.pinchart, Sylwester Nawrocki, Kyungmin Park

Hi Sakari,

2011. 6. 5., 오후 8:55, Sakari Ailus 작성:

> On Thu, May 26, 2011 at 04:12:47PM +0900, Kim, HeungJun wrote:
>> Hi Sakari,
> 
> Hi HeungJun,
> 
>> 2011-05-25 ?????? 10:54, Sakari Ailus ??? ???:
>>> Hi HeungJun,
>>> 
>>> Thanks for the patch!
>> Also, thanks for the interests of this driver!

[snip]
>> 
>>> shouldn't the underlying low level controls be exposed to user space as
>>> such?
>>> 
>>> There definitely are different approaches to this; providing higher level
>>> interface is restricting but on the other hand it may be better depending on
>>> an application.
>>> 
>>> Some of these parameters would already have a V4L2 control for them.
>> Actually, I have a plan to prepare RFC to expose some controls and
>> things related with control timing. :)
> 
> I'm looking forward to this. Control timing is also something I'm interested
> in.

Yeah, we're still thinking of this, but it's true we still don't have clean solution about that
in the linux side. Especially, the timing of camera(the more Higher-performance sensor
the more tricky to control timing) is sensitive, but anyway it's also true the camera device
not using linux is available to handle well now.

My point of thinking is that going in/out the kernel-space/user-space occurs the latency,
and this latency occurs imbalance of image. For example, before the capturing, the flash
or adequate exposure value should be maintained constantly, but if any other process
interrupts between flash-ctrl and exposure-ctrl or something occurs the delay, we can
not get the prefect image configured by our purpose.

But, I'm still considering how to handle this now.

If my thought will somewhat concrete, it's taking not much longer, and I'll soon make
another threads or RFC about this subject. Let's talk in this thread later. :)

[snip]
>>>> +
>>>> +/* The regulator consumer names for external voltage regulators */
>>>> +static struct regulator_bulk_data supplies[] = {
>>>> +	{
>>>> +		.supply = "core",	/* ARM core power, 1.2V */
>>>> +	}, {
>>>> +		.supply	= "dig_18",	/* digital power 1, 1.8V */
>>>> +	}, {
>>>> +		.supply	= "d_sensor",	/* sensor power 1, 1.8V */
>>>> +	}, {
>>>> +		.supply	= "dig_28",	/* digital power 2, 2.8V */
>>>> +	}, {
>>>> +		.supply	= "a_sensor",	/* analog power */
>>>> +	}, {
>>>> +		.supply	= "dig_12",	/* digital power 3, 1.2V */
>>>> +	},
>>>> +};
>>> 
>>> This looks like something that belongs to board code, or perhaps in the near
>>> future, to the device tree. The power supplies that are required by a device
>>> is highly board dependent.
>> If the regulator name is not common all M-5MOLS, You're right.
>> But the regulator name of M-5MOLS is fixed.
> 
> As far as I understand, M-5MOLS is a sensor which you can, in principle,
> attach to more or less random hardware. The regulators are not part of the
> sensor. If someone adds a board which has regulators names or otherwise
> arranged differently, this change must be done at that time.

Sylwester already explained about this, and I agree with Sylwester.
And I want to add more comments.

The point is the regulator consumer names and the sensor's power names do not
depend on(or are irrelevant) each other, and we can not define or explain about
the platform or board only having the regulator's names in the drivers.

Namely, it means these names in the driver is not a platform side, and if the regulator
framework is available in any other drivers, we can use regulator name for convenient.
So then, if the name is clear and been not changed until now, I think this usage of
regulator framework's is fine.

[snip]
>>> 
>>> #define I2C_SIZE_U8	1
>> I already try to do like this, using width as definition.
>> But it makes many lines over 80 characters. Moreover, using just number
>> is more simple in this case.
> 
> Wrapping lines is also possible but sometimes it's just better not to.

I didn't know that before. I'll keep that in mind. Thanks :)

[snip]
>>>> 
>>>> +
>>>> +	usleep_range(200, 200);
>>> 
>>> Why to sleep always? Does the sensor require a delay between each I2C
>>> access?
>> It's experimental values. The M-5MOLS I2C communication is a litte sensitive,
>> and I expect that this sensor is integrated with another ARM-core and internal
>> Firmware, and ARM-core's performance is not good. So, the dealy should be needed.
> 
> Perhaps a comment telling this would be good?

like /* This value comes from experiment. */?

As I say the value itself, the value had the ranges about at least 150ms in my case.
For being clear, I just use 200ms.
 
[snip]
>>>> 
>>>> +
>>>> +static int m5mols_enum_mbus_code(struct v4l2_subdev *sd,
>>>> +				 struct v4l2_subdev_fh *fh,
>>>> +				 struct v4l2_subdev_mbus_code_enum *code)
>>>> +{
>>>> +	ifv (!code || code->index >= SIZE_DEFAULT_FFMT)
>>> 
>>> Is it possible that code == NULL?
>> It depends on the driver using this subdev driver. 
>> The test have done on the s5p-fimc driver for now, but I don't have
>> any trouble cause of code == NULL.
>> 
>> But, probably the code can get through by userspace, so it should be
>> needed I guess.
> 
> The contents are from user space but the pointer is not. No need to check
> for NULL. See subdev_do_ioctl() in v4l2-subdev.c.

Ok, it's good to know.

[snip]

And I think there is not another big problem.
Thanks for the reviews!

Best Regards,
Heungjun Kim


> Regards,
> 
> -- 
> Sakari Ailus
> sakari dot ailus at iki dot fi
> --
> To unsubscribe from this list: send the line "unsubscribe linux-media" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>>> 


^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH v2 3/4] m5mols: remove union in the m5mols_get_version(), and VERSION_SIZE
  2011-06-05 12:11                 ` Sakari Ailus
@ 2011-06-06  9:20                   ` Kim HeungJun
  0 siblings, 0 replies; 34+ messages in thread
From: Kim HeungJun @ 2011-06-06  9:20 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: Kim HeungJun, HeungJun, Kim, linux-media, mchehab, s.nawrocki,
	Kyungmin Park


2011. 6. 5., 오후 9:11, Sakari Ailus 작성:

> On Sun, Jun 05, 2011 at 03:03:47PM +0300, Sakari Ailus wrote:
> [clip]
>>> -	/* store version information swapped for being readable */
>>> -	info->ver	= version.ver;
>>> 	info->ver.fw	= be16_to_cpu(info->ver.fw);
>>> 	info->ver.hw	= be16_to_cpu(info->ver.hw);
>>> 	info->ver.param	= be16_to_cpu(info->ver.param);
>> 
>> As you have a local variable ver pointing to info->ver, you should also use
>> it here.
> 
> With this change,
Ok, I missed that. I'll fix this and resend another version.

Thanks!


> 
> Acked-by: Sakari Ailus <sakari.ailus@iki.fi>
> 
> -- 
> Sakari Ailus
> sakari dot ailus at iki dot fi
> --
> To unsubscribe from this list: send the line "unsubscribe linux-media" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH v9] Add support for M-5MOLS 8 Mega Pixel camera ISP
  2011-06-05 13:13               ` Sylwester Nawrocki
@ 2011-06-06 10:17                 ` Sakari Ailus
  0 siblings, 0 replies; 34+ messages in thread
From: Sakari Ailus @ 2011-06-06 10:17 UTC (permalink / raw)
  To: Sylwester Nawrocki
  Cc: Kim, HeungJun, linux-media, mchehab, hverkuil, laurent.pinchart,
	Sylwester Nawrocki, Kyungmin Park, Mark Brown

On Sun, Jun 05, 2011 at 03:13:40PM +0200, Sylwester Nawrocki wrote:
> Hi Sakari,

Hi Sylwester,

> On 06/05/2011 01:55 PM, Sakari Ailus wrote:
> > On Thu, May 26, 2011 at 04:12:47PM +0900, Kim, HeungJun wrote:
> >> Hi Sakari,
> > 
> > Hi HeungJun,
> > 
> >> 2011-05-25 ?????? 10:54, Sakari Ailus ??? ???:
> >>> Hi HeungJun,
> >>>
> >>> Thanks for the patch!
> >> Also, thanks for the interests of this driver!
> > 
> > You'we welcome! :-)
> > 
> >>>
> >>> I'm happy to see that Samsung is interested in getting such a driver to
> >>> mainline. :-) I suppose that theoretically nothing would prevent plugging
> >>> such a sensor to the OMAP 3 ISP, for example. It's just that the sensor
> >>> already does image processing and the ISP might not be that useful because
> >>> of this. But the interfaces would match, both in software and in hardware.
> >> This sensor(actually integrated ISP functionality as you know) is powerful,
> >> and I think this driver can help to make the controls for digital camera.
> >>
> >> But, TI OMAP 3 has also ISP independently, so I think having the ISP module
> >> in the Processor is more better option cause of choice of various sensors,
> >> although the driver's developer has more issues which should be handled.
> >>
> >> I hope to handle ISP directly in the Samsung Processor.
> >>
> >>>
> >>> This is a subdev driver and uses the control framework. Good. I have
> >>> comments on the code below.
> >> Before that, this driver is already merged in Mauro's branch, and
> >> I have spent a few months making this drivers for submitting this.
> >> Some of your comments looks good and needed for this driver.
> >> But, if I fix this and resend it and another comments happened,
> >> this may be endless alone fight to reach "mergeing". :)
> > 
> > Sounds good to me. I have a few additional comments.
> > 
> >> So, I want that I keep this comments in mind, and I'll guarantee the fixes
> >> will be adapted the next time by type of the patch, after this driver patch
> >> is merged fully in 3.0.
> > [clip]
> > 
> ...
> 
> >>>> +
> >>>> +/* The regulator consumer names for external voltage regulators */
> >>>> +static struct regulator_bulk_data supplies[] = {
> >>>> +	{
> >>>> +		.supply = "core",	/* ARM core power, 1.2V */
> >>>> +	}, {
> >>>> +		.supply	= "dig_18",	/* digital power 1, 1.8V */
> >>>> +	}, {
> >>>> +		.supply	= "d_sensor",	/* sensor power 1, 1.8V */
> >>>> +	}, {
> >>>> +		.supply	= "dig_28",	/* digital power 2, 2.8V */
> >>>> +	}, {
> >>>> +		.supply	= "a_sensor",	/* analog power */
> >>>> +	}, {
> >>>> +		.supply	= "dig_12",	/* digital power 3, 1.2V */
> >>>> +	},
> >>>> +};
> >>>
> >>> This looks like something that belongs to board code, or perhaps in the near
> >>> future, to the device tree. The power supplies that are required by a device
> >>> is highly board dependent.
> >> If the regulator name is not common all M-5MOLS, You're right.
> >> But the regulator name of M-5MOLS is fixed.
> > 
> > As far as I understand, M-5MOLS is a sensor which you can, in principle,
> > attach to more or less random hardware. The regulators are not part of the
> > sensor. If someone adds a board which has regulators names or otherwise
> > arranged differently, this change must be done at that time.
> 
> As you may know the above names are _regulator supply names_, not names
> of the actual regulators. One voltage regulator can have multiple 
> "regulator supply names", i.e. the "pads" to which particular loads
> can be connected).
> 
> In particular all the above regulator supplies could be assigned to single
> regulator in the board code, if there is, for example, only one GPIO enabling
> all required voltage regulators.
> 
> The regulator API allows you to map the regulators and regulator loads 
> without passing any information as platform data. Also the regulators'
> hierarchy can be modelled if some of the regulators require other regulator
> to be enabled in advance.
> 
> IMHO as long as the driver handles each voltage required by the device there
> should be no need to abuse the platform data.
> It's a different story though when some of the sensor revisions have voltage 
> supplies arranged differently.

Thanks for the explanation.

So I agree the code may stay as it.

Regards,

-- 
Sakari Ailus
sakari.ailus@iki.fi

^ permalink raw reply	[flat|nested] 34+ messages in thread

end of thread, other threads:[~2011-06-06 10:20 UTC | newest]

Thread overview: 34+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-03-16 13:38 [PATCH] Add support for M-5MOLS 8 Mega Pixel camera Kim, Heungjun
2011-03-19 13:37 ` Sylwester Nawrocki
2011-03-19 15:11   ` Kim HeungJun
2011-03-19 19:28     ` Sylwester Nawrocki
2011-03-21  6:08       ` Kim, HeungJun
2011-04-04 12:20 ` Sungchun Kang
2011-04-05  5:36   ` Kim, HeungJun
2011-04-05  5:38     ` Kim, HeungJun
2011-05-13 10:06 ` [PATCH v7] Add support for M-5MOLS 8 Mega Pixel camera ISP HeungJun, Kim
2011-05-13 11:38   ` HeungJun, Kim
2011-05-16  1:03     ` [PATCH v8] " HeungJun, Kim
2011-05-20  5:56       ` [PATCH v9] " HeungJun, Kim
2011-05-25 13:54         ` Sakari Ailus
2011-05-26  7:12           ` Kim, HeungJun
2011-06-05 11:55             ` Sakari Ailus
2011-06-05 12:11               ` Hans Verkuil
2011-06-05 13:08                 ` Sakari Ailus
2011-06-05 13:13               ` Sylwester Nawrocki
2011-06-06 10:17                 ` Sakari Ailus
2011-06-06  9:14               ` Kim HeungJun
2011-05-27 12:58           ` [PATCH 0/5] Fix micellaneous issues for M-5MOLS driver HeungJun, Kim
2011-05-31  7:35             ` [PATCH v2 0/4] " HeungJun, Kim
2011-05-31  7:35             ` [PATCH v2 1/4] m5mols: Fix capture image size register definition HeungJun, Kim
2011-05-31  7:36             ` [PATCH v2 2/4] m5mols: add m5mols_read_u8/u16/u32() according to I2C byte width HeungJun, Kim
2011-05-31  7:36             ` [PATCH v2 3/4] m5mols: remove union in the m5mols_get_version(), and VERSION_SIZE HeungJun, Kim
2011-06-05 12:03               ` Sakari Ailus
2011-06-05 12:11                 ` Sakari Ailus
2011-06-06  9:20                   ` Kim HeungJun
2011-05-31  7:36             ` [PATCH v2 4/4] m5mols: add parenthesis <> for the head and back of email address HeungJun, Kim
2011-05-27 12:58           ` [PATCH 1/5] m5mols: fix reading wrong size of captured main/thumb image HeungJun, Kim
2011-05-27 12:58           ` [PATCH 2/5] m5mols: add m5mols_read_u8/u16/u32() according to I2C byte width HeungJun, Kim
2011-05-27 12:58           ` [PATCH 3/5] m5mols: remove union in the m5mols_get_version(), and VERSION_SIZE HeungJun, Kim
2011-05-27 12:58           ` [PATCH 4/5] m5mols: rename m5mols_capture_error_handler() to proper name HeungJun, Kim
2011-05-27 12:58           ` [PATCH 5/5] m5mols: add parenthesis <> for the head and back of email address HeungJun, Kim

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.