[tuxbox] / tuxbox / driver / saa7126 / saa7126_core.c  

tuxbox: tuxbox/driver/saa7126/saa7126_core.c

File: [tuxbox] / tuxbox / driver / saa7126 / saa7126_core.c (download) (as text)
Revision: 1.49, Sat May 27 10:24:37 2006 UTC (4 years, 3 months ago) by barf
Branch: MAIN
CVS Tags: last_oldmake_head, HEAD
Changes since 1.48: +19 -11 lines
General overhaul of controld etc to get the video outputs right, see http://forum.tuxbox.org/forum/viewtopic.php?t=39520
/*
 * $Id: saa7126_core.c,v 1.49 2006/05/27 10:24:37 barf Exp $
 * 
 * Philips SAA7126 digital video encoder
 *
 * Copyright (C) 2000-2001 Gillem <htoa@gmx.net>
 * Copyright (C) 2002 Andreas Oberritter <obi@saftware.de>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * References:
 * http://www.semiconductors.philips.com/acrobat/datasheets/SAA7126H_7127H_2.pdf
 *
 */

#include <linux/module.h>
#include <linux/init.h>
#include <linux/devfs_fs_kernel.h>
#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/video_encoder.h>
#include <linux/videodev.h>

#include <dbox/saa7126_core.h>

#include <tuxbox/info_dbox2.h>

TUXBOX_INFO(dbox2_mid);

#ifndef CONFIG_DEVFS_FS
#error device filesystem required
#endif

#define dprintk if (0) printk



/*
 * id = (1 << (board_1 - 1)) | (1 << (board_n - 1))
 * reg = register address
 * len = buffer size
 * buf = data bytes
 */

struct saa7126_initdata {

	u8 id;
	u8 reg;
	u8 len;
	u8 buf[28];

} __attribute__ ((packed));


static struct saa7126_initdata saa7126_inittab_pal [] =
{
	{ 0x07, 0x26,  8, { 0x00, 0x00, 0x21, 0x1d, 0x00, 0x00, 0x00, 0x0f } },	/* common */

	/* gain luminance, gain colour difference */
	{ 0x01, 0x38,  3, { 0x1a, 0x1a, 0x03 } },				/* nokia, recommended */
	{ 0x02, 0x38,  3, { 0x1c, 0x1c, 0x03 } },				/* philips */
	{ 0x04, 0x38,  3, { 0x1e, 0x1e, 0x03 } },				/* sagem */

	{ 0x01, 0x54, 12, { 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6b, 0x7d,
			    0xaf, 0x33, 0x35, 0x35 } },				/* nokia */
	{ 0x02, 0x54, 12, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6b, 0x78,
			    0xab, 0x1b, 0x26, 0x26 } },				/* philips */
	{ 0x04, 0x54, 12, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6b, 0x75,
			    0xa5, 0x37, 0x39, 0x39 } },				/* sagem */

	{ 0x01, 0x61, 28, { 0x06, 0x2f, 0xcb, 0x8a, 0x09, 0x2a, 0x00, 0x00,
		 	    0x00, 0x00, 0x52, 0x28, 0x01, 0x20, 0x31, 0x7d,
			    0xbc, 0x60, 0x42, 0x05, 0x00, 0x05, 0x16, 0x04,
			    0x16, 0x16, 0x36, 0x60 } },				/* nokia */
	{ 0x02, 0x61, 28, { 0x16, 0x2f, 0xcb, 0x8a, 0x09, 0x2a, 0x00, 0x00,
			    0x00, 0x00, 0x72, 0x00, 0x00, 0xa0, 0x31, 0x7d,
			    0xbb, 0x60, 0x42, 0x07, 0x00, 0x05, 0x16, 0x04,
			    0x16, 0x16, 0x36, 0x60 } },				/* philips */
	{ 0x04, 0x61, 28, { 0x06, 0x2f, 0xcb, 0x8a, 0x09, 0x2a, 0x00, 0x00,
			    0x00, 0x00, 0x52, 0x6F, 0x00, 0xA0, 0x31, 0x7d,
			    0xbf, 0x60, 0x42, 0x07, 0x00, 0x05, 0x16, 0x04,
			    0x16, 0x16, 0x36, 0x60 } },				/* sagem */

	{ 0x07, 0x7d,  3, { 0x00, 0x00, 0x00 } },				/* common */

	{ 0xff },								/* end */
};


