diff mbox

soc_camera with V4L2 driver

Message ID 20110418225538.155F.B41FCDD0@s9.dion.ne.jp (mailing list archive)
State Not Applicable
Headers show

Commit Message

Akira Tsukamoto April 18, 2011, 1:55 p.m. UTC
Hello Guennadi,

> > Sorry for the sudden email but may I have your advice on soc_camera?

> You can have a look at another driver 
> for a Sharp camera sensor:
> 
> drivers/media/video/rj54n1cb0c.c
> 
> and at its platform glue in arch/sh/boards/mach-kfr2r09/setup.c, there you 
> find
> 
> struct platform_device kfr2r09_camera
> 
> which links to
> 
> struct soc_camera_link rj54n1_link

> > The ARM cpu is made by Renesas.
> 
> Then, perhaps, something similar to
> 
> arch/arm/mach-shmobile/board-ap4evb.c

Thank you for your suggestion, I was able to bind my driver
with soc_camera(I believe...).

I attached my draft driver files at the end of this email
for any suggestions.

In the beginning, I would like to explain the fundamental 
information.

1) 2 megapixel camera module is connected to 
   the ARM board, Renesas ag5evm, through I2C.
    arch/arm/mach-shmobile/board-ag5evm.c
2) The camera module is connected to CEU on the ag5evm.
3) I followed your instruction to bind the camera
   sensor driver to soc_camera as attached and 
   builds and boots fine.
4) I have not received the data sheet from the vender 
   for the camera yet ;)
5) But I have the prototype board on my hand.
6) I can not implement the details of the driver without
   the data sheet but would like to start implement the 
   outline, so I could save my time while I am waiting 
   for the data sheet.

Also,
I would like to know, if I need to bind to 
   sh_mobile_ceu_camera.c
too, and how, because the camera is connected to CEU.
(I never knew the word CEU until I started to work with
this project...)

Thank you and best regards,

Akira

------------- rj65na20.c
/*
 *  Copyright (C) 2011 Nomovok Ltd.
 *
 *  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; version 2 of the License.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
#include <media/soc_camera.h>
#include <media/soc_mediabus.h>

MODULE_DESCRIPTION("Sharp RJ65NA20 sensor driver");
MODULE_AUTHOR("Akira Tsukamoto <akira.tsukamoto@nomovok.com>");
MODULE_LICENSE("GPL");

static int debug;
module_param(debug, int, 1);
MODULE_PARM_DESC(debug, "Debug level (0-1)");

/* 
 * TODO This should go inside the following
 * include/media/v4l2-chip-ident.h
 */
/* Sharp RJ65NA20, 0x???? = ?? */
#define V4L2_IDENT_RJ65NA20	00


#define I2C_WRITE_BYTES		2
#define I2C_READ_BYTES		1

#define R00_RJ65NA20_CHIP_VERSION	0x00 /* TODO fix me when after receiving data sheet */
#define R00				0x00
#define R01				0x01

#define RJ65NA20_VERSION		0x01

#define RJ65NA20_WIDTH			1600
#define RJ65NA20_HEIGHT			1200

/* supported controls */
static struct v4l2_queryctrl rj65na20_qctrl[] = { /* TODO fix me when after receiving data sheet */
	{
		.id      = V4L2_CID_AUTO_WHITE_BALANCE,
		.type    = V4L2_CTRL_TYPE_BOOLEAN,
		.name    = "Auto White Balance",
		.minimum = 0,
		.maximum = 1,
		.step    = 1,
		.default_value = 1,
		.flags = 0,
	}, {
		.id	 = V4L2_CID_EXPOSURE_AUTO,
		.type    = V4L2_CTRL_TYPE_INTEGER,
		.name    = "Auto Exposure",
		.minimum = 0,
		.maximum = 1,
		.step    = 1,
		.default_value  = 1,
		.flags = 0,
	}, {
		.id      = V4L2_CID_HFLIP,
		.type    = V4L2_CTRL_TYPE_BOOLEAN,
		.name    = "Mirror",
		.minimum = 0,
		.maximum = 1,
		.step    = 1,
		.default_value = 0,
		.flags = 0,
	}, {
		.id      = V4L2_CID_VFLIP,
		.type    = V4L2_CTRL_TYPE_BOOLEAN,
		.name    = "Vflip",
		.minimum = 0,
		.maximum = 1,
		.step    = 1,
		.default_value = 0,
		.flags = 0,
	}, {
	}
};

