All of lore.kernel.org
 help / color / mirror / Atom feed
* Re: [patch 5/5] saa7121 driver for s6000 data port
@ 2009-03-26 15:08 Hans Verkuil
  2009-03-30  9:56 ` Daniel Glöckner
  0 siblings, 1 reply; 8+ messages in thread
From: Hans Verkuil @ 2009-03-26 15:08 UTC (permalink / raw)
  To: Daniel Glöckner
  Cc: Mauro Carvalho Chehab, Chris Zankel, linux-media,
	Daniel Glöckner


> This patch adds a driver to support the saa7121 PAL/NTSC video encoder
> in combination with the s6000 data port driver.
>
> The chip is configured for embedded BT.656 syncs as this mode should
> be supported on all devices.
>
> The driver presents two outputs to applications and while it is true
> that the device has these two outputs, both of them are always active.
> The only difference on the "Y/C" output is that it disables the luma
> notch filter.

Hi Daniel,

The big problem with this driver and the micron driver is that this
implementation ties it directly to the s6000 data port driver. Thus making
it impossible to reuse in another system.

I've been working on a new framework for devices like this and almost all
i2c v4l drivers are now converted to v4l2_subdev in our v4l-dvb tree. It
will also be merged in 2.6.30. Please take a look at v4l2-framework.txt in
the v4l-dvb repository for more information.

I'm sure you will have questions later, please don't hesitate to ask! It's
a recent development but very much needed. Otherwise we will end up with a
lot of duplicate i2c drivers, each tied to their own platform or
framework. That's clearly something we do not want.

Regards,

         Hans

>
> Signed-off-by: Daniel Glöckner <dg@emlix.com>
> ---
>  drivers/media/video/s6dp/Kconfig        |    7 +
>  drivers/media/video/s6dp/Makefile       |    1 +
>  drivers/media/video/s6dp/s6dp-saa7121.c |  478
> +++++++++++++++++++++++++++++++
>  3 files changed, 486 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/media/video/s6dp/s6dp-saa7121.c
>
> diff --git a/drivers/media/video/s6dp/Kconfig
> b/drivers/media/video/s6dp/Kconfig
> index 853e6b1..c95904c 100644
> --- a/drivers/media/video/s6dp/Kconfig
> +++ b/drivers/media/video/s6dp/Kconfig
> @@ -20,3 +20,10 @@ config VIDEO_S6DP_MT9D131
>  	default n
>  	help
>  	  Enables the MT9D131 camera driver.
> +
> +config VIDEO_S6DP_SAA7121
> +	tristate "SAA7121 video encoder"
> +	depends on VIDEO_S6000
> +	default n
> +	help
> +	  Enables the SAA7121 video encoder driver.
> diff --git a/drivers/media/video/s6dp/Makefile
> b/drivers/media/video/s6dp/Makefile
> index af0bc0f..61d86c9 100644
> --- a/drivers/media/video/s6dp/Makefile
> +++ b/drivers/media/video/s6dp/Makefile
> @@ -1,2 +1,3 @@
>  obj-$(CONFIG_VIDEO_S6000) += s6dp.o
>  obj-$(CONFIG_VIDEO_S6DP_MT9D131) += s6dp-mt9d131.o
> +obj-$(CONFIG_VIDEO_S6DP_SAA7121) += s6dp-saa7121.o
> diff --git a/drivers/media/video/s6dp/s6dp-saa7121.c
> b/drivers/media/video/s6dp/s6dp-saa7121.c
> new file mode 100644
> index 0000000..70799cd
> --- /dev/null
> +++ b/drivers/media/video/s6dp/s6dp-saa7121.c
> @@ -0,0 +1,478 @@
> +/*
> + * drivers/media/video/s6dp/s6dp-saa7121.c
> + *
> + * Description: Driver for SAA7121 chips hooked up to a S6000 family data
> port
> + *	(c) 2009 emlix GmbH <info@emlix.com>
> + *
> + * Author:	Daniel Gloeckner <dg@emlix.com>
> + */
> +
> +#include <media/s6dp-link.h>
> +#include "s6dp.h"
> +#include <linux/i2c.h>
> +
> +static const u8 initial_setup[][2] = {
> +	{0x3a, 0x13}
> +};
> +
> +static const u8 pal_values[][2] = {
> +	{0x28, 33}, {0x29, 29}, {0x5a, 0x00}, {0x5b, 125},
> +	{0x5c, 175}, {0x5d, 35}, {0x5e, 53}, {0x5f, 0x40+53},
> +	{0x61, 0x06}, {0x62, 47}, {0x63, 0xcb}, {0x64, 0x8a},
> +	{0x65, 0x09}, {0x66, 0x2a}, {0x6c, 0x05}, {0x6d, 0x20},
> +	{0x6e, 0xa0}
> +};
> +
> +static const u8 pal_nc_values[][2] = {
> +	{0x28, 33}, {0x29, 37}, {0x5a, 0x00}, {0x5b, 125},
> +	{0x5c, 175}, {0x5d, 35}, {0x5e, 53}, {0x5f, 0xc0+53},
> +	{0x61, 0x06}, {0x62, 47}, {0x63, 0x46}, {0x64, 0x94},
> +	{0x65, 0xf6}, {0x66, 0x21}, {0x6c, 0x05}, {0x6d, 0x20},
> +	{0x6e, 0xa0}
> +};
> +
> +static const u8 pal_m_values[][2] = {
> +	{0x28, 25}, {0x29, 29}, {0x5a, 0x00}, {0x5b, 118},
> +	{0x5c, 165}, {0x5d, 45}, {0x5e, 49}, {0x5f, 0xc0+59},
> +	{0x61, 0x17}, {0x62, 45}, {0x63, 0xe3}, {0x64, 0xef},
> +	{0x65, 0xe6}, {0x66, 0x21}, {0x6c, 0xf9}, {0x6d, 0x00},
> +	{0x6e, 0xa0}
> +};
> +
> +static const u8 ntsc_values[][2] = {
> +	{0x28, 25}, {0x29, 29}, {0x5a, 0x88}, {0x5b, 118},
> +	{0x5c, 165}, {0x5d, 42}, {0x5e, 46}, {0x5f, 0xc0+46},
> +	{0x61, 0x15}, {0x62, 63}, {0x63, 0x1f}, {0x64, 0x7c},
> +	{0x65, 0xf0}, {0x66, 0x21}, {0x6c, 0xf9}, {0x6d, 0x00},
> +	{0x6e, 0x80}
> +};
> +
> +static const u8 ntsc_jp_values[][2] = {
> +	{0x28, 25}, {0x29, 29}, {0x5a, 0x88}, {0x5b, 118},
> +	{0x5c, 165}, {0x5d, 19}, {0x5e, 46}, {0x5f, 0xc0+46},
> +	{0x61, 0x05}, {0x62, 62}, {0x63, 0x1f}, {0x64, 0x7c},
> +	{0x65, 0xf0}, {0x66, 0x21}, {0x6c, 0xf9}, {0x6d, 0x00},
> +	{0x6e, 0x80}
> +};
> +
> +struct saa7121 {
> +	int std;
> +	int yc;
> +	struct v4l2_pix_format fmt;
> +	struct v4l2_rect crop;
> +	u8 regs[128];
> +};
> +
> +static int saa7121_write_regs(struct i2c_client *client)
> +{
> +	struct saa7121 *me = i2c_get_clientdata(client);
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(me->regs); i++) {
> +		int ret = i2c_smbus_write_byte_data(client, i, me->regs[i]);
> +		if (ret < 0)
> +			return ret;
> +	}
> +	return 0;
> +}
> +
> +static void saa7121_change_regs(struct saa7121 *me, const u8 (*regs)[2],
> +				int num)
> +{
> +	int i;
> +	for (i = 0; i < num; i++)
> +		me->regs[regs[i][0]] = regs[i][1];
> +}
> +
> +static const struct {
> +	v4l2_std_id mask;
> +	const char *name;
> +	const u8 (*regs)[2];
> +	int num;
> +} standards[] = {
> +	{
> +		V4L2_STD_PAL | V4L2_STD_PAL_N,
> +		"PAL",
> +		pal_values,
> +		ARRAY_SIZE(pal_values)
> +	},
> +	{
> +		V4L2_STD_NTSC_M | V4L2_STD_NTSC_M_KR,
> +		"NTSC",
> +		ntsc_values,
> +		ARRAY_SIZE(ntsc_values)
> +	},
> +	{
> +		V4L2_STD_NTSC_M_JP,
> +		"NTSC-JP",
> +		ntsc_jp_values,
> +		ARRAY_SIZE(ntsc_jp_values)
> +	},
> +	{
> +		V4L2_STD_PAL_M,
> +		"PAL-M",
> +		pal_m_values,
> +		ARRAY_SIZE(pal_nc_values)
> +	},
> +	{
> +		V4L2_STD_PAL_Nc,
> +		"PAL-Nc",
> +		pal_nc_values,
> +		ARRAY_SIZE(pal_nc_values)
> +	},
> +};
> +
> +static int saa7121_e_std(void *ctx, struct v4l2_standard *std)
> +{
> +	if (std->index >= ARRAY_SIZE(standards))
> +		return -EINVAL;
> +	std->id = standards[std->index].mask;
> +	strcpy(std->name, standards[std->index].name);
> +	if (std->id & V4L2_STD_625_50) {
> +		std->frameperiod.numerator = 1;
> +		std->frameperiod.denominator = 25;
> +		std->framelines = 625;
> +	} else  {
> +		std->frameperiod.numerator = 1001;
> +		std->frameperiod.denominator = 30000;
> +		std->framelines = 525;
> +	}
> +	return 0;
> +}
> +
> +static int saa7121_cropcap(void *ctx, struct v4l2_cropcap *cap)
> +{
> +	struct i2c_client *client = ctx;
> +	struct saa7121 *me = i2c_get_clientdata(client);
> +
> +	if (standards[me->std].mask & V4L2_STD_625_50) {
> +		cap->bounds.top = 23 * 2;
> +		cap->bounds.left = 132;
> +		cap->bounds.width = 720;
> +		cap->bounds.height = 576;
> +		cap->defrect.left = 140;
> +		cap->defrect.top = 23 * 2;
> +		cap->defrect.width = 702;
> +		cap->defrect.height = 576;
> +		cap->pixelaspect.numerator = 54;
> +		cap->pixelaspect.denominator = 59;
> +	} else {
> +		cap->bounds.top = 20 * 2;
> +		cap->bounds.left = 122;
> +		cap->bounds.width = 720;
> +		cap->bounds.height = 487;
> +		cap->defrect.left = 130;
> +		cap->defrect.top = 22 * 2;
> +		cap->defrect.width = 704;
> +		cap->defrect.height = 480;
> +		cap->pixelaspect.numerator = 11;
> +		cap->pixelaspect.denominator = 10;
> +	}
> +	return 0;
> +}
> +
> +static int saa7121_s_crop(void *ctx, struct v4l2_crop *crop, int busy)
> +{
> +	struct i2c_client *client = ctx;
> +	struct saa7121 *me = i2c_get_clientdata(client);
> +	struct v4l2_cropcap cap;
> +
> +	if (busy)
> +		return -EBUSY;
> +
> +	saa7121_cropcap(ctx, &cap);
> +	me->crop = crop->c;
> +
> +	if (me->crop.width > cap.bounds.width)
> +		me->crop.width = cap.bounds.width;
> +	me->crop.left += me->crop.width & 1;
> +	me->crop.width &= ~1;
> +	if (me->crop.height > cap.bounds.height)
> +		me->crop.height = cap.bounds.height;
> +	if (me->crop.left < cap.bounds.left)
> +		me->crop.left = cap.bounds.left;
> +	if (me->crop.left > cap.bounds.left + cap.bounds.width - me->crop.width)
> +		me->crop.left = cap.bounds.left + cap.bounds.width
> +				 - me->crop.width;
> +	me->crop.left &= ~1;
> +	if (me->crop.top < cap.bounds.top)
> +		me->crop.top = cap.bounds.top;
> +	if (me->crop.top > cap.bounds.top + cap.bounds.height - me->crop.height)
> +		me->crop.top = cap.bounds.top + cap.bounds.height
> +				- me->crop.height;
> +	me->crop.top &= ~1;
> +	me->fmt.width = me->crop.width;
> +	me->fmt.height = me->crop.height;
> +	return 0;
> +}
> +
> +static int saa7121_g_crop(void *ctx, struct v4l2_crop *crop)
> +{
> +	struct i2c_client *client = ctx;
> +	struct saa7121 *me = i2c_get_clientdata(client);
> +
> +	crop->c = me->crop;
> +	return 0;
> +}
> +
> +static int saa7121_s_fmt(void *ctx, int try_fmt, struct v4l2_pix_format
> *fmt,
> +			 int busy)
> +{
> +	struct i2c_client *client = ctx;
> +	struct saa7121 *me = i2c_get_clientdata(client);
> +	struct v4l2_cropcap cap;
> +
> +	if (!try_fmt && busy)
> +		return -EBUSY;
> +
> +	saa7121_cropcap(ctx, &cap);
> +	fmt->pixelformat = V4L2_PIX_FMT_UYVY;
> +	fmt->field = V4L2_FIELD_ALTERNATE;
> +
> +	if (standards[me->std].mask & V4L2_STD_625_50)
> +		fmt->colorspace = V4L2_COLORSPACE_470_SYSTEM_BG;
> +	else if (standards[me->std].mask == V4L2_STD_PAL_M)
> +		fmt->colorspace = V4L2_COLORSPACE_470_SYSTEM_M;
> +	else
> +		fmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
> +
> +	fmt->width &= ~1;
> +	if (fmt->width > cap.bounds.width)
> +		fmt->width = cap.bounds.width;
> +	if (fmt->height > cap.bounds.height)
> +		fmt->height = cap.bounds.height;
> +
> +	if (!try_fmt) {
> +		me->fmt = *fmt;
> +		me->crop.width = fmt->width;
> +		me->crop.height = fmt->height;
> +		if (me->crop.left > cap.bounds.left + cap.bounds.width
> +				     - fmt->width) {
> +			me->crop.left = cap.bounds.left + cap.bounds.width
> +					 - fmt->width;
> +			me->crop.left &= ~1;
> +		}
> +		if (me->crop.top > cap.bounds.top + cap.bounds.height
> +				    - fmt->height) {
> +			me->crop.top = cap.bounds.top + cap.bounds.height
> +					- fmt->height;
> +			me->crop.top &= ~1;
> +		}
> +	}
> +	return 0;
> +}
> +
> +static int saa7121_g_fmt(void *ctx, struct v4l2_pix_format *fmt)
> +{
> +	struct i2c_client *client = ctx;
> +	struct saa7121 *me = i2c_get_clientdata(client);
> +	*fmt = me->fmt;
> +	return 0;
> +}
> +
> +static void saa7121_g_mode(void *ctx, struct s6dp_mode *mode)
> +{
> +	struct i2c_client *client = ctx;
> +	struct saa7121 *me = i2c_get_clientdata(client);
> +	struct v4l2_cropcap cap;
> +
> +	saa7121_cropcap(ctx, &cap);
> +	mode->type = S6_DP_VIDEO_CFG_MODE_422_SERIAL;
> +	mode->progressive = 0;
> +	mode->embedded_sync = 1;
> +	mode->relaxed_framing = 0;
> +	mode->ten_bit = 0;
> +	mode->line_and_crc = 0;
> +	mode->micron_mode = 0;
> +	if (standards[me->std].mask & V4L2_STD_625_50) {
> +		mode->pixel_total = 864;
> +		mode->framelines = 625;
> +		mode->odd_vsync_offset = 623;
> +		mode->odd_vsync_len = 24;
> +		mode->odd_first = 22;
> +		mode->odd_total = 312;
> +		mode->even_vsync_offset = 310;
> +		mode->even_vsync_len = 25;
> +		mode->even_first = 335;
> +		mode->hsync_offset = 0;
> +		mode->hsync_len = 4;
> +	} else {
> +		mode->pixel_total = 858;
> +		mode->framelines = 525;
> +		mode->odd_vsync_offset = 522;
> +		mode->odd_vsync_len = 19;
> +		mode->odd_first = 16;
> +		mode->odd_total = 262;
> +		mode->even_vsync_offset = 260;
> +		mode->even_vsync_len = 19;
> +		mode->even_first = 279;
> +		mode->hsync_offset = 0;
> +		mode->hsync_len = 4;
> +	}
> +	mode->even_active = me->crop.height / 2;
> +	mode->odd_active = me->crop.height - mode->even_active;
> +	mode->odd_first += (me->crop.top - cap.bounds.top) / 2;
> +	mode->even_first += (me->crop.top - cap.bounds.top) / 2;
> +	mode->pixel_active = me->crop.width;
> +	mode->pixel_offset = me->crop.left - cap.bounds.left;
> +	mode->pixel_padding = 720 - mode->pixel_active - mode->pixel_offset;
> +}
> +
> +static void saa7121_reconfigure(struct i2c_client *client)
> +{
> +	struct saa7121 *me = i2c_get_clientdata(client);
> +
> +	saa7121_change_regs(me, standards[me->std].regs,
> +			    standards[me->std].num);
> +	if (me->yc)
> +		me->regs[0x5f] &= 0x3f;
> +
> +	saa7121_write_regs(client);
> +}
> +
> +static int saa7121_s_std(void *ctx, v4l2_std_id *mask, int busy)
> +{
> +	struct i2c_client *client = ctx;
> +	struct saa7121 *me = i2c_get_clientdata(client);
> +	int i;
> +
> +	if (busy && !(standards[me->std].mask & V4L2_STD_625_50)
> +		     == !(*mask & V4L2_STD_625_50))
> +		return -EBUSY;
> +
> +	for (i = 0; i < ARRAY_SIZE(standards); i++) {
> +		if (standards[i].mask & *mask) {
> +			me->std = i;
> +			saa7121_reconfigure(client);
> +			*mask = standards[i].mask;
> +			saa7121_s_fmt(ctx, 0, &me->fmt, 0);
> +			return 0;
> +		}
> +	}
> +	return -EINVAL;
> +}
> +
> +static int saa7121_e_outp(void *ctx, struct v4l2_output *outp)
> +{
> +	int i;
> +	if (outp->index > 1)
> +		return -EINVAL;
> +
> +	outp->type = V4L2_OUTPUT_TYPE_ANALOG;
> +	for (i = 0; i < ARRAY_SIZE(standards); i++)
> +		outp->std |= standards[i].mask;
> +
> +	strcpy(outp->name, outp->index ? "Y/C" : "CVBS");
> +	return 0;
> +}
> +
> +static int saa7121_s_outp(void *ctx, unsigned int nr, int busy)
> +{
> +	struct i2c_client *client = ctx;
> +	struct saa7121 *me = i2c_get_clientdata(client);
> +
> +	if (nr > 1)
> +		return -EINVAL;
> +
> +	/*
> +	 * both outputs are always active
> +	 * we just disable the cross color filter for Y/C
> +	 */
> +	me->yc = nr;
> +	saa7121_reconfigure(client);
> +	return 0;
> +}
> +
> +static int saa7121_probe(struct i2c_client *client,
> +			 const struct i2c_device_id *id)
> +{
> +	struct saa7121 *me;
> +	struct s6dp_link *link;
> +	s32 val;
> +
> +	if (!client->dev.platform_data)
> +		return -EINVAL;
> +	link = client->dev.platform_data;
> +
> +	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_BYTE
> +					| I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
> +		return -ENODEV;
> +
> +	val = i2c_smbus_read_byte(client);
> +	if (val < 0) {
> +		printk(KERN_ERR "saa7121: can't read status byte\n");
> +		return -EIO;
> +	}
> +	if ((val & 0xE0) != 0x20) {
> +		printk(KERN_ERR "saa7121: unsupported chip version %i"
> +				" (status = 0x%02x)\n", val >> 5, val);
> +		return -ENODEV;
> +	}
> +	me = kzalloc(sizeof(*me), GFP_KERNEL);
> +	if (!me)
> +		return -ENOMEM;
> +	me->std = V4L2_STD_PAL;
> +	i2c_set_clientdata(client, me);
> +
> +	saa7121_change_regs(me, initial_setup, ARRAY_SIZE(initial_setup));
> +	saa7121_change_regs(me, pal_values, ARRAY_SIZE(pal_values));
> +	if (saa7121_write_regs(client) < 0) {
> +		printk(KERN_ERR "saa7121: can't write registers\n");
> +		kfree(me);
> +		return -EIO;
> +	}
> +
> +	link->g_mode = saa7121_g_mode;
> +	link->e_std = saa7121_e_std;
> +	link->s_std = saa7121_s_std;
> +	link->s_fmt = saa7121_s_fmt;
> +	link->g_fmt = saa7121_g_fmt;
> +	link->cropcap = saa7121_cropcap;
> +	link->s_crop = saa7121_s_crop;
> +	link->g_crop = saa7121_g_crop;
> +	link->dir.egress.e_outp = saa7121_e_outp;
> +	link->dir.egress.s_outp = saa7121_s_outp;
> +	link->context = client;
> +	printk(KERN_INFO "saa7121 probed successfully\n");
> +	return 0;
> +}
> +
> +static int saa7121_remove(struct i2c_client *client)
> +{
> +	struct saa7121_data *data;
> +	data = i2c_get_clientdata(client);
> +	i2c_set_clientdata(client, NULL);
> +	kfree(data);
> +	return 0;
> +}
> +
> +static const struct i2c_device_id saa7121_id[] = {
> +	{ "saa7121", 0 },
> +	{ }
> +};
> +
> +static struct i2c_driver saa7121_driver = {
> +	.driver = {
> +		.name   = "s6dp-saa7121",
> +	},
> +	.probe          = saa7121_probe,
> +	.remove         = saa7121_remove,
> +	.id_table	= saa7121_id,
> +};
> +
> +static int __init saa7121_init(void)
> +{
> +	return i2c_add_driver(&saa7121_driver);
> +}
> +
> +static void __exit saa7121_exit(void)
> +{
> +	i2c_del_driver(&saa7121_driver);
> +}
> +
> +MODULE_AUTHOR("Daniel Gloeckner <dg@emlix.com>");
> +MODULE_DESCRIPTION("SAA7121 driver");
> +MODULE_LICENSE("GPL");
> +
> +module_init(saa7121_init);
> +module_exit(saa7121_exit);
> --
> 1.6.2.107.ge47ee
>
> --
> 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 - video4linux developer - sponsored by TANDBERG


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

* Re: [patch 5/5] saa7121 driver for s6000 data port
  2009-03-26 15:08 [patch 5/5] saa7121 driver for s6000 data port Hans Verkuil
@ 2009-03-30  9:56 ` Daniel Glöckner
  2009-03-30 10:03   ` Hans Verkuil
  0 siblings, 1 reply; 8+ messages in thread
From: Daniel Glöckner @ 2009-03-30  9:56 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: Mauro Carvalho Chehab, Chris Zankel, linux-media

On 03/26/2009 04:08 PM, Hans Verkuil wrote:
> I've been working on a new framework for devices like this and almost all
> i2c v4l drivers are now converted to v4l2_subdev in our v4l-dvb tree. It
> will also be merged in 2.6.30. Please take a look at v4l2-framework.txt in
> the v4l-dvb repository for more information.
> 
> I'm sure you will have questions later, please don't hesitate to ask! It's
> a recent development but very much needed. Otherwise we will end up with a
> lot of duplicate i2c drivers, each tied to their own platform or
> framework. That's clearly something we do not want.

Hi Hans,

the problem I see with the v4l2-framework in this case is that in its current
state it does not allow to exchange information regarding the bus parameters
between the sub device and the controller.

It seems the soc-camera framework is a better choice here, but to make it work
with the saa7121 one would first have to implement support for video output.

What do you recommend?


  Daniel

-- 
Dipl.-Math. Daniel Glöckner, emlix GmbH, http://www.emlix.com
Fon +49 551 30664-0, Fax -11, Bahnhofsallee 1b, 37081 Göttingen, Germany
Geschäftsführung: Dr. Uwe Kracke, Dr. Cord Seele, Ust-IdNr.: DE 205 198 055
Sitz der Gesellschaft: Göttingen, Amtsgericht Göttingen HR B 3160

emlix - your embedded linux partner

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

* Re: [patch 5/5] saa7121 driver for s6000 data port
  2009-03-30  9:56 ` Daniel Glöckner