static struct saa7126_initdata saa7126_inittab_ntsc [] =
{
	{ 0x07, 0x26,  8, { 0x00, 0x00, 0x19, 0x1d, 0x00, 0x00, 0x00, 0x0f } },

	{ 0x01, 0x38,  3, { 0x1a, 0x1a, 0x03 } },
	{ 0x02, 0x38,  3, { 0x1c, 0x1c, 0x03 } },
	{ 0x04, 0x38,  3, { 0x1e, 0x1e, 0x03 } },

	{ 0x01, 0x54, 12, { 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa3, 0x7d,
			    0xaf, 0x33, 0x35, 0x35 } },
	{ 0x02, 0x54, 12, { 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa3, 0x78,
			    0xab, 0x1b, 0x26, 0x26 } },
	{ 0x04, 0x54, 12, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa3, 0x75,
			    0xa5, 0x37, 0x39, 0x39 } },

	{ 0x01, 0x61, 28, { 0x05, 0x43, 0x1f, 0x7c, 0xf0, 0x21, 0x00, 0x00,
		 	    0x00, 0x00, 0x52, 0x28, 0x01, 0x20, 0x31, 0x7d,
			    0xbc, 0x60, 0x54, 0x05, 0x00, 0x06, 0x10, 0x05,
			    0x10, 0x16, 0x36, 0xe0 } },
	{ 0x02, 0x61, 28, { 0x15, 0x43, 0x1f, 0x7c, 0xf0, 0x21, 0x00, 0x00,
			    0x00, 0x00, 0x72, 0x00, 0x00, 0xa0, 0x31, 0x7d,
			    0xb0, 0x60, 0x54, 0x07, 0x00, 0x06, 0x10, 0x05,
			    0x16, 0x16, 0x36, 0xe0 } },
	{ 0x04, 0x61, 28, { 0x05, 0x43, 0x1f, 0x7c, 0xf0, 0x21, 0x00, 0x00,
			    0x00, 0x00, 0x52, 0x6F, 0x00, 0xA0, 0x31, 0x7d,
			    0xbf, 0x60, 0x54, 0x07, 0x00, 0x06, 0x10, 0x05,
			    0x10, 0x16, 0x36, 0xe0 } },

	{ 0x07, 0x7e,  2, { 0x00, 0x00 } },

	{ 0xff },
};


/*
WSS-Bits
--------
for PAL-625 there are 8 combinations:

    Index  Bits  Aspectr Format Pos      ActiveLines RecommendedDisplay
    -----------------------------------------------------------------------------------------
	0 (01000)   4:3   full    -          576      centered, black side bars
	1 (00001)  14:9   lbx    center      504      centered, black side bars (or full)
	2 (00010)  14:9   lbx    top         504      centered, black side bars (or full)
	3 (01011)  16:9   lbx    center      430      full width
	4 (00100)  16:9   lbx    top         430      full width
	5 (01101) >16:9   lbx    center       -       full width (lbx'ed) or zoomed in (full)
	6 (01110)  14:9   full   center      576
	7 (00111)  16:9   anam    -          576

for NTSC-525 only bits 0 and 1 are used, rest is set to 0:

    Bit 0	1
    ---------------------
    b0  4:3	16:9
    b1  normal	letterbox

*/
static const unsigned char wss_data[8] =
{
	0x08, 0x01, 0x02, 0x0b, 0x04, 0x0d, 0x0e, 0x07
};



/*
 * i2c detection
 */

static unsigned short normal_i2c[] = { 0x44, I2C_CLIENT_END };
static unsigned short normal_i2c_range[] = { 0x44, 0x44, I2C_CLIENT_END };
I2C_CLIENT_INSMOD;



/*
 * saa device
 */

static int saa7126_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);
static int saa7126_open(struct inode *inode, struct file *file);

static struct file_operations saa7126_fops = {
	owner:		THIS_MODULE,
	ioctl:		saa7126_ioctl,
	open:		saa7126_open,
};



/*
 * module parameters
 */

static int mode;
static int ntsc;



/*
 * device counter
 */