struct rj65na20 {
	struct v4l2_subdev sd;
	unsigned int width, height;
	unsigned int autowhitebalance:1;
	unsigned int autoexposure:1;
	unsigned int hflip:1;
	unsigned int vflip:1;
};

static inline struct rj65na20 *to_rj65na20(struct v4l2_subdev *sd)
{
	return container_of(sd, struct rj65na20, sd);
}

static u8 rj65na20_read(struct v4l2_subdev *sd, u8 addr)
{
	struct i2c_client *c = v4l2_get_subdevdata(sd);
	u8 buffer, val;
	int rc;

	rc = i2c_master_send(c, &addr, I2C_READ_BYTES);
	if (rc != I2C_READ_BYTES)
		v4l2_dbg(0, debug, sd,
			"i2c i/o error: rc == %d (should be %d)\n",
			rc, I2C_READ_BYTES);

	msleep(10); /* TODO specify correct value later */

	rc = i2c_master_recv(c, (char *)&buffer, I2C_READ_BYTES);
	if (rc != I2C_READ_BYTES)
		v4l2_dbg(0, debug, sd,
			"i2c i/o error: rc == %d (should be %d)\n",
		rc, I2C_READ_BYTES);

	val = buffer;

	v4l2_dbg(2, debug, sd, "rj65na20: read 0x%02x = 0x%02x\n", addr, val);

	return val;
}

static void rj65na20_write(struct v4l2_subdev *sd, u8 addr, u8 value)
{
	struct i2c_client *c = v4l2_get_subdevdata(sd);
	u8 buffer[I2C_WRITE_BYTES];
	int rc;

	buffer[0] = addr;
	buffer[1] = value;

	v4l2_dbg(2, debug, sd,
		 "rj65na20: writing 0x%02x 0x%02x\n", addr, value);
	rc = i2c_master_send(c, buffer, I2C_WRITE_BYTES);
	if (rc != I2C_WRITE_BYTES)
		v4l2_dbg(0, debug, sd,
			"i2c i/o error: rc == %d (should be %d)\n",
			rc, I2C_WRITE_BYTES);
}


struct i2c_reg_value {
	u8	reg;
	u8	value;
};

/*
 * Initialization values of registers for the device
 * likly to change on final hardware
 */
#define INIT_REGS_PAGE	0x100
#define INIT_PAGES	22
static const struct i2c_reg_value rj65na20_init_page[][0x100] = {
/* TODO this is dummy values, need to fix it with correct values */
	{
		/* PAGE0 */
		{0x00, 0x00},
		{0xff, 0x00},
	{

	},
};

static void set_resolution(struct v4l2_subdev *sd)
{
	/* TODO fix me when after receiving data sheet */
}

static void set_white_balance(struct v4l2_subdev *sd)
{
	/* TODO fix me when after receiving data sheet */
}

static void set_exposure(struct v4l2_subdev *sd)
{
	/* TODO fix me when after receiving data sheet */
}

static void set_flip(struct v4l2_subdev *sd)
{
	/* TODO fix me when after receiving data sheet */
}

static int rj65na20_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
	struct rj65na20 *core = to_rj65na20(sd);

	v4l2_dbg(1, debug, sd, "g_ctrl called\n");

	switch (ctrl->id) {
	case V4L2_CID_AUTO_WHITE_BALANCE:
		ctrl->value = core->autowhitebalance;
		return 0;
	case V4L2_CID_EXPOSURE_AUTO:
		ctrl->value = core->autoexposure;
		return 0;
	case V4L2_CID_HFLIP:
		ctrl->value = core->hflip ? 1 : 0;
		return 0;
	case V4L2_CID_VFLIP:
		ctrl->value = core->vflip ? 1 : 0;
		return 0;
	}

	return -EINVAL;
}