@ 2009-03-30 10:03   ` Hans Verkuil
  2009-03-30 12:12     ` Daniel Glöckner
  0 siblings, 1 reply; 8+ messages in thread
From: Hans Verkuil @ 2009-03-30 10:03 UTC (permalink / raw)
  To: Daniel Glöckner; +Cc: Mauro Carvalho Chehab, Chris Zankel, linux-media

On Monday 30 March 2009 11:56:25 Daniel Glöckner wrote:
> On 03/26/2009 04:08 PM, Hans Verkuil wrote:
> > I've been working on a new framework for devices like this and almost
> > all i2c v4l drivers are now converted to v4l2_subdev in our v4l-dvb
> > tree. It will also be merged in 2.6.30. Please take a look at
> > v4l2-framework.txt in the v4l-dvb repository for more information.
> >
> > I'm sure you will have questions later, please don't hesitate to ask!
> > It's a recent development but very much needed. Otherwise we will end
> > up with a lot of duplicate i2c drivers, each tied to their own platform
> > or framework. That's clearly something we do not want.
>
> Hi Hans,
>
> the problem I see with the v4l2-framework in this case is that in its
> current state it does not allow to exchange information regarding the bus
> parameters between the sub device and the controller.

What exactly do you need? If there is something missing, then it should be 
added. But my guess is that you can pass such information via the s_routing 
callback. That's what all other drivers that use v4l2_subdev do.