static int saa7126_id;



/*
 * invalid i2c driver id ;)
 */

#define I2C_DRIVERID_SAA7126	1



/*
 * maximum amount of bytes transfered at once
 */

#define SAA7126_I2C_BLOCK_SIZE	0x40



/*
 * private data struct for each saa
 */

struct saa7126
{
	devfs_handle_t devfs_handle;
	struct i2c_client *i2c_client;

	u8 standby;
	u8 reg_2d;
	u8 reg_3a;
	u8 reg_61;

	int norm;
	int enable;
};


static struct i2c_driver i2c_driver_saa7126;


static int saa7126_readbuf (struct i2c_client *client, char reg, char *buf, short len)
{
	int ret;
	struct i2c_msg msg [] = { { addr: client->addr, flags: 0, len: 1, buf: &reg },
			{ addr: client->addr, flags: I2C_M_RD, len: len, buf: buf } };

	dprintk("[%s]: %s\n", __FILE__, __FUNCTION__);
	ret = i2c_transfer(client->adapter, msg, 2);

	if (ret != 2)
		printk("%s: i2c read error (ret == %d)\n", __FUNCTION__, ret);

	return 0;
}


static int saa7126_writebuf (struct i2c_client *client, u8 reg, u8 *data, u8 datalen)
{
	u8 buf[SAA7126_I2C_BLOCK_SIZE + 1];
	u8 len;
	u16 i;

	for (i = 0; i < datalen; i += len) {

		if ((datalen - i) >= (SAA7126_I2C_BLOCK_SIZE))
			len = SAA7126_I2C_BLOCK_SIZE;
		else
			len = datalen - i;
	
		buf[0] = reg + i;
		memcpy(buf + 1, data + i, len);
		i2c_master_send(client, buf, len + 1);
	}

	return 0;
}


static int saa7126_writereg(struct i2c_client *client, u8 reg, u8 val)
{
	char msg[2] = { reg, val };

	if (i2c_master_send(client, msg, 2) != 2)
		return -1;

	return 0;
}

static u8 saa7126_readreg(struct i2c_client *client, u8 reg)
{
	u8 val;

	saa7126_readbuf(client, reg, &val, 1);

	return val;
}


static int saa7126_write_inittab (struct i2c_client *client, char init)
{
	unsigned short i;
	struct saa7126 *encoder = (struct saa7126 *) client->data;
	struct saa7126_initdata *inittab;

	switch (encoder->norm) {
	case VIDEO_MODE_NTSC:
		inittab = saa7126_inittab_ntsc;
		break;
	case VIDEO_MODE_PAL:
		inittab = saa7126_inittab_pal;
		break;
	default:
		return -EINVAL;
	}

	/* init all null register with 0x00 */
	for (i = 0x01; i <= 0x25; i++  )
		saa7126_writereg(client, i, 0x00);
		
	for (i = 0x2e; i <= 0x37; i++ )
		saa7126_writereg(client, i, 0x00);

	saa7126_writereg(client, 0x60, 0x00);

	for (i = 0; inittab[i].id != 0xff; i++){
		if (inittab[i].id & (1 << (tuxbox_dbox2_mid - 1))){
			saa7126_writebuf(client, inittab[i].reg, inittab[i].buf, inittab[i].len);
			if (init){	/* need to init these two the first time */
				if (inittab[i].reg<=0x2d && (inittab[i].reg+inittab[i].len)>0x2d){
					encoder->reg_2d = inittab[i].buf[(0x2d-inittab[i].reg)];
				} else if (inittab[i].reg<=0x3a && (inittab[i].reg+inittab[i].len)>0x3a){
					encoder->reg_3a = inittab[i].buf[(0x3a-inittab[i].reg)];
				}
			}
		}
	}
	encoder->standby = 0;

	saa7126_writereg(client, 0x2d, encoder->reg_2d);
	saa7126_writereg(client, 0x3a, encoder->reg_3a);
	encoder->reg_61 = saa7126_readreg(client, 0x61);
	return 0;
}

