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;

	ret = __find_resolution(sd, format, &type, &resolution);
	if (ret < 0)
		return ret;

	sfmt = __find_format(info, fh, fmt->which, type);
	if (!sfmt)
		return 0;


	format->code = m5mols_default_ffmt[type].code;
	format->colorspace = V4L2_COLORSPACE_JPEG;
	format->field = V4L2_FIELD_NONE;

	if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
		*sfmt = *format;
		info->resolution = resolution;
		info->res_type = type;
	}

	return 0;
}
Esempio n. 2
0
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;
}
Esempio n. 3
0
static irqreturn_t m5mols_irq_handler(int irq, void *data)
{
	struct m5mols_info *info = to_m5mols(data);

	atomic_set(&info->irq_done, 1);
	wake_up_interruptible(&info->irq_waitq);

	return IRQ_HANDLED;
}
Esempio n. 4
0
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;
}
Esempio n. 5
0
/**
 * 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, u8 reg)
{
	struct m5mols_info *info = to_m5mols(sd);
	u8 mask = is_available_af(info) ? REG_INT_AF : 0;
	u8 dummy;
	int ret;

	ret = m5mols_read_u8(sd, SYSTEM_INT_FACTOR, &dummy);
	if (!ret)
		ret = m5mols_write(sd, SYSTEM_INT_ENABLE, reg & ~mask);
	return ret;
}
Esempio n. 6
0
int m5mols_wait_interrupt(struct v4l2_subdev *sd, u8 irq_mask, u32 timeout)
{
	struct m5mols_info *info = to_m5mols(sd);

	int ret = wait_event_interruptible_timeout(info->irq_waitq,
				atomic_add_unless(&info->irq_done, -1, 0),
				msecs_to_jiffies(timeout));
	if (ret <= 0)
		return ret ? ret : -ETIMEDOUT;

	return m5mols_busy_wait(sd, SYSTEM_INT_FACTOR, irq_mask,
				M5MOLS_I2C_RDY_WAIT_FL | irq_mask, -1);
}
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);
}
Esempio n. 8
0
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;

	format = __find_format(info, fh, fmt->which, info->res_type);
	if (!format)
		return -EINVAL;

	fmt->format = *format;
	return 0;
}
Esempio n. 9
0
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 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;
}
Esempio n. 11
0
/**
 * 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);
	struct m5mols_version *ver = &info->ver;
	u8 *str = ver->str;
	int i;
	int ret;

	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;

	for (i = 0; i < VERSION_STRING_SIZE; i++) {
		ret = m5mols_read_u8(sd, SYSTEM_VER_STRING, &str[i]);
		if (ret)
			return ret;
	}

	ver->fw = be16_to_cpu(ver->fw);
	ver->hw = be16_to_cpu(ver->hw);
	ver->param = be16_to_cpu(ver->param);
	ver->awb = be16_to_cpu(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;
}
static int m5mols_into_capture(struct v4l2_subdev *sd, int res_size)
{
	struct m5mols_info *info = to_m5mols(sd);
	u32 reg;
	int ret, timeout = 1;
	u32 temp = 0;

	info->captured = false;

	/*
	 * The sequence of preparing Capture mode.
	 * 1. Clear Interrupt bit (for dummy)
	 * 2. Enable Capture bit at Interrupt
	 * 3. Lock AE/AWB
	 * 4. Enter Still Capture mode
	 */

	ret = m5mols_set_mode(sd, MODE_MONITOR);
	if (!ret)
		/* FIXME: setting capture exposure at the middle of a amount. */
		ret = i2c_w16_ae(sd, CAT3_MANUAL_GAIN_CAP, 0x90);
	if (!ret)
		ret = m5mols_set_ae_lock(info, true);
	if (!ret)
		ret = m5mols_set_awb_lock(info, true);
	if (!ret)
		ret = i2c_r8_system(sd, CAT0_INT_FACTOR, &reg);
	if (!ret)
		ret = i2c_w8_system(sd, CAT0_INT_ENABLE, 1 << INT_BIT_CAPTURE);
	if (!ret)
		ret = m5mols_set_mode(sd, MODE_CAPTURE);
	if (!ret)
		timeout = wait_event_interruptible_timeout(info->cap_wait,
				info->captured, msecs_to_jiffies(2000));

	/* disable all interrupt & clear interrupt */
	ret = i2c_w8_system(sd, CAT0_INT_ENABLE, 0x0);
	if (!ret)
		ret = i2c_r8_system(sd, CAT0_INT_FACTOR, &reg);
	if (ret)
		return -EPERM;

	/* If all timeout exhausted, return error. */
	if (!timeout)
		return -ETIMEDOUT;

	ret = i2c_r32_capt_ctrl(sd, CATC_CAP_IMAGE_SIZE, &temp);
	info->captured = false;
	return ret;
}
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;
}
Esempio n. 14
0
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 int m5mols_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
{
	struct v4l2_subdev *sd = to_sd(ctrl);
	struct m5mols_info *info = to_m5mols(sd);
	int ret = 0;

	switch (ctrl->id) {
	case V4L2_CID_CAM_JPEG_ENCODEDSIZE:
		ctrl->cur.val = info->cap.total;
		break;
	default:
		ret = -EINVAL;
		break;
	}
	return ret;
}
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_setup_default(sd);
	} else {
		ret = m5mols_sensor_power(info, false);
	}

	return ret;
}
Esempio n. 17
0
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);
}
Esempio n. 18
0
/**
 * 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
 *
 * Returns 0 on success, or else negative errno.
 */