static int rj65na20_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
	struct rj65na20 *core = to_rj65na20(sd);
	u8 i, n;
	int ret = -EINVAL;

	n = ARRAY_SIZE(rj65na20_qctrl);

	/* varify args */
	for (i = 0; i < n; i++) {
		if (ctrl->id != rj65na20_qctrl[i].id)
			continue;
		if (ctrl->value < rj65na20_qctrl[i].minimum ||
		    ctrl->value > rj65na20_qctrl[i].maximum)
			return -ERANGE;
		v4l2_dbg(1, debug, sd, "s_ctrl: id=%d, value=%d\n",
					ctrl->id, ctrl->value);
	}

	switch (ctrl->id) {
	case V4L2_CID_AUTO_WHITE_BALANCE:
		core->autowhitebalance = ctrl->value;
		set_white_balance(sd);
		return 0;
	case V4L2_CID_EXPOSURE_AUTO:
		core->autoexposure = ctrl->value;
		set_exposure(sd);
		return 0;
	case V4L2_CID_HFLIP:
		core->hflip = ctrl->value;
		set_flip(sd);
		return 0;
	case V4L2_CID_VFLIP:
		core->vflip = ctrl->value;
		set_flip(sd);
		return 0;
	default:
		return -EINVAL;
	}

	return ret;
}

#if 0 /* TODO does the camera need to specify colorspace? */
static int rj65na20_try_fmt(struct v4l2_subdev *sd,
				struct v4l2_mbus_framefmt *mf)
{
	/* TODO fix me when after receiving data sheet */
	const struct rj65na20_datafmt *fmt = rj65na20_find_datafmt(mf->code);

	v4l2_dbg(1, debug, sd, "rj65na20_try_fmt called: %u\n", mf->code);

	if (!fmt) {
		mf->code        = rj65na20_colour_fmts[0].code;
		mf->colorspace  = rj65na20_colour_fmts[0].colorspace;
	}

	mf->width       = RJ65NA20_WIDTH;
	mf->height      = RJ65NA20_HEIGHT;
	mf->field	= V4L2_FIELD_NONE;

	return 0;
}

static int rj65na20_s_fmt(struct v4l2_subdev *sd,
			struct v4l2_mbus_framefmt *mf)
{
	/* TODO fix me when after receiving data sheet */
	struct rj65na20 *core = to_rj65na20(sd);

	v4l2_dbg(1, debug, sd, "rj65na20_s_fmt called: %u\n", mf->code);

	if (!rj65na20_find_datafmt(mf->code))
		return -EINVAL;

	rj65na20_try_fmt(sd, mf);

	core->fmt = rj65na20_find_datafmt(mf->code);

	return 0;
}

static int rj65na20_g_fmt(struct v4l2_subdev *sd,
			struct v4l2_mbus_framefmt *mf)
{
	/* TODO fix me when after receiving data sheet */
	struct rj65na20 *core = to_rj65na20(sd);

	const struct rj65na20_datafmt *fmt = core->fmt;

	mf->code        = fmt->code;
	mf->colorspace  = fmt->colorspace;
	mf->width       = RJ65NA20_WIDTH;
	mf->height      = RJ65NA20_HEIGHT;
	mf->field	= V4L2_FIELD_NONE;

	return 0;
}
#endif 

static int rj65na20_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
{
	/* TODO fix me when after receiving data sheet */
	struct v4l2_rect *rect = &a->c;

	a->type		= V4L2_BUF_TYPE_VIDEO_CAPTURE;
	rect->top	= 0;
	rect->left	= 0;
	rect->width	= RJ65NA20_WIDTH;
	rect->height	= RJ65NA20_HEIGHT;

	return 0;
}

static int rj65na20_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
{
	/* TODO fix me when after receiving data sheet */
	a->bounds.left                  = 0;
	a->bounds.top                   = 0;
	a->bounds.width                 = RJ65NA20_WIDTH;
	a->bounds.height                = RJ65NA20_HEIGHT;
	a->defrect                      = a->bounds;
	a->type                         = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	a->pixelaspect.numerator        = 1;
	a->pixelaspect.denominator      = 1;

	return 0;
}

static int rj65na20_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
				enum v4l2_mbus_pixelcode *code)
{
	/* TODO fix me when after receiving data sheet */
	/*
	if ((unsigned int)index >= ARRAY_SIZE(rj65na20_colour_fmts))
		return -EINVAL;

	*code = rj65na20_colour_fmts[index].code;
	*/
	return 0;
}

static int rj65na20_s_stream(struct v4l2_subdev *sd, int enable)
{
	/* TODO fix me when after receiving data sheet */
//	struct i2c_client *client = v4l2_get_subdevdata(sd);
//	return rj65na20_write(client, MODE_SELECT, !!enable);
	return 0;
}

static int rj65na20_g_chip_ident(struct v4l2_subdev *sd,
			       struct v4l2_dbg_chip_ident *id)
{
	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	= V4L2_IDENT_RJ65NA20;
	id->revision	= 0;

	return 0;
}