#define REG_2D_RGB 	0x0f /* RGB + CVBS (for sync) */
#define REG_2D_FBAS	0x9e /* 10011110   */
#define REG_2D_SVIDEO	0xbf /* 10111111   */
#define REG_2D_YUV_V 	0xcf /* reg 2D = 11001111, all DAC's on, RGB + VBS */
#define REG_2D_YUV_C	0x8f /* reg 2D = 10001111, all DAC's on, RGB + CVBS */

static int saa7126_set_mode(struct i2c_client *client, int inp)
{
	struct saa7126 *encoder = (struct saa7126 *) client->data;
	encoder->reg_3a = 0x03;		/* by default switch YUV to RGB-matrix on */

	// reg_2D: VBS-EN1 VBS-EN0 CVBS-EN C-EN		CVBS-TRI R-TRI G-TRI B-TRI
	
	switch (inp) {
	case SAA_MODE_RGB:
		encoder->reg_2d = REG_2D_RGB;
		break;
	case SAA_MODE_FBAS:
		encoder->reg_2d = REG_2D_FBAS;
		break;
	case SAA_MODE_SVIDEO:
	  	encoder->reg_2d = REG_2D_SVIDEO;
		break;
	case SAA_MODE_YUV_V:
		encoder->reg_2d = REG_2D_YUV_V;
		encoder->reg_3a = 0x0b; /* reg 3A = 00001011, bypass RGB-matrix */
		break;
	case SAA_MODE_YUV_C:
		encoder->reg_2d = REG_2D_YUV_C;
		encoder->reg_3a = 0x0b; /* reg 3A = 00001011, bypass RGB-matrix */
		break;
	default:
		return -EINVAL;
	}

	saa7126_writereg(client, 0x2d, encoder->reg_2d);
	saa7126_writereg(client, 0x3a, encoder->reg_3a);
	return 0;
}

static int saa7126_get_mode(struct i2c_client *client)
{
	struct saa7126 *encoder = (struct saa7126 *) client->data;

	switch (encoder->reg_2d) {
	case REG_2D_RGB:
		return SAA_MODE_RGB;
		break;
#if REG_2D_FBAS != REG_2D_SVIDEO 
	case REG_2D_FBAS:
		return SAA_MODE_FBAS;
		break;
#endif
	case REG_2D_SVIDEO:
		return SAA_MODE_SVIDEO;
		break;
	case REG_2D_YUV_V:
		return SAA_MODE_YUV_V;
		break;
	case REG_2D_YUV_C:
		return SAA_MODE_YUV_C;
		break;
	default:
		return -EINVAL;
	}
}

static int saa7126_set_norm(struct i2c_client *client, u8 pal)
{
	struct saa7126 *encoder = (struct saa7126 *) client->data;

	if (pal)
		encoder->norm = VIDEO_MODE_PAL;
	else
		encoder->norm = VIDEO_MODE_NTSC;

	return saa7126_write_inittab(client,0);	/* not init */
}


static int saa7126_set_standby(struct i2c_client *client, u8 enable)
{
	struct saa7126 *encoder = (struct saa7126 *) client->data;

	if ((enable) && (!encoder->standby)) {

		/* write 2dh */
		saa7126_writereg(client, 0x2d, (encoder->reg_2d & 0xf0));
		/* write 61h */
		saa7126_writereg(client, 0x61, (encoder->reg_61 | 0xc0));

		encoder->standby = 1;

	}

	else if ((!enable) && (encoder->standby)) {

		/* write 2dh */
		saa7126_writereg(client, 0x2d, encoder->reg_2d);
		/* write 61h */
		saa7126_writereg(client, 0x61, encoder->reg_61);

		encoder->standby = 0;
	}

	else {
		return -EINVAL;
	}

	return 0;
}



/* -------------------------------------------------------------------------
 * the functional interface to the i2c busses
 * -------------------------------------------------------------------------
 */

