RE: [PATCH/RFC 9/9] mt9t031: make the use of the soc-camera client API optional

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

 



Guennadi,

Thanks for your time for updating this driver. But I still don't think
it is in a state to be re-used on TI's VPFE platform. Please see
below for my comments.

Murali Karicheri
Software Design Engineer
Texas Instruments Inc.
Germantown, MD 20874
email: m-karicheri2@xxxxxx

>-----Original Message-----
>From: Guennadi Liakhovetski [mailto:g.liakhovetski@xxxxxx]
>Sent: Friday, October 30, 2009 10:02 AM
>To: Linux Media Mailing List
>Cc: Hans Verkuil; Laurent Pinchart; Sakari Ailus; Karicheri, Muralidharan
>Subject: [PATCH/RFC 9/9] mt9t031: make the use of the soc-camera client API
>optional
>
>Now that we have moved most of the functions over to the v4l2-subdev API,
>only
>quering and setting bus parameters are still performed using the legacy
>soc-camera client API. Make the use of this API optional for mt9t031.
>
>Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@xxxxxx>
>---
>
>Muralidharan, this one is for you to test. To differentiate between the
>soc-camera case and a generic user I check i2c client's platform data
>(client->dev.platform_data), so, you have to make sure your user doesn't
>use that field for something else.
>
Currently I am using this field for bus parameters such as pclk polarity.
If there is an API (bus parameter) to set this after probing the sensor, that may work too. I will check your latest driver and let you know if 
I see an issue in migrating to this version.

>One more note: I'm not sure about where v4l2_device_unregister_subdev()
>should be called. In soc-camera the core calls
>v4l2_i2c_new_subdev_board(), which then calls
>v4l2_device_register_subdev(). Logically, it's also the core that then
>calls v4l2_device_unregister_subdev(). Whereas I see many other client
>drivers call v4l2_device_unregister_subdev() internally. So, if your
>bridge driver does not call v4l2_device_unregister_subdev() itself and
>expects the client to call it, there will be a slight problem with that
>too.

In my bridge driver also v4l2_i2c_new_subdev_board() is called to load
up the sub device. When the bridge driver is removed (remove() call), it calls v4l2_device_unregister() which will unregister the v4l2 device and all
sub devices (in turn calls v4l2_device_unregister_subdev()). But most
of the sub devices also calls v4l2_device_unregister_subdev() in the
remove() function of the module (so also the version of the mt9t031
that I use). So even if that call is kept in the mt9t031 sensor driver (not sure if someone use it as a standalone driver), it would just return since the v4l2_dev ptr in sd ptr would have been set to null as a result of the bridge driver remove() call. What do you think?

See also some comments below..

>
> drivers/media/video/mt9t031.c |  146 ++++++++++++++++++++-----------------
>----
> 1 files changed, 70 insertions(+), 76 deletions(-)
>
>diff --git a/drivers/media/video/mt9t031.c b/drivers/media/video/mt9t031.c
>index c95c277..49357bd 100644
>--- a/drivers/media/video/mt9t031.c
>+++ b/drivers/media/video/mt9t031.c
>@@ -204,6 +204,59 @@ static unsigned long mt9t031_query_bus_param(struct
>soc_camera_device *icd)
> 	return soc_camera_apply_sensor_flags(icl, MT9T031_BUS_PARAM);
> }
>
>+static const struct v4l2_queryctrl mt9t031_controls[] = {
>+	{
>+		.id		= V4L2_CID_VFLIP,
>+		.type		= V4L2_CTRL_TYPE_BOOLEAN,
>+		.name		= "Flip Vertically",
>+		.minimum	= 0,
>+		.maximum	= 1,
>+		.step		= 1,
>+		.default_value	= 0,
>+	}, {
>+		.id		= V4L2_CID_HFLIP,
>+		.type		= V4L2_CTRL_TYPE_BOOLEAN,
>+		.name		= "Flip Horizontally",
>+		.minimum	= 0,
>+		.maximum	= 1,
>+		.step		= 1,
>+		.default_value	= 0,
>+	}, {
>+		.id		= V4L2_CID_GAIN,
>+		.type		= V4L2_CTRL_TYPE_INTEGER,
>+		.name		= "Gain",
>+		.minimum	= 0,
>+		.maximum	= 127,
>+		.step		= 1,
>+		.default_value	= 64,
>+		.flags		= V4L2_CTRL_FLAG_SLIDER,
>+	}, {
>+		.id		= V4L2_CID_EXPOSURE,
>+		.type		= V4L2_CTRL_TYPE_INTEGER,
>+		.name		= "Exposure",
>+		.minimum	= 1,
>+		.maximum	= 255,
>+		.step		= 1,
>+		.default_value	= 255,
>+		.flags		= V4L2_CTRL_FLAG_SLIDER,
>+	}, {
>+		.id		= V4L2_CID_EXPOSURE_AUTO,
>+		.type		= V4L2_CTRL_TYPE_BOOLEAN,
>+		.name		= "Automatic Exposure",
>+		.minimum	= 0,
>+		.maximum	= 1,
>+		.step		= 1,
>+		.default_value	= 1,
>+	}
>+};
>+
>+static struct soc_camera_ops mt9t031_ops = {
>+	.set_bus_param		= mt9t031_set_bus_param,
>+	.query_bus_param	= mt9t031_query_bus_param,
>+	.controls		= mt9t031_controls,
>+	.num_controls		= ARRAY_SIZE(mt9t031_controls),
>+};
>+

