diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2005-09-06 15:19:37 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2005-09-07 16:58:01 -0700 |
commit | d203a7eca85101f0a8ce1dc139ee91196142da36 (patch) | |
tree | 83c7d467ef3b6e3e9a2b9ad56ee4f5b2f8d76aa5 | |
parent | 0bb6fcc13ae4fad98e0d610458975e47be0d2203 (diff) |
[PATCH] Indycam / VINO drivers
Rewrite of the Indycam / VINO video v4l2 drivers for the SGI Indy.
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Signed-off-by: Mikael Nousiainen <tmnousia@cc.hut.fi>
Cc: <video4linux-list@redhat.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r-- | drivers/media/video/Makefile | 2 | ||||
-rw-r--r-- | drivers/media/video/indycam.c | 412 | ||||
-rw-r--r-- | drivers/media/video/indycam.h | 112 | ||||
-rw-r--r-- | drivers/media/video/saa7191.c | 512 | ||||
-rw-r--r-- | drivers/media/video/saa7191.h | 139 | ||||
-rw-r--r-- | drivers/media/video/vino.c | 4273 | ||||
-rw-r--r-- | drivers/media/video/vino.h | 61 |
7 files changed, 5319 insertions, 192 deletions
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 810e7aac0a5..3e6f5347da2 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -29,7 +29,7 @@ obj-$(CONFIG_VIDEO_ZORAN_LML33R10) += saa7114.o adv7170.o zr36060.o obj-$(CONFIG_VIDEO_ZORAN) += zr36067.o videocodec.o obj-$(CONFIG_VIDEO_PMS) += pms.o obj-$(CONFIG_VIDEO_PLANB) += planb.o -obj-$(CONFIG_VIDEO_VINO) += vino.o +obj-$(CONFIG_VIDEO_VINO) += vino.o saa7191.o indycam.o obj-$(CONFIG_VIDEO_STRADIS) += stradis.o obj-$(CONFIG_VIDEO_CPIA) += cpia.o obj-$(CONFIG_VIDEO_CPIA_PP) += cpia_pp.o diff --git a/drivers/media/video/indycam.c b/drivers/media/video/indycam.c new file mode 100644 index 00000000000..b2b0384cd4b --- /dev/null +++ b/drivers/media/video/indycam.c @@ -0,0 +1,412 @@ +/* + * indycam.c - Silicon Graphics IndyCam digital camera driver + * + * Copyright (C) 2003 Ladislav Michl <ladis@linux-mips.org> + * Copyright (C) 2004,2005 Mikael Nousiainen <tmnousia@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/kernel.h> +#include <linux/major.h> +#include <linux/slab.h> +#include <linux/mm.h> +#include <linux/sched.h> + +#include <linux/videodev.h> +/* IndyCam decodes stream of photons into digital image representation ;-) */ +#include <linux/video_decoder.h> +#include <linux/i2c.h> + +#include "indycam.h" + +//#define INDYCAM_DEBUG + +#define INDYCAM_MODULE_VERSION "0.0.3" + +MODULE_DESCRIPTION("SGI IndyCam driver"); +MODULE_VERSION(INDYCAM_MODULE_VERSION); +MODULE_AUTHOR("Mikael Nousiainen <tmnousia@cc.hut.fi>"); +MODULE_LICENSE("GPL"); + +#ifdef INDYCAM_DEBUG +#define dprintk(x...) printk("IndyCam: " x); +#define indycam_regdump(client) indycam_regdump_debug(client) +#else +#define dprintk(x...) +#define indycam_regdump(client) +#endif + +#define VINO_ADAPTER (I2C_ALGO_SGI | I2C_HW_SGI_VINO) + +struct indycam { + struct i2c_client *client; + int version; +}; + +static struct i2c_driver i2c_driver_indycam; + +static const unsigned char initseq[] = { + INDYCAM_CONTROL_AGCENA, /* INDYCAM_CONTROL */ + INDYCAM_SHUTTER_DEFAULT, /* INDYCAM_SHUTTER */ + INDYCAM_GAIN_DEFAULT, /* INDYCAM_GAIN */ + 0x00, /* INDYCAM_BRIGHTNESS (read-only) */ + INDYCAM_RED_BALANCE_DEFAULT, /* INDYCAM_RED_BALANCE */ + INDYCAM_BLUE_BALANCE_DEFAULT, /* INDYCAM_BLUE_BALANCE */ + INDYCAM_RED_SATURATION_DEFAULT, /* INDYCAM_RED_SATURATION */ + INDYCAM_BLUE_SATURATION_DEFAULT,/* INDYCAM_BLUE_SATURATION */ +}; + +/* IndyCam register handling */ + +static int indycam_read_reg(struct i2c_client *client, unsigned char reg, + unsigned char *value) +{ + int ret; + + if (reg == INDYCAM_RESET) { + dprintk("indycam_read_reg(): " + "skipping write-only register %d\n", reg); + *value = 0; + return 0; + } + + ret = i2c_smbus_read_byte_data(client, reg); + if (ret < 0) { + printk(KERN_ERR "IndyCam: indycam_read_reg(): read failed, " + "register = 0x%02x\n", reg); + return ret; + } + + *value = (unsigned char)ret; + + return 0; +} + +static int indycam_write_reg(struct i2c_client *client, unsigned char reg, + unsigned char value) +{ + int err; + + if ((reg == INDYCAM_BRIGHTNESS) + || (reg == INDYCAM_VERSION)) { + dprintk("indycam_write_reg(): " + "skipping read-only register %d\n", reg); + return 0; + } + + dprintk("Writing Reg %d = 0x%02x\n", reg, value); + err = i2c_smbus_write_byte_data(client, reg, value); + if (err) { + printk(KERN_ERR "IndyCam: indycam_write_reg(): write failed, " + "register = 0x%02x, value = 0x%02x\n", reg, value); + } + return err; +} + +static int indycam_write_block(struct i2c_client *client, unsigned char reg, + unsigned char length, unsigned char *data) +{ + unsigned char i; + int err; + + for (i = reg; i < length; i++) { + err = indycam_write_reg(client, reg + i, data[i]); + if (err) + return err; + } + + return 0; +} + +/* Helper functions */ + +#ifdef INDYCAM_DEBUG +static void indycam_regdump_debug(struct i2c_client *client) +{ + int i; + unsigned char val; + + for (i = 0; i < 9; i++) { + indycam_read_reg(client, i, &val); + dprintk("Reg %d = 0x%02x\n", i, val); + } +} +#endif + +static int indycam_get_controls(struct i2c_client *client, + struct indycam_control *ctrl) +{ + unsigned char ctrl_reg; + + indycam_read_reg(client, INDYCAM_CONTROL, &ctrl_reg); + ctrl->agc = (ctrl_reg & INDYCAM_CONTROL_AGCENA) + ? INDYCAM_VALUE_ENABLED + : INDYCAM_VALUE_DISABLED; + ctrl->awb = (ctrl_reg & INDYCAM_CONTROL_AWBCTL) + ? INDYCAM_VALUE_ENABLED + : INDYCAM_VALUE_DISABLED; + indycam_read_reg(client, INDYCAM_SHUTTER, + (unsigned char *)&ctrl->shutter); + indycam_read_reg(client, INDYCAM_GAIN, + (unsigned char *)&ctrl->gain); + indycam_read_reg(client, INDYCAM_RED_BALANCE, + (unsigned char *)&ctrl->red_balance); + indycam_read_reg(client, INDYCAM_BLUE_BALANCE, + (unsigned char *)&ctrl->blue_balance); + indycam_read_reg(client, INDYCAM_RED_SATURATION, + (unsigned char *)&ctrl->red_saturation); + indycam_read_reg(client, INDYCAM_BLUE_SATURATION, + (unsigned char *)&ctrl->blue_saturation); + indycam_read_reg(client, INDYCAM_GAMMA, + (unsigned char *)&ctrl->gamma); + + return 0; +} + +static int indycam_set_controls(struct i2c_client *client, + struct indycam_control *ctrl) +{ + unsigned char ctrl_reg; + + indycam_read_reg(client, INDYCAM_CONTROL, &ctrl_reg); + if (ctrl->agc != INDYCAM_VALUE_UNCHANGED) { + if (ctrl->agc) + ctrl_reg |= INDYCAM_CONTROL_AGCENA; + else + ctrl_reg &= ~INDYCAM_CONTROL_AGCENA; + } + if (ctrl->awb != INDYCAM_VALUE_UNCHANGED) { + if (ctrl->awb) + ctrl_reg |= INDYCAM_CONTROL_AWBCTL; + else + ctrl_reg &= ~INDYCAM_CONTROL_AWBCTL; + } + indycam_write_reg(client, INDYCAM_CONTROL, ctrl_reg); + + if (ctrl->shutter >= 0) + indycam_write_reg(client, INDYCAM_SHUTTER, ctrl->shutter); + if (ctrl->gain >= 0) + indycam_write_reg(client, INDYCAM_GAIN, ctrl->gain); + if (ctrl->red_balance >= 0) + indycam_write_reg(client, INDYCAM_RED_BALANCE, + ctrl->red_balance); + if (ctrl->blue_balance >= 0) + indycam_write_reg(client, INDYCAM_BLUE_BALANCE, + ctrl->blue_balance); + if (ctrl->red_saturation >= 0) + indycam_write_reg(client, INDYCAM_RED_SATURATION, + ctrl->red_saturation); + if (ctrl->blue_saturation >= 0) + indycam_write_reg(client, INDYCAM_BLUE_SATURATION, + ctrl->blue_saturation); + if (ctrl->gamma >= 0) + indycam_write_reg(client, INDYCAM_GAMMA, ctrl->gamma); + + return 0; +} + +/* I2C-interface */ + +static int indycam_attach(struct i2c_adapter *adap, int addr, int kind) +{ + int err = 0; + struct indycam *camera; + struct i2c_client *client; + + printk(KERN_INFO "SGI IndyCam driver version %s\n", + INDYCAM_MODULE_VERSION); + + client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL); + if (!client) + return -ENOMEM; + camera = kmalloc(sizeof(struct indycam), GFP_KERNEL); + if (!camera) { + err = -ENOMEM; + goto out_free_client; + } + + memset(client, 0, sizeof(struct i2c_client)); + memset(camera, 0, sizeof(struct indycam)); + + client->addr = addr; + client->adapter = adap; + client->driver = &i2c_driver_indycam; + client->flags = 0; + strcpy(client->name, "IndyCam client"); + i2c_set_clientdata(client, camera); + + camera->client = client; + + err = i2c_attach_client(client); + if (err) + goto out_free_camera; + + camera->version = i2c_smbus_read_byte_data(client, INDYCAM_VERSION); + if (camera->version != CAMERA_VERSION_INDY && + camera->version != CAMERA_VERSION_MOOSE) { + err = -ENODEV; + goto out_detach_client; + } + printk(KERN_INFO "IndyCam v%d.%d detected\n", + INDYCAM_VERSION_MAJOR(camera->version), + INDYCAM_VERSION_MINOR(camera->version)); + + indycam_regdump(client); + + // initialize + err = indycam_write_block(client, 0, sizeof(initseq), + (unsigned char *)&initseq); + if (err) { + printk(KERN_ERR "IndyCam initalization failed\n"); + err = -EIO; + goto out_detach_client; + } + + indycam_regdump(client); + + // white balance + err = indycam_write_reg(client, INDYCAM_CONTROL, + INDYCAM_CONTROL_AGCENA | INDYCAM_CONTROL_AWBCTL); + if (err) { + printk(KERN_ERR "IndyCam white balance " + "initialization failed\n"); + err = -EIO; + goto out_detach_client; + } + + indycam_regdump(client); + + printk(KERN_INFO "IndyCam initialized\n"); + + return 0; + +out_detach_client: + i2c_detach_client(client); +out_free_camera: + kfree(camera); +out_free_client: + kfree(client); + return err; +} + +static int indycam_probe(struct i2c_adapter *adap) +{ + /* Indy specific crap */ + if (adap->id == VINO_ADAPTER) + return indycam_attach(adap, INDYCAM_ADDR, 0); + /* Feel free to add probe here :-) */ + return -ENODEV; +} + +static int indycam_detach(struct i2c_client *client) +{ + struct indycam *camera = i2c_get_clientdata(client); + + i2c_detach_client(client); + kfree(camera); + kfree(client); + return 0; +} + +static int indycam_command(struct i2c_client *client, unsigned int cmd, + void *arg) +{ + // struct indycam *camera = i2c_get_clientdata(client); + + /* The old video_decoder interface just isn't enough, + * so we'll use some custom commands. */ + switch (cmd) { + case DECODER_GET_CAPABILITIES: { + struct video_decoder_capability *cap = arg; + + cap->flags = VIDEO_DECODER_NTSC; + cap->inputs = 1; + cap->outputs = 1; + break; + } + case DECODER_GET_STATUS: { + int *iarg = arg; + + *iarg = DECODER_STATUS_GOOD | DECODER_STATUS_NTSC | + DECODER_STATUS_COLOR; + break; + } + case DECODER_SET_NORM: { + int *iarg = arg; + + switch (*iarg) { + case VIDEO_MODE_NTSC: + break; + default: + return -EINVAL; + } + break; + } + case DECODER_SET_INPUT: { + int *iarg = arg; + + if (*iarg != 0) + return -EINVAL; + break; + } + case DECODER_SET_OUTPUT: { + int *iarg = arg; + + if (*iarg != 0) + return -EINVAL; + break; + } + case DECODER_ENABLE_OUTPUT: { + /* Always enabled */ + break; + } + case DECODER_SET_PICTURE: { + // struct video_picture *pic = arg; + /* TODO: convert values for indycam_set_controls() */ + break; + } + case DECODER_INDYCAM_GET_CONTROLS: { + struct indycam_control *ctrl = arg; + indycam_get_controls(client, ctrl); + } + case DECODER_INDYCAM_SET_CONTROLS: { + struct indycam_control *ctrl = arg; + indycam_set_controls(client, ctrl); + } + default: + return -EINVAL; + } + + return 0; +} + +static struct i2c_driver i2c_driver_indycam = { + .owner = THIS_MODULE, + .name = "indycam", + .id = I2C_DRIVERID_INDYCAM, + .flags = I2C_DF_NOTIFY, + .attach_adapter = indycam_probe, + .detach_client = indycam_detach, + .command = indycam_command, +}; + +static int __init indycam_init(void) +{ + return i2c_add_driver(&i2c_driver_indycam); +} + +static void __exit indycam_exit(void) +{ + i2c_del_driver(&i2c_driver_indycam); +} + +module_init(indycam_init); +module_exit(indycam_exit); diff --git a/drivers/media/video/indycam.h b/drivers/media/video/indycam.h new file mode 100644 index 00000000000..d9ddb6b79a0 --- /dev/null +++ b/drivers/media/video/indycam.h @@ -0,0 +1,112 @@ +/* + * indycam.h - Silicon Graphics IndyCam digital camera driver + * + * Copyright (C) 2003 Ladislav Michl <ladis@linux-mips.org> + * Copyright (C) 2004,2005 Mikael Nousiainen <tmnousia@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _INDYCAM_H_ +#define _INDYCAM_H_ + +/* I2C address for the Guinness Camera */ +#define INDYCAM_ADDR 0x56 + +/* Camera version */ +#define CAMERA_VERSION_INDY 0x10 /* v1.0 */ +#define CAMERA_VERSION_MOOSE 0x12 /* v1.2 */ +#define INDYCAM_VERSION_MAJOR(x) (((x) & 0xf0) >> 4) +#define INDYCAM_VERSION_MINOR(x) ((x) & 0x0f) + +/* Register bus addresses */ +#define INDYCAM_CONTROL 0x00 +#define INDYCAM_SHUTTER 0x01 +#define INDYCAM_GAIN 0x02 +#define INDYCAM_BRIGHTNESS 0x03 /* read-only */ +#define INDYCAM_RED_BALANCE 0x04 +#define INDYCAM_BLUE_BALANCE 0x05 +#define INDYCAM_RED_SATURATION 0x06 +#define INDYCAM_BLUE_SATURATION 0x07 +#define INDYCAM_GAMMA 0x08 +#define INDYCAM_VERSION 0x0e /* read-only */ +#define INDYCAM_RESET 0x0f /* write-only */ + +#define INDYCAM_LED 0x46 +#define INDYCAM_ORIENTATION 0x47 +#define INDYCAM_BUTTON 0x48 + +/* Field definitions of registers */ +#define INDYCAM_CONTROL_AGCENA (1<<0) /* automatic gain control */ +#define INDYCAM_CONTROL_AWBCTL (1<<1) /* automatic white balance */ + /* 2-3 are reserved */ +#define INDYCAM_CONTROL_EVNFLD (1<<4) /* read-only */ + +#define INDYCAM_SHUTTER_10000 0x02 /* 1/10000 second */ +#define INDYCAM_SHUTTER_4000 0x04 /* 1/4000 second */ +#define INDYCAM_SHUTTER_2000 0x08 /* 1/2000 second */ +#define INDYCAM_SHUTTER_1000 0x10 /* 1/1000 second */ +#define INDYCAM_SHUTTER_500 0x20 /* 1/500 second */ +#define INDYCAM_SHUTTER_250 0x3f /* 1/250 second */ +#define INDYCAM_SHUTTER_125 0x7e /* 1/125 second */ +#define INDYCAM_SHUTTER_100 0x9e /* 1/100 second */ +#define INDYCAM_SHUTTER_60 0x00 /* 1/60 second */ + +#define INDYCAM_LED_ACTIVE 0x10 +#define INDYCAM_LED_INACTIVE 0x30 +#define INDYCAM_ORIENTATION_BOTTOM_TO_TOP 0x40 +#define INDYCAM_BUTTON_RELEASED 0x10 + +#define INDYCAM_SHUTTER_MIN 0x00 +#define INDYCAM_SHUTTER_MAX 0xff +#define INDYCAM_GAIN_MIN 0x00 +#define INDYCAM_GAIN_MAX 0xff +#define INDYCAM_RED_BALANCE_MIN 0x00 /* the effect is the opposite? */ +#define INDYCAM_RED_BALANCE_MAX 0xff +#define INDYCAM_BLUE_BALANCE_MIN 0x00 /* the effect is the opposite? */ +#define INDYCAM_BLUE_BALANCE_MAX 0xff +#define INDYCAM_RED_SATURATION_MIN 0x00 +#define INDYCAM_RED_SATURATION_MAX 0xff +#define INDYCAM_BLUE_SATURATION_MIN 0x00 +#define INDYCAM_BLUE_SATURATION_MAX 0xff +#define INDYCAM_GAMMA_MIN 0x00 +#define INDYCAM_GAMMA_MAX 0xff + +/* Driver interface definitions */ + +#define INDYCAM_VALUE_ENABLED 1 +#define INDYCAM_VALUE_DISABLED 0 +#define INDYCAM_VALUE_UNCHANGED -1 + +/* When setting controls, a value of -1 leaves the control unchanged. */ +struct indycam_control { + int agc; /* boolean */ + int awb; /* boolean */ + int shutter; + int gain; + int red_balance; + int blue_balance; + int red_saturation; + int blue_saturation; + int gamma; +}; + +#define DECODER_INDYCAM_GET_CONTROLS _IOR('d', 193, struct indycam_control) +#define DECODER_INDYCAM_SET_CONTROLS _IOW('d', 194, struct indycam_control) + +/* Default values for controls */ + +#define INDYCAM_AGC_DEFAULT INDYCAM_VALUE_ENABLED +#define INDYCAM_AWB_DEFAULT INDYCAM_VALUE_ENABLED + +#define INDYCAM_SHUTTER_DEFAULT INDYCAM_SHUTTER_60 +#define INDYCAM_GAIN_DEFAULT 0x80 +#define INDYCAM_RED_BALANCE_DEFAULT 0x18 +#define INDYCAM_BLUE_BALANCE_DEFAULT 0xa4 +#define INDYCAM_RED_SATURATION_DEFAULT 0x80 +#define INDYCAM_BLUE_SATURATION_DEFAULT 0xc0 +#define INDYCAM_GAMMA_DEFAULT 0x80 + +#endif diff --git a/drivers/media/video/saa7191.c b/drivers/media/video/saa7191.c new file mode 100644 index 00000000000..454f5c1199b --- /dev/null +++ b/drivers/media/video/saa7191.c @@ -0,0 +1,512 @@ +/* + * saa7191.c - Philips SAA7191 video decoder driver + * + * Copyright (C) 2003 Ladislav Michl <ladis@linux-mips.org> + * Copyright (C) 2004,2005 Mikael Nousiainen <tmnousia@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/kernel.h> +#include <linux/major.h> +#include <linux/slab.h> +#include <linux/mm.h> +#include <linux/sched.h> + +#include <linux/videodev.h> +#include <linux/video_decoder.h> +#include <linux/i2c.h> + +#include "saa7191.h" + +#define SAA7191_MODULE_VERSION "0.0.3" + +MODULE_DESCRIPTION("Philips SAA7191 video decoder driver"); +MODULE_VERSION(SAA7191_MODULE_VERSION); +MODULE_AUTHOR("Mikael Nousiainen <tmnousia@cc.hut.fi>"); +MODULE_LICENSE("GPL"); + +#define VINO_ADAPTER (I2C_ALGO_SGI | I2C_HW_SGI_VINO) + +struct saa7191 { + struct i2c_client *client; + + /* the register values are stored here as the actual + * I2C-registers are write-only */ + unsigned char reg[25]; + + unsigned char norm; + unsigned char input; +}; + +static struct i2c_driver i2c_driver_saa7191; + +static const unsigned char initseq[] = { + 0, /* Subaddress */ + 0x50, /* SAA7191_REG_IDEL */ + 0x30, /* SAA7191_REG_HSYB */ + 0x00, /* SAA7191_REG_HSYS */ + 0xe8, /* SAA7191_REG_HCLB */ + 0xb6, /* SAA7191_REG_HCLS */ + 0xf4, /* SAA7191_REG_HPHI */ + 0x01, /* SAA7191_REG_LUMA - chrominance trap active (CVBS) */ + 0x00, /* SAA7191_REG_HUEC */ + 0xf8, /* SAA7191_REG_CKTQ */ + 0xf8, /* SAA7191_REG_CKTS */ + 0x90, /* SAA7191_REG_PLSE */ + 0x90, /* SAA7191_REG_SESE */ + 0x00, /* SAA7191_REG_GAIN */ + 0x0c, /* SAA7191_REG_STDC - not SECAM, slow time constant */ + 0x78, /* SAA7191_REG_IOCK - chrominance from CVBS, GPSW1 & 2 off */ + 0x99, /* SAA7191_REG_CTL3 - automatic field detection */ + 0x00, /* SAA7191_REG_CTL4 */ + 0x2c, /* SAA7191_REG_CHCV */ + 0x00, /* unused */ + 0x00, /* unused */ + 0x34, /* SAA7191_REG_HS6B */ + 0x0a, /* SAA7191_REG_HS6S */ + 0xf4, /* SAA7191_REG_HC6B */ + 0xce, /* SAA7191_REG_HC6S */ + 0xf4, /* SAA7191_REG_HP6I */ +}; + +/* SAA7191 register handling */ + +static unsigned char saa7191_read_reg(struct i2c_client *client, + unsigned char reg) +{ + return ((struct saa7191 *)i2c_get_clientdata(client))->reg[reg]; +} + +static int saa7191_read_status(struct i2c_client *client, + unsigned char *value) +{ + int ret; + + ret = i2c_master_recv(client, value, 1); + if (ret < 0) { + printk(KERN_ERR "SAA7191: saa7191_read_status(): read failed"); + return ret; + } + + return 0; +} + + +static int saa7191_write_reg(struct i2c_client *client, unsigned char reg, + unsigned char value) +{ + + ((struct saa7191 *)i2c_get_clientdata(client))->reg[reg] = value; + return i2c_smbus_write_byte_data(client, reg, value); +} + +/* the first byte of data must be the first subaddress number (register) */ +static int saa7191_write_block(struct i2c_client *client, + unsigned char length, unsigned char *data) +{ + int i; + int ret; + + struct saa7191 *decoder = (struct saa7191 *)i2c_get_clientdata(client); + for (i = 0; i < (length - 1); i++) { + decoder->reg[data[0] + i] = data[i + 1]; + } + + ret = i2c_master_send(client, data, length); + if (ret < 0) { + printk(KERN_ERR "SAA7191: saa7191_write_block(): " + "write failed"); + return ret; + } + + return 0; +} + +/* Helper functions */ + +static int saa7191_set_input(struct i2c_client *client, int input) +{ + unsigned char luma = saa7191_read_reg(client, SAA7191_REG_LUMA); + unsigned char iock = saa7191_read_reg(client, SAA7191_REG_IOCK); + int err; + + switch (input) { + case SAA7191_INPUT_COMPOSITE: /* Set Composite input */ + iock &= ~(SAA7191_IOCK_CHRS | SAA7191_IOCK_GPSW1 + | SAA7191_IOCK_GPSW2); + /* Chrominance trap active */ + luma &= ~SAA7191_LUMA_BYPS; + break; + case SAA7191_INPUT_SVIDEO: /* Set S-Video input */ + iock |= SAA7191_IOCK_CHRS | SAA7191_IOCK_GPSW2; + /* Chrominance trap bypassed */ + luma |= SAA7191_LUMA_BYPS; + break; + default: + return -EINVAL; + } + + err = saa7191_write_reg(client, SAA7191_REG_LUMA, luma); + if (err) + return -EIO; + err = saa7191_write_reg(client, SAA7191_REG_IOCK, iock); + if (err) + return -EIO; + + return 0; +} + +static int saa7191_set_norm(struct i2c_client *client, int norm) +{ + struct saa7191 *decoder = i2c_get_clientdata(client); + unsigned char stdc = saa7191_read_reg(client, SAA7191_REG_STDC); + unsigned char ctl3 = saa7191_read_reg(client, SAA7191_REG_CTL3); + unsigned char chcv = saa7191_read_reg(client, SAA7191_REG_CHCV); + int err; + + switch(norm) { + case SAA7191_NORM_AUTO: { + unsigned char status; + + // does status depend on current norm ? + if (saa7191_read_status(client, &status)) + return -EIO; + + stdc &= ~SAA7191_STDC_SECS; + ctl3 &= ~SAA7191_CTL3_FSEL; + ctl3 |= SAA7191_CTL3_AUFD; + chcv = (status & SAA7191_STATUS_FIDT) + ? SAA7191_CHCV_NTSC : SAA7191_CHCV_PAL; + break; + } + case SAA7191_NORM_PAL: + stdc &= ~SAA7191_STDC_SECS; + ctl3 &= ~(SAA7191_CTL3_AUFD | SAA7191_CTL3_FSEL); + chcv = SAA7191_CHCV_PAL; + break; + case SAA7191_NORM_NTSC: + stdc &= ~SAA7191_STDC_SECS; + ctl3 &= ~SAA7191_CTL3_AUFD; + ctl3 |= SAA7191_CTL3_FSEL; + chcv = SAA7191_CHCV_NTSC; + break; + case SAA7191_NORM_SECAM: + stdc |= SAA7191_STDC_SECS; + ctl3 &= ~(SAA7191_CTL3_AUFD | SAA7191_CTL3_FSEL); + chcv = SAA7191_CHCV_PAL; + break; + default: + return -EINVAL; + } + + err = saa7191_write_reg(client, SAA7191_REG_CTL3, ctl3); + if (err) + return -EIO; + err = saa7191_write_reg(client, SAA7191_REG_STDC, stdc); + if (err) + return -EIO; + err = saa7191_write_reg(client, SAA7191_REG_CHCV, chcv); + if (err) + return -EIO; + + decoder->norm = norm; + + return 0; +} + +static int saa7191_get_controls(struct i2c_client *client, + struct saa7191_control *ctrl) +{ + unsigned char hue = saa7191_read_reg(client, SAA7191_REG_HUEC); + unsigned char stdc = saa7191_read_reg(client, SAA7191_REG_STDC); + + if (hue < 0x80) { + hue += 0x80; + } else { + hue -= 0x80; + } + ctrl->hue = hue; + + ctrl->vtrc = (stdc & SAA7191_STDC_VTRC) + ? SAA7191_VALUE_ENABLED : SAA7191_VALUE_DISABLED; + + return 0; +} + +static int saa7191_set_controls(struct i2c_client *client, + struct saa7191_control *ctrl) +{ + int err; + + if (ctrl->hue >= 0) { + unsigned char hue = ctrl->hue & 0xff; + if (hue < 0x80) { + hue += 0x80; + } else { + hue -= 0x80; + } + err = saa7191_write_reg(client, SAA7191_REG_HUEC, hue); + if (err) + return -EIO; + } + if (ctrl->vtrc >= 0) { + unsigned char stdc = + saa7191_read_reg(client, SAA7191_REG_STDC); + + if (ctrl->vtrc) { + stdc |= SAA7191_STDC_VTRC; + } else { + stdc &= ~SAA7191_STDC_VTRC; + } + + err = saa7191_write_reg(client, SAA7191_REG_STDC, stdc); + if (err) + return -EIO; + } + + return 0; +} + +/* I2C-interface */ + +static int saa7191_attach(struct i2c_adapter *adap, int addr, int kind) +{ + int err = 0; + struct saa7191 *decoder; + struct i2c_client *client; + + printk(KERN_INFO "Philips SAA7191 driver version %s\n", + SAA7191_MODULE_VERSION); + + client = kmalloc(sizeof(*client), GFP_KERNEL); + if (!client) + return -ENOMEM; + decoder = kmalloc(sizeof(*decoder), GFP_KERNEL); + if (!decoder) { + err = -ENOMEM; + goto out_free_client; + } + + memset(client, 0, sizeof(struct i2c_client)); + memset(decoder, 0, sizeof(struct saa7191)); + + client->addr = addr; + client->adapter = adap; + client->driver = &i2c_driver_saa7191; + client->flags = 0; + strcpy(client->name, "saa7191 client"); + i2c_set_clientdata(client, decoder); + + decoder->client = client; + + err = i2c_attach_client(client); + if (err) + goto out_free_decoder; + + decoder->input = SAA7191_INPUT_COMPOSITE; + decoder->norm = SAA7191_NORM_AUTO; + + err = saa7191_write_block(client, sizeof(initseq), + (unsigned char *)initseq); + if (err) { + printk(KERN_ERR "SAA7191 initialization failed\n"); + goto out_detach_client; + } + + printk(KERN_INFO "SAA7191 initialized\n"); + + return 0; + +out_detach_client: + i2c_detach_client(client); +out_free_decoder: + kfree(decoder); +out_free_client: + kfree(client); + return err; +} + +static int saa7191_probe(struct i2c_adapter *adap) +{ + /* Always connected to VINO */ + if (adap->id == VINO_ADAPTER) + return saa7191_attach(adap, SAA7191_ADDR, 0); + /* Feel free to add probe here :-) */ + return -ENODEV; +} + +static int saa7191_detach(struct i2c_client *client) +{ + struct saa7191 *decoder = i2c_get_clientdata(client); + + i2c_detach_client(client); + kfree(decoder); + kfree(client); + return 0; +} + +static int saa7191_command(struct i2c_client *client, unsigned int cmd, + void *arg) +{ + struct saa7191 *decoder = i2c_get_clientdata(client); + + switch (cmd) { + case DECODER_GET_CAPABILITIES: { + struct video_decoder_capability *cap = arg; + + cap->flags = VIDEO_DECODER_PAL | VIDEO_DECODER_NTSC | + VIDEO_DECODER_SECAM | VIDEO_DECODER_AUTO; + cap->inputs = (client->adapter->id == VINO_ADAPTER) ? 2 : 1; + cap->outputs = 1; + break; + } + case DECODER_GET_STATUS: { + int *iarg = arg; + unsigned char status; + int res = 0; + + if (saa7191_read_status(client, &status)) { + return -EIO; + } + if ((status & SAA7191_STATUS_HLCK) == 0) + res |= DECODER_STATUS_GOOD; + if (status & SAA7191_STATUS_CODE) + res |= DECODER_STATUS_COLOR; + switch (decoder->norm) { + case SAA7191_NORM_NTSC: + res |= DECODER_STATUS_NTSC; + break; + case SAA7191_NORM_PAL: + res |= DECODER_STATUS_PAL; + break; + case SAA7191_NORM_SECAM: + res |= DECODER_STATUS_SECAM; + break; + case SAA7191_NORM_AUTO: + default: + if (status & SAA7191_STATUS_FIDT) + res |= DECODER_STATUS_NTSC; + else + res |= DECODER_STATUS_PAL; + break; + } + *iarg = res; + break; + } + case DECODER_SET_NORM: { + int *iarg = arg; + + switch (*iarg) { + case VIDEO_MODE_AUTO: + return saa7191_set_norm(client, SAA7191_NORM_AUTO); + case VIDEO_MODE_PAL: + return saa7191_set_norm(client, SAA7191_NORM_PAL); + case VIDEO_MODE_NTSC: + return saa7191_set_norm(client, SAA7191_NORM_NTSC); + case VIDEO_MODE_SECAM: + return saa7191_set_norm(client, SAA7191_NORM_SECAM); + default: + return -EINVAL; + } + break; + } + case DECODER_SET_INPUT: { + int *iarg = arg; + + switch (client->adapter->id) { + case VINO_ADAPTER: + return saa7191_set_input(client, *iarg); + default: + if (*iarg != 0) + return -EINVAL; + } + break; + } + case DECODER_SET_OUTPUT: { + int *iarg = arg; + + /* not much choice of outputs */ + if (*iarg != 0) + return -EINVAL; + break; + } + case DECODER_ENABLE_OUTPUT: { + /* Always enabled */ + break; + } + case DECODER_SET_PICTURE: { + struct video_picture *pic = arg; + unsigned val; + int err; + + val = (pic->hue >> 8) - 0x80; + err = saa7191_write_reg(client, SAA7191_REG_HUEC, val); + if (err) + return -EIO; + break; + } + case DECODER_SAA7191_GET_STATUS: { + struct saa7191_status *status = arg; + unsigned char status_reg; + + if (saa7191_read_status(client, &status_reg)) + return -EIO; + status->signal = ((status_reg & SAA7191_STATUS_HLCK) == 0) + ? SAA7191_VALUE_ENABLED : SAA7191_VALUE_DISABLED; + status->ntsc = (status_reg & SAA7191_STATUS_FIDT) + ? SAA7191_VALUE_ENABLED : SAA7191_VALUE_DISABLED; + status->color = (status_reg & SAA7191_STATUS_CODE) + ? SAA7191_VALUE_ENABLED : SAA7191_VALUE_DISABLED; + + status->input = decoder->input; + status->norm = decoder->norm; + } + case DECODER_SAA7191_SET_NORM: { + int *norm = arg; + return saa7191_set_norm(client, *norm); + } + case DECODER_SAA7191_GET_CONTROLS: { + struct saa7191_control *ctrl = arg; + return saa7191_get_controls(client, ctrl); + } + case DECODER_SAA7191_SET_CONTROLS: { + struct saa7191_control *ctrl = arg; + return saa7191_set_controls(client, ctrl); + } + default: + return -EINVAL; + } + + return 0; +} + +static struct i2c_driver i2c_driver_saa7191 = { + .owner = THIS_MODULE, + .name = "saa7191", + .id = I2C_DRIVERID_SAA7191, + .flags = I2C_DF_NOTIFY, + .attach_adapter = saa7191_probe, + .detach_client = saa7191_detach, + .command = saa7191_command +}; + +static int saa7191_init(void) +{ + return i2c_add_driver(&i2c_driver_saa7191); +} + +static void saa7191_exit(void) +{ + i2c_del_driver(&i2c_driver_saa7191); +} + +module_init(saa7191_init); +module_exit(saa7191_exit); diff --git a/drivers/media/video/saa7191.h b/drivers/media/video/saa7191.h new file mode 100644 index 00000000000..27204503143 --- /dev/null +++ b/drivers/media/video/saa7191.h @@ -0,0 +1,139 @@ +/* + * saa7191.h - Philips SAA7191 video decoder driver + * + * Copyright (C) 2003 Ladislav Michl <ladis@linux-mips.org> + * Copyright (C) 2004,2005 Mikael Nousiainen <tmnousia@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _SAA7191_H_ +#define _SAA7191_H_ + +/* Philips SAA7191 DMSD I2C bus address */ +#define SAA7191_ADDR 0x8a + +/* Register subaddresses. */ +#define SAA7191_REG_IDEL 0x00 +#define SAA7191_REG_HSYB 0x01 +#define SAA7191_REG_HSYS 0x02 +#define SAA7191_REG_HCLB 0x03 +#define SAA7191_REG_HCLS 0x04 +#define SAA7191_REG_HPHI 0x05 +#define SAA7191_REG_LUMA 0x06 +#define SAA7191_REG_HUEC 0x07 +#define SAA7191_REG_CKTQ 0x08 +#define SAA7191_REG_CKTS 0x09 +#define SAA7191_REG_PLSE 0x0a +#define SAA7191_REG_SESE 0x0b +#define SAA7191_REG_GAIN 0x0c +#define SAA7191_REG_STDC 0x0d +#define SAA7191_REG_IOCK 0x0e +#define SAA7191_REG_CTL3 0x0f +#define SAA7191_REG_CTL4 0x10 +#define SAA7191_REG_CHCV 0x11 +#define SAA7191_REG_HS6B 0x14 +#define SAA7191_REG_HS6S 0x15 +#define SAA7191_REG_HC6B 0x16 +#define SAA7191_REG_HC6S 0x17 +#define SAA7191_REG_HP6I 0x18 +#define SAA7191_REG_STATUS 0xff /* not really a subaddress */ + +/* Status Register definitions */ +#define SAA7191_STATUS_CODE 0x01 /* color detected flag */ +#define SAA7191_STATUS_FIDT 0x20 /* format type NTSC/PAL */ +#define SAA7191_STATUS_HLCK 0x40 /* PLL unlocked/locked */ +#define SAA7191_STATUS_STTC 0x80 /* tv/vtr time constant */ + +/* Luminance Control Register definitions */ +#define SAA7191_LUMA_BYPS 0x80 + +/* Chroma Gain Control Settings Register definitions */ +/* 0=automatic colour-killer enabled, 1=forced colour on */ +#define SAA7191_GAIN_COLO 0x80 + +/* Standard/Mode Control Register definitions */ +/* tv/vtr mode bit: 0=TV mode (slow time constant), + * 1=VTR mode (fast time constant) */ +#define SAA7191_STDC_VTRC 0x80 +/* SECAM mode bit: 0=other standards, 1=SECAM */ +#define SAA7191_STDC_SECS 0x01 +/* the bit fields above must be or'd with this value */ +#define SAA7191_STDC_VALUE 0x0c + +/* I/O and Clock Control Register definitions */ +/* horizontal clock PLL: 0=PLL closed, + * 1=PLL circuit open and horizontal freq fixed */ +#define SAA7191_IOCK_HPLL 0x80 +/* S-VHS bit (chrominance from CVBS or from chrominance input): + * 0=controlled by BYPS-bit, 1=from chrominance input */ |