static int saa7126_detect_client(struct i2c_adapter *adapter, int address,
		unsigned short flags, int kind)
{
	int ret;
	struct i2c_client *client;
	struct saa7126 *encoder;
	char devfs_name[10];

	dprintk("[%s]: %s\n", __FILE__, __FUNCTION__);

	client = kmalloc(sizeof(struct i2c_client) +
			sizeof(struct saa7126), GFP_KERNEL);

	if (!client)
		return -ENOMEM;

	sprintf(devfs_name, "dbox/saa%d", saa7126_id);

	client->data = client + 1;
	encoder = (struct saa7126 *) (client->data);
	memset(encoder, 0x00, sizeof(struct saa7126));
	
	client->addr = address;
	client->data = encoder;
	client->adapter = adapter;
	client->driver = &i2c_driver_saa7126;
	client->flags = 0;

#if 0
	if (kind < 0) {
		/* TODO: detect */
	}
#endif

	strcpy(client->name, "saa7126");
	client->id = saa7126_id++;

	encoder->i2c_client = client;
	encoder->enable = 1;

	if (ntsc)
		encoder->norm = VIDEO_MODE_NTSC;
	else
		encoder->norm = VIDEO_MODE_PAL;

	encoder->devfs_handle = devfs_register (NULL, devfs_name, DEVFS_FL_DEFAULT, 0, 0,
			S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH,
			&saa7126_fops, encoder);

	if (!encoder->devfs_handle) {
		kfree(client);
		return -EIO;
	}

	if ((ret = i2c_attach_client(client))) {
		kfree(client);
		return ret;
	}

	dprintk("[%s]: chip found @ 0x%x\n", __FILE__,  client->addr);

	saa7126_write_inittab(client, 1); /* init */
	/* set video mode */
	saa7126_set_mode(client, mode);

	return 0;
}

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

static int saa7126_attach(struct i2c_adapter *adapter)
{
#ifdef MODULE
	MOD_INC_USE_COUNT;
#endif

	dprintk("[%s]: %s\n", __FILE__, __FUNCTION__);

	return i2c_probe(adapter, &addr_data, saa7126_detect_client);
}

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

static int saa7126_detach(struct i2c_client *client)
{
	int ret;
	struct saa7126 *encoder = (struct saa7126 *) client->data;

	dprintk("[%s]: %s\n", __FILE__, __FUNCTION__);

	devfs_unregister(encoder->devfs_handle);
	saa7126_id--;

	saa7126_set_standby(client, 1);

	if ((ret = i2c_detach_client(client))) {
		printk("[%s]: i2c_detach_client failed\n", __FILE__);
		return ret;
	}

	kfree(client);
	return 0;
}

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

static int saa7126_command(struct i2c_client *client, unsigned int cmd, void *parg)
{
	struct saa7126 *encoder = client->data;
	unsigned long arg = (unsigned long) parg;

	dprintk("[%s]: %s\n", __FILE__, __FUNCTION__);

	switch (cmd) {
	case ENCODER_GET_CAPABILITIES:{
		struct video_encoder_capability *cap = parg;
		cap->flags = VIDEO_ENCODER_PAL | VIDEO_ENCODER_NTSC;
		cap->inputs  = 1;
		cap->outputs = 1;
		break;
	}

	case ENCODER_SET_NORM:
		switch (arg) {
		case VIDEO_MODE_NTSC:
			saa7126_set_norm(client, 0);
		        saa7126_writereg(client, 0x2d, encoder->reg_2d);
        		saa7126_writereg(client, 0x3a, encoder->reg_3a);
			break;
		case VIDEO_MODE_PAL:
			saa7126_set_norm(client, 1);
		        saa7126_writereg(client, 0x2d, encoder->reg_2d);
        		saa7126_writereg(client, 0x3a, encoder->reg_3a);
			break;
		default:
			return -EINVAL;
		}
		break;

	case ENCODER_SET_INPUT:
		switch (arg) {
		case 0: /* avia */
			saa7126_writereg(client, 0x3a, 0x02);
			break;
		case 1: /* color bar */
			saa7126_writereg(client, 0x3a, 0x80);
			break;
		default:
			return -EINVAL;
		}
		break;

	case ENCODER_SET_OUTPUT:
		/* not much choice of outputs */
		if (arg != 0)
			return -EINVAL;
		break;

	case ENCODER_ENABLE_OUTPUT:
		encoder->enable = !!arg;
		break;
	default:
		return -EINVAL;
	}

	return 0;
}

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

static int saa7126_input_control(struct i2c_client *client, u8 inp)
{
	return saa7126_writereg(client, 0x3a, inp & 0x9f);
}

static int saa7126_output_control(struct i2c_client *client, u8 inp)
{
	return saa7126_writereg(client, 0x2d, inp);
}

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