> It seems the soc-camera framework is a better choice here, but to make it
> work with the saa7121 one would first have to implement support for video
> output.

This framework will also be converted to use v4l2_subdev for the 
communication with i2c drivers.

> What do you recommend?

Actually, I recommend that you first look at the existing saa7127.c source. 
I don't know how many differences there are between the saa7121 and 
saa7127, but perhaps support for the saa7121 can be added there rather than 
introducing a new driver. Of course, that only works if the differences are 
not too big.

Regards,

	Hans

-- 
Hans Verkuil - video4linux developer - sponsored by TANDBERG

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

* Re: [patch 5/5] saa7121 driver for s6000 data port
  2009-03-30 10:03   ` Hans Verkuil
@ 2009-03-30 12:12     ` Daniel Glöckner
  2009-03-30 12:50       ` Hans Verkuil
  0 siblings, 1 reply; 8+ messages in thread
From: Daniel Glöckner @ 2009-03-30 12:12 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: Mauro Carvalho Chehab, Chris Zankel, linux-media

On 03/30/2009 12:03 PM, Hans Verkuil wrote:
> What exactly do you need? If there is something missing, then it should be 
> added. But my guess is that you can pass such information via the s_routing 
> callback. That's what all other drivers that use v4l2_subdev do.

The s_routing callback looks very limited. One can pass only two u32 values.

The parameters that have to be negotiated are:
- What is the on-wire video format?
- how many data lines are connected?
- synchronization using embedded SAV/EAV codes or using dedicated pins?
- polarity of sync lines?
- valid CRC and line number in digital blanking?
- what is the layout of the digital image?
- how many odd lines are there? how many even? (including blanking)
- how many horizontal pixels? (incl. blanking)
- where is the active region?
- on which pixels/lines do we start/end horizontal/vertical sync?

>> It seems the soc-camera framework is a better choice here, but to make it
>> work with the saa7121 one would first have to implement support for video
>> output.
> 
> This framework will also be converted to use v4l2_subdev for the 
> communication with i2c drivers.

So it shouldn't matter which one I chose?

> Actually, I recommend that you first look at the existing saa7127.c source.
> I don't know how many differences there are between the saa7121 and 
> saa7127, but perhaps support for the saa7121 can be added there rather than 
> introducing a new driver. Of course, that only works if the differences are 
> not too big.

The chips appear to be very similar, sharing most of the registers. However, the
aforementioned problem still exists with this driver. A driver connecting this
sub device must know beforehand that it has to send standard BT.656 video frames
with SAV/EAV codes.

  Daniel


-- 
Dipl.-Math. Daniel Glöckner, emlix GmbH, http://www.emlix.com
Fon +49 551 30664-0, Fax -11, Bahnhofsallee 1b, 37081 Göttingen, Germany
Geschäftsführung: Dr. Uwe Kracke, Dr. Cord Seele, Ust-IdNr.: DE 205 198 055
Sitz der Gesellschaft: Göttingen, Amtsgericht Göttingen HR B 3160

emlix - your embedded linux partner

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

* Re: [patch 5/5] saa7121 driver for s6000 data port
  2009-03-30 12:12     ` Daniel Glöckner