static struct v4l2_subdev_video_ops rj65na20_subdev_video_ops = {
	.s_stream	= rj65na20_s_stream,
//	.s_mbus_fmt	= rj65na20_s_fmt,
//	.g_mbus_fmt	= rj65na20_g_fmt,
//	.try_mbus_fmt	= rj65na20_try_fmt,
	.enum_mbus_fmt	= rj65na20_enum_fmt,
	.g_crop		= rj65na20_g_crop,
	.cropcap	= rj65na20_cropcap,
};

static struct v4l2_subdev_core_ops rj65na20_subdev_core_ops = {
	.g_chip_ident	= rj65na20_g_chip_ident,
};

static struct v4l2_subdev_ops rj65na20_subdev_ops = {
	.core	= &rj65na20_subdev_core_ops,
	.video	= &rj65na20_subdev_video_ops,
};

/* The camera is connected through CEU */
static unsigned long rj65na20_query_bus_param(struct soc_camera_device *icd)
{
	return 0;
}

static int rj65na20_set_bus_param(struct soc_camera_device *icd,
				  unsigned long flags)
{
	return -1;
}

static struct soc_camera_ops rj65na20_ops = {
	.query_bus_param	= rj65na20_query_bus_param,
	.set_bus_param		= rj65na20_set_bus_param,
};

/****************************************************************************
                        I2C Client & Driver
 ****************************************************************************/

/*
 * This is i2c testing purpose, 
 * this function will be removed from final code
 */