static int saa7126_vps_set_data(struct i2c_client *client, char *buf)
{
	u8 reg_54;

	/* read vps status */
	reg_54 = saa7126_readreg(client, 0x54);

	if (buf[0] == 1) {
		reg_54 |= 0x80;
		i2c_master_send(client, buf, 5);
	}

	else if (buf[1] == 0) {
		reg_54 &= ~0x80;
	}

	else {
		return -EINVAL;
	}

	/* write vps status */
	saa7126_writereg(client, 0x54, reg_54);

	return 0;
}

static int saa7126_vps_get_data(struct i2c_client *client, char *buf)
{
	u8 reg_54;

	/* read vps status */
	reg_54 = saa7126_readreg(client, 0x54);

	if (reg_54 & 0x80)
		buf[0] = 1;
	else
		buf[0] = 0;

	/* read vps date */
	saa7126_readbuf(client, 0x54, buf + 1, 5);

	return 0;
}

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

static int saa7126_wss_get(struct i2c_client *client)
{
	if (saa7126_readreg(client, 0x27) & 0x80) {

		u8 i;
		u8 reg_26 = saa7126_readreg(client, 0x26) & 0x0f;

		for (i = 0; i < 8; i++)
			if (wss_data[i] == reg_26)
				return i;
	}

	return SAA_WSS_OFF;
}

static int saa7126_wss_set(struct i2c_client *client, int i)
{
	if (i == SAA_WSS_OFF) {
		saa7126_writereg(client, 0x27, 0x00);
		return 0;
	}

	if (i > 7)
		return -EINVAL;

	saa7126_writereg(client, 0x26, wss_data[i]);
	saa7126_writereg(client, 0x27, 0x80); // no WSS (c) bit set by default!

	return 0;
}

static int saa7126_csync_set(struct i2c_client *client, u8 val)
{
	u8 reg;
	if (val == 0) {
		reg = saa7126_readreg(client, 0x75);
		reg &= 0x07; /* Clear CSYNCA Bits */
		saa7126_writereg(client, 0x75, reg);
		reg = saa7126_readreg(client, 0x3A);
		reg &= ~0x04; /* Clear CSYNC Bit */
		saa7126_writereg(client, 0x3A, reg);
	}
	else if(val<32) {
		reg = saa7126_readreg(client, 0x75);
		reg = (reg & 0x07) | (val<<3); /* Set CSYNCA Bits */
		saa7126_writereg(client, 0x75, reg);
		reg = saa7126_readreg(client, 0x3A);
		reg |= 0x04; /* Set CSYNC Bit */
		saa7126_writereg(client, 0x3A, reg);
		return 0;
	}
	else
		return -EINVAL;

	return 0;
}

static int saa7126_csync_get(struct i2c_client *client)
{
	u8 reg;
	reg = saa7126_readreg(client, 0x3A);
	reg &= 0x04; /* GET CSYNC Bit */
	if (reg){
		reg = (saa7126_readreg(client, 0x75) | ~0x07) >> 3;
		return reg;
	} else
		return 0;
}

static int saa7126_ttx_set(struct i2c_client *client, u8 enable)
{
	u8 reg;
	reg = saa7126_readreg(client, 0x6f);

	if (enable)
		reg |= 0x20;
	else
		reg &= ~0x20;

	saa7126_writereg(client, 0x6f, reg);
	return 0;		
}

static int saa7126_ttx_get(struct i2c_client *client)
{
	return !!(saa7126_readreg(client, 0x6f)&0x20);
}

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