@ 2009-03-30 12:50       ` Hans Verkuil
  2009-03-30 13:36         ` Daniel Glöckner
  0 siblings, 1 reply; 8+ messages in thread
From: Hans Verkuil @ 2009-03-30 12:50 UTC (permalink / raw)
  To: Daniel Glöckner; +Cc: Mauro Carvalho Chehab, Chris Zankel, linux-media

On Monday 30 March 2009 14:12:10 Daniel Glöckner wrote:
> On 03/30/2009 12:03 PM, Hans Verkuil wrote:
> > What exactly do you need? If there is something missing, then it should be 
> > added. But my guess is that you can pass such information via the s_routing 
> > callback. That's what all other drivers that use v4l2_subdev do.
> 
> The s_routing callback looks very limited. One can pass only two u32 values.

If a driver needs it, it can be extended. In particular I always thought that
a third config value would be useful.

> The parameters that have to be negotiated are:
> - What is the on-wire video format?

That might go to such a config value.

> - how many data lines are connected?

routing

> - synchronization using embedded SAV/EAV codes or using dedicated pins?

config value and/or routing.

> - polarity of sync lines?

config

> - valid CRC and line number in digital blanking?

Do you really need to control these?

> - what is the layout of the digital image?
> - how many odd lines are there? how many even? (including blanking)
> - how many horizontal pixels? (incl. blanking)
> - where is the active region?
> - on which pixels/lines do we start/end horizontal/vertical sync?