static int rj65na20_probe_test(struct i2c_client *c,
				const struct i2c_device_id *id)
{
	struct rj65na20 *core;
	struct v4l2_subdev *sd;
	int rc;
	u8 addr, value, read;
	u8 write[I2C_WRITE_BYTES];

	printk("APE5R: 2M camera probe: start\n");

	sd = &core->sd;

	/* Check if the adapter supports the needed features */
	if (!i2c_check_functionality(c->adapter,
	     I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
		return -EIO;

	addr = 0x03;
	value = 0x01;

	printk("APE5R: 2M camera probe: write before\n");

	write[0] = addr;
	write[1] = value;

	v4l2_dbg(2, debug, sd,
		 "rj65na20: writing 0x%02x 0x%04x\n", addr, value);
	rc = i2c_master_send(c, write, I2C_WRITE_BYTES);
	if (rc != I2C_WRITE_BYTES)
		v4l2_dbg(0, debug, sd,
			"i2c i/o error: rc == %d (should be %d)\n", 
			rc, I2C_WRITE_BYTES);

	printk("APE5R: 2M camera probe: write after\n");

	rc = i2c_master_send(c, &addr, I2C_READ_BYTES);
	if (rc != I2C_READ_BYTES)
		v4l2_dbg(0, debug, sd,
			"i2c i/o error: rc == %d (should be %d)\n", 
			rc, I2C_READ_BYTES);

	msleep(10);

	printk("APE5R: 2M camera probe: read send addr\n");

	rc = i2c_master_recv(c, (char *)&read, I2C_READ_BYTES);
	if (rc != I2C_READ_BYTES)
		v4l2_dbg(0, debug, sd,
			"i2c i/o error: rc == %d (should be %d)\n", 
			rc, I2C_READ_BYTES);

	printk("APE5R: 2M camera probe: read=0x%02X\n", read);

	printk("APE5R: 2M camera probe: end\n");

//	BUG_ON(__func__); /* halt the testing */

	return 0;
}

static int rj65na20_probe(struct i2c_client *c,
				const struct i2c_device_id *id)
{
	u8 version;
	struct rj65na20 *core;
	struct v4l2_subdev *sd;
	struct soc_camera_device *icd = c->dev.platform_data;
	struct soc_camera_link *icl;

	if (!icd) {
		dev_err(&c->dev, "rj65na20: missing soc-camera data!\n");
		return -EINVAL;
	}

	icl = to_soc_camera_link(icd);
	if (!icl) {
		dev_err(&c->dev, "rj65na20: missing platform data!\n");
		return -EINVAL;
	}

	/* Check if the adapter supports the needed i2c features */
	if (!i2c_check_functionality(c->adapter,
	     I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
		return -EIO;

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

	sd = &core->sd;
	v4l2_i2c_subdev_init(sd, c, &rj65na20_subdev_ops);

	/* Check if the sensor is really a Sharp RJ65NA20 */
	version = rj65na20_read(sd, 0x01);
	if ((version != RJ65NA20_VERSION)) {
		v4l2_info(sd, "*** unknown camera chip detected (0x%02x).\n",
			  version);
		kfree(core);
		return -EINVAL;
	}

	icd->ops     = &rj65na20_ops;

	core->width  = RJ65NA20_WIDTH;
	core->height = RJ65NA20_HEIGHT;

	v4l_info(c, "chip found @ 0x%02x (%s - chip version 0x%04x)\n",
		 c->addr << 1, c->adapter->name, version);

	return 0;
}

static int rj65na20_remove(struct i2c_client *c)
{
	struct v4l2_subdev *sd = i2c_get_clientdata(c);

	v4l2_dbg(1, debug, sd,
		"rj65na20.c: removing rj65na20 adapter on address 0x%x\n",
		c->addr << 1);

	v4l2_device_unregister_subdev(sd);

	kfree(to_rj65na20(sd));

	return 0;
}

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

static const struct i2c_device_id rj65na20_id[] = {
	{ "rj65na20", 0 },
	{ }
};

MODULE_DEVICE_TABLE(i2c, rj65na20_id);

static struct i2c_driver rj65na20_driver = {
	.driver = {
		.owner  = THIS_MODULE,
		.name   = "rj65na20",
	},
	.probe          = rj65na20_probe_test,
	.remove         = rj65na20_remove,
	.id_table       = rj65na20_id,
};

static __init int init_rj65na20(void)
{
	return i2c_add_driver(&rj65na20_driver);
}

static __exit void exit_rj65na20(void)
{
	i2c_del_driver(&rj65na20_driver);
}

module_init(init_rj65na20);
module_exit(exit_rj65na20);

EOF (rj65na20.c)
-----------------------------

+
+	&rj65na20_camera,
 };
 
 static struct map_desc ag5evm_io_desc[] __initdata = {
@@ -748,6 +778,7 @@ static void __init ag5evm_init(void)
 	struct clk *sub_clk = clk_get(NULL, "sub_clk");
 	struct clk *extal2_clk = clk_get(NULL, "extal2");
 	struct clk *fsia_clk = clk_get(NULL, "fsia_clk");
+	struct clk *vck1_clk = clk_get(NULL, "vck1_clk");
 	clk_set_parent(sub_clk, extal2_clk);
 
 	__raw_writel(__raw_readl(SUBCKCR) & ~(1<<9), SUBCKCR);
@@ -853,6 +884,43 @@ static void __init ag5evm_init(void)
 	__raw_writel(0x2a8b9111, DSI1PHYCR);
 	clk_enable(clk_get(NULL, "dsi-tx"));

+	/* 2M camera */
+	gpio_request(GPIO_PORT44, NULL);
+	gpio_direction_output(GPIO_PORT44, 0);
+	udelay(10);
+	gpio_set_value(GPIO_PORT44, 1);
+
 	/* Unreset LCD Panel */
 	gpio_request(GPIO_PORT217, NULL);
 	gpio_direction_output(GPIO_PORT217, 0);

Comments

Guennadi Liakhovetski April 18, 2011, 2:18 p.m. UTC | #1
Hello Akira

On Mon, 18 Apr 2011, Akira Tsukamoto wrote:

> Hello Guennadi,
> 
> > > Sorry for the sudden email but may I have your advice on soc_camera?
> 
> > You can have a look at another driver 
> > for a Sharp camera sensor:
> > 
> > drivers/media/video/rj54n1cb0c.c
> > 
> > and at its platform glue in arch/sh/boards/mach-kfr2r09/setup.c, there you 
> > find
> > 
> > struct platform_device kfr2r09_camera
> > 
> > which links to
> > 
> > struct soc_camera_link rj54n1_link
> 
> > > The ARM cpu is made by Renesas.
> > 
> > Then, perhaps, something similar to
> > 
> > arch/arm/mach-shmobile/board-ap4evb.c
> 
> Thank you for your suggestion, I was able to bind my driver
> with soc_camera(I believe...).
> 
> I attached my draft driver files at the end of this email
> for any suggestions.

Thanks for attaching your sources.

> In the beginning, I would like to explain the fundamental 
> information.
> 
> 1) 2 megapixel camera module is connected to 
>    the ARM board, Renesas ag5evm, through I2C.
>     arch/arm/mach-shmobile/board-ag5evm.c
> 2) The camera module is connected to CEU on the ag5evm.
> 3) I followed your instruction to bind the camera
>    sensor driver to soc_camera as attached and 
>    builds and boots fine.
> 4) I have not received the data sheet from the vender 
>    for the camera yet ;)
> 5) But I have the prototype board on my hand.
> 6) I can not implement the details of the driver without
>    the data sheet but would like to start implement the 
>    outline, so I could save my time while I am waiting 
>    for the data sheet.
> 
> Also,
> I would like to know, if I need to bind to 
>    sh_mobile_ceu_camera.c
> too, and how, because the camera is connected to CEU.