[MK] Why don't you implement queryctrl ops in core? query_bus_param
& set_bus_param() can be implemented as a sub device operation as well
right? I think we need to get the bus parameter RFC implemented and
this driver could be targeted for it's first use so that we could
work together to get it accepted. I didn't get a chance to study your bus image format RFC, but plan to review it soon and to see if it can be
used in my platform as well. For use of this driver in our platform,
all reference to soc_ must be removed. I am ok if the structure is
re-used, but if this driver calls any soc_camera function, it canot
be used in my platform.

BTW, I am attaching a version of the driver that we use in our kernel tree for your reference which will give you an idea of my requirement.

> /* target must be _even_ */
> static u16 mt9t031_skip(s32 *source, s32 target, s32 max)
> {
>@@ -223,10 +276,9 @@ static u16 mt9t031_skip(s32 *source, s32 target, s32
>max)
> }
>
> /* rect is the sensor rectangle, the caller guarantees parameter validity
>*/
>-static int mt9t031_set_params(struct soc_camera_device *icd,
>+static int mt9t031_set_params(struct i2c_client *client,
> 			      struct v4l2_rect *rect, u16 xskip, u16 yskip)
> {
>-	struct i2c_client *client =
>to_i2c_client(to_soc_camera_control(icd));
> 	struct mt9t031 *mt9t031 = to_mt9t031(client);
> 	int ret;
> 	u16 xbin, ybin;
>@@ -307,7 +359,7 @@ static int mt9t031_set_params(struct soc_camera_device
>*icd,
> 		if (ret >= 0) {
> 			const u32 shutter_max = MT9T031_MAX_HEIGHT + vblank;
> 			const struct v4l2_queryctrl *qctrl =
>-				soc_camera_find_qctrl(icd->ops,
>+				soc_camera_find_qctrl(&mt9t031_ops,
> 						      V4L2_CID_EXPOSURE);
> 			mt9t031->exposure = (shutter_max / 2 + (total_h - 1) *
> 				 (qctrl->maximum - qctrl->minimum)) /
>@@ -333,7 +385,6 @@ static int mt9t031_s_crop(struct v4l2_subdev *sd,
>struct v4l2_crop *a)
> 	struct v4l2_rect rect = a->c;
> 	struct i2c_client *client = sd->priv;
> 	struct mt9t031 *mt9t031 = to_mt9t031(client);
>-	struct soc_camera_device *icd = client->dev.platform_data;
>
> 	rect.width = ALIGN(rect.width, 2);
> 	rect.height = ALIGN(rect.height, 2);
>@@ -344,7 +395,7 @@ static int mt9t031_s_crop(struct v4l2_subdev *sd,
>struct v4l2_crop *a)
> 	soc_camera_limit_side(&rect.top, &rect.height,
> 		     MT9T031_ROW_SKIP, MT9T031_MIN_HEIGHT, MT9T031_MAX_HEIGHT);
>
>-	return mt9t031_set_params(icd, &rect, mt9t031->xskip, mt9t031-
>>yskip);
>+	return mt9t031_set_params(client, &rect, mt9t031->xskip, mt9t031-
>>yskip);
> }
>
> static int mt9t031_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
>@@ -391,7 +442,6 @@ static int mt9t031_s_fmt(struct v4l2_subdev *sd,
> {
> 	struct i2c_client *client = sd->priv;
> 	struct mt9t031 *mt9t031 = to_mt9t031(client);
>-	struct soc_camera_device *icd = client->dev.platform_data;
> 	u16 xskip, yskip;
> 	struct v4l2_rect rect = mt9t031->rect;
>
>@@ -403,7 +453,7 @@ static int mt9t031_s_fmt(struct v4l2_subdev *sd,
> 	yskip = mt9t031_skip(&rect.height, imgf->height, MT9T031_MAX_HEIGHT);
>
> 	/* mt9t031_set_params() doesn't change width and height */
>-	return mt9t031_set_params(icd, &rect, xskip, yskip);
>+	return mt9t031_set_params(client, &rect, xskip, yskip);
> }
>
> /*
>@@ -476,59 +526,6 @@ static int mt9t031_s_register(struct v4l2_subdev *sd,
> }
> #endif
>
>-static const struct v4l2_queryctrl mt9t031_controls[] = {
>-	{
>-		.id		= V4L2_CID_VFLIP,
>-		.type		= V4L2_CTRL_TYPE_BOOLEAN,
>-		.name		= "Flip Vertically",
>-		.minimum	= 0,
>-		.maximum	= 1,
>-		.step		= 1,
>-		.default_value	= 0,
>-	}, {
>-		.id		= V4L2_CID_HFLIP,
>-		.type		= V4L2_CTRL_TYPE_BOOLEAN,
>-		.name		= "Flip Horizontally",
>-		.minimum	= 0,
>-		.maximum	= 1,
>-		.step		= 1,
>-		.default_value	= 0,
>-	}, {
>-		.id		= V4L2_CID_GAIN,
>-		.type		= V4L2_CTRL_TYPE_INTEGER,
>-		.name		= "Gain",
>-		.minimum	= 0,
>-		.maximum	= 127,
>-		.step		= 1,
>-		.default_value	= 64,
>-		.flags		= V4L2_CTRL_FLAG_SLIDER,
>-	}, {
>-		.id		= V4L2_CID_EXPOSURE,
>-		.type		= V4L2_CTRL_TYPE_INTEGER,
>-		.name		= "Exposure",
>-		.minimum	= 1,
>-		.maximum	= 255,
>-		.step		= 1,
>-		.default_value	= 255,
>-		.flags		= V4L2_CTRL_FLAG_SLIDER,
>-	}, {
>-		.id		= V4L2_CID_EXPOSURE_AUTO,
>-		.type		= V4L2_CTRL_TYPE_BOOLEAN,
>-		.name		= "Automatic Exposure",
>-		.minimum	= 0,
>-		.maximum	= 1,
>-		.step		= 1,
>-		.default_value	= 1,
>-	}
>-};
>-
>-static struct soc_camera_ops mt9t031_ops = {
>-	.set_bus_param		= mt9t031_set_bus_param,
>-	.query_bus_param	= mt9t031_query_bus_param,
>-	.controls		= mt9t031_controls,
>-	.num_controls		= ARRAY_SIZE(mt9t031_controls),
>-};
>-
> static int mt9t031_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control
>*ctrl)
> {
> 	struct i2c_client *client = sd->priv;
>@@ -565,7 +562,6 @@ static int mt9t031_s_ctrl(struct v4l2_subdev *sd,
>struct v4l2_control *ctrl)
> {
> 	struct i2c_client *client = sd->priv;
> 	struct mt9t031 *mt9t031 = to_mt9t031(client);
>-	struct soc_camera_device *icd = client->dev.platform_data;
> 	const struct v4l2_queryctrl *qctrl;
> 	int data;
>
>@@ -657,7 +653,8 @@ static int mt9t031_s_ctrl(struct v4l2_subdev *sd,
>struct v4l2_control *ctrl)
>
> 			if (set_shutter(client, total_h) < 0)
> 				return -EIO;
>-			qctrl = soc_camera_find_qctrl(icd->ops,
>V4L2_CID_EXPOSURE);
>+			qctrl = soc_camera_find_qctrl(&mt9t031_ops,
>+						      V4L2_CID_EXPOSURE);

[MK] Why do we still need this call? In my version of the sensor driver,
I just implement the queryctrl() operation in core_ops. This cannot work
since soc_camera_find_qctrl() is implemented only in SoC camera.

> 			mt9t031->exposure = (shutter_max / 2 + (total_h - 1) *
> 				 (qctrl->maximum - qctrl->minimum)) /
> 				shutter_max + qctrl->minimum;
>@@ -751,18 +748,16 @@ static int mt9t031_probe(struct i2c_client *client,
> 	struct mt9t031 *mt9t031;
> 	struct soc_camera_device *icd = client->dev.platform_data;
> 	struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
>-	struct soc_camera_link *icl;
> 	int ret;
>
>-	if (!icd) {
>-		dev_err(&client->dev, "MT9T031: missing soc-camera data!\n");
>-		return -EINVAL;
>-	}
>+	if (icd) {
>+		struct soc_camera_link *icl = to_soc_camera_link(icd);
>+		if (!icl) {
>+			dev_err(&client->dev, "MT9T031 driver needs platform
>data\n");
>+			return -EINVAL;
>+		}
>
>-	icl = to_soc_camera_link(icd);
>-	if (!icl) {
>-		dev_err(&client->dev, "MT9T031 driver needs platform data\n");
>-		return -EINVAL;
>+		icd->ops = &mt9t031_ops;
> 	}
>
> 	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) {
>@@ -777,9 +772,6 @@ static int mt9t031_probe(struct i2c_client *client,
>
> 	v4l2_i2c_subdev_init(&mt9t031->subdev, client, &mt9t031_subdev_ops);
>
>-	/* Second stage probe - when a capture adapter is there */
>-	icd->ops		= &mt9t031_ops;
>-
> 	mt9t031->rect.left	= MT9T031_COLUMN_SKIP;
> 	mt9t031->rect.top	= MT9T031_ROW_SKIP;
> 	mt9t031->rect.width	= MT9T031_MAX_WIDTH;
>@@ -801,7 +793,8 @@ static int mt9t031_probe(struct i2c_client *client,
> 	mt9t031_disable(client);
>
> 	if (ret) {
>-		icd->ops = NULL;
>+		if (icd)
>+			icd->ops = NULL;
> 		i2c_set_clientdata(client, NULL);
> 		kfree(mt9t031);
> 	}
>@@ -814,7 +807,8 @@ static int mt9t031_remove(struct i2c_client *client)
> 	struct mt9t031 *mt9t031 = to_mt9t031(client);
> 	struct soc_camera_device *icd = client->dev.platform_data;
>
>-	icd->ops = NULL;
>+	if (icd)
>+		icd->ops = NULL;
> 	i2c_set_clientdata(client, NULL);
> 	client->driver = NULL;
> 	kfree(mt9t031);
>--
>1.6.2.4
>

/*
 * Driver for MT9T031 CMOS Image Sensor from Micron
 *
 * Copyright (C) 2008, Guennadi Liakhovetski, DENX Software Engineering <lg@xxxxxxx>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/videodev2.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/log2.h>

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

/* mt9t031 i2c address 0x5d
 * The platform has to define i2c_board_info
 * and call i2c_register_board_info() */

/* mt9t031 selected register addresses */
#define MT9T031_CHIP_VERSION		0x00
#define MT9T031_ROW_START		0x01
#define MT9T031_COLUMN_START		0x02
#define MT9T031_WINDOW_HEIGHT		0x03
#define MT9T031_WINDOW_WIDTH		0x04
#define MT9T031_HORIZONTAL_BLANKING	0x05
#define MT9T031_VERTICAL_BLANKING	0x06
#define MT9T031_OUTPUT_CONTROL		0x07
#define MT9T031_SHUTTER_WIDTH_UPPER	0x08
#define MT9T031_SHUTTER_WIDTH		0x09
#define MT9T031_PIXEL_CLOCK_CONTROL	0x0a
#define MT9T031_FRAME_RESTART		0x0b
#define MT9T031_SHUTTER_DELAY		0x0c
#define MT9T031_RESET			0x0d
#define MT9T031_READ_MODE_1		0x1e
#define MT9T031_READ_MODE_2		0x20
#define MT9T031_READ_MODE_3		0x21
#define MT9T031_ROW_ADDRESS_MODE	0x22
#define MT9T031_COLUMN_ADDRESS_MODE	0x23
#define MT9T031_GLOBAL_GAIN		0x35
#define MT9T031_CHIP_ENABLE		0xF8

#define MT9T031_MAX_HEIGHT		1536
#define MT9T031_MAX_WIDTH		2048
#define MT9T031_MIN_HEIGHT		2
#define MT9T031_MIN_WIDTH		2
#define MT9T031_HORIZONTAL_BLANK	142
#define MT9T031_VERTICAL_BLANK		25
#define MT9T031_COLUMN_SKIP		32
#define MT9T031_ROW_SKIP		20
#define MT9T031_DEFAULT_WIDTH		640
#define MT9T031_DEFAULT_HEIGHT		480

#define MT9T031_BUS_PARAM	(SOCAM_PCLK_SAMPLE_RISING |	\
	SOCAM_PCLK_SAMPLE_FALLING | SOCAM_HSYNC_ACTIVE_HIGH |	\
	SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_DATA_ACTIVE_HIGH |	\
	SOCAM_MASTER | SOCAM_DATAWIDTH_10)


/* Debug functions */
static int debug;
module_param(debug, bool, 0644);
MODULE_PARM_DESC(debug, "Debug level (0-1)");

static const struct v4l2_fmtdesc mt9t031_formats[] = {
	{
		.index = 0,
		.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
		.description = "Bayer (sRGB) 10 bit",
		.pixelformat = V4L2_PIX_FMT_SGRBG10,
	},
};
static const unsigned int mt9t031_num_formats = ARRAY_SIZE(mt9t031_formats);

static const struct v4l2_queryctrl mt9t031_controls[] = {
	{
		.id		= V4L2_CID_VFLIP,
		.type		= V4L2_CTRL_TYPE_BOOLEAN,
		.name		= "Flip Vertically",
		.minimum	= 0,
		.maximum	= 1,
		.step		= 1,
		.default_value	= 0,
	}, {
		.id		= V4L2_CID_HFLIP,
		.type		= V4L2_CTRL_TYPE_BOOLEAN,
		.name		= "Flip Horizontally",
		.minimum	= 0,
		.maximum	= 1,
		.step		= 1,
		.default_value	= 0,
	}, {
		.id		= V4L2_CID_GAIN,
		.type		= V4L2_CTRL_TYPE_INTEGER,
		.name		= "Gain",
		.minimum	= 0,
		.maximum	= 127,
		.step		= 1,
		.default_value	= 64,
		.flags		= V4L2_CTRL_FLAG_SLIDER,
	}, {
		.id		= V4L2_CID_EXPOSURE,
		.type		= V4L2_CTRL_TYPE_INTEGER,
		.name		= "Exposure",
		.minimum	= 1,
		.maximum	= 255,
		.step		= 1,
		.default_value	= 255,
		.flags		= V4L2_CTRL_FLAG_SLIDER,
	}, {
		.id		= V4L2_CID_EXPOSURE_AUTO,
		.type		= V4L2_CTRL_TYPE_BOOLEAN,
		.name		= "Automatic Exposure",
		.minimum	= 0,
		.maximum	= 1,
		.step		= 1,
		.default_value	= 1,
	}
};
static const unsigned int mt9t031_num_controls = ARRAY_SIZE(mt9t031_controls);

struct mt9t031 {
	struct v4l2_subdev sd;
	int model;	/* V4L2_IDENT_MT9T031* codes from v4l2-chip-ident.h */
	unsigned char autoexposure;
	u16 xskip;
	u16 yskip;
	u32 width;
	u32 height;
	unsigned short x_min;           /* Camera capabilities */
	unsigned short y_min;
	unsigned short x_current;       /* Current window location */
	unsigned short y_current;
	unsigned short width_min;
	unsigned short width_max;
	unsigned short height_min;
	unsigned short height_max;
	unsigned short y_skip_top;      /* Lines to skip at the top */
	unsigned short gain;
	unsigned short exposure;
};

static inline struct mt9t031 *to_mt9t031(struct v4l2_subdev *sd)
{
	return container_of(sd, struct mt9t031, sd);
}

static int reg_read(struct i2c_client *client, const u8 reg)
{
	s32 data;

	data = i2c_smbus_read_word_data(client, reg);
	return data < 0 ? data : swab16(data);
}

static int reg_write(struct i2c_client *client, const u8 reg,
		     const u16 data)
{
	return i2c_smbus_write_word_data(client, reg, swab16(data));
}

static int reg_set(struct i2c_client *client, const u8 reg,
		   const u16 data)
{
	int ret;

	ret = reg_read(client, reg);
	if (ret < 0)
		return ret;
	return reg_write(client, reg, ret | data);
}

static int reg_clear(struct i2c_client *client, const u8 reg,
		     const u16 data)
{
	int ret;

	ret = reg_read(client, reg);
	if (ret < 0)
		return ret;
	return reg_write(client, reg, ret & ~data);
}

static int set_shutter(struct v4l2_subdev *sd, const u32 data)
{
	struct i2c_client *client = v4l2_get_subdevdata(sd);
	int ret;

	ret = reg_write(client, MT9T031_SHUTTER_WIDTH_UPPER, data >> 16);

	if (ret >= 0)
		ret = reg_write(client, MT9T031_SHUTTER_WIDTH, data & 0xffff);

	return ret;
}

static int get_shutter(struct v4l2_subdev *sd, u32 *data)
{
	int ret;
	struct i2c_client *client = v4l2_get_subdevdata(sd);

	ret = reg_read(client, MT9T031_SHUTTER_WIDTH_UPPER);
	*data = ret << 16;

	if (ret >= 0)
		ret = reg_read(client, MT9T031_SHUTTER_WIDTH);
	*data |= ret & 0xffff;

	return ret < 0 ? ret : 0;
}

static int mt9t031_init(struct v4l2_subdev *sd, u32 val)
{
	int ret;
	struct i2c_client *client = v4l2_get_subdevdata(sd);

	/* Disable chip output, synchronous option update */
	ret = reg_write(client, MT9T031_RESET, 1);
	if (ret >= 0)
		ret = reg_write(client, MT9T031_RESET, 0);
	if (ret >= 0)
		ret = reg_clear(client, MT9T031_OUTPUT_CONTROL, 2);

	return ret >= 0 ? 0 : -EIO;
}

static int mt9t031_s_stream(struct v4l2_subdev *sd, int enable)
{
	struct i2c_client *client = v4l2_get_subdevdata(sd);

	/* Switch to master "normal" mode */
	if (enable) {
		if (reg_set(client, MT9T031_OUTPUT_CONTROL, 2) < 0)
			return -EIO;
	} else {
	/* Switch to master "" mode */
		if (reg_clear(client, MT9T031_OUTPUT_CONTROL, 2) < 0)
			return -EIO;
	}
	return 0;
}

/* Round up minima and round down maxima */
static void recalculate_limits(struct mt9t031 *mt9t031,
			       u16 xskip, u16 yskip)
{
	mt9t031->x_min = (MT9T031_COLUMN_SKIP + xskip - 1) / xskip;
	mt9t031->y_min = (MT9T031_ROW_SKIP + yskip - 1) / yskip;
	mt9t031->width_min = (MT9T031_MIN_WIDTH + xskip - 1) / xskip;
	mt9t031->height_min = (MT9T031_MIN_HEIGHT + yskip - 1) / yskip;
	mt9t031->width_max = MT9T031_MAX_WIDTH / xskip;
	mt9t031->height_max = MT9T031_MAX_HEIGHT / yskip;
}

const struct v4l2_queryctrl *mt9t031_find_qctrl(u32 id)
{
	int i;

	for (i = 0; i < mt9t031_num_controls; i++) {
		if (mt9t031_controls[i].id == id)
			return &mt9t031_controls[i];
	}
	return NULL;
}

static int mt9t031_set_params(struct v4l2_subdev *sd,
			      struct v4l2_rect *rect, u16 xskip, u16 yskip)
{
	struct mt9t031 *mt9t031 = to_mt9t031(sd);
	struct i2c_client *client = v4l2_get_subdevdata(sd);

	int ret;
	u16 xbin, ybin, width, height, left, top;
	const u16 hblank = MT9T031_HORIZONTAL_BLANK,
		vblank = MT9T031_VERTICAL_BLANK;

	/* Make sure we don't exceed sensor limits */
	if (rect->left + rect->width > mt9t031->width_max)
		rect->left =
		(mt9t031->width_max - rect->width) / 2 + mt9t031->x_min;

	if (rect->top + rect->height > mt9t031->height_max)
		rect->top =
		(mt9t031->height_max - rect->height) / 2 + mt9t031->y_min;

	width = rect->width * xskip;
	height = rect->height * yskip;
	left = rect->left * xskip;
	top = rect->top * yskip;

	xbin = min(xskip, (u16)3);
	ybin = min(yskip, (u16)3);

	v4l2_dbg(1, debug, sd, "xskip %u, width %u/%u, yskip %u,"
		"height %u/%u\n", xskip, width, rect->width, yskip,
		height, rect->height);

	/* Could just do roundup(rect->left, [xy]bin * 2); but this is cheaper */
	switch (xbin) {
	case 2:
		left = (left + 3) & ~3;
		break;
	case 3:
		left = roundup(left, 6);
	}

	switch (ybin) {
	case 2:
		top = (top + 3) & ~3;
		break;
	case 3:
		top = roundup(top, 6);
	}

	/* Disable register update, reconfigure atomically */
	ret = reg_set(client, MT9T031_OUTPUT_CONTROL, 1);
	if (ret < 0)
		return ret;

	/* Blanking and start values - default... */
	ret = reg_write(client, MT9T031_HORIZONTAL_BLANKING, hblank);
	if (ret >= 0)
		ret = reg_write(client, MT9T031_VERTICAL_BLANKING, vblank);

	if (yskip != mt9t031->yskip || xskip != mt9t031->xskip) {
		/* Binning, skipping */
		if (ret >= 0)
			ret = reg_write(client, MT9T031_COLUMN_ADDRESS_MODE,
					((xbin - 1) << 4) | (xskip - 1));
		if (ret >= 0)
			ret = reg_write(client, MT9T031_ROW_ADDRESS_MODE,
					((ybin - 1) << 4) | (yskip - 1));
	}
	v4l2_dbg(1, debug, sd, "new physical left %u, top %u\n", left, top);

	/* The caller provides a supported format, as guaranteed by
	 * icd->try_fmt_cap(), soc_camera_s_crop() and soc_camera_cropcap() */
	if (ret >= 0)
		ret = reg_write(client, MT9T031_COLUMN_START, left);
	if (ret >= 0)
		ret = reg_write(client, MT9T031_ROW_START, top);
	if (ret >= 0)
		ret = reg_write(client, MT9T031_WINDOW_WIDTH, width - 1);
	if (ret >= 0)
		ret = reg_write(client, MT9T031_WINDOW_HEIGHT,
				height + mt9t031->y_skip_top - 1);
	if (ret >= 0 && mt9t031->autoexposure) {
		ret = set_shutter(sd, height + mt9t031->y_skip_top + vblank);
		if (ret >= 0) {
			const u32 shutter_max = MT9T031_MAX_HEIGHT + vblank;
			const struct v4l2_queryctrl *qctrl =
				mt9t031_find_qctrl(V4L2_CID_EXPOSURE);
			mt9t031->exposure = (shutter_max / 2 + (height +
					 mt9t031->y_skip_top + vblank - 1) *
					 (qctrl->maximum - qctrl->minimum)) /
				shutter_max + qctrl->minimum;
		}
	}

	/* Re-enable register update, commit all changes */
	if (ret >= 0) {
		ret = reg_clear(client, MT9T031_OUTPUT_CONTROL, 1);
		/* update the values */
		mt9t031->width	= rect->width,
		mt9t031->height	= rect->height,
		mt9t031->x_current = rect->left;
		mt9t031->y_current = rect->top;
	}
	return ret < 0 ? ret : 0;
}

static int mt9t031_set_fmt(struct v4l2_subdev *sd,
			   struct v4l2_format *f)
{
	struct mt9t031 *mt9t031 = to_mt9t031(sd);
	int ret;
	u16 xskip, yskip;
	struct v4l2_rect rect = {
		.left	= mt9t031->x_current,
		.top	= mt9t031->y_current,
		.width	= f->fmt.pix.width,
		.height	= f->fmt.pix.height,
	};

	/*
	 * try_fmt has put rectangle within limits.
	 * S_FMT - use binning and skipping for scaling, recalculate
	 * limits, used for cropping
	 */
	/* Is this more optimal than just a division? */
	for (xskip = 8; xskip > 1; xskip--)
		if (rect.width * xskip <= MT9T031_MAX_WIDTH)
			break;

	for (yskip = 8; yskip > 1; yskip--)
		if (rect.height * yskip <= MT9T031_MAX_HEIGHT)
			break;

	recalculate_limits(mt9t031, xskip, yskip);

	ret = mt9t031_set_params(sd, &rect, xskip, yskip);
	if (!ret) {
		mt9t031->xskip = xskip;
		mt9t031->yskip = yskip;
	}
	return ret;
}

static int mt9t031_try_fmt(struct v4l2_subdev *sd,
			   struct v4l2_format *f)
{
	struct v4l2_pix_format *pix = &f->fmt.pix;

	if (pix->height < MT9T031_MIN_HEIGHT)
		pix->height = MT9T031_MIN_HEIGHT;
	if (pix->height > MT9T031_MAX_HEIGHT)
		pix->height = MT9T031_MAX_HEIGHT;
	if (pix->width < MT9T031_MIN_WIDTH)
		pix->width = MT9T031_MIN_WIDTH;
	if (pix->width > MT9T031_MAX_WIDTH)
		pix->width = MT9T031_MAX_WIDTH;

	pix->width &= ~0x01; /* has to be even */
	pix->height &= ~0x01; /* has to be even */
	return 0;
}

static int mt9t031_get_chip_id(struct v4l2_subdev *sd,
			       struct v4l2_dbg_chip_ident *id)
{
	struct mt9t031 *mt9t031 = to_mt9t031(sd);
	struct i2c_client *client = v4l2_get_subdevdata(sd);;

	if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
		return -EINVAL;

	if (id->match.addr != client->addr)
		return -ENODEV;

	id->ident	= mt9t031->model;
	id->revision	= 0;

	return 0;
}

#ifdef CONFIG_VIDEO_ADV_DEBUG
static int mt9t031_get_register(struct v4l2_subdev *sd,
				struct v4l2_dbg_register *reg)
{
	struct i2c_client *client = v4l2_get_subdevdata(sd);;
	struct mt9t031 *mt9t031 = to_mt9t031(sd);

	if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff)
		return -EINVAL;

	if (reg->match.addr != client->addr)
		return -ENODEV;

	reg->val = reg_read(client, reg->reg);

	if (reg->val > 0xffff)
		return -EIO;

	return 0;
}

static int mt9t031_set_register(struct v4l2_subdev *sd,
				struct v4l2_dbg_register *reg)
{
	struct i2c_client *client = v4l2_get_subdevdata(sd);
	struct mt9t031 *mt9t031 = to_mt9t031(sd);

	if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff)
		return -EINVAL;

	if (reg->match.addr != client->addr)
		return -ENODEV;

	if (reg_write(client, reg->reg, reg->val) < 0)
		return -EIO;

	return 0;
}
#endif


static int mt9t031_get_control(struct v4l2_subdev *, struct v4l2_control *);
static int mt9t031_set_control(struct v4l2_subdev *, struct v4l2_control *);
static int mt9t031_queryctrl(struct v4l2_subdev *, struct v4l2_queryctrl *);

static const struct v4l2_subdev_core_ops mt9t031_core_ops = {
	.g_chip_ident = mt9t031_get_chip_id,
	.init = mt9t031_init,
	.queryctrl = mt9t031_queryctrl,
	.g_ctrl	= mt9t031_get_control,
	.s_ctrl	= mt9t031_set_control,
#ifdef CONFIG_VIDEO_ADV_DEBUG
	.get_register = mt9t031_get_register,
	.set_register = mt9t031_set_register,
#endif
};

static const struct v4l2_subdev_video_ops mt9t031_video_ops = {
	.s_fmt = mt9t031_set_fmt,
	.try_fmt = mt9t031_try_fmt,
	.s_stream = mt9t031_s_stream,
};

static const struct v4l2_subdev_ops mt9t031_ops = {
	.core = &mt9t031_core_ops,
	.video = &mt9t031_video_ops,
};

static int mt9t031_queryctrl(struct v4l2_subdev *sd,
			    struct v4l2_queryctrl *qctrl)
{
	const struct v4l2_queryctrl *temp_qctrl;

	temp_qctrl = mt9t031_find_qctrl(qctrl->id);
	if (!temp_qctrl) {
		v4l2_err(sd, "control id %d not supported", qctrl->id);
		return -EINVAL;
	}
	memcpy(qctrl, temp_qctrl, sizeof(*qctrl));
	return 0;
}

static int mt9t031_get_control(struct v4l2_subdev *sd,
			       struct v4l2_control *ctrl)
{
	struct i2c_client *client = v4l2_get_subdevdata(sd);
	struct mt9t031 *mt9t031 = to_mt9t031(sd);
	int data;

	switch (ctrl->id) {
	case V4L2_CID_VFLIP:
		data = reg_read(client, MT9T031_READ_MODE_2);
		if (data < 0)
			return -EIO;
		ctrl->value = !!(data & 0x8000);
		break;
	case V4L2_CID_HFLIP:
		data = reg_read(client, MT9T031_READ_MODE_2);
		if (data < 0)
			return -EIO;
		ctrl->value = !!(data & 0x4000);
		break;
	case V4L2_CID_EXPOSURE_AUTO:
		ctrl->value = mt9t031->autoexposure;
		break;
	}
	return 0;
}

static int mt9t031_set_control(struct v4l2_subdev *sd,
			       struct v4l2_control *ctrl)
{
	struct mt9t031 *mt9t031 = to_mt9t031(sd);
	const struct v4l2_queryctrl *qctrl = NULL;
	int data;
	struct i2c_client *client = v4l2_get_subdevdata(sd);

	if (NULL == ctrl)
		return -EINVAL;

	qctrl = mt9t031_find_qctrl(ctrl->id);
	if (!qctrl) {
		v4l2_err(sd, "control id %d not supported", ctrl->id);
		return -EINVAL;
	}

	switch (ctrl->id) {
	case V4L2_CID_VFLIP:
		if (ctrl->value)
			data = reg_set(client, MT9T031_READ_MODE_2, 0x8000);
		else
			data = reg_clear(client, MT9T031_READ_MODE_2, 0x8000);
		if (data < 0)
			return -EIO;
		break;
	case V4L2_CID_HFLIP:
		if (ctrl->value)
			data = reg_set(client, MT9T031_READ_MODE_2, 0x4000);
		else
			data = reg_clear(client, MT9T031_READ_MODE_2, 0x4000);
		if (data < 0)
			return -EIO;
		break;
	case V4L2_CID_GAIN:
		if (ctrl->value > qctrl->maximum || ctrl->value < qctrl->minimum)
			return -EINVAL;
		/* See Datasheet Table 7, Gain settings. */
		if (ctrl->value <= qctrl->default_value) {
			/* Pack it into 0..1 step 0.125, register values 0..8 */
			unsigned long range = qctrl->default_value - qctrl->minimum;
			data = ((ctrl->value - qctrl->minimum) * 8 + range / 2) / range;

			v4l2_dbg(1, debug, sd, "Setting gain %d\n", data);
			data = reg_write(client, MT9T031_GLOBAL_GAIN, data);
			if (data < 0)
				return -EIO;
		} else {
			/* Pack it into 1.125..128 variable step, register values 9..0x7860 */
			/* We assume qctrl->maximum - qctrl->default_value - 1 > 0 */
			unsigned long range = qctrl->maximum - qctrl->default_value - 1;
			/* calculated gain: map 65..127 to 9..1024 step 0.125 */
			unsigned long gain = ((ctrl->value - qctrl->default_value - 1) *
					       1015 + range / 2) / range + 9;

			if (gain <= 32)
				/* calculated gain 9..32 -> 9..32 */
				data = gain;
			else if (gain <= 64)
				/* calculated gain 33..64 -> 0x51..0x60 */
				data = ((gain - 32) * 16 + 16) / 32 + 80;
			else
				/*
				 * calculated gain 65..1024 -> (1..120) << 8 +
				 * 0x60
				 */
				data = (((gain - 64 + 7) * 32) & 0xff00) | 0x60;

			v4l2_dbg(1, debug, sd, "Setting gain from 0x%x to"
				 "0x%x\n",
				 reg_read(client, MT9T031_GLOBAL_GAIN), data);

			data = reg_write(client, MT9T031_GLOBAL_GAIN, data);
			if (data < 0)
				return -EIO;
		}

		/* Success */
		mt9t031->gain = ctrl->value;
		break;
	case V4L2_CID_EXPOSURE:
		/* mt9t031 has maximum == default */
		if (ctrl->value > qctrl->maximum ||
		    ctrl->value < qctrl->minimum)
			return -EINVAL;
		else {
			const unsigned long range =
				qctrl->maximum - qctrl->minimum;
			const u32 shutter =
				((ctrl->value - qctrl->minimum) * 1048 +
					range / 2) / range + 1;
			u32 old;

			get_shutter(sd, &old);
			v4l2_dbg(1, debug, sd,
				"Setting shutter width from %u to %u\n",
				old, shutter);
			if (set_shutter(sd, shutter) < 0)
				return -EIO;
			mt9t031->exposure = ctrl->value;
			mt9t031->autoexposure = 0;
		}
		break;
	case V4L2_CID_EXPOSURE_AUTO:
		if (ctrl->value) {
			const u16 vblank = MT9T031_VERTICAL_BLANK;
			const u32 shutter_max = MT9T031_MAX_HEIGHT + vblank;
			if (set_shutter(sd, mt9t031->height +
					mt9t031->y_skip_top + vblank) < 0)
				return -EIO;

			qctrl = mt9t031_find_qctrl(V4L2_CID_EXPOSURE);
			mt9t031->exposure =
				(shutter_max / 2 + (mt9t031->height +
				mt9t031->y_skip_top + vblank - 1) *
				(qctrl->maximum - qctrl->minimum)) /
				shutter_max + qctrl->minimum;
			mt9t031->autoexposure = 1;
		} else
			mt9t031->autoexposure = 0;
		break;
	}
	return 0;
}

/* Interface active, can use i2c. If it fails, it can indeed mean, that
 * this wasn't our capture interface, so, we wait for the right one */
static int mt9t031_detect(struct i2c_client *client, int *model)
{
	s32 data;

	/* Enable the chip */
	data = reg_write(client, MT9T031_CHIP_ENABLE, 1);
	dev_dbg(&client->dev, "write: %d\n", data);

	/* Read out the chip version register */
	data = reg_read(client, MT9T031_CHIP_VERSION);

	switch (data) {
	case 0x1621:
		*model = V4L2_IDENT_MT9T031;
		break;
	default:
		dev_err(&client->dev,
			"No MT9T031 chip detected, register read %x\n", data);
		return -ENODEV;
	}

	dev_info(&client->dev, "Detected a MT9T031 chip ID %x\n", data);
	return 0;
}

static int mt9t031_probe(struct i2c_client *client,
			 const struct i2c_device_id *did)
{
	struct mt9t031 *mt9t031;
	struct v4l2_subdev *sd;
	int pclk_pol;
	int ret;

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

	if (!client->dev.platform_data) {
		dev_err(&client->dev, "No platform data!!\n");
		return -ENODEV;
	}

	pclk_pol = (int)client->dev.platform_data;

	mt9t031 = kzalloc(sizeof(struct mt9t031), GFP_KERNEL);
	if (!mt9t031)
		return -ENOMEM;

	ret = mt9t031_detect(client, &mt9t031->model);
	if (ret)
		goto clean;

	mt9t031->x_min		= MT9T031_COLUMN_SKIP;
	mt9t031->y_min		= MT9T031_ROW_SKIP;
	mt9t031->width		= MT9T031_DEFAULT_WIDTH;
	mt9t031->height		= MT9T031_DEFAULT_WIDTH;
	mt9t031->x_current	= mt9t031->x_min;
	mt9t031->y_current	= mt9t031->y_min;
	mt9t031->width_min	= MT9T031_MIN_WIDTH;
	mt9t031->width_max	= MT9T031_MAX_WIDTH;
	mt9t031->height_min	= MT9T031_MIN_HEIGHT;
	mt9t031->height_max	= MT9T031_MAX_HEIGHT;
	mt9t031->y_skip_top	= 10;
	mt9t031->autoexposure = 1;
	mt9t031->xskip = 1;
	mt9t031->yskip = 1;

	/* Register with V4L2 layer as slave device */
	sd = &mt9t031->sd;
	v4l2_i2c_subdev_init(sd, client, &mt9t031_ops);
	if (!pclk_pol)
		reg_clear(v4l2_get_subdevdata(sd),
			  MT9T031_PIXEL_CLOCK_CONTROL, 0x8000);
	else
		reg_set(v4l2_get_subdevdata(sd),
			MT9T031_PIXEL_CLOCK_CONTROL, 0x8000);

	v4l2_info(sd, "%s decoder driver registered !!\n", sd->name);
	return 0;

clean:
	kfree(mt9t031);
	return ret;
}

static int mt9t031_remove(struct i2c_client *client)
{
	struct v4l2_subdev *sd = i2c_get_clientdata(client);
	struct mt9t031 *mt9t031 = to_mt9t031(sd);

	v4l2_device_unregister_subdev(sd);

	kfree(mt9t031);
	return 0;
}

static const struct i2c_device_id mt9t031_id[] = {
	{ "mt9t031", 0 },
	{ }
};
MODULE_DEVICE_TABLE(i2c, mt9t031_id);

static struct i2c_driver mt9t031_i2c_driver = {
	.driver = {
		.name = "mt9t031",
	},
	.probe		= mt9t031_probe,
	.remove		= mt9t031_remove,
	.id_table	= mt9t031_id,
};

static int __init mt9t031_mod_init(void)
{
	return i2c_add_driver(&mt9t031_i2c_driver);
}

static void __exit mt9t031_mod_exit(void)
{
	i2c_del_driver(&mt9t031_i2c_driver);
}

module_init(mt9t031_mod_init);
module_exit(mt9t031_mod_exit);

MODULE_DESCRIPTION("Micron MT9T031 Camera driver");
MODULE_AUTHOR("Guennadi Liakhovetski <lg@xxxxxxx>");
MODULE_LICENSE("GPL v2");

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