It's a PAL/NTSC encoder, so the standard specified with s_std_output will
map to the corresponding values that you need to put in. This is knowledge
that the i2c driver implements.

> 
> >> It seems the soc-camera framework is a better choice here, but to make it
> >> work with the saa7121 one would first have to implement support for video
> >> output.
> > 
> > This framework will also be converted to use v4l2_subdev for the 
> > communication with i2c drivers.
> 
> So it shouldn't matter which one I chose?

You will have to do the work anyway. Better to go with the new framework then
having to do the work twice.

> > Actually, I recommend that you first look at the existing saa7127.c source.
> > I don't know how many differences there are between the saa7121 and 
> > saa7127, but perhaps support for the saa7121 can be added there rather than 
> > introducing a new driver. Of course, that only works if the differences are 
> > not too big.
> 
> The chips appear to be very similar, sharing most of the registers. However, the
> aforementioned problem still exists with this driver. A driver connecting this
> sub device must know beforehand that it has to send standard BT.656 video frames
> with SAV/EAV codes.

So? If some future driver wants to do this differently, then we add the
necessary code to the i2c driver. It's not fixed in stone, you know :-)

Basically a driver only implements what can be tested. There is little point
in adding a full feature set for a device if you are unable to test the code
as well. So if a newer board appears in the future that needs to use
something new, then we add support for that to the i2c driver.

Looking at the datasheets I don't think you should make a new driver for
this. Unless something crops up that makes it hard to use saa7127.c I think
you should extend that driver to support saa7121 and add support for the
missing functionality. But only what is necessary for your setup.

Regards,

	Hans

-- 
Hans Verkuil - video4linux developer - sponsored by TANDBERG

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

* Re: [patch 5/5] saa7121 driver for s6000 data port
  2009-03-30 12:50       ` Hans Verkuil
@ 2009-03-30 13:36         ` Daniel Glöckner
  2009-03-30 13:41           ` Hans Verkuil
  0 siblings, 1 reply; 8+ messages in thread
From: Daniel Glöckner @ 2009-03-30 13:36 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: Mauro Carvalho Chehab, Chris Zankel, linux-media

On 03/30/2009 02:50 PM, Hans Verkuil wrote:
> On Monday 30 March 2009 14:12:10 Daniel Glöckner wrote:
>> - valid CRC and line number in digital blanking?
> 
> Do you really need to control these?

Not on this board..

> It's a PAL/NTSC encoder, so the standard specified with s_std_output will
> map to the corresponding values that you need to put in. This is knowledge
> that the i2c driver implements.

There is a micron camera connected to the controller that can output any
resolution up to 1600x1200 and we don't have standard ids for all those HD
formats supported by encoders like the ADV7197. It would be really nice to
have an interface that covers all this while being symmetric for input and output.

  Daniel


-- 
Dipl.-Math. Daniel Glöckner, emlix GmbH, http://www.emlix.com
Fon +49 551 30664-0, Fax -11, Bahnhofsallee 1b, 37081 Göttingen, Germany
Geschäftsführung: Dr. Uwe Kracke, Dr. Cord Seele, Ust-IdNr.: DE 205 198 055
Sitz der Gesellschaft: Göttingen, Amtsgericht Göttingen HR B 3160

emlix - your embedded linux partner

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

* Re: [patch 5/5] saa7121 driver for s6000 data port
  2009-03-30 13:36         ` Daniel Glöckner
@ 2009-03-30 13:41           ` Hans Verkuil
  0 siblings, 0 replies; 8+ messages in thread
From: Hans Verkuil @ 2009-03-30 13:41 UTC (permalink / raw)
  To: Daniel Glöckner; +Cc: Mauro Carvalho Chehab, Chris Zankel, linux-media

On Monday 30 March 2009 15:36:42 Daniel Glöckner wrote:
> On 03/30/2009 02:50 PM, Hans Verkuil wrote:
> > On Monday 30 March 2009 14:12:10 Daniel Glöckner wrote:
> >> - valid CRC and line number in digital blanking?
> > 
> > Do you really need to control these?
> 
> Not on this board..
> 
> > It's a PAL/NTSC encoder, so the standard specified with s_std_output will
> > map to the corresponding values that you need to put in. This is knowledge
> > that the i2c driver implements.
> 
> There is a micron camera connected to the controller that can output any
> resolution up to 1600x1200 and we don't have standard ids for all those HD
> formats supported by encoders like the ADV7197. It would be really nice to
> have an interface that covers all this while being symmetric for input and output.

This is a known issue. I expect to see some proposals for this in the near
future since Texas Instruments need this as well for their davinci architecture.

Regards,

	Hans

-- 
Hans Verkuil - video4linux developer - sponsored by TANDBERG

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

* [patch 5/5] saa7121 driver for s6000 data port
  2009-03-26 14:36 [patch 1/5] s6000 data port driver Daniel Glöckner
@ 2009-03-26 14:36 ` Daniel Glöckner
  0 siblings, 0 replies; 8+ messages in thread