static int m5mols_read(struct v4l2_subdev *sd, u32 size, u32 reg, u32 *val)
{
	struct i2c_client *client = v4l2_get_subdevdata(sd);
	struct m5mols_info *info = to_m5mols(sd);
	u8 rbuf[M5MOLS_I2C_MAX_SIZE + 1];
	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;

	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 == 2) {
		*val = m5mols_swap_byte(&rbuf[1], size);
		return 0;
	}

	if (info->isp_ready)
		v4l2_err(sd, "read failed: size:%d cat:%02x cmd:%02x. %d\n",
			 size, category, cmd, ret);

	return ret < 0 ? ret : -EIO;
}
Esempio n. 19
0
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;
	int ret = 0;

	mutex_lock(&info->lock);

	format = __find_format(info, fh, fmt->which, info->res_type);
	if (format)
		fmt->format = *format;
	else
		ret = -EINVAL;

	mutex_unlock(&info->lock);
	return ret;
}
Esempio n. 20
0
/**
 * m5mols_write - I2C command write function
 * @reg: combination of size, category and command for the I2C packet
 * @val: value to write
 *
 * Returns 0 on success, or else negative errno.
 */
int m5mols_write(struct v4l2_subdev *sd, u32 reg, u32 val)
{
	struct i2c_client *client = v4l2_get_subdevdata(sd);
	struct m5mols_info *info = to_m5mols(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 == 1)
		return 0;

	if (info->isp_ready)
		v4l2_err(sd, "write failed: cat:%02x cmd:%02x ret:%d\n",
			 category, cmd, ret);

	return ret < 0 ? ret : -EIO;
}
Esempio n. 21
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;
}
Esempio n. 22
0
static int m5mols_set_frame_desc(struct v4l2_subdev *sd, unsigned int pad,
				 struct v4l2_mbus_frame_desc *fd)
{
	struct m5mols_info *info = to_m5mols(sd);
	struct v4l2_mbus_framefmt *mf = &info->ffmt[M5MOLS_RESTYPE_CAPTURE];

	if (pad != 0 || fd == NULL)
		return -EINVAL;