Yes, you do. Look at the board-ap4evb and board-mackerel files at 
everything, containing the "ceu" string in it. The former configures a 
serially connected camera sensor over the MIPI CSI-2 bus, the latter over 
the parallel interface.

I haven't reviewed your sources in detail, just two comments, regarding 
something, that caught my eye:

> (I never knew the word CEU until I started to work with
> this project...)
> 
> Thank you and best regards,
> 
> Akira

[snip]

> --- linux_kernel_bsp/arch/arm/mach-shmobile/board-ag5evm.c	2011-03-22 12:30:14.000000000 +0900
> +++ linux_kernel/arch/arm/mach-shmobile/board-ag5evm.c	2011-04-18 14:39:20.000000000 +0900
> @@ -59,6 +59,7 @@
>  
>  #include <sound/sh_fsi.h>
>  #include <video/sh_mobile_lcdc.h>
> +#include <media/soc_camera.h>
>  
>  static struct r8a66597_platdata usb_host_data = {
>  	.on_chip	= 1,
> @@ -317,11 +318,38 @@ static struct platform_device fsi_device
>  	},
>  };
>  
> +static struct i2c_board_info rj65na20_info = {
> +	I2C_BOARD_INFO("rj65na20", 0x40),
> +};
> +
> +struct soc_camera_link rj65na20_link = {
> +	.bus_id         = 0,
> +	.board_info     = &rj65na20_info,
> +	.i2c_adapter_id = 0,
> +	.module_name    = "rj65na20",
> +};
> +
> +static struct platform_device rj65na20_camera = {
> +	.name	= "soc-camera-pdrv-2M",

This name has to match with what's advertised in 
drivers/media/video/soc_camera.c, namely "soc-camera-pdrv"

> +	.id	= 0,
> +	.dev	= {
> +		.platform_data = &rj65na20_link,
> +	},
> +};
> +
>  static struct i2c_board_info i2c0_devices[] = {
>  	{
>  		I2C_BOARD_INFO("ag5evm_ts", 0x20),
>  		.irq	= pint2irq(12),	/* PINTC3 */
>  	},
> +	/* 2M camera */
> +	{
> +		I2C_BOARD_INFO("rj65na20", 0x40),
> +	},

No, you do not have to include this here, the sensor must not be registered 
automatically during the board initialisation.

Thanks
Guennadi

>  };
>  
>  static struct i2c_board_info i2c1_devices[] = {
> @@ -548,6 +576,8 @@ static struct platform_device *ag5evm_de
>  
>  	&usb_mass_storage_device,
>  	&android_usb_device,
> +
> +	&rj65na20_camera,
>  };
>  
>  static struct map_desc ag5evm_io_desc[] __initdata = {
> @@ -748,6 +778,7 @@ static void __init ag5evm_init(void)
>  	struct clk *sub_clk = clk_get(NULL, "sub_clk");
>  	struct clk *extal2_clk = clk_get(NULL, "extal2");
>  	struct clk *fsia_clk = clk_get(NULL, "fsia_clk");
> +	struct clk *vck1_clk = clk_get(NULL, "vck1_clk");
>  	clk_set_parent(sub_clk, extal2_clk);
>  
>  	__raw_writel(__raw_readl(SUBCKCR) & ~(1<<9), SUBCKCR);
> @@ -853,6 +884,43 @@ static void __init ag5evm_init(void)
>  	__raw_writel(0x2a8b9111, DSI1PHYCR);
>  	clk_enable(clk_get(NULL, "dsi-tx"));
> 
> +	/* 2M camera */
> +	gpio_request(GPIO_PORT44, NULL);
> +	gpio_direction_output(GPIO_PORT44, 0);
> +	udelay(10);
> +	gpio_set_value(GPIO_PORT44, 1);
> +
>  	/* Unreset LCD Panel */
>  	gpio_request(GPIO_PORT217, NULL);
>  	gpio_direction_output(GPIO_PORT217, 0);
> 
> -- 
> Akira Tsukamoto
> 

---
Guennadi Liakhovetski, Ph.D.
Freelance Open-Source Software Developer
http://www.open-technology.de/
--
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
Akira Tsukamoto April 18, 2011, 3:10 p.m. UTC | #2
Hello Guennadi,

> > Also,
> > I would like to know, if I need to bind to 
> >    sh_mobile_ceu_camera.c
> > too, and how, because the camera is connected to CEU.
> 
> Yes, you do. Look at the board-ap4evb and board-mackerel files at 
> everything, containing the "ceu" string in it. The former configures a 
> serially connected camera sensor over the MIPI CSI-2 bus, the latter over 
> the parallel interface.

Thank you, I will definitely look into those files about ceu.

> > +static struct platform_device rj65na20_camera = {
> > +	.name	= "soc-camera-pdrv-2M",
> 
> This name has to match with what's advertised in 
> drivers/media/video/soc_camera.c, namely "soc-camera-pdrv"

I will fix it.

> >  static struct i2c_board_info i2c0_devices[] = {
> >  	{
> >  		I2C_BOARD_INFO("ag5evm_ts", 0x20),
> >  		.irq	= pint2irq(12),	/* PINTC3 */
> >  	},
> > +	/* 2M camera */
> > +	{
> > +		I2C_BOARD_INFO("rj65na20", 0x40),
> > +	},
> 
> No, you do not have to include this here, the sensor must not be registered 
> automatically during the board initialisation.

Thank you for your review. It is already midnight in Tokyo,
so I will try it first in the morning tomorrow.

With kind regards,

FYI:
>    the ARM board, Renesas, through I2C.
>     arch/arm/mach-shmobile/board-ag5evm.c

This file is temporal situation just for me to start with.

Akira
Akira Tsukamoto April 18, 2011, 3:57 p.m. UTC | #3
Hello Guennadi,

> > >  static struct i2c_board_info i2c0_devices[] = {
> > >  	{
> > >  		I2C_BOARD_INFO("ag5evm_ts", 0x20),
> > >  		.irq	= pint2irq(12),	/* PINTC3 */
> > >  	},
> > > +	/* 2M camera */
> > > +	{
> > > +		I2C_BOARD_INFO("rj65na20", 0x40),
> > > +	},
> > 
> > No, you do not have to include this here, the sensor must not be registered 
> > automatically during the board initialisation.

I have one more question before sleeping :)

The camera module needs to be initialized by writing values to the registers.
Do I need to write init function at the following?

static const struct v4l2_subdev_core_ops rj65na20_core_ops = {
         .reset = rj65na20_reset,
[snip]
}

With kind regards,

Akira
Guennadi Liakhovetski April 18, 2011, 4:05 p.m. UTC | #4
On Tue, 19 Apr 2011, Akira Tsukamoto wrote:

> Hello Guennadi,
> 
> > > >  static struct i2c_board_info i2c0_devices[] = {
> > > >  	{
> > > >  		I2C_BOARD_INFO("ag5evm_ts", 0x20),
> > > >  		.irq	= pint2irq(12),	/* PINTC3 */
> > > >  	},
> > > > +	/* 2M camera */
> > > > +	{
> > > > +		I2C_BOARD_INFO("rj65na20", 0x40),
> > > > +	},
> > > 
> > > No, you do not have to include this here, the sensor must not be registered 
> > > automatically during the board initialisation.
> 
> I have one more question before sleeping :)
> 
> The camera module needs to be initialized by writing values to the registers.
> Do I need to write init function at the following?
> 
> static const struct v4l2_subdev_core_ops rj65na20_core_ops = {
>          .reset = rj65na20_reset,
> [snip]
> }

AFAICS neither soc_camera.c, nor sh_mobile_ceu_camera.c call the .reset 
subdevice core method, so, no, at the moment implementing it wouldn't 
produce any result. Either you have to choose one of the methods, that are 
currently called, or you have to add a call to .reset() as required.

Thanks
Guennadi
---
Guennadi Liakhovetski, Ph.D.
Freelance Open-Source Software Developer
http://www.open-technology.de/
--
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
Hans Verkuil April 18, 2011, 4:55 p.m. UTC | #5
> On Tue, 19 Apr 2011, Akira Tsukamoto wrote:
>
>> Hello Guennadi,
>>
>> > > >  static struct i2c_board_info i2c0_devices[] = {
>> > > >  	{
>> > > >  		I2C_BOARD_INFO("ag5evm_ts", 0x20),
>> > > >  		.irq	= pint2irq(12),	/* PINTC3 */
>> > > >  	},
>> > > > +	/* 2M camera */
>> > > > +	{
>> > > > +		I2C_BOARD_INFO("rj65na20", 0x40),
>> > > > +	},
>> > >
>> > > No, you do not have to include this here, the sensor must not be
>> registered
>> > > automatically during the board initialisation.
>>
>> I have one more question before sleeping :)
>>
>> The camera module needs to be initialized by writing values to the
>> registers.
>> Do I need to write init function at the following?
>>
>> static const struct v4l2_subdev_core_ops rj65na20_core_ops = {
>>          .reset = rj65na20_reset,
>> [snip]
>> }
>
> AFAICS neither soc_camera.c, nor sh_mobile_ceu_camera.c call the .reset
> subdevice core method, so, no, at the moment implementing it wouldn't
> produce any result. Either you have to choose one of the methods, that are
> currently called, or you have to add a call to .reset() as required.

I don't really like the use of reset for this. The reset op is a pretty
poorly defined op. There is a registration op as well these days that
might be better suited for this (see struct v4l2_subdev_internal_ops).

Regards,

        Hans

>
> Thanks
> Guennadi
> ---
> Guennadi Liakhovetski, Ph.D.
> Freelance Open-Source Software Developer
> http://www.open-technology.de/
> --
> 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
>


--
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
Akira Tsukamoto April 19, 2011, 12:21 p.m. UTC | #6
Hello Hans,

> >> The camera module needs to be initialized by writing values to the
> >> registers.
> >> Do I need to write init function at the following?
> >>
> >> static const struct v4l2_subdev_core_ops rj65na20_core_ops = {
> >>          .reset = rj65na20_reset,
> >> [snip]
> >> }
> >
> > AFAICS neither soc_camera.c, nor sh_mobile_ceu_camera.c call the .reset
> > subdevice core method, so, no, at the moment implementing it wouldn't
> > produce any result. Either you have to choose one of the methods, that are
> > currently called, or you have to add a call to .reset() as required.
> 
> I don't really like the use of reset for this. The reset op is a pretty
> poorly defined op. There is a registration op as well these days that
> might be better suited for this (see struct v4l2_subdev_internal_ops).

May I ask to guide me how to implement:

struct v4l2_subdev_internal_ops {
	int (*registered)(struct v4l2_subdev *sd);
	void (*unregistered)(struct v4l2_subdev *sd);
};

for the initializing camera module by writing values 
to the register?

With kind regards,

Akira
diff mbox

Patch

--- linux_kernel_bsp/arch/arm/mach-shmobile/board-ag5evm.c	2011-03-22 12:30:14.000000000 +0900
+++ linux_kernel/arch/arm/mach-shmobile/board-ag5evm.c	2011-04-18 14:39:20.000000000 +0900
@@ -59,6 +59,7 @@ 
 
 #include <sound/sh_fsi.h>
 #include <video/sh_mobile_lcdc.h>
+#include <media/soc_camera.h>
 
 static struct r8a66597_platdata usb_host_data = {
 	.on_chip	= 1,
@@ -317,11 +318,38 @@  static struct platform_device fsi_device
 	},
 };
 
+static struct i2c_board_info rj65na20_info = {
+	I2C_BOARD_INFO("rj65na20", 0x40),
+};
+
+struct soc_camera_link rj65na20_link = {
+	.bus_id         = 0,
+	.board_info     = &rj65na20_info,
+	.i2c_adapter_id = 0,
+	.module_name    = "rj65na20",
+};
+
+static struct platform_device rj65na20_camera = {
+	.name	= "soc-camera-pdrv-2M",
+	.id	= 0,
+	.dev	= {
+		.platform_data = &rj65na20_link,
+	},
+};
+
 static struct i2c_board_info i2c0_devices[] = {
 	{
 		I2C_BOARD_INFO("ag5evm_ts", 0x20),
 		.irq	= pint2irq(12),	/* PINTC3 */
 	},
+	/* 2M camera */
+	{
+		I2C_BOARD_INFO("rj65na20", 0x40),
+	},
 };
 
 static struct i2c_board_info i2c1_devices[] = {
@@ -548,6 +576,8 @@  static struct platform_device *ag5evm_de
 
 	&usb_mass_storage_device,
 	&android_usb_device,