From: Daniel Glöckner @ 2009-03-26 14:36 UTC (permalink / raw)
  To: Mauro Carvalho Chehab; +Cc: Chris Zankel, linux-media, Daniel Glöckner

This patch adds a driver to support the saa7121 PAL/NTSC video encoder
in combination with the s6000 data port driver.

The chip is configured for embedded BT.656 syncs as this mode should
be supported on all devices.

The driver presents two outputs to applications and while it is true
that the device has these two outputs, both of them are always active.
The only difference on the "Y/C" output is that it disables the luma
notch filter.

Signed-off-by: Daniel Glöckner <dg@emlix.com>
---
 drivers/media/video/s6dp/Kconfig        |    7 +
 drivers/media/video/s6dp/Makefile       |    1 +
 drivers/media/video/s6dp/s6dp-saa7121.c |  478 +++++++++++++++++++++++++++++++
 3 files changed, 486 insertions(+), 0 deletions(-)
 create mode 100644 drivers/media/video/s6dp/s6dp-saa7121.c

diff --git a/drivers/media/video/s6dp/Kconfig b/drivers/media/video/s6dp/Kconfig
index 853e6b1..c95904c 100644
--- a/drivers/media/video/s6dp/Kconfig
+++ b/drivers/media/video/s6dp/Kconfig
@@ -20,3 +20,10 @@ config VIDEO_S6DP_MT9D131
 	default n
 	help
 	  Enables the MT9D131 camera driver.
+
+config VIDEO_S6DP_SAA7121
+	tristate "SAA7121 video encoder"
+	depends on VIDEO_S6000
+	default n
+	help
+	  Enables the SAA7121 video encoder driver.
diff --git a/drivers/media/video/s6dp/Makefile b/drivers/media/video/s6dp/Makefile
index af0bc0f..61d86c9 100644
--- a/drivers/media/video/s6dp/Makefile
+++ b/drivers/media/video/s6dp/Makefile
@@ -1,2 +1,3 @@
 obj-$(CONFIG_VIDEO_S6000) += s6dp.o
 obj-$(CONFIG_VIDEO_S6DP_MT9D131) += s6dp-mt9d131.o