	fd->entry[0].flags = V4L2_MBUS_FRAME_DESC_FL_LEN_MAX;
	fd->num_entries = 1;
	fd->entry[0].length = clamp_t(u32, fd->entry[0].length,
				      mf->width * mf->height,
				      M5MOLS_MAIN_JPEG_SIZE_MAX);
	mutex_lock(&info->lock);
	info->cap.buf_size = fd->entry[0].length;
	mutex_unlock(&info->lock);

	return 0;
}
Esempio n. 23
0
static int m5mols_s_stream(struct v4l2_subdev *sd, int enable)
{
	struct m5mols_info *info = to_m5mols(sd);

	if (enable) {
		if (info->code == to_code(M5MOLS_RES_MON)) {
			v4l2_info(sd, "%s : monitor mode\n", __func__);
			return m5mols_start_monitor(sd);
		}
		if (info->code == to_code(M5MOLS_RES_CAPTURE)) {
			v4l2_info(sd, "%s : capture mode\n", __func__);
			return  m5mols_start_capture(sd);
		}
		return -EINVAL;
	} else {
		if (is_streaming(sd))
			return m5mols_set_mode(sd, MODE_PARMSET);
		return -EINVAL;
	}
}
/*
 * get_res_preset - find out M5MOLS register value from requested resolution.
 *
 * @width: requested width
 * @height: requested height
 * @type: requested type of each modes. It supports only monitor mode now.
 */
static int get_res_preset(struct v4l2_subdev *sd, u16 width, u16 height,
		enum m5mols_res_type type)
{
	struct m5mols_info *info = to_m5mols(sd);
	int i;

	for (i = 0; i < ARRAY_SIZE(m5mols_resolutions); i++) {
		if ((m5mols_resolutions[i].type == type) &&
			(m5mols_resolutions[i].width == width) &&
			(m5mols_resolutions[i].height == height))
			break;
	}

	if (i >= ARRAY_SIZE(m5mols_resolutions)) {
		v4l2msg("no matching resolution\n");
		return -EINVAL;
	}

	return m5mols_resolutions[i].value;
}
Esempio n. 25
0
static int m5mols_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad,
				 struct v4l2_mbus_frame_desc *fd)
{
	struct m5mols_info *info = to_m5mols(sd);

	if (pad != 0 || fd == NULL)
		return -EINVAL;

	mutex_lock(&info->lock);
	/*
	 * .get_frame_desc is only used for compressed formats,
	 * thus we always return the capture frame parameters here.
	 */
	fd->entry[0].length = info->cap.buf_size;
	fd->entry[0].pixelcode = info->ffmt[M5MOLS_RESTYPE_CAPTURE].code;
	mutex_unlock(&info->lock);

	fd->entry[0].flags = V4L2_MBUS_FRAME_DESC_FL_LEN_MAX;
	fd->num_entries = 1;

	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;
	int ret = -EINVAL;

	res_type = to_res_type(sd, ffmt->code);
	if (res_type == M5MOLS_RES_MAX)
		return -EINVAL;

	/* If user set portrait for preview, it is substitued width width height
	 * unless get_res_preset will fail that M5MOLS did not support
	 * reverse WVGA */
	if (ffmt->width < ffmt->height) {
		int temp;
		temp  = ffmt->width;
		ffmt->width = ffmt->height;
		ffmt->height = temp;
	}
	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)
		ret = m5mols_into_monitor(sd, size);
	else
		ret = m5mols_into_capture(sd, 0);

	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 ret;
}
Esempio n. 27
0
static int m5mols_s_stream(struct v4l2_subdev *sd, int enable)
{
	struct m5mols_info *info = to_m5mols(sd);
	u32 code;
	int ret;

	mutex_lock(&info->lock);
	code = info->ffmt[info->res_type].code;

	if (enable) {
		if (is_code(code, M5MOLS_RESTYPE_MONITOR))
			ret = m5mols_start_monitor(info);
		if (is_code(code, M5MOLS_RESTYPE_CAPTURE))
			ret = m5mols_start_capture(info);
		else
			ret = -EINVAL;
	} else {
		ret = m5mols_set_mode(info, REG_PARAMETER);
	}

	mutex_unlock(&info->lock);
	return ret;
}
/*
 * get_version - get M5MOLS sensor versions.
 */