static int saa7126_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
	struct saa7126 *encoder = (struct saa7126 *) file->private_data;
	struct i2c_client *client = (struct i2c_client *) encoder->i2c_client;

	char saa_data[SAA_DATA_SIZE];
	void *parg = (void *) arg;
	int val;

	dprintk("[%s]: %s\n", __FILE__, __FUNCTION__);

	/* no ioctl in power save mode */
	if ((encoder->standby) && (cmd != SAAIOSPOWERSAVE) && (cmd != SAAIOGPOWERSAVE))
		return -EINVAL;

	switch (cmd) {
	case SAAIOGREG:
		saa7126_readbuf(client, 0x00, saa_data, sizeof(saa_data));
		return copy_to_user((void *) arg, saa_data, sizeof(saa_data));

	case SAAIOSINP:
		if (copy_from_user(&val, parg, sizeof(val)))
			return -EFAULT;

		return saa7126_input_control(client, val);

	case SAAIOSOUT:
		if (copy_from_user(&val, parg, sizeof(val)))
			return -EFAULT;

		return saa7126_output_control(client, val);

	case SAAIOSMODE:
		if (copy_from_user(&val, parg, sizeof(val)))
			return -EFAULT;

		return saa7126_set_mode(client, val);

	case SAAIOGMODE:
		val = saa7126_get_mode(client);
		return (copy_to_user(parg, &val, sizeof(val)));
		
	case SAAIOSENC:
		if (copy_from_user(&val, parg, sizeof(val)))
			return -EFAULT;

		return saa7126_set_norm(client, val);

	case SAAIOSPOWERSAVE:
		if (copy_from_user(&val, parg, sizeof(val)))
			return -EFAULT;

		return saa7126_set_standby(client, val);

	case SAAIOGPOWERSAVE:
		return copy_to_user(parg, &encoder->standby, sizeof(encoder->standby));

	case SAAIOSVPSDATA:
		if (copy_from_user(saa_data, parg, 6))
			return -EFAULT;

		return saa7126_vps_set_data(client, saa_data);

	case SAAIOGVPSDATA:
		saa7126_vps_get_data(client, saa_data);
		return copy_to_user(parg, saa_data, 6);

	case SAAIOSWSS:
		if (copy_from_user(&val, parg,sizeof(val)))
			return -EFAULT;

		return saa7126_wss_set(client, val);

	case SAAIOGWSS:
		val = saa7126_wss_get(client);

		if (val < 0)
			return val;

		return copy_to_user(parg, &val, sizeof(val));

	case SAA_READREG:
		if (copy_from_user(&val, parg, sizeof(val)))
			return -EFAULT;

		val = saa7126_readreg(client, val);

		return (copy_to_user(parg, &val, sizeof(val)));

	case SAA_WRITEREG:
		if (copy_from_user(&val, parg, sizeof(val)))
			return -EFAULT;
		return saa7126_writereg(client, (val >> 8) & 0xff, val & 0xff);

	case SAAIOSCSYNC:
		if (copy_from_user(&val, parg, sizeof(val)))
				return -EFAULT;
		return saa7126_csync_set(client, val);

	case SAAIOGCSYNC:
		val = saa7126_csync_get(client);
		return (copy_to_user(parg, &val, sizeof(val)));
	
	case SAAIOSTTX:
		if (copy_from_user(&val, parg, sizeof(val)))
			return -EFAULT;
		return saa7126_ttx_set(client,val);

	case SAAIOGTTX:
		val = saa7126_ttx_get(client);
		return (copy_to_user(parg, &val, sizeof(val)));
		
	 default:
		return -EINVAL;
	}

	return 0;
}

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

static int saa7126_open (struct inode *inode, struct file *file)
{
	return 0;
}

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

static struct i2c_driver i2c_driver_saa7126 = {
	.name           = "saa7126",
	.id             = I2C_DRIVERID_SAA7126,
	.flags          = I2C_DF_NOTIFY,
	.attach_adapter = &saa7126_attach,
	.detach_client  = &saa7126_detach,
	.command        = &saa7126_command
};

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

int __init saa7126_init(void)
{
	i2c_add_driver(&i2c_driver_saa7126);

	return 0;
}

void __exit saa7126_exit(void)
{
	i2c_del_driver(&i2c_driver_saa7126);
}

module_init(saa7126_init);
module_exit(saa7126_exit);
MODULE_DESCRIPTION("SAA7126 digital PAL/NTSC encoder");
MODULE_AUTHOR("Gillem <htoa@gmx.net>, Andreas Oberritter <obi@saftware.de>");
MODULE_LICENSE("GPL");
MODULE_PARM(mode,"i");
MODULE_PARM(ntsc,"i");

cvs-admin email: cvs-root at cvs dot tuxbox dot org

Powered by ViewCVS 1.0-dev
(Powered by ViewCVS)

ViewCVS and CVS Help