+obj-$(CONFIG_VIDEO_S6DP_SAA7121) += s6dp-saa7121.o
diff --git a/drivers/media/video/s6dp/s6dp-saa7121.c b/drivers/media/video/s6dp/s6dp-saa7121.c
new file mode 100644
index 0000000..70799cd
--- /dev/null
+++ b/drivers/media/video/s6dp/s6dp-saa7121.c
@@ -0,0 +1,478 @@
+/*
+ * drivers/media/video/s6dp/s6dp-saa7121.c
+ *
+ * Description: Driver for SAA7121 chips hooked up to a S6000 family data port
+ *	(c) 2009 emlix GmbH <info@emlix.com>
+ *
+ * Author:	Daniel Gloeckner <dg@emlix.com>
+ */
+
+#include <media/s6dp-link.h>
+#include "s6dp.h"
+#include <linux/i2c.h>
+
+static const u8 initial_setup[][2] = {
+	{0x3a, 0x13}
+};
+
+static const u8 pal_values[][2] = {
+	{0x28, 33}, {0x29, 29}, {0x5a, 0x00}, {0x5b, 125},
+	{0x5c, 175}, {0x5d, 35}, {0x5e, 53}, {0x5f, 0x40+53},
+	{0x61, 0x06}, {0x62, 47}, {0x63, 0xcb}, {0x64, 0x8a},
+	{0x65, 0x09}, {0x66, 0x2a}, {0x6c, 0x05}, {0x6d, 0x20},
+	{0x6e, 0xa0}
+};
+
+static const u8 pal_nc_values[][2] = {
+	{0x28, 33}, {0x29, 37}, {0x5a, 0x00}, {0x5b, 125},
+	{0x5c, 175}, {0x5d, 35}, {0x5e, 53}, {0x5f, 0xc0+53},
+	{0x61, 0x06}, {0x62, 47}, {0x63, 0x46}, {0x64, 0x94},
+	{0x65, 0xf6}, {0x66, 0x21}, {0x6c, 0x05}, {0x6d, 0x20},
+	{0x6e, 0xa0}
+};
+
+static const u8 pal_m_values[][2] = {
+	{0x28, 25}, {0x29, 29}, {0x5a, 0x00}, {0x5b, 118},
+	{0x5c, 165}, {0x5d, 45}, {0x5e, 49}, {0x5f, 0xc0+59},
+	{0x61, 0x17}, {0x62, 45}, {0x63, 0xe3}, {0x64, 0xef},
+	{0x65, 0xe6}, {0x66, 0x21}, {0x6c, 0xf9}, {0x6d, 0x00},
+	{0x6e, 0xa0}
+};
+
+static const u8 ntsc_values[][2] = {
+	{0x28, 25}, {0x29, 29}, {0x5a, 0x88}, {0x5b, 118},
+	{0x5c, 165}, {0x5d, 42}, {0x5e, 46}, {0x5f, 0xc0+46},
+	{0x61, 0x15}, {0x62, 63}, {0x63, 0x1f}, {0x64, 0x7c},
+	{0x65, 0xf0}, {0x66, 0x21}, {0x6c, 0xf9}, {0x6d, 0x00},
+	{0x6e, 0x80}
+};
+
+static const u8 ntsc_jp_values[][2] = {
+	{0x28, 25}, {0x29, 29}, {0x5a, 0x88}, {0x5b, 118},
+	{0x5c, 165}, {0x5d, 19}, {0x5e, 46}, {0x5f, 0xc0+46},
+	{0x61, 0x05}, {0x62, 62}, {0x63, 0x1f}, {0x64, 0x7c},
+	{0x65, 0xf0}, {0x66, 0x21}, {0x6c, 0xf9}, {0x6d, 0x00},
+	{0x6e, 0x80}
+};
+
+struct saa7121 {
+	int std;
+	int yc;
+	struct v4l2_pix_format fmt;
+	struct v4l2_rect crop;
+	u8 regs[128];
+};
+
+static int saa7121_write_regs(struct i2c_client *client)
+{
+	struct saa7121 *me = i2c_get_clientdata(client);
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(me->regs); i++) {
+		int ret = i2c_smbus_write_byte_data(client, i, me->regs[i]);
+		if (ret < 0)
+			return ret;
+	}
+	return 0;
+}
+
+static void saa7121_change_regs(struct saa7121 *me, const u8 (*regs)[2],
+				int num)
+{
+	int i;
+	for (i = 0; i < num; i++)
+		me->regs[regs[i][0]] = regs[i][1];
+}
+
+static const struct {
+	v4l2_std_id mask;
+	const char *name;
+	const u8 (*regs)[2];
+	int num;
+} standards[] = {
+	{
+		V4L2_STD_PAL | V4L2_STD_PAL_N,
+		"PAL",
+		pal_values,
+		ARRAY_SIZE(pal_values)
+	},
+	{
+		V4L2_STD_NTSC_M | V4L2_STD_NTSC_M_KR,
+		"NTSC",
+		ntsc_values,
+		ARRAY_SIZE(ntsc_values)
+	},
+	{
+		V4L2_STD_NTSC_M_JP,
+		"NTSC-JP",
+		ntsc_jp_values,
+		ARRAY_SIZE(ntsc_jp_values)
+	},
+	{
+		V4L2_STD_PAL_M,
+		"PAL-M",
+		pal_m_values,
+		ARRAY_SIZE(pal_nc_values)
+	},
+	{
+		V4L2_STD_PAL_Nc,
+		"PAL-Nc",
+		pal_nc_values,
+		ARRAY_SIZE(pal_nc_values)
+	},
+};
+
+static int saa7121_e_std(void *ctx, struct v4l2_standard *std)
+{
+	if (std->index >= ARRAY_SIZE(standards))
+		return -EINVAL;
+	std->id = standards[std->index].mask;
+	strcpy(std->name, standards[std->index].name);
+	if (std->id & V4L2_STD_625_50) {
+		std->frameperiod.numerator = 1;
+		std->frameperiod.denominator = 25;
+		std->framelines = 625;
+	} else  {
+		std->frameperiod.numerator = 1001;
+		std->frameperiod.denominator = 30000;
+		std->framelines = 525;
+	}
+	return 0;
+}
+
+static int saa7121_cropcap(void *ctx, struct v4l2_cropcap *cap)
+{
+	struct i2c_client *client = ctx;
+	struct saa7121 *me = i2c_get_clientdata(client);
+
+	if (standards[me->std].mask & V4L2_STD_625_50) {
+		cap->bounds.top = 23 * 2;
+		cap->bounds.left = 132;
+		cap->bounds.width = 720;
+		cap->bounds.height = 576;
+		cap->defrect.left = 140;
+		cap->defrect.top = 23 * 2;
+		cap->defrect.width = 702;
+		cap->defrect.height = 576;
+		cap->pixelaspect.numerator = 54;
+		cap->pixelaspect.denominator = 59;
+	} else {
+		cap->bounds.top = 20 * 2;
+		cap->bounds.left = 122;
+		cap->bounds.width = 720;
+		cap->bounds.height = 487;
+		cap->defrect.left = 130;
+		cap->defrect.top = 22 * 2;
+		cap->defrect.width = 704;
+		cap->defrect.height = 480;
+		cap->pixelaspect.numerator = 11;
+		cap->pixelaspect.denominator = 10;
+	}
+	return 0;
+}
+
+static int saa7121_s_crop(void *ctx, struct v4l2_crop *crop, int busy)
+{
+	struct i2c_client *client = ctx;
+	struct saa7121 *me = i2c_get_clientdata(client);
+	struct v4l2_cropcap cap;
+
+	if (busy)
+		return -EBUSY;
+
+	saa7121_cropcap(ctx, &cap);
+	me->crop = crop->c;
+
+	if (me->crop.width > cap.bounds.width)
+		me->crop.width = cap.bounds.width;
+	me->crop.left += me->crop.width & 1;
+	me->crop.width &= ~1;
+	if (me->crop.height > cap.bounds.height)
+		me->crop.height = cap.bounds.height;
+	if (me->crop.left < cap.bounds.left)
+		me->crop.left = cap.bounds.left;
+	if (me->crop.left > cap.bounds.left + cap.bounds.width - me->crop.width)
+		me->crop.left = cap.bounds.left + cap.bounds.width
+				 - me->crop.width;
+	me->crop.left &= ~1;
+	if (me->crop.top < cap.bounds.top)
+		me->crop.top = cap.bounds.top;
+	if (me->crop.top > cap.bounds.top + cap.bounds.height - me->crop.height)
+		me->crop.top = cap.bounds.top + cap.bounds.height
+				- me->crop.height;
+	me->crop.top &= ~1;
+	me->fmt.width = me->crop.width;
+	me->fmt.height = me->crop.height;
+	return 0;
+}
+
+static int saa7121_g_crop(void *ctx, struct v4l2_crop *crop)
+{
+	struct i2c_client *client = ctx;
+	struct saa7121 *me = i2c_get_clientdata(client);
+
+	crop->c = me->crop;
+	return 0;
+}
+
+static int saa7121_s_fmt(void *ctx, int try_fmt, struct v4l2_pix_format *fmt,
+			 int busy)
+{
+	struct i2c_client *client = ctx;
+	struct saa7121 *me = i2c_get_clientdata(client);
+	struct v4l2_cropcap cap;
+
+	if (!try_fmt && busy)
+		return -EBUSY;
+
+	saa7121_cropcap(ctx, &cap);
+	fmt->pixelformat = V4L2_PIX_FMT_UYVY;
+	fmt->field = V4L2_FIELD_ALTERNATE;
+
+	if (standards[me->std].mask & V4L2_STD_625_50)
+		fmt->colorspace = V4L2_COLORSPACE_470_SYSTEM_BG;
+	else if (standards[me->std].mask == V4L2_STD_PAL_M)
+		fmt->colorspace = V4L2_COLORSPACE_470_SYSTEM_M;
+	else
+		fmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
+
+	fmt->width &= ~1;
+	if (fmt->width > cap.bounds.width)
+		fmt->width = cap.bounds.width;
+	if (fmt->height > cap.bounds.height)
+		fmt->height = cap.bounds.height;
+
+	if (!try_fmt) {
+		me->fmt = *fmt;
+		me->crop.width = fmt->width;
+		me->crop.height = fmt->height;
+		if (me->crop.left > cap.bounds.left + cap.bounds.width
+				     - fmt->width) {
+			me->crop.left = cap.bounds.left + cap.bounds.width
+					 - fmt->width;
+			me->crop.left &= ~1;
+		}
+		if (me->crop.top > cap.bounds.top + cap.bounds.height
+				    - fmt->height) {
+			me->crop.top = cap.bounds.top + cap.bounds.height
+					- fmt->height;
+			me->crop.top &= ~1;
+		}
+	}
+	return 0;
+}
+
+static int saa7121_g_fmt(void *ctx, struct v4l2_pix_format *fmt)
+{
+	struct i2c_client *client = ctx;
+	struct saa7121 *me = i2c_get_clientdata(client);
+	*fmt = me->fmt;
+	return 0;
+}
+
+static void saa7121_g_mode(void *ctx, struct s6dp_mode *mode)
+{
+	struct i2c_client *client = ctx;
+	struct saa7121 *me = i2c_get_clientdata(client);
+	struct v4l2_cropcap cap;
+
+	saa7121_cropcap(ctx, &cap);
+	mode->type = S6_DP_VIDEO_CFG_MODE_422_SERIAL;
+	mode->progressive = 0;
+	mode->embedded_sync = 1;
+	mode->relaxed_framing = 0;
+	mode->ten_bit = 0;
+	mode->line_and_crc = 0;
+	mode->micron_mode = 0;
+	if (standards[me->std].mask & V4L2_STD_625_50) {
+		mode->pixel_total = 864;
+		mode->framelines = 625;
+		mode->odd_vsync_offset = 623;
+		mode->odd_vsync_len = 24;
+		mode->odd_first = 22;
+		mode->odd_total = 312;
+		mode->even_vsync_offset = 310;
+		mode->even_vsync_len = 25;
+		mode->even_first = 335;
+		mode->hsync_offset = 0;
+		mode->hsync_len = 4;
+	} else {
+		mode->pixel_total = 858;
+		mode->framelines = 525;
+		mode->odd_vsync_offset = 522;
+		mode->odd_vsync_len = 19;
+		mode->odd_first = 16;
+		mode->odd_total = 262;
+		mode->even_vsync_offset = 260;
+		mode->even_vsync_len = 19;
+		mode->even_first = 279;
+		mode->hsync_offset = 0;
+		mode->hsync_len = 4;
+	}
+	mode->even_active = me->crop.height / 2;
+	mode->odd_active = me->crop.height - mode->even_active;
+	mode->odd_first += (me->crop.top - cap.bounds.top) / 2;
+	mode->even_first += (me->crop.top - cap.bounds.top) / 2;
+	mode->pixel_active = me->crop.width;
+	mode->pixel_offset = me->crop.left - cap.bounds.left;
+	mode->pixel_padding = 720 - mode->pixel_active - mode->pixel_offset;
+}
+
+static void saa7121_reconfigure(struct i2c_client *client)
+{
+	struct saa7121 *me = i2c_get_clientdata(client);
+
+	saa7121_change_regs(me, standards[me->std].regs,
+			    standards[me->std].num);
+	if (me->yc)
+		me->regs[0x5f] &= 0x3f;
+
+	saa7121_write_regs(client);
+}
+
+static int saa7121_s_std(void *ctx, v4l2_std_id *mask, int busy)
+{
+	struct i2c_client *client = ctx;
+	struct saa7121 *me = i2c_get_clientdata(client);
+	int i;
+
+	if (busy && !(standards[me->std].mask & V4L2_STD_625_50)
+		     == !(*mask & V4L2_STD_625_50))
+		return -EBUSY;
+
+	for (i = 0; i < ARRAY_SIZE(standards); i++) {
+		if (standards[i].mask & *mask) {
+			me->std = i;
+			saa7121_reconfigure(client);
+			*mask = standards[i].mask;
+			saa7121_s_fmt(ctx, 0, &me->fmt, 0);
+			return 0;
+		}
+	}
+	return -EINVAL;
+}
+
+static int saa7121_e_outp(void *ctx, struct v4l2_output *outp)
+{
+	int i;
+	if (outp->index > 1)
+		return -EINVAL;
+
+	outp->type = V4L2_OUTPUT_TYPE_ANALOG;
+	for (i = 0; i < ARRAY_SIZE(standards); i++)
+		outp->std |= standards[i].mask;
+
+	strcpy(outp->name, outp->index ? "Y/C" : "CVBS");
+	return 0;
+}
+
+static int saa7121_s_outp(void *ctx, unsigned int nr, int busy)
+{
+	struct i2c_client *client = ctx;
+	struct saa7121 *me = i2c_get_clientdata(client);
+
+	if (nr > 1)
+		return -EINVAL;
+
+	/*
+	 * both outputs are always active
+	 * we just disable the cross color filter for Y/C
+	 */
+	me->yc = nr;
+	saa7121_reconfigure(client);
+	return 0;
+}
+
+static int saa7121_probe(struct i2c_client *client,
+			 const struct i2c_device_id *id)
+{
+	struct saa7121 *me;
+	struct s6dp_link *link;
+	s32 val;
+
+	if (!client->dev.platform_data)
+		return -EINVAL;
+	link = client->dev.platform_data;
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_BYTE
+					| I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
+		return -ENODEV;
+
+	val = i2c_smbus_read_byte(client);
+	if (val < 0) {
+		printk(KERN_ERR "saa7121: can't read status byte\n");
+		return -EIO;
+	}
+	if ((val & 0xE0) != 0x20) {
+		printk(KERN_ERR "saa7121: unsupported chip version %i"
+				" (status = 0x%02x)\n", val >> 5, val);
+		return -ENODEV;
+	}
+	me = kzalloc(sizeof(*me), GFP_KERNEL);
+	if (!me)
+		return -ENOMEM;
+	me->std = V4L2_STD_PAL;
+	i2c_set_clientdata(client, me);
+
+	saa7121_change_regs(me, initial_setup, ARRAY_SIZE(initial_setup));
+	saa7121_change_regs(me, pal_values, ARRAY_SIZE(pal_values));
+	if (saa7121_write_regs(client) < 0) {
+		printk(KERN_ERR "saa7121: can't write registers\n");
+		kfree(me);
+		return -EIO;
+	}
+
+	link->g_mode = saa7121_g_mode;
+	link->e_std = saa7121_e_std;
+	link->s_std = saa7121_s_std;
+	link->s_fmt = saa7121_s_fmt;
+	link->g_fmt = saa7121_g_fmt;
+	link->cropcap = saa7121_cropcap;
+	link->s_crop = saa7121_s_crop;
+	link->g_crop = saa7121_g_crop;
+	link->dir.egress.e_outp = saa7121_e_outp;
+	link->dir.egress.s_outp = saa7121_s_outp;
+	link->context = client;
+	printk(KERN_INFO "saa7121 probed successfully\n");
+	return 0;
+}
+
+static int saa7121_remove(struct i2c_client *client)
+{
+	struct saa7121_data *data;
+	data = i2c_get_clientdata(client);
+	i2c_set_clientdata(client, NULL);
+	kfree(data);
+	return 0;
+}
+
+static const struct i2c_device_id saa7121_id[] = {
+	{ "saa7121", 0 },
+	{ }
+};
+
+static struct i2c_driver saa7121_driver = {
+	.driver = {
+		.name   = "s6dp-saa7121",
+	},
+	.probe          = saa7121_probe,
+	.remove         = saa7121_remove,
+	.id_table	= saa7121_id,
+};
+
+static int __init saa7121_init(void)
+{
+	return i2c_add_driver(&saa7121_driver);
+}
+
+static void __exit saa7121_exit(void)
+{
+	i2c_del_driver(&saa7121_driver);
+}
+
+MODULE_AUTHOR("Daniel Gloeckner <dg@emlix.com>");
+MODULE_DESCRIPTION("SAA7121 driver");
+MODULE_LICENSE("GPL");
+
+module_init(saa7121_init);
+module_exit(saa7121_exit);
-- 
1.6.2.107.ge47ee


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

end of thread, other threads:[~2009-03-30 13:42 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-03-26 15:08 [patch 5/5] saa7121 driver for s6000 data port Hans Verkuil
2009-03-30  9:56 ` Daniel Glöckner
2009-03-30 10:03   ` Hans Verkuil
2009-03-30 12:12     ` Daniel Glöckner
2009-03-30 12:50       ` Hans Verkuil
2009-03-30 13:36         ` Daniel Glöckner
2009-03-30 13:41           ` Hans Verkuil
  -- strict thread matches above, loose matches on Subject: below --
2009-03-26 14:36 [patch 1/5] s6000 data port driver Daniel Glöckner
2009-03-26 14:36 ` [patch 5/5] saa7121 driver for s6000 data port Daniel Glöckner

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.