static int get_version(struct v4l2_subdev *sd)
{
	struct m5mols_info *info = to_m5mols(sd);
	union {
		struct m5mols_version	ver;
		u8			bytes[10];
	} value;
	int ret, i;

	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;
	}

	info->ver = value.ver;

	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 int m5mols_s_ctrl(struct v4l2_ctrl *ctrl)
{
	struct v4l2_subdev *sd = to_sd(ctrl);
	struct m5mols_info *info = to_m5mols(sd);
	int ispstate = info->mode;
	int ret;

	/*
	 * If needed, defer restoring the controls until
	 * the device is fully initialized.
	 */
	if (!info->isp_ready) {
		info->ctrl_sync = 0;
		return 0;
	}

	ret = m5mols_mode(info, REG_PARAMETER);
	if (ret < 0)
		return ret;
	ret = m5mols_set_ctrl(ctrl);
	if (ret < 0)
		return ret;
	return m5mols_mode(info, ispstate);
}
/* TODO: not verified. */
static int m5mols_start_capture(struct v4l2_subdev *sd)
{
	struct m5mols_info *info = to_m5mols(sd);
	u32 reg, size;
	int ret, timeout;

	u8 reg_capt_fmt[] = {
		0x10,	/* JPEG with header + Thumbnail JPEG(YUV422@QVGA) */
	}; /* YUV422, JPEG(422), JPEG(420) */
	info->captured = false;

	size = get_res_preset(sd,
			info->fmt[M5MOLS_RES_CAPTURE].width,
			info->fmt[M5MOLS_RES_CAPTURE].height,
			M5MOLS_RES_CAPTURE);
	if (size < 0)
		return -EINVAL;
	ret = 0;
	/*
	 * The sequence of Starting Capture mode.
	 * 1. Select capture Single or Multi
	 * 2. Select format (YUV422, JPEG(YUV420, YUV422))
	 * 3. Set image size preset of Capture
	 * 4. Read Interrupt bit (for dummy)
	 * 5. Enable Capture bit at Interrupt
	 * 6. Start Capture
	 * 7. Check interrupt and register value
	 * 8. Get Image & Thumb size
	 */
	ret = i2c_w8_capt_ctrl(sd, CATC_CAP_SEL_FRAME, true);	/* single capture */
	if (!ret)
		ret = i2c_w8_capt_parm(sd, CATB_YUVOUT_MAIN, reg_capt_fmt[0]);
	if (!ret)
		ret = i2c_w8_capt_parm(sd, CATB_MAIN_IMAGE_SIZE, size);
	if (!ret)
		ret = i2c_r8_system(sd, CAT0_INT_FACTOR, &reg);
	if (!ret)
		ret = i2c_w8_system(sd, CAT0_INT_ENABLE, 1 << INT_BIT_CAPTURE);
	if (!ret)
		ret = i2c_w8_capt_ctrl(sd, CATC_CAP_START, true);
	if (!ret) {
		timeout = wait_event_interruptible_timeout(info->cap_wait,
				info->captured, msecs_to_jiffies(2000));

		if (info->captured) {
			ret = m5mols_get_info_capture(sd);

			if (!ret)
				v4l2_subdev_notify(sd, info->cap.total, NULL);
			else
				return ret;
		}

		/* disable all interrupt & clear interrupt */
		ret = i2c_w8_system(sd, CAT0_INT_ENABLE, 0x0);
		if (!ret)
			ret = i2c_r8_system(sd, CAT0_INT_FACTOR, &reg);
		if (ret)
			return -EPERM;

		/* If all timeout exhausted, return error. */
		if (!timeout)
			return -ETIMEDOUT;

		info->captured = false;

		ret = 0;
	}

	/* TODO: complete capture. */

	return ret;
}