From 516c714c6c823e412dc485ca9c5307348cddd097 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 6 Sep 2010 14:05:43 -0300 Subject: V4L/DVB: Add documentation about the Ibmcam/Konica new gspca driver formats Cc: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/v4l/pixfmt.xml | 14 ++++++++++++++ Documentation/DocBook/v4l/videodev2.h.xml | 2 ++ 2 files changed, 16 insertions(+) (limited to 'Documentation') diff --git a/Documentation/DocBook/v4l/pixfmt.xml b/Documentation/DocBook/v4l/pixfmt.xml index c4ad0a8e42dc..5376aefeeffc 100644 --- a/Documentation/DocBook/v4l/pixfmt.xml +++ b/Documentation/DocBook/v4l/pixfmt.xml @@ -787,6 +787,20 @@ http://www.thedirks.org/winnov/ 'TM60' Used by Trident tm6000 + + V4L2_PIX_FMT_CIT_YYVYUY + 'CITV' + Used by xirlink CIT, found at IBM webcams. + Uses one line of Y then 1 line of VYUY + + + + V4L2_PIX_FMT_KONICA420 + 'KONI' + Used by Konica webcams. + YUV420 planar in blocks of 256 pixels. + + V4L2_PIX_FMT_YYUV 'YYUV' diff --git a/Documentation/DocBook/v4l/videodev2.h.xml b/Documentation/DocBook/v4l/videodev2.h.xml index 865b06d9e679..eda385891ade 100644 --- a/Documentation/DocBook/v4l/videodev2.h.xml +++ b/Documentation/DocBook/v4l/videodev2.h.xml @@ -364,6 +364,8 @@ struct v4l2_pix_format { #define V4L2_PIX_FMT_OV518 v4l2_fourcc('O', '5', '1', '8') /* ov518 JPEG */ #define V4L2_PIX_FMT_TM6000 v4l2_fourcc('T', 'M', '6', '0') /* tm5600/tm60x0 */ #define V4L2_PIX_FMT_STV0680 v4l2_fourcc('S', '6', '8', '0') /* stv0680 bayer */ +#define V4L2_PIX_FMT_CIT_YYVYUY v4l2_fourcc('C', 'I', 'T', 'V') /* one line of Y then 1 line of VYUY */ +#define V4L2_PIX_FMT_KONICA420 v4l2_fourcc('K', 'O', 'N', 'I') /* YUV420 planar in blocks of 256 pixels */ /* * F O R M A T E N U M E R A T I O N -- cgit v1.2.3 From 692d5522646fdf432329efbe5092dc9c5ca83e85 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 30 Jul 2010 17:24:55 -0300 Subject: V4L/DVB: v4l: Add a v4l2_subdev host private data field The existing priv field stores subdev private data owned by the subdev driver. Host (bridge) drivers might need to store per-subdev host-specific data, such as a pointer to platform data. Add a v4l2_subdev host_priv field to store host-specific data, and rename the existing priv field to dev_priv. Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/v4l2-framework.txt | 5 +++++ include/media/v4l2-subdev.h | 20 ++++++++++++++++---- 2 files changed, 21 insertions(+), 4 deletions(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt index e831aaca66f8..f5fdb395287f 100644 --- a/Documentation/video4linux/v4l2-framework.txt +++ b/Documentation/video4linux/v4l2-framework.txt @@ -192,6 +192,11 @@ You also need a way to go from the low-level struct to v4l2_subdev. For the common i2c_client struct the i2c_set_clientdata() call is used to store a v4l2_subdev pointer, for other busses you may have to use other methods. +Bridges might also need to store per-subdev private data, such as a pointer to +bridge-specific per-subdev private data. The v4l2_subdev structure provides +host private data for that purpose that can be accessed with +v4l2_get_subdev_hostdata() and v4l2_set_subdev_hostdata(). + From the bridge driver perspective you load the sub-device module and somehow obtain the v4l2_subdev pointer. For i2c devices this is easy: you call i2c_get_clientdata(). For other busses something similar needs to be done. diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 905879d7742f..b0316a7cf08d 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -438,17 +438,28 @@ struct v4l2_subdev { /* can be used to group similar subdevs, value is driver-specific */ u32 grp_id; /* pointer to private data */ - void *priv; + void *dev_priv; + void *host_priv; }; static inline void v4l2_set_subdevdata(struct v4l2_subdev *sd, void *p) { - sd->priv = p; + sd->dev_priv = p; } static inline void *v4l2_get_subdevdata(const struct v4l2_subdev *sd) { - return sd->priv; + return sd->dev_priv; +} + +static inline void v4l2_set_subdev_hostdata(struct v4l2_subdev *sd, void *p) +{ + sd->host_priv = p; +} + +static inline void *v4l2_get_subdev_hostdata(const struct v4l2_subdev *sd) +{ + return sd->host_priv; } static inline void v4l2_subdev_init(struct v4l2_subdev *sd, @@ -462,7 +473,8 @@ static inline void v4l2_subdev_init(struct v4l2_subdev *sd, sd->flags = 0; sd->name[0] = '\0'; sd->grp_id = 0; - sd->priv = NULL; + sd->dev_priv = NULL; + sd->host_priv = NULL; } /* Call an ops of a v4l2_subdev, doing the right checks against -- cgit v1.2.3 From d2f918bba7a482bee18cc0ede7791f7d846dd5d0 Mon Sep 17 00:00:00 2001 From: Malcolm Priestley Date: Thu, 2 Sep 2010 17:29:30 -0300 Subject: V4L/DVB: Support or LME2510(C) DM04/QQBOX USB DVB-S BOXES DM04/QQBOX DVB-S USB BOX with LME2510C+SHARP:BS2F7HZ7395 or LME2510+LGTDQT-P001F tuner. [mchehab@redhat.com: Fix merge conflicts/compilation and CodingStyle issues] Signed-off-by: Malcolm Priestley Signed-off-by: Mauro Carvalho Chehab --- Documentation/dvb/get_dvb_firmware | 46 +- Documentation/dvb/lmedm04.txt | 55 ++ drivers/media/IR/keymaps/Makefile | 1 + drivers/media/IR/keymaps/rc-lme2510.c | 68 +++ drivers/media/dvb/dvb-usb/Kconfig | 11 + drivers/media/dvb/dvb-usb/Makefile | 3 + drivers/media/dvb/dvb-usb/lmedm04.c | 936 ++++++++++++++++++++++++++++++++++ drivers/media/dvb/dvb-usb/lmedm04.h | 187 +++++++ include/media/rc-map.h | 1 + 9 files changed, 1307 insertions(+), 1 deletion(-) create mode 100644 Documentation/dvb/lmedm04.txt create mode 100644 drivers/media/IR/keymaps/rc-lme2510.c create mode 100644 drivers/media/dvb/dvb-usb/lmedm04.c create mode 100644 drivers/media/dvb/dvb-usb/lmedm04.h (limited to 'Documentation') diff --git a/Documentation/dvb/get_dvb_firmware b/Documentation/dvb/get_dvb_firmware index 350959f4e41b..59690de8ebfe 100644 --- a/Documentation/dvb/get_dvb_firmware +++ b/Documentation/dvb/get_dvb_firmware @@ -26,7 +26,8 @@ use IO::Handle; "dec3000s", "vp7041", "dibusb", "nxt2002", "nxt2004", "or51211", "or51132_qam", "or51132_vsb", "bluebird", "opera1", "cx231xx", "cx18", "cx23885", "pvrusb2", "mpc718", - "af9015", "ngene", "az6027"); + "af9015", "ngene", "az6027", "lme2510_lg", "lme2510c_s7395", + "lme2510c_s7395_old"); # Check args syntax() if (scalar(@ARGV) != 1); @@ -584,6 +585,49 @@ sub az6027{ $firmware; } + +sub lme2510_lg { + my $sourcefile = "LMEBDA_DVBS.sys"; + my $hash = "fc6017ad01e79890a97ec53bea157ed2"; + my $outfile = "dvb-usb-lme2510-lg.fw"; + my $hasho = "caa065d5fdbd2c09ad57b399bbf55cad"; + + checkstandard(); + + verify($sourcefile, $hash); + extract($sourcefile, 4168, 3841, $outfile); + verify($outfile, $hasho); + $outfile; +} + +sub lme2510c_s7395 { + my $sourcefile = "US2A0D.sys"; + my $hash = "b0155a8083fb822a3bd47bc360e74601"; + my $outfile = "dvb-usb-lme2510c-s7395.fw"; + my $hasho = "3a3cf1aeebd17b6ddc04cebe131e94cf"; + + checkstandard(); + + verify($sourcefile, $hash); + extract($sourcefile, 37248, 3720, $outfile); + verify($outfile, $hasho); + $outfile; +} + +sub lme2510c_s7395_old { + my $sourcefile = "LMEBDA_DVBS7395C.sys"; + my $hash = "7572ae0eb9cdf91baabd7c0ba9e09b31"; + my $outfile = "dvb-usb-lme2510c-s7395.fw"; + my $hasho = "90430c5b435eb5c6f88fd44a9d950674"; + + checkstandard(); + + verify($sourcefile, $hash); + extract($sourcefile, 4208, 3881, $outfile); + verify($outfile, $hasho); + $outfile; +} + # --------------------------------------------------------------- # Utilities diff --git a/Documentation/dvb/lmedm04.txt b/Documentation/dvb/lmedm04.txt new file mode 100644 index 000000000000..4bde457fafc4 --- /dev/null +++ b/Documentation/dvb/lmedm04.txt @@ -0,0 +1,55 @@ +To extract firmware for the DM04/QQBOX you need to copy the +following file(s) to this directory. + +for DM04+/QQBOX LME2510C (Sharp 7395 Tuner) +------------------------------------------- + +The Sharp 7395 driver can be found in windows/system32/driver + +US2A0D.sys (dated 17 Mar 2009) + + +and run +./get_dvb_firmware lme2510c_s7395 + + will produce + dvb-usb-lme2510c-s7395.fw + +An alternative but older firmware can be found on the driver +disk DVB-S_EN_3.5A in BDADriver/driver + +LMEBDA_DVBS7395C.sys (dated 18 Jan 2008) + +and run +./get_dvb_firmware lme2510c_s7395_old + + will produce + dvb-usb-lme2510c-s7395.fw + +-------------------------------------------------------------------- + +The LG firmware can be found on the driver +disk DM04+_5.1A[LG] in BDADriver/driver + +for DM04 LME2510 (LG Tuner) +--------------------------- + +LMEBDA_DVBS.sys (dated 13 Nov 2007) + +and run +./get_dvb_firmware lme2510_lg + + will produce + dvb-usb-lme2510-lg.fw + + +Other LG firmware can be extracted manually from US280D.sys +only found in windows/system32/driver. +However, this firmware does not run very well under Windows +or with the Linux driver. + +dd if=US280D.sys ibs=1 skip=36856 count=3976 of=dvb-usb-lme2510-lg.fw + +--------------------------------------------------------------------- + +Copy the firmware file(s) to /lib/firmware diff --git a/drivers/media/IR/keymaps/Makefile b/drivers/media/IR/keymaps/Makefile index c032b9d2e51a..f755b21eef1a 100644 --- a/drivers/media/IR/keymaps/Makefile +++ b/drivers/media/IR/keymaps/Makefile @@ -39,6 +39,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \ rc-kworld-315u.o \ rc-kworld-plus-tv-analog.o \ rc-lirc.o \ + rc-lme2510.o \ rc-manli.o \ rc-msi-tvanywhere.o \ rc-msi-tvanywhere-plus.o \ diff --git a/drivers/media/IR/keymaps/rc-lme2510.c b/drivers/media/IR/keymaps/rc-lme2510.c new file mode 100644 index 000000000000..40dcf0b4e21a --- /dev/null +++ b/drivers/media/IR/keymaps/rc-lme2510.c @@ -0,0 +1,68 @@ +/* LME2510 remote control + * + * + * Copyright (C) 2010 Malcolm Priestley (tvboxspy@gmail.com) + * + * 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. + */ + +#include + + +static struct ir_scancode lme2510_rc[] = { + { 0xba45, KEY_0 }, + { 0xa05f, KEY_1 }, + { 0xaf50, KEY_2 }, + { 0xa25d, KEY_3 }, + { 0xbe41, KEY_4 }, + { 0xf50a, KEY_5 }, + { 0xbd42, KEY_6 }, + { 0xb847, KEY_7 }, + { 0xb649, KEY_8 }, + { 0xfa05, KEY_9 }, + { 0xbc43, KEY_POWER }, + { 0xb946, KEY_SUBTITLE }, + { 0xf906, KEY_PAUSE }, + { 0xfc03, KEY_MEDIA_REPEAT}, + { 0xfd02, KEY_PAUSE }, + { 0xa15e, KEY_VOLUMEUP }, + { 0xa35c, KEY_VOLUMEDOWN }, + { 0xf609, KEY_CHANNELUP }, + { 0xe51a, KEY_CHANNELDOWN }, + { 0xe11e, KEY_PLAY }, + { 0xe41b, KEY_ZOOM }, + { 0xa659, KEY_MUTE }, + { 0xa55a, KEY_TV }, + { 0xe718, KEY_RECORD }, + { 0xf807, KEY_EPG }, + { 0xfe01, KEY_STOP }, + +}; + +static struct rc_keymap lme2510_map = { + .map = { + .scan = lme2510_rc, + .size = ARRAY_SIZE(lme2510_rc), + .ir_type = IR_TYPE_UNKNOWN, + .name = RC_MAP_LME2510, + } +}; + +static int __init init_rc_lme2510_map(void) +{ + return ir_register_map(&lme2510_map); +} + +static void __exit exit_rc_lme2510_map(void) +{ + ir_unregister_map(&lme2510_map); +} + +module_init(init_rc_lme2510_map) +module_exit(exit_rc_lme2510_map) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Malcolm Priestley tvboxspy@gmail.com"); diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig index c57f828e1eb9..4734ec073e01 100644 --- a/drivers/media/dvb/dvb-usb/Kconfig +++ b/drivers/media/dvb/dvb-usb/Kconfig @@ -347,3 +347,14 @@ config DVB_USB_AZ6027 select DVB_STB6100 if !DVB_FE_CUSTOMISE help Say Y here to support the AZ6027 device + +config DVB_USB_LME2510 + tristate "LME DM04/QQBOX DVB-S USB2.0 support" + depends on DVB_USB + select DVB_TDA10086 if !DVB_FE_CUSTOMISE + select DVB_TDA826X if !DVB_FE_CUSTOMISE + select DVB_STV0288 if !DVB_FE_CUSTOMISE + select DVB_IX2505V if !DVB_FE_CUSTOMISE + depends on IR_CORE + help + Say Y here to support the LME DM04/QQBOX DVB-S USB2.0 . diff --git a/drivers/media/dvb/dvb-usb/Makefile b/drivers/media/dvb/dvb-usb/Makefile index 1a192453b0e7..5b1d12f2d591 100644 --- a/drivers/media/dvb/dvb-usb/Makefile +++ b/drivers/media/dvb/dvb-usb/Makefile @@ -88,6 +88,9 @@ obj-$(CONFIG_DVB_USB_EC168) += dvb-usb-ec168.o dvb-usb-az6027-objs = az6027.o obj-$(CONFIG_DVB_USB_AZ6027) += dvb-usb-az6027.o +dvb-usb-lmedm04-objs = lmedm04.o +obj-$(CONFIG_DVB_USB_LME2510) += dvb-usb-lmedm04.o + EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/ # due to tuner-xc3028 EXTRA_CFLAGS += -Idrivers/media/common/tuners diff --git a/drivers/media/dvb/dvb-usb/lmedm04.c b/drivers/media/dvb/dvb-usb/lmedm04.c new file mode 100644 index 000000000000..d5374ac075d3 --- /dev/null +++ b/drivers/media/dvb/dvb-usb/lmedm04.c @@ -0,0 +1,936 @@ +/* DVB USB compliant linux driver for + * + * DM04/QQBOX DVB-S USB BOX LME2510C + SHARP:BS2F7HZ7395 + * LME2510 + LGTDQT-P001F + * + * MVB7395 (LME2510C+SHARP:BS2F7HZ7395) + * SHARP:BS2F7HZ7395 = (STV0288+Sharp IX2505V) + * + * MV001F (LME2510 +LGTDQY-P001F) + * LG TDQY - P001F =(TDA8263 + TDA10086H) + * + * For firmware see Documentation/dvb/lmedm04.txt + * + * I2C addresses: + * 0xd0 - STV0288 - Demodulator + * 0xc0 - Sharp IX2505V - Tuner + * --or-- + * 0x1c - TDA10086 - Demodulator + * 0xc0 - TDA8263 - Tuner + * + * ***Please Note*** + * There are other variants of the DM04 + * ***NOT SUPPORTED*** + * MVB0001F (LME2510C+LGTDQT-P001F) + * MV0194 (LME2510+SHARP0194) + * MVB0194 (LME2510C+SHARP0194) + * + * + * VID = 3344 PID LME2510=1122 LME2510C=1120 + * + * Copyright (C) 2010 Malcolm Priestley (tvboxspy@gmail.com) + * LME2510(C)(C) Leaguerme (Shenzhen) MicroElectronics Co., Ltd. + * + * 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. + * + * 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. + * + * + * see Documentation/dvb/README.dvb-usb for more information + * + * Known Issues : + * LME2510: Non Intel USB chipsets fail to maintain High Speed on + * Boot or Hot Plug. + * + * DiSEqC functions are not fully supported in this driver. The main + * reason is the frontend is cut off during streaming. Allowing frontend + * access will stall the driver. Applications that attempt to this, the + * commands are ignored. + * + * PID functions have been removed from this driver version due to + * problems with different firmware and application versions. + */ +#define DVB_USB_LOG_PREFIX "LME2510(C)" +#include +#include +#include + +#include "dvb-usb.h" +#include "lmedm04.h" +#include "tda826x.h" +#include "tda10086.h" +#include "stv0288.h" +#include "ix2505v.h" + + + +/* debug */ +static int dvb_usb_lme2510_debug; +#define l_dprintk(var, level, args...) do { \ + if ((var >= level)) \ + printk(KERN_DEBUG DVB_USB_LOG_PREFIX ": " args); \ +} while (0) + +#define deb_info(level, args...) l_dprintk(dvb_usb_lme2510_debug, level, args) +#define debug_data_snipet(level, name, p) \ + deb_info(level, name" (%02x%02x%02x%02x%02x%02x%02x%02x)", \ + *p, *(p+1), *(p+2), *(p+3), *(p+4), \ + *(p+5), *(p+6), *(p+7)); + + +module_param_named(debug, dvb_usb_lme2510_debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able))." + DVB_USB_DEBUG_STATUS); + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); +#define TUNER_LG 0x1 +#define TUNER_S7395 0x2 + +struct lme2510_state { + u8 id; + u8 tuner_config; + u8 signal_lock; + u8 signal_level; + u8 signal_sn; + u8 time_key; + u8 i2c_talk_onoff; + u8 i2c_gate; + u8 i2c_tuner_gate_w; + u8 i2c_tuner_gate_r; + u8 i2c_tuner_addr; + void *buffer; + struct urb *lme_urb; + void *usb_buffer; + +}; + +static int lme2510_bulk_write(struct usb_device *dev, + u8 *snd, int len, u8 pipe) +{ + int ret, actual_l; + ret = usb_bulk_msg(dev, usb_sndbulkpipe(dev, pipe), + snd, len , &actual_l, 500); + return ret; +} + +static int lme2510_bulk_read(struct usb_device *dev, + u8 *rev, int len, u8 pipe) +{ + int ret, actual_l; + + ret = usb_bulk_msg(dev, usb_rcvbulkpipe(dev, pipe), + rev, len , &actual_l, 500); + return ret; +} + +static int lme2510_usb_talk(struct dvb_usb_device *d, + u8 *wbuf, int wlen, u8 *rbuf, int rlen) +{ + struct lme2510_state *st = d->priv; + u8 *buff; + int ret = 0; + + if (st->usb_buffer == NULL) { + st->usb_buffer = kmalloc(512, GFP_KERNEL); + if (st->usb_buffer == NULL) { + info("MEM Error no memory"); + return -ENOMEM; + } + } + buff = st->usb_buffer; + + /* the read/write capped at 512 */ + memcpy(buff, wbuf, (wlen > 512) ? 512 : wlen); + + + ret = mutex_lock_interruptible(&d->usb_mutex); + + if (ret < 0) + return -EAGAIN; + + ret |= usb_clear_halt(d->udev, usb_sndbulkpipe(d->udev, 0x01)); + msleep(5); + ret |= lme2510_bulk_write(d->udev, buff, wlen , 0x1); + + msleep(5); + ret |= usb_clear_halt(d->udev, usb_rcvbulkpipe(d->udev, 0x1)); + + msleep(5); + ret |= lme2510_bulk_read(d->udev, buff, (rlen > 512) ? + 512 : rlen , 0x1); + + if (rlen > 0) + memcpy(rbuf, buff, rlen); + + mutex_unlock(&d->usb_mutex); + + return (ret < 0) ? -ENODEV : 0; +} + +static int lme2510_remote_keypress(struct dvb_usb_adapter *adap, u16 keypress) +{ + struct dvb_usb_device *d = adap->dev; + + deb_info(1, "INT Key Keypress =%04x", keypress); + + if (keypress > 0) + ir_keydown(d->rc_input_dev, keypress, 0); + + return 0; +} + +static void lme2510_int_response(struct urb *lme_urb) +{ + struct dvb_usb_adapter *adap = lme_urb->context; + struct lme2510_state *st = adap->dev->priv; + static u8 *ibuf, *rbuf; + int i = 0, offset; + + switch (lme_urb->status) { + case 0: + case -ETIMEDOUT: + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + return; + default: + info("Error %x", lme_urb->status); + break; + } + + rbuf = (u8 *) lme_urb->transfer_buffer; + + offset = ((lme_urb->actual_length/8) > 4) + ? 4 : (lme_urb->actual_length/8) ; + + + for (i = 0; i < offset; ++i) { + ibuf = (u8 *)&rbuf[i*8]; + deb_info(5, "INT O/S C =%02x C/O=%02x Type =%02x%02x", + offset, i, ibuf[0], ibuf[1]); + + switch (ibuf[0]) { + case 0xaa: + debug_data_snipet(1, "INT Remote data snipet in", ibuf); + lme2510_remote_keypress(adap, + (u16)(ibuf[4]<<8)+ibuf[5]); + break; + case 0xbb: + switch (st->tuner_config) { + case TUNER_LG: + st->signal_lock = ibuf[2]; + st->signal_level = ibuf[4]; + st->signal_sn = ibuf[3]; + st->time_key = ibuf[7]; + break; + case TUNER_S7395: + /* Tweak for earlier firmware*/ + if (ibuf[1] == 0x03) { + st->signal_level = ibuf[3]; + st->signal_sn = ibuf[2]; + } else { + st->signal_level = ibuf[4]; + st->signal_sn = ibuf[5]; + } + break; + default: + break; + } + debug_data_snipet(5, "INT Remote data snipet in", ibuf); + break; + case 0xcc: + debug_data_snipet(1, "INT Control data snipet", ibuf); + break; + default: + debug_data_snipet(1, "INT Unknown data snipet", ibuf); + break; + } + } + usb_submit_urb(lme_urb, GFP_ATOMIC); +} + +static int lme2510_int_read(struct dvb_usb_adapter *adap) +{ + struct lme2510_state *lme_int = adap->dev->priv; + + lme_int->lme_urb = usb_alloc_urb(0, GFP_ATOMIC); + + if (lme_int->lme_urb == NULL) + return -ENOMEM; + + lme_int->buffer = usb_alloc_coherent(adap->dev->udev, 5000, GFP_ATOMIC, + &lme_int->lme_urb->transfer_dma); + + if (lme_int->buffer == NULL) + return -ENOMEM; + + usb_fill_int_urb(lme_int->lme_urb, + adap->dev->udev, + usb_rcvintpipe(adap->dev->udev, 0xa), + lme_int->buffer, + 4096, + lme2510_int_response, + adap, + 11); + + lme_int->lme_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + usb_submit_urb(lme_int->lme_urb, GFP_ATOMIC); + info("INT Interupt Service Started"); + + return 0; +} + +static int lme2510_return_status(struct usb_device *dev) +{ + int ret = 0; + u8 data[10] = {0}; + ret |= usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + 0x06, 0x80, 0x0302, 0x00, data, 0x0006, 200); + info("Firmware Status: %x (%x)", ret , data[2]); + + return (ret < 0) ? -ENODEV : data[2]; +} + +static int lme2510_msg(struct dvb_usb_device *d, + u8 *wbuf, int wlen, u8 *rbuf, int rlen) +{ + int ret = 0; + struct lme2510_state *st = d->priv; + + if (st->i2c_talk_onoff == 1) { + if ((wbuf[2] == 0x1c) & (wbuf[3] == 0x0e)) + msleep(80); /*take your time when waiting for tune*/ + + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + ret = lme2510_usb_talk(d, wbuf, wlen, rbuf, rlen); + + mutex_unlock(&d->i2c_mutex); + switch (st->tuner_config) { + case TUNER_S7395: + if (wbuf[3] == 0x24) + st->signal_lock = rbuf[1]; + break; + default: + break; + } + + } else { + switch (st->tuner_config) { + case TUNER_LG: + switch (wbuf[3]) { + case 0x0e: + rbuf[0] = 0x55; + rbuf[1] = st->signal_lock; + break; + case 0x43: + rbuf[0] = 0x55; + rbuf[1] = st->signal_level; + break; + case 0x1c: + rbuf[0] = 0x55; + rbuf[1] = st->signal_sn; + break; + default: + break; + } + break; + case TUNER_S7395: + switch (wbuf[3]) { + case 0x10: + rbuf[0] = 0x55; + rbuf[1] = (st->signal_level & 0x80) + ? 0 : (st->signal_level * 2); + break; + case 0x2d: + rbuf[0] = 0x55; + rbuf[1] = st->signal_sn; + break; + case 0x24: + rbuf[0] = 0x55; + rbuf[1] = (st->signal_level & 0x80) + ? 0 : st->signal_lock; + default: + break; + } + break; + default: + break; + + } + + deb_info(4, "I2C From Interupt Message out(%02x) in(%02x)", + wbuf[3], rbuf[1]); + + } + + return ret; +} + + +static int lme2510_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], + int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + struct lme2510_state *st = d->priv; + static u8 obuf[64], ibuf[512]; + int i, read, read_o; + u16 len; + u8 gate = st->i2c_gate; + + if (gate == 0) + gate = 5; + + if (num > 2) + warn("more than 2 i2c messages" + "at a time is not handled yet. TODO."); + + for (i = 0; i < num; i++) { + read_o = 1 & (msg[i].flags & I2C_M_RD); + read = i+1 < num && (msg[i+1].flags & I2C_M_RD); + read |= read_o; + gate = (msg[i].addr == st->i2c_tuner_addr) + ? (read) ? st->i2c_tuner_gate_r + : st->i2c_tuner_gate_w + : st->i2c_gate; + obuf[0] = gate | (read << 7); + + if (gate == 5) + obuf[1] = (read) ? 2 : msg[i].len + 1; + else + obuf[1] = msg[i].len + read + 1; + + obuf[2] = msg[i].addr; + if (read) { + if (read_o) + len = 3; + else { + memcpy(&obuf[3], msg[i].buf, msg[i].len); + obuf[msg[i].len+3] = msg[i+1].len; + len = msg[i].len+4; + } + } else { + memcpy(&obuf[3], msg[i].buf, msg[i].len); + len = msg[i].len+3; + } + + if (lme2510_msg(d, obuf, len, ibuf, 512) < 0) { + deb_info(1, "i2c transfer failed."); + return -EAGAIN; + } + + if (read) { + if (read_o) + memcpy(msg[i].buf, &ibuf[1], msg[i].len); + else { + memcpy(msg[i+1].buf, &ibuf[1], msg[i+1].len); + i++; + } + } + } + return i; +} + +static u32 lme2510_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static struct i2c_algorithm lme2510_i2c_algo = { + .master_xfer = lme2510_i2c_xfer, + .functionality = lme2510_i2c_func, +}; + +/* Callbacks for DVB USB */ +static int lme2510_identify_state(struct usb_device *udev, + struct dvb_usb_device_properties *props, + struct dvb_usb_device_description **desc, + int *cold) +{ + if (lme2510_return_status(udev) == 0x44) + *cold = 1; + else + *cold = 0; + return 0; +} + +static int lme2510_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) +{ + struct lme2510_state *st = adap->dev->priv; + static u8 reset[] = LME_RESET; + static u8 stream_on[] = LME_ST_ON_W; + static u8 clear_reg_3[] = LME_CLEAR_PID; + static u8 rbuf[1]; + int ret = 0, len = 2, rlen = sizeof(rbuf); + + deb_info(1, "STM (%02x)", onoff); + + if (onoff == 1) { + st->i2c_talk_onoff = 0; + msleep(400); /* give enough time for i2c to stop */ + ret |= lme2510_usb_talk(adap->dev, + stream_on, len, rbuf, rlen); + } else { + deb_info(1, "STM Steam Off"); + if (st->tuner_config == TUNER_LG) + ret |= lme2510_usb_talk(adap->dev, clear_reg_3, + sizeof(clear_reg_3), rbuf, rlen); + else + ret |= lme2510_usb_talk(adap->dev, + reset, sizeof(reset), rbuf, rlen); + + msleep(400); + st->i2c_talk_onoff = 1; + } + + return (ret < 0) ? -ENODEV : 0; +} + +static int lme2510_int_service(struct dvb_usb_adapter *adap) +{ + struct dvb_usb_device *d = adap->dev; + struct input_dev *input_dev; + char *ir_codes = RC_MAP_LME2510; + int ret = 0; + + info("STA Configuring Remote"); + + usb_make_path(d->udev, d->rc_phys, sizeof(d->rc_phys)); + + strlcat(d->rc_phys, "/ir0", sizeof(d->rc_phys)); + + input_dev = input_allocate_device(); + if (!input_dev) + return -ENOMEM; + + input_dev->name = "LME2510 Remote Control"; + input_dev->phys = d->rc_phys; + + usb_to_input_id(d->udev, &input_dev->id); + + ret |= ir_input_register(input_dev, ir_codes, NULL, "LME 2510"); + + if (ret) { + input_free_device(input_dev); + return ret; + } + + d->rc_input_dev = input_dev; + /* Start the Interupt */ + ret = lme2510_int_read(adap); + + if (ret < 0) { + ir_input_unregister(input_dev); + input_free_device(input_dev); + } + return (ret < 0) ? -ENODEV : 0; +} + +static u8 check_sum(u8 *p, u8 len) +{ + u8 sum = 0; + while (len--) + sum += *p++; + return sum; +} + +static int lme2510_download_firmware(struct usb_device *dev, + const struct firmware *fw) +{ + int ret = 0; + u8 data[512] = {0}; + u16 j, wlen, len_in, start, end; + u8 packet_size, dlen, i; + u8 *fw_data; + + packet_size = 0x31; + len_in = 1; + + + info("FRM Starting Firmware Download"); + + for (i = 1; i < 3; i++) { + start = (i == 1) ? 0 : 512; + end = (i == 1) ? 512 : fw->size; + for (j = start; j < end; j += (packet_size+1)) { + fw_data = (u8 *)(fw->data + j); + if ((end - j) > packet_size) { + data[0] = i; + dlen = packet_size; + } else { + data[0] = i | 0x80; + dlen = (u8)(end - j)-1; + } + data[1] = dlen; + memcpy(&data[2], fw_data, dlen+1); + wlen = (u8) dlen + 4; + data[wlen-1] = check_sum(fw_data, dlen+1); + deb_info(1, "Data S=%02x:E=%02x CS= %02x", data[3], + data[dlen+2], data[dlen+3]); + ret |= lme2510_bulk_write(dev, data, wlen, 1); + ret |= lme2510_bulk_read(dev, data, len_in , 1); + ret |= (data[0] == 0x88) ? 0 : -1; + } + } + usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + 0x06, 0x80, 0x0200, 0x00, data, 0x0109, 1000); + + + data[0] = 0x8a; + len_in = 1; + msleep(2000); + ret |= lme2510_bulk_write(dev, data , len_in, 1); /*Resetting*/ + ret |= lme2510_bulk_read(dev, data, len_in, 1); + msleep(400); + + if (ret < 0) + info("FRM Firmware Download Failed (%04x)" , ret); + else + info("FRM Firmware Download Completed - Resetting Device"); + + + return (ret < 0) ? -ENODEV : 0; +} + + +static int lme2510_kill_urb(struct usb_data_stream *stream) +{ + int i; + for (i = 0; i < stream->urbs_submitted; i++) { + deb_info(3, "killing URB no. %d.", i); + + /* stop the URB */ + usb_kill_urb(stream->urb_list[i]); + } + stream->urbs_submitted = 0; + return 0; +} + +static struct tda10086_config tda10086_config = { + .demod_address = 0x1c, + .invert = 0, + .diseqc_tone = 1, + .xtal_freq = TDA10086_XTAL_16M, +}; + +static struct stv0288_config lme_config = { + .demod_address = 0xd0, + .min_delay_ms = 15, + .inittab = s7395_inittab, +}; + +static struct ix2505v_config lme_tuner = { + .tuner_address = 0xc0, + .min_delay_ms = 100, + .tuner_gain = 0x0, + .tuner_chargepump = 0x3, +}; + + +static int dm04_lme2510_set_voltage(struct dvb_frontend *fe, + fe_sec_voltage_t voltage) +{ + struct dvb_usb_adapter *adap = fe->dvb->priv; + struct lme2510_state *st = adap->dev->priv; + static u8 voltage_low[] = LME_VOLTAGE_L; + static u8 voltage_high[] = LME_VOLTAGE_H; + static u8 reset[] = LME_RESET; + static u8 clear_reg_3[] = LME_CLEAR_PID; + static u8 rbuf[1]; + int ret = 0, len = 3, rlen = 1; + + msleep(100); + + if (st->tuner_config == TUNER_LG) + ret |= lme2510_usb_talk(adap->dev, clear_reg_3, + sizeof(clear_reg_3), rbuf, rlen); + else + ret |= lme2510_usb_talk(adap->dev, + reset, sizeof(reset), rbuf, rlen); + + /*always check & stop streaming*/ + lme2510_kill_urb(&adap->stream); + adap->feedcount = 0; + + switch (voltage) { + + case SEC_VOLTAGE_18: + ret |= lme2510_usb_talk(adap->dev, + voltage_high, len, rbuf, rlen); + break; + + case SEC_VOLTAGE_OFF: + case SEC_VOLTAGE_13: + default: + ret |= lme2510_usb_talk(adap->dev, + voltage_low, len, rbuf, rlen); + break; + + + }; + st->i2c_talk_onoff = 1; + return (ret < 0) ? -ENODEV : 0; +} + +static int dm04_lme2510_frontend_attach(struct dvb_usb_adapter *adap) +{ + int ret = 0; + struct lme2510_state *st = adap->dev->priv; + + /* Interupt Start */ + ret = lme2510_int_service(adap); + if (ret < 0) { + info("INT Unable to start Interupt Service"); + return -ENODEV; + } + + st->i2c_talk_onoff = 1; + st->i2c_gate = 4; + + adap->fe = dvb_attach(tda10086_attach, &tda10086_config, + &adap->dev->i2c_adap); + + if (adap->fe) { + info("TUN Found Frontend TDA10086"); + memcpy(&adap->fe->ops.info.name, + &"DM04_LG_TDQY-P001F DVB-S", 24); + adap->fe->ops.set_voltage = dm04_lme2510_set_voltage; + st->i2c_tuner_gate_w = 4; + st->i2c_tuner_gate_r = 4; + st->i2c_tuner_addr = 0xc0; + if (dvb_attach(tda826x_attach, adap->fe, 0xc0, + &adap->dev->i2c_adap, 1)) { + info("TUN TDA8263 Found"); + st->tuner_config = TUNER_LG; + return 0; + } + kfree(adap->fe); + adap->fe = NULL; + } + st->i2c_gate = 5; + adap->fe = dvb_attach(stv0288_attach, &lme_config, + &adap->dev->i2c_adap); + + if (adap->fe) { + info("FE Found Stv0288"); + memcpy(&adap->fe->ops.info.name, + &"DM04_SHARP:BS2F7HZ7395", 22); + adap->fe->ops.set_voltage = dm04_lme2510_set_voltage; + st->i2c_tuner_gate_w = 4; + st->i2c_tuner_gate_r = 5; + st->i2c_tuner_addr = 0xc0; + if (dvb_attach(ix2505v_attach , adap->fe, &lme_tuner, + &adap->dev->i2c_adap)) { + st->tuner_config = TUNER_S7395; + info("TUN Sharp IX2505V silicon tuner"); + return 0; + } + kfree(adap->fe); + adap->fe = NULL; + } + + info("DM04 Not Supported"); + return -ENODEV; +} + +static int lme2510_powerup(struct dvb_usb_device *d, int onoff) +{ + struct lme2510_state *st = d->priv; + st->i2c_talk_onoff = 1; + return 0; +} + +/* DVB USB Driver stuff */ +static struct dvb_usb_device_properties lme2510_properties; +static struct dvb_usb_device_properties lme2510c_properties; + +static int lme2510_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + int ret = 0; + + usb_reset_configuration(udev); + + usb_set_interface(udev, intf->cur_altsetting->desc.bInterfaceNumber, 1); + + if (udev->speed != USB_SPEED_HIGH) { + ret = usb_reset_device(udev); + info("DEV Failed to connect in HIGH SPEED mode"); + return -ENODEV; + } + + if (0 == dvb_usb_device_init(intf, &lme2510_properties, + THIS_MODULE, NULL, adapter_nr)) { + info("DEV registering device driver"); + return 0; + } + if (0 == dvb_usb_device_init(intf, &lme2510c_properties, + THIS_MODULE, NULL, adapter_nr)) { + info("DEV registering device driver"); + return 0; + } + + info("DEV lme2510 Error"); + return -ENODEV; + +} + +static struct usb_device_id lme2510_table[] = { + { USB_DEVICE(0x3344, 0x1122) }, /* LME2510 */ + { USB_DEVICE(0x3344, 0x1120) }, /* LME2510C */ + {} /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, lme2510_table); + +static struct dvb_usb_device_properties lme2510_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + .usb_ctrl = DEVICE_SPECIFIC, + .download_firmware = lme2510_download_firmware, + .firmware = "dvb-usb-lme2510-lg.fw", + + .size_of_priv = sizeof(struct lme2510_state), + .num_adapters = 1, + .adapter = { + { + .streaming_ctrl = lme2510_streaming_ctrl, + .frontend_attach = dm04_lme2510_frontend_attach, + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_BULK, + .count = 10, + .endpoint = 0x06, + .u = { + .bulk = { + .buffersize = 4096, + + } + } + } + } + }, + .power_ctrl = lme2510_powerup, + .identify_state = lme2510_identify_state, + .i2c_algo = &lme2510_i2c_algo, + .generic_bulk_ctrl_endpoint = 0, + .num_device_descs = 1, + .devices = { + { "DM04 LME2510 DVB-S USB 2.0", + { &lme2510_table[0], NULL }, + }, + + } +}; + +static struct dvb_usb_device_properties lme2510c_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + .usb_ctrl = DEVICE_SPECIFIC, + .download_firmware = lme2510_download_firmware, + .firmware = "dvb-usb-lme2510c-s7395.fw", + .size_of_priv = sizeof(struct lme2510_state), + .num_adapters = 1, + .adapter = { + { + .streaming_ctrl = lme2510_streaming_ctrl, + .frontend_attach = dm04_lme2510_frontend_attach, + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_BULK, + .count = 8, + .endpoint = 0x8, + .u = { + .bulk = { + .buffersize = 4096, + + } + } + } + } + }, + .power_ctrl = lme2510_powerup, + .identify_state = lme2510_identify_state, + .i2c_algo = &lme2510_i2c_algo, + .generic_bulk_ctrl_endpoint = 0, + .num_device_descs = 1, + .devices = { + { "DM04 LME2510C USB2.0", + { &lme2510_table[1], NULL }, + }, + } +}; + +void lme2510_exit_int(struct dvb_usb_device *d) +{ + struct lme2510_state *st = d->priv; + if (st->lme_urb != NULL) { + st->i2c_talk_onoff = 0; + st->signal_lock = 0; + st->signal_level = 0; + st->signal_sn = 0; + kfree(st->usb_buffer); + usb_kill_urb(st->lme_urb); + usb_free_coherent(d->udev, 5000, st->buffer, + st->lme_urb->transfer_dma); + info("Interupt Service Stopped"); + ir_input_unregister(d->rc_input_dev); + info("Remote Stopped"); + } + return; +} + +void lme2510_exit(struct usb_interface *intf) +{ + struct dvb_usb_device *d = usb_get_intfdata(intf); + if (d != NULL) { + d->adapter[0].feedcount = 0; + lme2510_exit_int(d); + dvb_usb_device_exit(intf); + } + +} + +static struct usb_driver lme2510_driver = { + .name = "LME2510C_DVBS", + .probe = lme2510_probe, + .disconnect = lme2510_exit, + .id_table = lme2510_table, +}; + +/* module stuff */ +static int __init lme2510_module_init(void) +{ + int result = usb_register(&lme2510_driver); + if (result) { + err("usb_register failed. Error number %d", result); + return result; + } + + return 0; +} + +static void __exit lme2510_module_exit(void) +{ + /* deregister this driver from the USB subsystem */ + usb_deregister(&lme2510_driver); +} + +module_init(lme2510_module_init); +module_exit(lme2510_module_exit); + +MODULE_AUTHOR("Malcolm Priestley "); +MODULE_DESCRIPTION("LM2510(C) DVB-S USB2.0"); +MODULE_VERSION("1.4"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/dvb-usb/lmedm04.h b/drivers/media/dvb/dvb-usb/lmedm04.h new file mode 100644 index 000000000000..5a66c7ec4511 --- /dev/null +++ b/drivers/media/dvb/dvb-usb/lmedm04.h @@ -0,0 +1,187 @@ +/* DVB USB compliant linux driver for + * + * DM04/QQBOX DVB-S USB BOX LME2510C + SHARP:BS2F7HZ7395 + * LME2510C + LGTDQT-P001F + * + * MVB7395 (LME2510C+SHARP:BS2F7HZ7395) + * SHARP:BS2F7HZ7395 = (STV0288+Sharp IX2505V) + * + * MVB0001F (LME2510C+LGTDQT-P001F) + * LG TDQY - P001F =(TDA8263 + TDA10086H) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 2. + * * + * see Documentation/dvb/README.dvb-usb for more information + */ +#ifndef _DVB_USB_LME2510_H_ +#define _DVB_USB_LME2510_H_ + +/* Streamer & PID + * + * Note: These commands do not actually stop the streaming + * but form some kind of packet filtering/stream count + * or tuning related functions. + * 06 XX + * offset 1 = 00 Enable Streaming + * + * + * PID + * 03 XX XX ----> reg number ---> setting....20 XX + * offset 1 = length + * offset 2 = start of data + * end byte -1 = 20 + * end byte = clear pid always a0, other wise 9c, 9a ?? + * + * RESET (also clears PID filter) + * 3a 01 00 +*/ +#define LME_ST_ON_W {0x06, 0x00} +#define LME_RESET {0x3a, 0x01, 0x00} +#define LME_CLEAR_PID {0x03, 0x02, 0x20, 0xa0} + + +/* LME Power Control + * 07 XX XX + * offset 1 = 01 Power? my device cannot be powered down + * offset 2 = 00=Voltage low 01=Voltage high + */ + +#define LME_VOLTAGE_L {0x07, 0x01, 0x00} +#define LME_VOLTAGE_H {0x07, 0x01, 0x01} + + +/* Initial stv0288 settings for 7395 Frontend */ +static u8 s7395_inittab[] = { + 0x00, 0x11, + 0x01, 0x15, + 0x02, 0x20, + 0x03, 0x8e, + 0x04, 0x8e, + 0x05, 0x12, + 0x06, 0xff, + 0x07, 0x20, + 0x08, 0x00, + 0x09, 0x00, + 0x0a, 0x04, + 0x0b, 0x00, + 0x0c, 0x00, + 0x0d, 0x00, + 0x0e, 0xc1, + 0x0f, 0x54, + 0x10, 0x40, + 0x11, 0x7a, + 0x12, 0x03, + 0x13, 0x48, + 0x14, 0x84, + 0x15, 0xc5, + 0x16, 0xb8, + 0x17, 0x9c, + 0x18, 0x00, + 0x19, 0xa6, + 0x1a, 0x88, + 0x1b, 0x8f, + 0x1c, 0xf0, + 0x1e, 0x80, + 0x20, 0x0b, + 0x21, 0x54, + 0x22, 0xff, + 0x23, 0x01, + 0x24, 0x9a, + 0x25, 0x7f, + 0x26, 0x00, + 0x27, 0x00, + 0x28, 0x46, + 0x29, 0x66, + 0x2a, 0x90, + 0x2b, 0xfa, + 0x2c, 0xd9, + 0x2d, 0x02, + 0x2e, 0xb1, + 0x2f, 0x00, + 0x30, 0x0, + 0x31, 0x1e, + 0x32, 0x14, + 0x33, 0x0f, + 0x34, 0x09, + 0x35, 0x0c, + 0x36, 0x05, + 0x37, 0x2f, + 0x38, 0x16, + 0x39, 0xbd, + 0x3a, 0x0, + 0x3b, 0x13, + 0x3c, 0x11, + 0x3d, 0x30, + 0x3e, 0x00, + 0x3f, 0x00, + 0x40, 0x63, + 0x41, 0x04, + 0x42, 0x60, + 0x43, 0x00, + 0x44, 0x00, + 0x45, 0x00, + 0x46, 0x00, + 0x47, 0x00, + 0x4a, 0x00, + 0x4b, 0xd1, + 0x4c, 0x33, + 0x50, 0x12, + 0x51, 0x36, + 0x52, 0x21, + 0x53, 0x94, + 0x54, 0xb2, + 0x55, 0x29, + 0x56, 0x64, + 0x57, 0x2b, + 0x58, 0x54, + 0x59, 0x86, + 0x5a, 0x00, + 0x5b, 0x9b, + 0x5c, 0x08, + 0x5d, 0x7f, + 0x5e, 0xff, + 0x5f, 0x8d, + 0x70, 0x0, + 0x71, 0x0, + 0x72, 0x0, + 0x74, 0x0, + 0x75, 0x0, + 0x76, 0x0, + 0x81, 0x0, + 0x82, 0x3f, + 0x83, 0x3f, + 0x84, 0x0, + 0x85, 0x0, + 0x88, 0x0, + 0x89, 0x0, + 0x8a, 0x0, + 0x8b, 0x0, + 0x8c, 0x0, + 0x90, 0x0, + 0x91, 0x0, + 0x92, 0x0, + 0x93, 0x0, + 0x94, 0x1c, + 0x97, 0x0, + 0xa0, 0x48, + 0xa1, 0x0, + 0xb0, 0xb8, + 0xb1, 0x3a, + 0xb2, 0x10, + 0xb3, 0x82, + 0xb4, 0x80, + 0xb5, 0x82, + 0xb6, 0x82, + 0xb7, 0x82, + 0xb8, 0x20, + 0xb9, 0x0, + 0xf0, 0x0, + 0xf1, 0x0, + 0xf2, 0xc0, + 0xff, 0xff, +}; + +#endif diff --git a/include/media/rc-map.h b/include/media/rc-map.h index 6c0324eb4914..57281b1008e4 100644 --- a/include/media/rc-map.h +++ b/include/media/rc-map.h @@ -96,6 +96,7 @@ void rc_map_init(void); #define RC_MAP_KWORLD_315U "rc-kworld-315u" #define RC_MAP_KWORLD_PLUS_TV_ANALOG "rc-kworld-plus-tv-analog" #define RC_MAP_LIRC "rc-lirc" +#define RC_MAP_LME2510 "rc-lme2510" #define RC_MAP_MANLI "rc-manli" #define RC_MAP_MSI_TVANYWHERE_PLUS "rc-msi-tvanywhere-plus" #define RC_MAP_MSI_TVANYWHERE "rc-msi-tvanywhere" -- cgit v1.2.3 From f44026dbe42d38f2e2dfdc7bf35cafc7e44794d7 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 6 Aug 2010 12:52:43 -0300 Subject: V4L/DVB: Documentation: update now that the vtx/videotext API has been removed Remove all references to /dev/vtx in the documentation, except for some historical comments in dev-teletext.xml. Documentation/devices.txt is not updated, this will go through Alan Cox who maintains this file. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/v4l/compat.xml | 24 +++++++++++++++-------- Documentation/DocBook/v4l/dev-teletext.xml | 29 +++++++++++++--------------- Documentation/DocBook/v4l/v4l2.xml | 10 +++++++++- Documentation/feature-removal-schedule.txt | 23 ---------------------- Documentation/ioctl/ioctl-number.txt | 1 - Documentation/video4linux/bttv/MAKEDEV | 1 - Documentation/video4linux/v4l2-framework.txt | 5 ++--- 7 files changed, 40 insertions(+), 53 deletions(-) (limited to 'Documentation') diff --git a/Documentation/DocBook/v4l/compat.xml b/Documentation/DocBook/v4l/compat.xml index 54447f0d0784..c9ce61d981f5 100644 --- a/Documentation/DocBook/v4l/compat.xml +++ b/Documentation/DocBook/v4l/compat.xml @@ -21,11 +21,15 @@ API. Opening and Closing Devices For compatibility reasons the character device file names -recommended for V4L2 video capture, overlay, radio, teletext and raw +recommended for V4L2 video capture, overlay, radio and raw vbi capture devices did not change from those used by V4L. They are listed in and below in . + The teletext devices (minor range 192-223) have been removed in +V4L2 and no longer exist. There is no hardware available anymore for handling +pure teletext. Instead raw or sliced VBI is used. + The V4L videodev module automatically assigns minor numbers to drivers in load order, depending on the registered device type. We recommend that V4L2 drivers by default @@ -65,13 +69,6 @@ not compatible with V4L or V4L2. , /dev/radio63 64-127 - - Teletext decoder - /dev/vtx, -/dev/vtx0 to -/dev/vtx31 - 192-223 - Raw VBI capture /dev/vbi, @@ -2345,6 +2342,17 @@ more information. +
+ V4L2 in Linux 2.6.37 + + + Remove the vtx (videotext/teletext) API. This API was no longer +used and no hardware exists to verify the API. Nor were any userspace applications found +that used it. It was originally scheduled for removal in 2.6.35. + + + +
Relation of V4L2 to other Linux multimedia APIs diff --git a/Documentation/DocBook/v4l/dev-teletext.xml b/Documentation/DocBook/v4l/dev-teletext.xml index 76184e8ed618..414b1cfff9f4 100644 --- a/Documentation/DocBook/v4l/dev-teletext.xml +++ b/Documentation/DocBook/v4l/dev-teletext.xml @@ -1,35 +1,32 @@ Teletext Interface - This interface aims at devices receiving and demodulating + This interface was aimed at devices receiving and demodulating Teletext data [, ], evaluating the Teletext packages and storing formatted pages in cache memory. Such devices are usually implemented as microcontrollers with serial -interface (I2C) and can be found on older +interface (I2C) and could be found on old TV cards, dedicated Teletext decoding cards and home-brew devices connected to the PC parallel port. - The Teletext API was designed by Martin Buck. It is defined in + The Teletext API was designed by Martin Buck. It was defined in the kernel header file linux/videotext.h, the specification is available from ftp://ftp.gwdg.de/pub/linux/misc/videotext/. (Videotext is the name of -the German public television Teletext service.) Conventional character -device file names are /dev/vtx and -/dev/vttuner, with device number 83, 0 and 83, 16 -respectively. A similar interface exists for the Philips SAA5249 -Teletext decoder [specification?] with character device file names -/dev/tlkN, device number 102, N. +the German public television Teletext service.) Eventually the Teletext API was integrated into the V4L API with character device file names /dev/vtx0 to /dev/vtx31, device major number 81, minor numbers -192 to 223. For reference the V4L Teletext API specification is -reproduced here in full: "Teletext interfaces talk the existing VTX -API." Teletext devices with major number 83 and 102 will be removed in -Linux 2.6. +192 to 223. - There are no plans to replace the Teletext API or to integrate -it into V4L2. Please write to the linux-media mailing list: &v4l-ml; -when the need arises. + However, teletext decoders were quickly replaced by more +generic VBI demodulators and those dedicated teletext decoders no longer exist. +For many years the vtx devices were still around, even though nobody used +them. So the decision was made to finally remove support for the Teletext API in +kernel 2.6.37. + + Modern devices all use the raw or +sliced VBI API. + + 2.6.37 + 2010-08-06 + hv + Removed obsolete vtx (videotext) API. + + 2.6.33 2009-12-03 diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 842aa9de84a6..2372fb221969 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -498,29 +498,6 @@ When: April 2011 Why: Superseded by xt_CT Who: Netfilter developer team ---------------------------- - -What: video4linux /dev/vtx teletext API support -When: 2.6.35 -Files: drivers/media/video/saa5246a.c drivers/media/video/saa5249.c - include/linux/videotext.h -Why: The vtx device nodes have been superseded by vbi device nodes - for many years. No applications exist that use the vtx support. - Of the two i2c drivers that actually support this API the saa5249 - has been impossible to use for a year now and no known hardware - that supports this device exists. The saa5246a is theoretically - supported by the old mxb boards, but it never actually worked. - - In summary: there is no hardware that can use this API and there - are no applications actually implementing this API. - - The vtx support still reserves minors 192-223 and we would really - like to reuse those for upcoming new functionality. In the unlikely - event that new hardware appears that wants to use the functionality - provided by the vtx API, then that functionality should be build - around the sliced VBI API instead. -Who: Hans Verkuil - ---------------------------- What: IRQF_DISABLED diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt index 33223ff121d8..10f5af8b73f7 100644 --- a/Documentation/ioctl/ioctl-number.txt +++ b/Documentation/ioctl/ioctl-number.txt @@ -278,7 +278,6 @@ Code Seq#(hex) Include File Comments 'z' 10-4F drivers/s390/crypto/zcrypt_api.h conflict! 0x80 00-1F linux/fb.h -0x81 00-1F linux/videotext.h 0x88 00-3F media/ovcamchip.h 0x89 00-06 arch/x86/include/asm/sockios.h 0x89 0B-DF linux/sockios.h diff --git a/Documentation/video4linux/bttv/MAKEDEV b/Documentation/video4linux/bttv/MAKEDEV index 9d112f7fd5f7..093c0cd18042 100644 --- a/Documentation/video4linux/bttv/MAKEDEV +++ b/Documentation/video4linux/bttv/MAKEDEV @@ -19,7 +19,6 @@ function makedev () { echo "*** new device names ***" makedev video 0 makedev radio 64 -makedev vtx 192 makedev vbi 224 #echo "*** old device names (for compatibility only) ***" diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt index f5fdb395287f..8fb9de4f4875 100644 --- a/Documentation/video4linux/v4l2-framework.txt +++ b/Documentation/video4linux/v4l2-framework.txt @@ -44,8 +44,8 @@ All drivers have the following structure: 2) A way of initializing and commanding sub-devices (if any). -3) Creating V4L2 device nodes (/dev/videoX, /dev/vbiX, /dev/radioX and - /dev/vtxX) and keeping track of device-node specific data. +3) Creating V4L2 device nodes (/dev/videoX, /dev/vbiX and /dev/radioX) + and keeping track of device-node specific data. 4) Filehandle-specific structs containing per-filehandle data; @@ -488,7 +488,6 @@ types exist: VFL_TYPE_GRABBER: videoX for video input/output devices VFL_TYPE_VBI: vbiX for vertical blank data (i.e. closed captions, teletext) VFL_TYPE_RADIO: radioX for radio tuners -VFL_TYPE_VTX: vtxX for teletext devices (deprecated, don't use) The last argument gives you a certain amount of control over the device device node number used (i.e. the X in videoX). Normally you will pass -1 -- cgit v1.2.3 From 87660547def9d343dd5d21644b7a79a15a9b83c1 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 4 Sep 2010 00:53:30 -0300 Subject: devices.txt: Remove the old obsolete vtx device nodes Teletext interface is provided via VBI interface for a long time. There's no need nor is there any known program using those old /dev/vtx nodes. Signed-off-by: Mauro Carvalho Chehab --- Documentation/devices.txt | 3 --- 1 file changed, 3 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devices.txt b/Documentation/devices.txt index d0d1df6cb5de..76b1d2f22b6a 100644 --- a/Documentation/devices.txt +++ b/Documentation/devices.txt @@ -1495,9 +1495,6 @@ Your cooperation is appreciated. 64 = /dev/radio0 Radio device ... 127 = /dev/radio63 Radio device - 192 = /dev/vtx0 Teletext device - ... - 223 = /dev/vtx31 Teletext device 224 = /dev/vbi0 Vertical blank interrupt ... 255 = /dev/vbi31 Vertical blank interrupt -- cgit v1.2.3 From 860e7f474cb5bd9a691ebde302ac094093a1f5a7 Mon Sep 17 00:00:00 2001 From: Jean-François Moine Date: Mon, 13 Sep 2010 06:40:17 -0300 Subject: V4L/DVB: gspca - sonixj: Webcam 0c45:6102 added MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tested-by: Simon Danner Signed-off-by: Jean-François Moine Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/gspca.txt | 1 + drivers/media/video/gspca/sonixj.c | 2 +- drivers/media/video/sn9c102/sn9c102_devtable.h | 2 ++ 3 files changed, 4 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/gspca.txt b/Documentation/video4linux/gspca.txt index 56ba7bba7168..3a2f40779fba 100644 --- a/Documentation/video4linux/gspca.txt +++ b/Documentation/video4linux/gspca.txt @@ -302,6 +302,7 @@ sonixj 0c45:60fb Surfer NoName sonixj 0c45:60fc LG-LIC300 sonixj 0c45:60fe Microdia Audio sonixj 0c45:6100 PC Camera (SN9C128) +sonixj 0c45:6102 PC Camera (SN9C128) sonixj 0c45:610a PC Camera (SN9C128) sonixj 0c45:610b PC Camera (SN9C128) sonixj 0c45:610c PC Camera (SN9C128) diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c index 6e410121df25..89e488cb9bb8 100644 --- a/drivers/media/video/gspca/sonixj.c +++ b/drivers/media/video/gspca/sonixj.c @@ -3019,7 +3019,7 @@ static const __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x0c45, 0x60fe), BS(SN9C105, OV7630)}, #endif {USB_DEVICE(0x0c45, 0x6100), BS(SN9C120, MI0360)}, /*sn9c128*/ -/* {USB_DEVICE(0x0c45, 0x6102), BS(SN9C120, PO2030N)}, * / GC0305*/ + {USB_DEVICE(0x0c45, 0x6102), BS(SN9C120, PO2030N)}, /* /GC0305*/ /* {USB_DEVICE(0x0c45, 0x6108), BS(SN9C120, OM6802)}, */ {USB_DEVICE(0x0c45, 0x610a), BS(SN9C120, OV7648)}, /*sn9c128*/ {USB_DEVICE(0x0c45, 0x610b), BS(SN9C120, OV7660)}, /*sn9c128*/ diff --git a/drivers/media/video/sn9c102/sn9c102_devtable.h b/drivers/media/video/sn9c102/sn9c102_devtable.h index b6643ca7656a..62ee1f035d6b 100644 --- a/drivers/media/video/sn9c102/sn9c102_devtable.h +++ b/drivers/media/video/sn9c102/sn9c102_devtable.h @@ -116,7 +116,9 @@ static const struct usb_device_id sn9c102_id_table[] = { { SN9C102_USB_DEVICE(0x0c45, 0x60fe, BRIDGE_SN9C105), }, /* SN9C120 */ { SN9C102_USB_DEVICE(0x0458, 0x7025, BRIDGE_SN9C120), }, +#if !defined CONFIG_USB_GSPCA_SONIXJ && !defined CONFIG_USB_GSPCA_SONIXJ_MODULE { SN9C102_USB_DEVICE(0x0c45, 0x6102, BRIDGE_SN9C120), }, +#endif { SN9C102_USB_DEVICE(0x0c45, 0x6108, BRIDGE_SN9C120), }, { SN9C102_USB_DEVICE(0x0c45, 0x610f, BRIDGE_SN9C120), }, { SN9C102_USB_DEVICE(0x0c45, 0x6130, BRIDGE_SN9C120), }, -- cgit v1.2.3 From 2a3b501ffd28b2be355f558479543028ccdead48 Mon Sep 17 00:00:00 2001 From: Alexander Goncharov Date: Mon, 13 Sep 2010 06:58:16 -0300 Subject: V4L/DVB: gspca - sonixj: Add webcam 0c45:612b MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Alexander Goncharov Signed-off-by: Jean-François Moine Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/gspca.txt | 1 + drivers/media/video/gspca/sonixj.c | 1 + 2 files changed, 2 insertions(+) (limited to 'Documentation') diff --git a/Documentation/video4linux/gspca.txt b/Documentation/video4linux/gspca.txt index 3a2f40779fba..6a562eeeb4cd 100644 --- a/Documentation/video4linux/gspca.txt +++ b/Documentation/video4linux/gspca.txt @@ -309,6 +309,7 @@ sonixj 0c45:610c PC Camera (SN9C128) sonixj 0c45:610e PC Camera (SN9C128) sonixj 0c45:6128 Microdia/Sonix SNP325 sonixj 0c45:612a Avant Camera +sonixj 0c45:612b Speed-Link REFLECT2 sonixj 0c45:612c Typhoon Rasy Cam 1.3MPix sonixj 0c45:6130 Sonix Pccam sonixj 0c45:6138 Sn9c120 Mo4000 diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c index 89e488cb9bb8..025f9ba5c681 100644 --- a/drivers/media/video/gspca/sonixj.c +++ b/drivers/media/video/gspca/sonixj.c @@ -3031,6 +3031,7 @@ static const __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x0c45, 0x6128), BS(SN9C120, OM6802)}, /*sn9c325?*/ /*bw600.inf:*/ {USB_DEVICE(0x0c45, 0x612a), BS(SN9C120, OV7648)}, /*sn9c325?*/ + {USB_DEVICE(0x0c45, 0x612b), BS(SN9C110, ADCM1700)}, {USB_DEVICE(0x0c45, 0x612c), BS(SN9C110, MO4000)}, {USB_DEVICE(0x0c45, 0x612e), BS(SN9C110, OV7630)}, /* {USB_DEVICE(0x0c45, 0x612f), BS(SN9C110, ICM105C)}, */ -- cgit v1.2.3 From 008d35f2f5256751a18f1f4aea79e3caf140098d Mon Sep 17 00:00:00 2001 From: Jean-François Moine Date: Mon, 13 Sep 2010 07:04:49 -0300 Subject: V4L/DVB: v4l2: Add illuminator controls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some media devices (microscopes) may have one or many illuminators. This patch makes them controlable by the applications. Acked-by: Hans Verkuil Acked-by: Andy Walls Signed-off-by: Jean-François Moine Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/v4l/controls.xml | 9 ++++++++- drivers/media/video/v4l2-ctrls.c | 4 ++++ include/linux/videodev2.h | 5 ++++- 3 files changed, 16 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/DocBook/v4l/controls.xml b/Documentation/DocBook/v4l/controls.xml index 8408caaee276..68c83449d7c9 100644 --- a/Documentation/DocBook/v4l/controls.xml +++ b/Documentation/DocBook/v4l/controls.xml @@ -311,11 +311,18 @@ minimum value disables backlight compensation. bits 8-15 Green color information, bits 16-23 Blue color information and bits 24-31 must be zero. + + V4L2_CID_ILLUMINATORS_1 + V4L2_CID_ILLUMINATORS_2 + boolean + Switch on or off the illuminator 1 or 2 of the device + (usually a microscope). + V4L2_CID_LASTP1 End of the predefined control IDs (currently -V4L2_CID_BG_COLOR + 1). +V4L2_CID_ILLUMINATORS_2 + 1). V4L2_CID_PRIVATE_BASE diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c index ea8d32cd425d..9d2502cd03ff 100644 --- a/drivers/media/video/v4l2-ctrls.c +++ b/drivers/media/video/v4l2-ctrls.c @@ -305,6 +305,8 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_ROTATE: return "Rotate"; case V4L2_CID_BG_COLOR: return "Background Color"; case V4L2_CID_CHROMA_GAIN: return "Chroma Gain"; + case V4L2_CID_ILLUMINATORS_1: return "Illuminator 1"; + case V4L2_CID_ILLUMINATORS_2: return "Illuminator 2"; /* MPEG controls */ /* Keep the order of the 'case's the same as in videodev2.h! */ @@ -419,6 +421,8 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_AUDIO_LIMITER_ENABLED: case V4L2_CID_AUDIO_COMPRESSION_ENABLED: case V4L2_CID_PILOT_TONE_ENABLED: + case V4L2_CID_ILLUMINATORS_1: + case V4L2_CID_ILLUMINATORS_2: *type = V4L2_CTRL_TYPE_BOOLEAN; *min = 0; *max = *step = 1; diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index 2d5ce17ca472..b06479f63271 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -1047,8 +1047,11 @@ enum v4l2_colorfx { #define V4L2_CID_CHROMA_GAIN (V4L2_CID_BASE+36) +#define V4L2_CID_ILLUMINATORS_1 (V4L2_CID_BASE+37) +#define V4L2_CID_ILLUMINATORS_2 (V4L2_CID_BASE+38) + /* last CID + 1 */ -#define V4L2_CID_LASTP1 (V4L2_CID_BASE+37) +#define V4L2_CID_LASTP1 (V4L2_CID_BASE+39) /* MPEG-class control IDs defined by V4L2 */ #define V4L2_CID_MPEG_BASE (V4L2_CTRL_CLASS_MPEG | 0x900) -- cgit v1.2.3 From 5e765c6e445da16527a04aa3931f21e5deb476d8 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 11 Sep 2010 08:36:55 -0300 Subject: V4L/DVB: V4L Doc: fix DocBook syntax errors Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/v4l/pixfmt.xml | 4 ++-- Documentation/DocBook/v4l/vidioc-g-dv-preset.xml | 3 +-- Documentation/DocBook/v4l/vidioc-g-dv-timings.xml | 3 +-- Documentation/DocBook/v4l/vidioc-query-dv-preset.xml | 2 +- 4 files changed, 5 insertions(+), 7 deletions(-) (limited to 'Documentation') diff --git a/Documentation/DocBook/v4l/pixfmt.xml b/Documentation/DocBook/v4l/pixfmt.xml index 5376aefeeffc..f4cc0a9e658e 100644 --- a/Documentation/DocBook/v4l/pixfmt.xml +++ b/Documentation/DocBook/v4l/pixfmt.xml @@ -787,14 +787,14 @@ http://www.thedirks.org/winnov/ 'TM60' Used by Trident tm6000 - + V4L2_PIX_FMT_CIT_YYVYUY 'CITV' Used by xirlink CIT, found at IBM webcams. Uses one line of Y then 1 line of VYUY - + V4L2_PIX_FMT_KONICA420 'KONI' Used by Konica webcams. diff --git a/Documentation/DocBook/v4l/vidioc-g-dv-preset.xml b/Documentation/DocBook/v4l/vidioc-g-dv-preset.xml index 3c6784e132f3..d733721a7519 100644 --- a/Documentation/DocBook/v4l/vidioc-g-dv-preset.xml +++ b/Documentation/DocBook/v4l/vidioc-g-dv-preset.xml @@ -16,8 +16,7 @@ int ioctl int fd int request - &v4l2-dv-preset; -*argp + struct v4l2_dv_preset *argp diff --git a/Documentation/DocBook/v4l/vidioc-g-dv-timings.xml b/Documentation/DocBook/v4l/vidioc-g-dv-timings.xml index ecc19576bb8f..d5ec6abf0ce2 100644 --- a/Documentation/DocBook/v4l/vidioc-g-dv-timings.xml +++ b/Documentation/DocBook/v4l/vidioc-g-dv-timings.xml @@ -16,8 +16,7 @@ int ioctl int fd int request - &v4l2-dv-timings; -*argp + struct v4l2_dv_timings *argp diff --git a/Documentation/DocBook/v4l/vidioc-query-dv-preset.xml b/Documentation/DocBook/v4l/vidioc-query-dv-preset.xml index 402229ee06f6..d272f7ab91b8 100644 --- a/Documentation/DocBook/v4l/vidioc-query-dv-preset.xml +++ b/Documentation/DocBook/v4l/vidioc-query-dv-preset.xml @@ -16,7 +16,7 @@ input int ioctl int fd int request - &v4l2-dv-preset; *argp + struct v4l2_dv_preset *argp -- cgit v1.2.3 From 0827d0664dfa046dc9359c1a45f613d916431ed7 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 11 Sep 2010 08:39:47 -0300 Subject: V4L/DVB: V4L Doc: document V4L2_CAP_RDS_OUTPUT capability We forgot to add this capability to the QUERYCAP documentation. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/v4l/vidioc-querycap.xml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/DocBook/v4l/vidioc-querycap.xml b/Documentation/DocBook/v4l/vidioc-querycap.xml index 6ab7e25b31b6..d499da93a450 100644 --- a/Documentation/DocBook/v4l/vidioc-querycap.xml +++ b/Documentation/DocBook/v4l/vidioc-querycap.xml @@ -184,7 +184,7 @@ data. V4L2_CAP_RDS_CAPTURE 0x00000100 - The device supports the RDS interface. + The device supports the RDS capture interface. V4L2_CAP_VIDEO_OUTPUT_OVERLAY @@ -205,6 +205,11 @@ driver capabilities. The device supports the &VIDIOC-S-HW-FREQ-SEEK; ioctl for hardware frequency seeking. + + V4L2_CAP_RDS_OUTPUT + 0x00000800 + The device supports the RDS output interface. + V4L2_CAP_TUNER 0x00010000 -- cgit v1.2.3 From 6756207a71a0f976ab027186d0966a2162aaf922 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 11 Sep 2010 11:38:51 -0300 Subject: V4L/DVB: V4L Doc: correct the documentation for VIDIOC_QUERYMENU The VIDIOC_QUERYMENU documentation was not correct. EINVAL can be returned if the driver does not support some of the menu items. I.e. in a list of MPEG bitrates a driver generally supports only a subset of these. This behavior has been in place for years, but was never properly documented. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/v4l/controls.xml | 3 --- Documentation/DocBook/v4l/vidioc-queryctrl.xml | 18 ++++++++++++------ 2 files changed, 12 insertions(+), 9 deletions(-) (limited to 'Documentation') diff --git a/Documentation/DocBook/v4l/controls.xml b/Documentation/DocBook/v4l/controls.xml index 68c83449d7c9..2fae3e87ce73 100644 --- a/Documentation/DocBook/v4l/controls.xml +++ b/Documentation/DocBook/v4l/controls.xml @@ -364,9 +364,6 @@ enumerate_menu (void) querymenu.index++) { if (0 == ioctl (fd, &VIDIOC-QUERYMENU;, &querymenu)) { printf (" %s\n", querymenu.name); - } else { - perror ("VIDIOC_QUERYMENU"); - exit (EXIT_FAILURE); } } } diff --git a/Documentation/DocBook/v4l/vidioc-queryctrl.xml b/Documentation/DocBook/v4l/vidioc-queryctrl.xml index 8e0e055ac934..0d5e8283cf32 100644 --- a/Documentation/DocBook/v4l/vidioc-queryctrl.xml +++ b/Documentation/DocBook/v4l/vidioc-queryctrl.xml @@ -103,8 +103,12 @@ structure. The driver fills the rest of the structure or returns an index is invalid. Menu items are enumerated by calling VIDIOC_QUERYMENU with successive index values from &v4l2-queryctrl; -minimum (0) to -maximum, inclusive. +minimum to +maximum, inclusive. Note that it is possible +for VIDIOC_QUERYMENU to return an &EINVAL; for some +indices between minimum and maximum. +In that case that particular menu item is not supported by this driver. Also note that +the minimum value is not necessarily 0. See also the examples in . @@ -139,7 +143,7 @@ string. This information is intended for the user. minimum Minimum value, inclusive. This field gives a lower bound for V4L2_CTRL_TYPE_INTEGER controls and the -lowest valid index (always 0) for V4L2_CTRL_TYPE_MENU controls. +lowest valid index for V4L2_CTRL_TYPE_MENU controls. For V4L2_CTRL_TYPE_STRING controls the minimum value gives the minimum length of the string. This length does not include the terminating zero. It may not be valid for any other type of control, including @@ -279,7 +283,7 @@ values which are actually different on the hardware. V4L2_CTRL_TYPE_MENU - 0 + ≥ 0 1 N-1 The control has a menu of N choices. The names of @@ -405,8 +409,10 @@ writing a value will cause the device to carry out a given action EINVAL The &v4l2-queryctrl; id -is invalid. The &v4l2-querymenu; id or -index is invalid. +is invalid. The &v4l2-querymenu; id is +invalid or index is out of range (less than +minimum or greater than maximum) +or this particular menu item is not supported by the driver. -- cgit v1.2.3 From a1de2e4b72f27b906cb2eea3003fd62377dbf5ea Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 11 Sep 2010 07:05:47 -0300 Subject: V4L/DVB: V4L Doc: removed duplicate link Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/v4l/pixfmt-packed-rgb.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/DocBook/v4l/pixfmt-packed-rgb.xml b/Documentation/DocBook/v4l/pixfmt-packed-rgb.xml index 26e879231088..4db272b8a0d3 100644 --- a/Documentation/DocBook/v4l/pixfmt-packed-rgb.xml +++ b/Documentation/DocBook/v4l/pixfmt-packed-rgb.xml @@ -739,7 +739,7 @@ defined in error. Drivers may interpret them as in b1 b0 - + V4L2_PIX_FMT_BGR666 'BGRH' -- cgit v1.2.3 From 7af97effb3f5a374482179aca490b5038de56fa6 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 30 Sep 2010 08:25:42 -0300 Subject: V4L/DVB: Deprecate cpia driver (used for parallel port webcams) cpia driver were re-written inside gspca driver, for USB devices. The only functionality that were not migrated is the support for parallel port, as: 1) the developer didn't find any hardware; 2) it doesn't seem important to keep support for a parallel port webcam, as this is an obsolete technology; 3) the changes at gspca for it to work with parallel port would be very large; 4) this driver still uses BKL. So, let's move it to drivers/staging and label it to die at 2.6.38, if nobody cares enough to port parallel port support to gspca or to create a new driver that uses the same gspca-cpia sub-driver. Signed-off-by: Mauro Carvalho Chehab --- Documentation/feature-removal-schedule.txt | 17 +- drivers/media/video/Kconfig | 40 - drivers/media/video/Makefile | 3 - drivers/media/video/cpia.c | 4032 ---------------------------- drivers/media/video/cpia.h | 432 --- drivers/media/video/cpia_pp.c | 869 ------ drivers/media/video/cpia_usb.c | 640 ----- drivers/staging/Kconfig | 2 + drivers/staging/Makefile | 1 + drivers/staging/cpia/Kconfig | 39 + drivers/staging/cpia/Makefile | 5 + drivers/staging/cpia/TODO | 8 + drivers/staging/cpia/cpia.c | 4032 ++++++++++++++++++++++++++++ drivers/staging/cpia/cpia.h | 432 +++ drivers/staging/cpia/cpia_pp.c | 869 ++++++ drivers/staging/cpia/cpia_usb.c | 640 +++++ 16 files changed, 6044 insertions(+), 6017 deletions(-) delete mode 100644 drivers/media/video/cpia.c delete mode 100644 drivers/media/video/cpia.h delete mode 100644 drivers/media/video/cpia_pp.c delete mode 100644 drivers/media/video/cpia_usb.c create mode 100644 drivers/staging/cpia/Kconfig create mode 100644 drivers/staging/cpia/Makefile create mode 100644 drivers/staging/cpia/TODO create mode 100644 drivers/staging/cpia/cpia.c create mode 100644 drivers/staging/cpia/cpia.h create mode 100644 drivers/staging/cpia/cpia_pp.c create mode 100644 drivers/staging/cpia/cpia_usb.c (limited to 'Documentation') diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 2372fb221969..448722555648 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -98,7 +98,7 @@ Who: Pavel Machek --------------------------- What: Video4Linux API 1 ioctls and from Video devices. -When: July 2009 +When: kernel 2.6.38 Files: include/linux/videodev.h Check: include/linux/videodev.h Why: V4L1 AP1 was replaced by V4L2 API during migration from 2.4 to 2.6 @@ -116,6 +116,21 @@ Who: Mauro Carvalho Chehab --------------------------- +What: Video4Linux obsolete drivers using V4L1 API +When: kernel 2.6.38 +Files: drivers/staging/cpia/* +Check: drivers/staging/cpia/cpia.c +Why: There are some drivers still using V4L1 API, despite all efforts we've done + to migrate. Those drivers are for obsolete hardware that the old maintainer + didn't care (or not have the hardware anymore), and that no other developer + could find any hardware to buy. They probably have no practical usage today, + and people with such old hardware could probably keep using an older version + of the kernel. Those drivers will be moved to staging on 2.6.37 and, if nobody + care enough to port and test them with V4L2 API, they'll be removed on 2.6.38. +Who: Mauro Carvalho Chehab + +--------------------------- + What: sys_sysctl When: September 2010 Option: CONFIG_SYSCTL_SYSCALL diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 2c0a8f776f8a..4f21ef877f37 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -599,46 +599,6 @@ config VIDEO_W9966 Check out for more information. -config VIDEO_CPIA - tristate "CPiA Video For Linux (DEPRECATED)" - depends on VIDEO_V4L1 - default n - ---help--- - This driver is DEPRECATED please use the gspca cpia1 module - instead. Note that you need atleast version 0.6.4 of libv4l for - the cpia1 gspca module. - - This is the video4linux driver for cameras based on Vision's CPiA - (Colour Processor Interface ASIC), such as the Creative Labs Video - Blaster Webcam II. If you have one of these cameras, say Y here - and select parallel port and/or USB lowlevel support below, - otherwise say N. This will not work with the Creative Webcam III. - - Please read for more - information. - - This driver is also available as a module (cpia). - -config VIDEO_CPIA_PP - tristate "CPiA Parallel Port Lowlevel Support" - depends on PARPORT_1284 && VIDEO_CPIA && PARPORT - help - This is the lowlevel parallel port support for cameras based on - Vision's CPiA (Colour Processor Interface ASIC), such as the - Creative Webcam II. If you have the parallel port version of one - of these cameras, say Y here, otherwise say N. It is also available - as a module (cpia_pp). - -config VIDEO_CPIA_USB - tristate "CPiA USB Lowlevel Support" - depends on VIDEO_CPIA && USB - help - This is the lowlevel USB support for cameras based on Vision's CPiA - (Colour Processor Interface ASIC), such as the Creative Webcam II. - If you have the USB version of one of these cameras, say Y here, - otherwise say N. This will not work with the Creative Webcam III. - It is also available as a module (cpia_usb). - source "drivers/media/video/cpia2/Kconfig" config VIDEO_VINO diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index a8f89899dd6b..b947160a8cb0 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -92,9 +92,6 @@ obj-$(CONFIG_VIDEO_W9966) += w9966.o obj-$(CONFIG_VIDEO_PMS) += pms.o obj-$(CONFIG_VIDEO_VINO) += vino.o obj-$(CONFIG_VIDEO_STRADIS) += stradis.o -obj-$(CONFIG_VIDEO_CPIA) += cpia.o -obj-$(CONFIG_VIDEO_CPIA_PP) += cpia_pp.o -obj-$(CONFIG_VIDEO_CPIA_USB) += cpia_usb.o obj-$(CONFIG_VIDEO_MEYE) += meye.o obj-$(CONFIG_VIDEO_SAA7134) += saa7134/ obj-$(CONFIG_VIDEO_CX88) += cx88/ diff --git a/drivers/media/video/cpia.c b/drivers/media/video/cpia.c deleted file mode 100644 index 933ae4c8cb9a..000000000000 --- a/drivers/media/video/cpia.c +++ /dev/null @@ -1,4032 +0,0 @@ -/* - * cpia CPiA driver - * - * Supports CPiA based Video Camera's. - * - * (C) Copyright 1999-2000 Peter Pregler - * (C) Copyright 1999-2000 Scott J. Bertin - * (C) Copyright 1999-2000 Johannes Erdfelt - * (C) Copyright 2000 STMicroelectronics - * - * 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. - */ - -/* define _CPIA_DEBUG_ for verbose debug output (see cpia.h) */ -/* #define _CPIA_DEBUG_ 1 */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "cpia.h" - -static int video_nr = -1; - -#ifdef MODULE -module_param(video_nr, int, 0); -MODULE_AUTHOR("Scott J. Bertin & Peter Pregler & Johannes Erdfelt "); -MODULE_DESCRIPTION("V4L-driver for Vision CPiA based cameras"); -MODULE_LICENSE("GPL"); -MODULE_SUPPORTED_DEVICE("video"); -#endif - -static unsigned short colorspace_conv; -module_param(colorspace_conv, ushort, 0444); -MODULE_PARM_DESC(colorspace_conv, - " Colorspace conversion:" - "\n 0 = disable, 1 = enable" - "\n Default value is 0" - ); - -#define ABOUT "V4L-Driver for Vision CPiA based cameras" - -#define CPIA_MODULE_CPIA (0<<5) -#define CPIA_MODULE_SYSTEM (1<<5) -#define CPIA_MODULE_VP_CTRL (5<<5) -#define CPIA_MODULE_CAPTURE (6<<5) -#define CPIA_MODULE_DEBUG (7<<5) - -#define INPUT (DATA_IN << 8) -#define OUTPUT (DATA_OUT << 8) - -#define CPIA_COMMAND_GetCPIAVersion (INPUT | CPIA_MODULE_CPIA | 1) -#define CPIA_COMMAND_GetPnPID (INPUT | CPIA_MODULE_CPIA | 2) -#define CPIA_COMMAND_GetCameraStatus (INPUT | CPIA_MODULE_CPIA | 3) -#define CPIA_COMMAND_GotoHiPower (OUTPUT | CPIA_MODULE_CPIA | 4) -#define CPIA_COMMAND_GotoLoPower (OUTPUT | CPIA_MODULE_CPIA | 5) -#define CPIA_COMMAND_GotoSuspend (OUTPUT | CPIA_MODULE_CPIA | 7) -#define CPIA_COMMAND_GotoPassThrough (OUTPUT | CPIA_MODULE_CPIA | 8) -#define CPIA_COMMAND_ModifyCameraStatus (OUTPUT | CPIA_MODULE_CPIA | 10) - -#define CPIA_COMMAND_ReadVCRegs (INPUT | CPIA_MODULE_SYSTEM | 1) -#define CPIA_COMMAND_WriteVCReg (OUTPUT | CPIA_MODULE_SYSTEM | 2) -#define CPIA_COMMAND_ReadMCPorts (INPUT | CPIA_MODULE_SYSTEM | 3) -#define CPIA_COMMAND_WriteMCPort (OUTPUT | CPIA_MODULE_SYSTEM | 4) -#define CPIA_COMMAND_SetBaudRate (OUTPUT | CPIA_MODULE_SYSTEM | 5) -#define CPIA_COMMAND_SetECPTiming (OUTPUT | CPIA_MODULE_SYSTEM | 6) -#define CPIA_COMMAND_ReadIDATA (INPUT | CPIA_MODULE_SYSTEM | 7) -#define CPIA_COMMAND_WriteIDATA (OUTPUT | CPIA_MODULE_SYSTEM | 8) -#define CPIA_COMMAND_GenericCall (OUTPUT | CPIA_MODULE_SYSTEM | 9) -#define CPIA_COMMAND_I2CStart (OUTPUT | CPIA_MODULE_SYSTEM | 10) -#define CPIA_COMMAND_I2CStop (OUTPUT | CPIA_MODULE_SYSTEM | 11) -#define CPIA_COMMAND_I2CWrite (OUTPUT | CPIA_MODULE_SYSTEM | 12) -#define CPIA_COMMAND_I2CRead (INPUT | CPIA_MODULE_SYSTEM | 13) - -#define CPIA_COMMAND_GetVPVersion (INPUT | CPIA_MODULE_VP_CTRL | 1) -#define CPIA_COMMAND_ResetFrameCounter (INPUT | CPIA_MODULE_VP_CTRL | 2) -#define CPIA_COMMAND_SetColourParams (OUTPUT | CPIA_MODULE_VP_CTRL | 3) -#define CPIA_COMMAND_SetExposure (OUTPUT | CPIA_MODULE_VP_CTRL | 4) -#define CPIA_COMMAND_SetColourBalance (OUTPUT | CPIA_MODULE_VP_CTRL | 6) -#define CPIA_COMMAND_SetSensorFPS (OUTPUT | CPIA_MODULE_VP_CTRL | 7) -#define CPIA_COMMAND_SetVPDefaults (OUTPUT | CPIA_MODULE_VP_CTRL | 8) -#define CPIA_COMMAND_SetApcor (OUTPUT | CPIA_MODULE_VP_CTRL | 9) -#define CPIA_COMMAND_SetFlickerCtrl (OUTPUT | CPIA_MODULE_VP_CTRL | 10) -#define CPIA_COMMAND_SetVLOffset (OUTPUT | CPIA_MODULE_VP_CTRL | 11) -#define CPIA_COMMAND_GetColourParams (INPUT | CPIA_MODULE_VP_CTRL | 16) -#define CPIA_COMMAND_GetColourBalance (INPUT | CPIA_MODULE_VP_CTRL | 17) -#define CPIA_COMMAND_GetExposure (INPUT | CPIA_MODULE_VP_CTRL | 18) -#define CPIA_COMMAND_SetSensorMatrix (OUTPUT | CPIA_MODULE_VP_CTRL | 19) -#define CPIA_COMMAND_ColourBars (OUTPUT | CPIA_MODULE_VP_CTRL | 25) -#define CPIA_COMMAND_ReadVPRegs (INPUT | CPIA_MODULE_VP_CTRL | 30) -#define CPIA_COMMAND_WriteVPReg (OUTPUT | CPIA_MODULE_VP_CTRL | 31) - -#define CPIA_COMMAND_GrabFrame (OUTPUT | CPIA_MODULE_CAPTURE | 1) -#define CPIA_COMMAND_UploadFrame (OUTPUT | CPIA_MODULE_CAPTURE | 2) -#define CPIA_COMMAND_SetGrabMode (OUTPUT | CPIA_MODULE_CAPTURE | 3) -#define CPIA_COMMAND_InitStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 4) -#define CPIA_COMMAND_FiniStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 5) -#define CPIA_COMMAND_StartStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 6) -#define CPIA_COMMAND_EndStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 7) -#define CPIA_COMMAND_SetFormat (OUTPUT | CPIA_MODULE_CAPTURE | 8) -#define CPIA_COMMAND_SetROI (OUTPUT | CPIA_MODULE_CAPTURE | 9) -#define CPIA_COMMAND_SetCompression (OUTPUT | CPIA_MODULE_CAPTURE | 10) -#define CPIA_COMMAND_SetCompressionTarget (OUTPUT | CPIA_MODULE_CAPTURE | 11) -#define CPIA_COMMAND_SetYUVThresh (OUTPUT | CPIA_MODULE_CAPTURE | 12) -#define CPIA_COMMAND_SetCompressionParams (OUTPUT | CPIA_MODULE_CAPTURE | 13) -#define CPIA_COMMAND_DiscardFrame (OUTPUT | CPIA_MODULE_CAPTURE | 14) -#define CPIA_COMMAND_GrabReset (OUTPUT | CPIA_MODULE_CAPTURE | 15) - -#define CPIA_COMMAND_OutputRS232 (OUTPUT | CPIA_MODULE_DEBUG | 1) -#define CPIA_COMMAND_AbortProcess (OUTPUT | CPIA_MODULE_DEBUG | 4) -#define CPIA_COMMAND_SetDramPage (OUTPUT | CPIA_MODULE_DEBUG | 5) -#define CPIA_COMMAND_StartDramUpload (OUTPUT | CPIA_MODULE_DEBUG | 6) -#define CPIA_COMMAND_StartDummyDtream (OUTPUT | CPIA_MODULE_DEBUG | 8) -#define CPIA_COMMAND_AbortStream (OUTPUT | CPIA_MODULE_DEBUG | 9) -#define CPIA_COMMAND_DownloadDRAM (OUTPUT | CPIA_MODULE_DEBUG | 10) -#define CPIA_COMMAND_Null (OUTPUT | CPIA_MODULE_DEBUG | 11) - -enum { - FRAME_READY, /* Ready to grab into */ - FRAME_GRABBING, /* In the process of being grabbed into */ - FRAME_DONE, /* Finished grabbing, but not been synced yet */ - FRAME_UNUSED, /* Unused (no MCAPTURE) */ -}; - -#define COMMAND_NONE 0x0000 -#define COMMAND_SETCOMPRESSION 0x0001 -#define COMMAND_SETCOMPRESSIONTARGET 0x0002 -#define COMMAND_SETCOLOURPARAMS 0x0004 -#define COMMAND_SETFORMAT 0x0008 -#define COMMAND_PAUSE 0x0010 -#define COMMAND_RESUME 0x0020 -#define COMMAND_SETYUVTHRESH 0x0040 -#define COMMAND_SETECPTIMING 0x0080 -#define COMMAND_SETCOMPRESSIONPARAMS 0x0100 -#define COMMAND_SETEXPOSURE 0x0200 -#define COMMAND_SETCOLOURBALANCE 0x0400 -#define COMMAND_SETSENSORFPS 0x0800 -#define COMMAND_SETAPCOR 0x1000 -#define COMMAND_SETFLICKERCTRL 0x2000 -#define COMMAND_SETVLOFFSET 0x4000 -#define COMMAND_SETLIGHTS 0x8000 - -#define ROUND_UP_EXP_FOR_FLICKER 15 - -/* Constants for automatic frame rate adjustment */ -#define MAX_EXP 302 -#define MAX_EXP_102 255 -#define LOW_EXP 140 -#define VERY_LOW_EXP 70 -#define TC 94 -#define EXP_ACC_DARK 50 -#define EXP_ACC_LIGHT 90 -#define HIGH_COMP_102 160 -#define MAX_COMP 239 -#define DARK_TIME 3 -#define LIGHT_TIME 3 - -/* Maximum number of 10ms loops to wait for the stream to become ready */ -#define READY_TIMEOUT 100 - -/* Developer's Guide Table 5 p 3-34 - * indexed by [mains][sensorFps.baserate][sensorFps.divisor]*/ -static u8 flicker_jumps[2][2][4] = -{ { { 76, 38, 19, 9 }, { 92, 46, 23, 11 } }, - { { 64, 32, 16, 8 }, { 76, 38, 19, 9} } -}; - -/* forward declaration of local function */ -static void reset_camera_struct(struct cam_data *cam); -static int find_over_exposure(int brightness); -static void set_flicker(struct cam_params *params, volatile u32 *command_flags, - int on); - - -/********************************************************************** - * - * Memory management - * - **********************************************************************/ -static void *rvmalloc(unsigned long size) -{ - void *mem; - unsigned long adr; - - size = PAGE_ALIGN(size); - mem = vmalloc_32(size); - if (!mem) - return NULL; - - memset(mem, 0, size); /* Clear the ram out, no junk to the user */ - adr = (unsigned long) mem; - while (size > 0) { - SetPageReserved(vmalloc_to_page((void *)adr)); - adr += PAGE_SIZE; - size -= PAGE_SIZE; - } - - return mem; -} - -static void rvfree(void *mem, unsigned long size) -{ - unsigned long adr; - - if (!mem) - return; - - adr = (unsigned long) mem; - while ((long) size > 0) { - ClearPageReserved(vmalloc_to_page((void *)adr)); - adr += PAGE_SIZE; - size -= PAGE_SIZE; - } - vfree(mem); -} - -/********************************************************************** - * - * /proc interface - * - **********************************************************************/ -#ifdef CONFIG_PROC_FS -static struct proc_dir_entry *cpia_proc_root=NULL; - -static int cpia_proc_show(struct seq_file *m, void *v) -{ - struct cam_data *cam = m->private; - int tmp; - char tmpstr[29]; - - seq_printf(m, "read-only\n-----------------------\n"); - seq_printf(m, "V4L Driver version: %d.%d.%d\n", - CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER); - seq_printf(m, "CPIA Version: %d.%02d (%d.%d)\n", - cam->params.version.firmwareVersion, - cam->params.version.firmwareRevision, - cam->params.version.vcVersion, - cam->params.version.vcRevision); - seq_printf(m, "CPIA PnP-ID: %04x:%04x:%04x\n", - cam->params.pnpID.vendor, cam->params.pnpID.product, - cam->params.pnpID.deviceRevision); - seq_printf(m, "VP-Version: %d.%d %04x\n", - cam->params.vpVersion.vpVersion, - cam->params.vpVersion.vpRevision, - cam->params.vpVersion.cameraHeadID); - - seq_printf(m, "system_state: %#04x\n", - cam->params.status.systemState); - seq_printf(m, "grab_state: %#04x\n", - cam->params.status.grabState); - seq_printf(m, "stream_state: %#04x\n", - cam->params.status.streamState); - seq_printf(m, "fatal_error: %#04x\n", - cam->params.status.fatalError); - seq_printf(m, "cmd_error: %#04x\n", - cam->params.status.cmdError); - seq_printf(m, "debug_flags: %#04x\n", - cam->params.status.debugFlags); - seq_printf(m, "vp_status: %#04x\n", - cam->params.status.vpStatus); - seq_printf(m, "error_code: %#04x\n", - cam->params.status.errorCode); - /* QX3 specific entries */ - if (cam->params.qx3.qx3_detected) { - seq_printf(m, "button: %4d\n", - cam->params.qx3.button); - seq_printf(m, "cradled: %4d\n", - cam->params.qx3.cradled); - } - seq_printf(m, "video_size: %s\n", - cam->params.format.videoSize == VIDEOSIZE_CIF ? - "CIF " : "QCIF"); - seq_printf(m, "roi: (%3d, %3d) to (%3d, %3d)\n", - cam->params.roi.colStart*8, - cam->params.roi.rowStart*4, - cam->params.roi.colEnd*8, - cam->params.roi.rowEnd*4); - seq_printf(m, "actual_fps: %3d\n", cam->fps); - seq_printf(m, "transfer_rate: %4dkB/s\n", - cam->transfer_rate); - - seq_printf(m, "\nread-write\n"); - seq_printf(m, "----------------------- current min" - " max default comment\n"); - seq_printf(m, "brightness: %8d %8d %8d %8d\n", - cam->params.colourParams.brightness, 0, 100, 50); - if (cam->params.version.firmwareVersion == 1 && - cam->params.version.firmwareRevision == 2) - /* 1-02 firmware limits contrast to 80 */ - tmp = 80; - else - tmp = 96; - - seq_printf(m, "contrast: %8d %8d %8d %8d" - " steps of 8\n", - cam->params.colourParams.contrast, 0, tmp, 48); - seq_printf(m, "saturation: %8d %8d %8d %8d\n", - cam->params.colourParams.saturation, 0, 100, 50); - tmp = (25000+5000*cam->params.sensorFps.baserate)/ - (1<params.sensorFps.divisor); - seq_printf(m, "sensor_fps: %4d.%03d %8d %8d %8d\n", - tmp/1000, tmp%1000, 3, 30, 15); - seq_printf(m, "stream_start_line: %8d %8d %8d %8d\n", - 2*cam->params.streamStartLine, 0, - cam->params.format.videoSize == VIDEOSIZE_CIF ? 288:144, - cam->params.format.videoSize == VIDEOSIZE_CIF ? 240:120); - seq_printf(m, "sub_sample: %8s %8s %8s %8s\n", - cam->params.format.subSample == SUBSAMPLE_420 ? - "420" : "422", "420", "422", "422"); - seq_printf(m, "yuv_order: %8s %8s %8s %8s\n", - cam->params.format.yuvOrder == YUVORDER_YUYV ? - "YUYV" : "UYVY", "YUYV" , "UYVY", "YUYV"); - seq_printf(m, "ecp_timing: %8s %8s %8s %8s\n", - cam->params.ecpTiming ? "slow" : "normal", "slow", - "normal", "normal"); - - if (cam->params.colourBalance.balanceMode == 2) { - sprintf(tmpstr, "auto"); - } else { - sprintf(tmpstr, "manual"); - } - seq_printf(m, "color_balance_mode: %8s %8s %8s" - " %8s\n", tmpstr, "manual", "auto", "auto"); - seq_printf(m, "red_gain: %8d %8d %8d %8d\n", - cam->params.colourBalance.redGain, 0, 212, 32); - seq_printf(m, "green_gain: %8d %8d %8d %8d\n", - cam->params.colourBalance.greenGain, 0, 212, 6); - seq_printf(m, "blue_gain: %8d %8d %8d %8d\n", - cam->params.colourBalance.blueGain, 0, 212, 92); - - if (cam->params.version.firmwareVersion == 1 && - cam->params.version.firmwareRevision == 2) - /* 1-02 firmware limits gain to 2 */ - sprintf(tmpstr, "%8d %8d %8d", 1, 2, 2); - else - sprintf(tmpstr, "%8d %8d %8d", 1, 8, 2); - - if (cam->params.exposure.gainMode == 0) - seq_printf(m, "max_gain: unknown %28s" - " powers of 2\n", tmpstr); - else - seq_printf(m, "max_gain: %8d %28s" - " 1,2,4 or 8 \n", - 1<<(cam->params.exposure.gainMode-1), tmpstr); - - switch(cam->params.exposure.expMode) { - case 1: - case 3: - sprintf(tmpstr, "manual"); - break; - case 2: - sprintf(tmpstr, "auto"); - break; - default: - sprintf(tmpstr, "unknown"); - break; - } - seq_printf(m, "exposure_mode: %8s %8s %8s" - " %8s\n", tmpstr, "manual", "auto", "auto"); - seq_printf(m, "centre_weight: %8s %8s %8s %8s\n", - (2-cam->params.exposure.centreWeight) ? "on" : "off", - "off", "on", "on"); - seq_printf(m, "gain: %8d %8d max_gain %8d 1,2,4,8 possible\n", - 1<params.exposure.gain, 1, 1); - if (cam->params.version.firmwareVersion == 1 && - cam->params.version.firmwareRevision == 2) - /* 1-02 firmware limits fineExp/2 to 127 */ - tmp = 254; - else - tmp = 510; - - seq_printf(m, "fine_exp: %8d %8d %8d %8d\n", - cam->params.exposure.fineExp*2, 0, tmp, 0); - if (cam->params.version.firmwareVersion == 1 && - cam->params.version.firmwareRevision == 2) - /* 1-02 firmware limits coarseExpHi to 0 */ - tmp = MAX_EXP_102; - else - tmp = MAX_EXP; - - seq_printf(m, "coarse_exp: %8d %8d %8d" - " %8d\n", cam->params.exposure.coarseExpLo+ - 256*cam->params.exposure.coarseExpHi, 0, tmp, 185); - seq_printf(m, "red_comp: %8d %8d %8d %8d\n", - cam->params.exposure.redComp, COMP_RED, 255, COMP_RED); - seq_printf(m, "green1_comp: %8d %8d %8d %8d\n", - cam->params.exposure.green1Comp, COMP_GREEN1, 255, - COMP_GREEN1); - seq_printf(m, "green2_comp: %8d %8d %8d %8d\n", - cam->params.exposure.green2Comp, COMP_GREEN2, 255, - COMP_GREEN2); - seq_printf(m, "blue_comp: %8d %8d %8d %8d\n", - cam->params.exposure.blueComp, COMP_BLUE, 255, COMP_BLUE); - - seq_printf(m, "apcor_gain1: %#8x %#8x %#8x %#8x\n", - cam->params.apcor.gain1, 0, 0xff, 0x1c); - seq_printf(m, "apcor_gain2: %#8x %#8x %#8x %#8x\n", - cam->params.apcor.gain2, 0, 0xff, 0x1a); - seq_printf(m, "apcor_gain4: %#8x %#8x %#8x %#8x\n", - cam->params.apcor.gain4, 0, 0xff, 0x2d); - seq_printf(m, "apcor_gain8: %#8x %#8x %#8x %#8x\n", - cam->params.apcor.gain8, 0, 0xff, 0x2a); - seq_printf(m, "vl_offset_gain1: %8d %8d %8d %8d\n", - cam->params.vlOffset.gain1, 0, 255, 24); - seq_printf(m, "vl_offset_gain2: %8d %8d %8d %8d\n", - cam->params.vlOffset.gain2, 0, 255, 28); - seq_printf(m, "vl_offset_gain4: %8d %8d %8d %8d\n", - cam->params.vlOffset.gain4, 0, 255, 30); - seq_printf(m, "vl_offset_gain8: %8d %8d %8d %8d\n", - cam->params.vlOffset.gain8, 0, 255, 30); - seq_printf(m, "flicker_control: %8s %8s %8s %8s\n", - cam->params.flickerControl.flickerMode ? "on" : "off", - "off", "on", "off"); - seq_printf(m, "mains_frequency: %8d %8d %8d %8d" - " only 50/60\n", - cam->mainsFreq ? 60 : 50, 50, 60, 50); - if(cam->params.flickerControl.allowableOverExposure < 0) - seq_printf(m, "allowable_overexposure: %4dauto auto %8d auto\n", - -cam->params.flickerControl.allowableOverExposure, - 255); - else - seq_printf(m, "allowable_overexposure: %8d auto %8d auto\n", - cam->params.flickerControl.allowableOverExposure, - 255); - seq_printf(m, "compression_mode: "); - switch(cam->params.compression.mode) { - case CPIA_COMPRESSION_NONE: - seq_printf(m, "%8s", "none"); - break; - case CPIA_COMPRESSION_AUTO: - seq_printf(m, "%8s", "auto"); - break; - case CPIA_COMPRESSION_MANUAL: - seq_printf(m, "%8s", "manual"); - break; - default: - seq_printf(m, "%8s", "unknown"); - break; - } - seq_printf(m, " none,auto,manual auto\n"); - seq_printf(m, "decimation_enable: %8s %8s %8s %8s\n", - cam->params.compression.decimation == - DECIMATION_ENAB ? "on":"off", "off", "on", - "off"); - seq_printf(m, "compression_target: %9s %9s %9s %9s\n", - cam->params.compressionTarget.frTargeting == - CPIA_COMPRESSION_TARGET_FRAMERATE ? - "framerate":"quality", - "framerate", "quality", "quality"); - seq_printf(m, "target_framerate: %8d %8d %8d %8d\n", - cam->params.compressionTarget.targetFR, 1, 30, 15); - seq_printf(m, "target_quality: %8d %8d %8d %8d\n", - cam->params.compressionTarget.targetQ, 1, 64, 5); - seq_printf(m, "y_threshold: %8d %8d %8d %8d\n", - cam->params.yuvThreshold.yThreshold, 0, 31, 6); - seq_printf(m, "uv_threshold: %8d %8d %8d %8d\n", - cam->params.yuvThreshold.uvThreshold, 0, 31, 6); - seq_printf(m, "hysteresis: %8d %8d %8d %8d\n", - cam->params.compressionParams.hysteresis, 0, 255, 3); - seq_printf(m, "threshold_max: %8d %8d %8d %8d\n", - cam->params.compressionParams.threshMax, 0, 255, 11); - seq_printf(m, "small_step: %8d %8d %8d %8d\n", - cam->params.compressionParams.smallStep, 0, 255, 1); - seq_printf(m, "large_step: %8d %8d %8d %8d\n", - cam->params.compressionParams.largeStep, 0, 255, 3); - seq_printf(m, "decimation_hysteresis: %8d %8d %8d %8d\n", - cam->params.compressionParams.decimationHysteresis, - 0, 255, 2); - seq_printf(m, "fr_diff_step_thresh: %8d %8d %8d %8d\n", - cam->params.compressionParams.frDiffStepThresh, - 0, 255, 5); - seq_printf(m, "q_diff_step_thresh: %8d %8d %8d %8d\n", - cam->params.compressionParams.qDiffStepThresh, - 0, 255, 3); - seq_printf(m, "decimation_thresh_mod: %8d %8d %8d %8d\n", - cam->params.compressionParams.decimationThreshMod, - 0, 255, 2); - /* QX3 specific entries */ - if (cam->params.qx3.qx3_detected) { - seq_printf(m, "toplight: %8s %8s %8s %8s\n", - cam->params.qx3.toplight ? "on" : "off", - "off", "on", "off"); - seq_printf(m, "bottomlight: %8s %8s %8s %8s\n", - cam->params.qx3.bottomlight ? "on" : "off", - "off", "on", "off"); - } - - return 0; -} - -static int cpia_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, cpia_proc_show, PDE(inode)->data); -} - -static int match(char *checkstr, char **buffer, size_t *count, - int *find_colon, int *err) -{ - int ret, colon_found = 1; - int len = strlen(checkstr); - ret = (len <= *count && strncmp(*buffer, checkstr, len) == 0); - if (ret) { - *buffer += len; - *count -= len; - if (*find_colon) { - colon_found = 0; - while (*count && (**buffer == ' ' || **buffer == '\t' || - (!colon_found && **buffer == ':'))) { - if (**buffer == ':') - colon_found = 1; - --*count; - ++*buffer; - } - if (!*count || !colon_found) - *err = -EINVAL; - *find_colon = 0; - } - } - return ret; -} - -static unsigned long int value(char **buffer, size_t *count, int *err) -{ - char *p; - unsigned long int ret; - ret = simple_strtoul(*buffer, &p, 0); - if (p == *buffer) - *err = -EINVAL; - else { - *count -= p - *buffer; - *buffer = p; - } - return ret; -} - -static ssize_t cpia_proc_write(struct file *file, const char __user *buf, - size_t count, loff_t *pos) -{ - struct cam_data *cam = PDE(file->f_path.dentry->d_inode)->data; - struct cam_params new_params; - char *page, *buffer; - int retval, find_colon; - int size = count; - unsigned long val = 0; - u32 command_flags = 0; - u8 new_mains; - - /* - * This code to copy from buf to page is shamelessly copied - * from the comx driver - */ - if (count > PAGE_SIZE) { - printk(KERN_ERR "count is %zu > %d!!!\n", count, (int)PAGE_SIZE); - return -ENOSPC; - } - - if (!(page = (char *)__get_free_page(GFP_KERNEL))) return -ENOMEM; - - if(copy_from_user(page, buf, count)) - { - retval = -EFAULT; - goto out; - } - - if (page[count-1] == '\n') - page[count-1] = '\0'; - else if (count < PAGE_SIZE) - page[count] = '\0'; - else if (page[count]) { - retval = -EINVAL; - goto out; - } - - buffer = page; - - if (mutex_lock_interruptible(&cam->param_lock)) - return -ERESTARTSYS; - - /* - * Skip over leading whitespace - */ - while (count && isspace(*buffer)) { - --count; - ++buffer; - } - - memcpy(&new_params, &cam->params, sizeof(struct cam_params)); - new_mains = cam->mainsFreq; - -#define MATCH(x) (match(x, &buffer, &count, &find_colon, &retval)) -#define VALUE (value(&buffer,&count, &retval)) -#define FIRMWARE_VERSION(x,y) (new_params.version.firmwareVersion == (x) && \ - new_params.version.firmwareRevision == (y)) - - retval = 0; - while (count && !retval) { - find_colon = 1; - if (MATCH("brightness")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val <= 100) - new_params.colourParams.brightness = val; - else - retval = -EINVAL; - } - command_flags |= COMMAND_SETCOLOURPARAMS; - if(new_params.flickerControl.allowableOverExposure < 0) - new_params.flickerControl.allowableOverExposure = - -find_over_exposure(new_params.colourParams.brightness); - if(new_params.flickerControl.flickerMode != 0) - command_flags |= COMMAND_SETFLICKERCTRL; - - } else if (MATCH("contrast")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val <= 100) { - /* contrast is in steps of 8, so round*/ - val = ((val + 3) / 8) * 8; - /* 1-02 firmware limits contrast to 80*/ - if (FIRMWARE_VERSION(1,2) && val > 80) - val = 80; - - new_params.colourParams.contrast = val; - } else - retval = -EINVAL; - } - command_flags |= COMMAND_SETCOLOURPARAMS; - } else if (MATCH("saturation")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val <= 100) - new_params.colourParams.saturation = val; - else - retval = -EINVAL; - } - command_flags |= COMMAND_SETCOLOURPARAMS; - } else if (MATCH("sensor_fps")) { - if (!retval) - val = VALUE; - - if (!retval) { - /* find values so that sensorFPS is minimized, - * but >= val */ - if (val > 30) - retval = -EINVAL; - else if (val > 25) { - new_params.sensorFps.divisor = 0; - new_params.sensorFps.baserate = 1; - } else if (val > 15) { - new_params.sensorFps.divisor = 0; - new_params.sensorFps.baserate = 0; - } else if (val > 12) { - new_params.sensorFps.divisor = 1; - new_params.sensorFps.baserate = 1; - } else if (val > 7) { - new_params.sensorFps.divisor = 1; - new_params.sensorFps.baserate = 0; - } else if (val > 6) { - new_params.sensorFps.divisor = 2; - new_params.sensorFps.baserate = 1; - } else if (val > 3) { - new_params.sensorFps.divisor = 2; - new_params.sensorFps.baserate = 0; - } else { - new_params.sensorFps.divisor = 3; - /* Either base rate would work here */ - new_params.sensorFps.baserate = 1; - } - new_params.flickerControl.coarseJump = - flicker_jumps[new_mains] - [new_params.sensorFps.baserate] - [new_params.sensorFps.divisor]; - if (new_params.flickerControl.flickerMode) - command_flags |= COMMAND_SETFLICKERCTRL; - } - command_flags |= COMMAND_SETSENSORFPS; - cam->exposure_status = EXPOSURE_NORMAL; - } else if (MATCH("stream_start_line")) { - if (!retval) - val = VALUE; - - if (!retval) { - int max_line = 288; - - if (new_params.format.videoSize == VIDEOSIZE_QCIF) - max_line = 144; - if (val <= max_line) - new_params.streamStartLine = val/2; - else - retval = -EINVAL; - } - } else if (MATCH("sub_sample")) { - if (!retval && MATCH("420")) - new_params.format.subSample = SUBSAMPLE_420; - else if (!retval && MATCH("422")) - new_params.format.subSample = SUBSAMPLE_422; - else - retval = -EINVAL; - - command_flags |= COMMAND_SETFORMAT; - } else if (MATCH("yuv_order")) { - if (!retval && MATCH("YUYV")) - new_params.format.yuvOrder = YUVORDER_YUYV; - else if (!retval && MATCH("UYVY")) - new_params.format.yuvOrder = YUVORDER_UYVY; - else - retval = -EINVAL; - - command_flags |= COMMAND_SETFORMAT; - } else if (MATCH("ecp_timing")) { - if (!retval && MATCH("normal")) - new_params.ecpTiming = 0; - else if (!retval && MATCH("slow")) - new_params.ecpTiming = 1; - else - retval = -EINVAL; - - command_flags |= COMMAND_SETECPTIMING; - } else if (MATCH("color_balance_mode")) { - if (!retval && MATCH("manual")) - new_params.colourBalance.balanceMode = 3; - else if (!retval && MATCH("auto")) - new_params.colourBalance.balanceMode = 2; - else - retval = -EINVAL; - - command_flags |= COMMAND_SETCOLOURBALANCE; - } else if (MATCH("red_gain")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val <= 212) { - new_params.colourBalance.redGain = val; - new_params.colourBalance.balanceMode = 1; - } else - retval = -EINVAL; - } - command_flags |= COMMAND_SETCOLOURBALANCE; - } else if (MATCH("green_gain")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val <= 212) { - new_params.colourBalance.greenGain = val; - new_params.colourBalance.balanceMode = 1; - } else - retval = -EINVAL; - } - command_flags |= COMMAND_SETCOLOURBALANCE; - } else if (MATCH("blue_gain")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val <= 212) { - new_params.colourBalance.blueGain = val; - new_params.colourBalance.balanceMode = 1; - } else - retval = -EINVAL; - } - command_flags |= COMMAND_SETCOLOURBALANCE; - } else if (MATCH("max_gain")) { - if (!retval) - val = VALUE; - - if (!retval) { - /* 1-02 firmware limits gain to 2 */ - if (FIRMWARE_VERSION(1,2) && val > 2) - val = 2; - switch(val) { - case 1: - new_params.exposure.gainMode = 1; - break; - case 2: - new_params.exposure.gainMode = 2; - break; - case 4: - new_params.exposure.gainMode = 3; - break; - case 8: - new_params.exposure.gainMode = 4; - break; - default: - retval = -EINVAL; - break; - } - } - command_flags |= COMMAND_SETEXPOSURE; - } else if (MATCH("exposure_mode")) { - if (!retval && MATCH("auto")) - new_params.exposure.expMode = 2; - else if (!retval && MATCH("manual")) { - if (new_params.exposure.expMode == 2) - new_params.exposure.expMode = 3; - if(new_params.flickerControl.flickerMode != 0) - command_flags |= COMMAND_SETFLICKERCTRL; - new_params.flickerControl.flickerMode = 0; - } else - retval = -EINVAL; - - command_flags |= COMMAND_SETEXPOSURE; - } else if (MATCH("centre_weight")) { - if (!retval && MATCH("on")) - new_params.exposure.centreWeight = 1; - else if (!retval && MATCH("off")) - new_params.exposure.centreWeight = 2; - else - retval = -EINVAL; - - command_flags |= COMMAND_SETEXPOSURE; - } else if (MATCH("gain")) { - if (!retval) - val = VALUE; - - if (!retval) { - switch(val) { - case 1: - new_params.exposure.gain = 0; - break; - case 2: - new_params.exposure.gain = 1; - break; - case 4: - new_params.exposure.gain = 2; - break; - case 8: - new_params.exposure.gain = 3; - break; - default: - retval = -EINVAL; - break; - } - new_params.exposure.expMode = 1; - if(new_params.flickerControl.flickerMode != 0) - command_flags |= COMMAND_SETFLICKERCTRL; - new_params.flickerControl.flickerMode = 0; - command_flags |= COMMAND_SETEXPOSURE; - if (new_params.exposure.gain > - new_params.exposure.gainMode-1) - retval = -EINVAL; - } - } else if (MATCH("fine_exp")) { - if (!retval) - val = VALUE/2; - - if (!retval) { - if (val < 256) { - /* 1-02 firmware limits fineExp/2 to 127*/ - if (FIRMWARE_VERSION(1,2) && val > 127) - val = 127; - new_params.exposure.fineExp = val; - new_params.exposure.expMode = 1; - command_flags |= COMMAND_SETEXPOSURE; - if(new_params.flickerControl.flickerMode != 0) - command_flags |= COMMAND_SETFLICKERCTRL; - new_params.flickerControl.flickerMode = 0; - command_flags |= COMMAND_SETFLICKERCTRL; - } else - retval = -EINVAL; - } - } else if (MATCH("coarse_exp")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val <= MAX_EXP) { - if (FIRMWARE_VERSION(1,2) && - val > MAX_EXP_102) - val = MAX_EXP_102; - new_params.exposure.coarseExpLo = - val & 0xff; - new_params.exposure.coarseExpHi = - val >> 8; - new_params.exposure.expMode = 1; - command_flags |= COMMAND_SETEXPOSURE; - if(new_params.flickerControl.flickerMode != 0) - command_flags |= COMMAND_SETFLICKERCTRL; - new_params.flickerControl.flickerMode = 0; - command_flags |= COMMAND_SETFLICKERCTRL; - } else - retval = -EINVAL; - } - } else if (MATCH("red_comp")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val >= COMP_RED && val <= 255) { - new_params.exposure.redComp = val; - new_params.exposure.compMode = 1; - command_flags |= COMMAND_SETEXPOSURE; - } else - retval = -EINVAL; - } - } else if (MATCH("green1_comp")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val >= COMP_GREEN1 && val <= 255) { - new_params.exposure.green1Comp = val; - new_params.exposure.compMode = 1; - command_flags |= COMMAND_SETEXPOSURE; - } else - retval = -EINVAL; - } - } else if (MATCH("green2_comp")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val >= COMP_GREEN2 && val <= 255) { - new_params.exposure.green2Comp = val; - new_params.exposure.compMode = 1; - command_flags |= COMMAND_SETEXPOSURE; - } else - retval = -EINVAL; - } - } else if (MATCH("blue_comp")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val >= COMP_BLUE && val <= 255) { - new_params.exposure.blueComp = val; - new_params.exposure.compMode = 1; - command_flags |= COMMAND_SETEXPOSURE; - } else - retval = -EINVAL; - } - } else if (MATCH("apcor_gain1")) { - if (!retval) - val = VALUE; - - if (!retval) { - command_flags |= COMMAND_SETAPCOR; - if (val <= 0xff) - new_params.apcor.gain1 = val; - else - retval = -EINVAL; - } - } else if (MATCH("apcor_gain2")) { - if (!retval) - val = VALUE; - - if (!retval) { - command_flags |= COMMAND_SETAPCOR; - if (val <= 0xff) - new_params.apcor.gain2 = val; - else - retval = -EINVAL; - } - } else if (MATCH("apcor_gain4")) { - if (!retval) - val = VALUE; - - if (!retval) { - command_flags |= COMMAND_SETAPCOR; - if (val <= 0xff) - new_params.apcor.gain4 = val; - else - retval = -EINVAL; - } - } else if (MATCH("apcor_gain8")) { - if (!retval) - val = VALUE; - - if (!retval) { - command_flags |= COMMAND_SETAPCOR; - if (val <= 0xff) - new_params.apcor.gain8 = val; - else - retval = -EINVAL; - } - } else if (MATCH("vl_offset_gain1")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val <= 0xff) - new_params.vlOffset.gain1 = val; - else - retval = -EINVAL; - } - command_flags |= COMMAND_SETVLOFFSET; - } else if (MATCH("vl_offset_gain2")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val <= 0xff) - new_params.vlOffset.gain2 = val; - else - retval = -EINVAL; - } - command_flags |= COMMAND_SETVLOFFSET; - } else if (MATCH("vl_offset_gain4")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val <= 0xff) - new_params.vlOffset.gain4 = val; - else - retval = -EINVAL; - } - command_flags |= COMMAND_SETVLOFFSET; - } else if (MATCH("vl_offset_gain8")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val <= 0xff) - new_params.vlOffset.gain8 = val; - else - retval = -EINVAL; - } - command_flags |= COMMAND_SETVLOFFSET; - } else if (MATCH("flicker_control")) { - if (!retval && MATCH("on")) { - set_flicker(&new_params, &command_flags, 1); - } else if (!retval && MATCH("off")) { - set_flicker(&new_params, &command_flags, 0); - } else - retval = -EINVAL; - - command_flags |= COMMAND_SETFLICKERCTRL; - } else if (MATCH("mains_frequency")) { - if (!retval && MATCH("50")) { - new_mains = 0; - new_params.flickerControl.coarseJump = - flicker_jumps[new_mains] - [new_params.sensorFps.baserate] - [new_params.sensorFps.divisor]; - if (new_params.flickerControl.flickerMode) - command_flags |= COMMAND_SETFLICKERCTRL; - } else if (!retval && MATCH("60")) { - new_mains = 1; - new_params.flickerControl.coarseJump = - flicker_jumps[new_mains] - [new_params.sensorFps.baserate] - [new_params.sensorFps.divisor]; - if (new_params.flickerControl.flickerMode) - command_flags |= COMMAND_SETFLICKERCTRL; - } else - retval = -EINVAL; - } else if (MATCH("allowable_overexposure")) { - if (!retval && MATCH("auto")) { - new_params.flickerControl.allowableOverExposure = - -find_over_exposure(new_params.colourParams.brightness); - if(new_params.flickerControl.flickerMode != 0) - command_flags |= COMMAND_SETFLICKERCTRL; - } else { - if (!retval) - val = VALUE; - - if (!retval) { - if (val <= 0xff) { - new_params.flickerControl. - allowableOverExposure = val; - if(new_params.flickerControl.flickerMode != 0) - command_flags |= COMMAND_SETFLICKERCTRL; - } else - retval = -EINVAL; - } - } - } else if (MATCH("compression_mode")) { - if (!retval && MATCH("none")) - new_params.compression.mode = - CPIA_COMPRESSION_NONE; - else if (!retval && MATCH("auto")) - new_params.compression.mode = - CPIA_COMPRESSION_AUTO; - else if (!retval && MATCH("manual")) - new_params.compression.mode = - CPIA_COMPRESSION_MANUAL; - else - retval = -EINVAL; - - command_flags |= COMMAND_SETCOMPRESSION; - } else if (MATCH("decimation_enable")) { - if (!retval && MATCH("off")) - new_params.compression.decimation = 0; - else if (!retval && MATCH("on")) - new_params.compression.decimation = 1; - else - retval = -EINVAL; - - command_flags |= COMMAND_SETCOMPRESSION; - } else if (MATCH("compression_target")) { - if (!retval && MATCH("quality")) - new_params.compressionTarget.frTargeting = - CPIA_COMPRESSION_TARGET_QUALITY; - else if (!retval && MATCH("framerate")) - new_params.compressionTarget.frTargeting = - CPIA_COMPRESSION_TARGET_FRAMERATE; - else - retval = -EINVAL; - - command_flags |= COMMAND_SETCOMPRESSIONTARGET; - } else if (MATCH("target_framerate")) { - if (!retval) - val = VALUE; - - if (!retval) { - if(val > 0 && val <= 30) - new_params.compressionTarget.targetFR = val; - else - retval = -EINVAL; - } - command_flags |= COMMAND_SETCOMPRESSIONTARGET; - } else if (MATCH("target_quality")) { - if (!retval) - val = VALUE; - - if (!retval) { - if(val > 0 && val <= 64) - new_params.compressionTarget.targetQ = val; - else - retval = -EINVAL; - } - command_flags |= COMMAND_SETCOMPRESSIONTARGET; - } else if (MATCH("y_threshold")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val < 32) - new_params.yuvThreshold.yThreshold = val; - else - retval = -EINVAL; - } - command_flags |= COMMAND_SETYUVTHRESH; - } else if (MATCH("uv_threshold")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val < 32) - new_params.yuvThreshold.uvThreshold = val; - else - retval = -EINVAL; - } - command_flags |= COMMAND_SETYUVTHRESH; - } else if (MATCH("hysteresis")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val <= 0xff) - new_params.compressionParams.hysteresis = val; - else - retval = -EINVAL; - } - command_flags |= COMMAND_SETCOMPRESSIONPARAMS; - } else if (MATCH("threshold_max")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val <= 0xff) - new_params.compressionParams.threshMax = val; - else - retval = -EINVAL; - } - command_flags |= COMMAND_SETCOMPRESSIONPARAMS; - } else if (MATCH("small_step")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val <= 0xff) - new_params.compressionParams.smallStep = val; - else - retval = -EINVAL; - } - command_flags |= COMMAND_SETCOMPRESSIONPARAMS; - } else if (MATCH("large_step")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val <= 0xff) - new_params.compressionParams.largeStep = val; - else - retval = -EINVAL; - } - command_flags |= COMMAND_SETCOMPRESSIONPARAMS; - } else if (MATCH("decimation_hysteresis")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val <= 0xff) - new_params.compressionParams.decimationHysteresis = val; - else - retval = -EINVAL; - } - command_flags |= COMMAND_SETCOMPRESSIONPARAMS; - } else if (MATCH("fr_diff_step_thresh")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val <= 0xff) - new_params.compressionParams.frDiffStepThresh = val; - else - retval = -EINVAL; - } - command_flags |= COMMAND_SETCOMPRESSIONPARAMS; - } else if (MATCH("q_diff_step_thresh")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val <= 0xff) - new_params.compressionParams.qDiffStepThresh = val; - else - retval = -EINVAL; - } - command_flags |= COMMAND_SETCOMPRESSIONPARAMS; - } else if (MATCH("decimation_thresh_mod")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val <= 0xff) - new_params.compressionParams.decimationThreshMod = val; - else - retval = -EINVAL; - } - command_flags |= COMMAND_SETCOMPRESSIONPARAMS; - } else if (MATCH("toplight")) { - if (!retval && MATCH("on")) - new_params.qx3.toplight = 1; - else if (!retval && MATCH("off")) - new_params.qx3.toplight = 0; - else - retval = -EINVAL; - command_flags |= COMMAND_SETLIGHTS; - } else if (MATCH("bottomlight")) { - if (!retval && MATCH("on")) - new_params.qx3.bottomlight = 1; - else if (!retval && MATCH("off")) - new_params.qx3.bottomlight = 0; - else - retval = -EINVAL; - command_flags |= COMMAND_SETLIGHTS; - } else { - DBG("No match found\n"); - retval = -EINVAL; - } - - if (!retval) { - while (count && isspace(*buffer) && *buffer != '\n') { - --count; - ++buffer; - } - if (count) { - if (*buffer == '\0' && count != 1) - retval = -EINVAL; - else if (*buffer != '\n' && *buffer != ';' && - *buffer != '\0') - retval = -EINVAL; - else { - --count; - ++buffer; - } - } - } - } -#undef MATCH -#undef VALUE -#undef FIRMWARE_VERSION - if (!retval) { - if (command_flags & COMMAND_SETCOLOURPARAMS) { - /* Adjust cam->vp to reflect these changes */ - cam->vp.brightness = - new_params.colourParams.brightness*65535/100; - cam->vp.contrast = - new_params.colourParams.contrast*65535/100; - cam->vp.colour = - new_params.colourParams.saturation*65535/100; - } - if((command_flags & COMMAND_SETEXPOSURE) && - new_params.exposure.expMode == 2) - cam->exposure_status = EXPOSURE_NORMAL; - - memcpy(&cam->params, &new_params, sizeof(struct cam_params)); - cam->mainsFreq = new_mains; - cam->cmd_queue |= command_flags; - retval = size; - } else - DBG("error: %d\n", retval); - - mutex_unlock(&cam->param_lock); - -out: - free_page((unsigned long)page); - return retval; -} - -static const struct file_operations cpia_proc_fops = { - .owner = THIS_MODULE, - .open = cpia_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .write = cpia_proc_write, -}; - -static void create_proc_cpia_cam(struct cam_data *cam) -{ - struct proc_dir_entry *ent; - - if (!cpia_proc_root || !cam) - return; - - ent = proc_create_data(video_device_node_name(&cam->vdev), - S_IRUGO|S_IWUSR, cpia_proc_root, - &cpia_proc_fops, cam); - if (!ent) - return; - - /* - size of the proc entry is 3736 bytes for the standard webcam; - the extra features of the QX3 microscope add 189 bytes. - (we have not yet probed the camera to see which type it is). - */ - ent->size = 3736 + 189; - cam->proc_entry = ent; -} - -static void destroy_proc_cpia_cam(struct cam_data *cam) -{ - if (!cam || !cam->proc_entry) - return; - - remove_proc_entry(video_device_node_name(&cam->vdev), cpia_proc_root); - cam->proc_entry = NULL; -} - -static void proc_cpia_create(void) -{ - cpia_proc_root = proc_mkdir("cpia", NULL); - - if (!cpia_proc_root) - LOG("Unable to initialise /proc/cpia\n"); -} - -static void __exit proc_cpia_destroy(void) -{ - remove_proc_entry("cpia", NULL); -} -#endif /* CONFIG_PROC_FS */ - -/* ----------------------- debug functions ---------------------- */ - -#define printstatus(cam) \ - DBG("%02x %02x %02x %02x %02x %02x %02x %02x\n",\ - cam->params.status.systemState, cam->params.status.grabState, \ - cam->params.status.streamState, cam->params.status.fatalError, \ - cam->params.status.cmdError, cam->params.status.debugFlags, \ - cam->params.status.vpStatus, cam->params.status.errorCode); - -/* ----------------------- v4l helpers -------------------------- */ - -/* supported frame palettes and depths */ -static inline int valid_mode(u16 palette, u16 depth) -{ - if ((palette == VIDEO_PALETTE_YUV422 && depth == 16) || - (palette == VIDEO_PALETTE_YUYV && depth == 16)) - return 1; - - if (colorspace_conv) - return (palette == VIDEO_PALETTE_GREY && depth == 8) || - (palette == VIDEO_PALETTE_RGB555 && depth == 16) || - (palette == VIDEO_PALETTE_RGB565 && depth == 16) || - (palette == VIDEO_PALETTE_RGB24 && depth == 24) || - (palette == VIDEO_PALETTE_RGB32 && depth == 32) || - (palette == VIDEO_PALETTE_UYVY && depth == 16); - - return 0; -} - -static int match_videosize( int width, int height ) -{ - /* return the best match, where 'best' is as always - * the largest that is not bigger than what is requested. */ - if (width>=352 && height>=288) - return VIDEOSIZE_352_288; /* CIF */ - - if (width>=320 && height>=240) - return VIDEOSIZE_320_240; /* SIF */ - - if (width>=288 && height>=216) - return VIDEOSIZE_288_216; - - if (width>=256 && height>=192) - return VIDEOSIZE_256_192; - - if (width>=224 && height>=168) - return VIDEOSIZE_224_168; - - if (width>=192 && height>=144) - return VIDEOSIZE_192_144; - - if (width>=176 && height>=144) - return VIDEOSIZE_176_144; /* QCIF */ - - if (width>=160 && height>=120) - return VIDEOSIZE_160_120; /* QSIF */ - - if (width>=128 && height>=96) - return VIDEOSIZE_128_96; - - if (width>=88 && height>=72) - return VIDEOSIZE_88_72; - - if (width>=64 && height>=48) - return VIDEOSIZE_64_48; - - if (width>=48 && height>=48) - return VIDEOSIZE_48_48; - - return -1; -} - -/* these are the capture sizes we support */ -static void set_vw_size(struct cam_data *cam) -{ - /* the col/row/start/end values are the result of simple math */ - /* study the SetROI-command in cpia developers guide p 2-22 */ - /* streamStartLine is set to the recommended value in the cpia */ - /* developers guide p 3-37 */ - switch(cam->video_size) { - case VIDEOSIZE_CIF: - cam->vw.width = 352; - cam->vw.height = 288; - cam->params.format.videoSize=VIDEOSIZE_CIF; - cam->params.roi.colStart=0; - cam->params.roi.rowStart=0; - cam->params.streamStartLine = 120; - break; - case VIDEOSIZE_SIF: - cam->vw.width = 320; - cam->vw.height = 240; - cam->params.format.videoSize=VIDEOSIZE_CIF; - cam->params.roi.colStart=2; - cam->params.roi.rowStart=6; - cam->params.streamStartLine = 120; - break; - case VIDEOSIZE_288_216: - cam->vw.width = 288; - cam->vw.height = 216; - cam->params.format.videoSize=VIDEOSIZE_CIF; - cam->params.roi.colStart=4; - cam->params.roi.rowStart=9; - cam->params.streamStartLine = 120; - break; - case VIDEOSIZE_256_192: - cam->vw.width = 256; - cam->vw.height = 192; - cam->params.format.videoSize=VIDEOSIZE_CIF; - cam->params.roi.colStart=6; - cam->params.roi.rowStart=12; - cam->params.streamStartLine = 120; - break; - case VIDEOSIZE_224_168: - cam->vw.width = 224; - cam->vw.height = 168; - cam->params.format.videoSize=VIDEOSIZE_CIF; - cam->params.roi.colStart=8; - cam->params.roi.rowStart=15; - cam->params.streamStartLine = 120; - break; - case VIDEOSIZE_192_144: - cam->vw.width = 192; - cam->vw.height = 144; - cam->params.format.videoSize=VIDEOSIZE_CIF; - cam->params.roi.colStart=10; - cam->params.roi.rowStart=18; - cam->params.streamStartLine = 120; - break; - case VIDEOSIZE_QCIF: - cam->vw.width = 176; - cam->vw.height = 144; - cam->params.format.videoSize=VIDEOSIZE_QCIF; - cam->params.roi.colStart=0; - cam->params.roi.rowStart=0; - cam->params.streamStartLine = 60; - break; - case VIDEOSIZE_QSIF: - cam->vw.width = 160; - cam->vw.height = 120; - cam->params.format.videoSize=VIDEOSIZE_QCIF; - cam->params.roi.colStart=1; - cam->params.roi.rowStart=3; - cam->params.streamStartLine = 60; - break; - case VIDEOSIZE_128_96: - cam->vw.width = 128; - cam->vw.height = 96; - cam->params.format.videoSize=VIDEOSIZE_QCIF; - cam->params.roi.colStart=3; - cam->params.roi.rowStart=6; - cam->params.streamStartLine = 60; - break; - case VIDEOSIZE_88_72: - cam->vw.width = 88; - cam->vw.height = 72; - cam->params.format.videoSize=VIDEOSIZE_QCIF; - cam->params.roi.colStart=5; - cam->params.roi.rowStart=9; - cam->params.streamStartLine = 60; - break; - case VIDEOSIZE_64_48: - cam->vw.width = 64; - cam->vw.height = 48; - cam->params.format.videoSize=VIDEOSIZE_QCIF; - cam->params.roi.colStart=7; - cam->params.roi.rowStart=12; - cam->params.streamStartLine = 60; - break; - case VIDEOSIZE_48_48: - cam->vw.width = 48; - cam->vw.height = 48; - cam->params.format.videoSize=VIDEOSIZE_QCIF; - cam->params.roi.colStart=8; - cam->params.roi.rowStart=6; - cam->params.streamStartLine = 60; - break; - default: - LOG("bad videosize value: %d\n", cam->video_size); - return; - } - - if(cam->vc.width == 0) - cam->vc.width = cam->vw.width; - if(cam->vc.height == 0) - cam->vc.height = cam->vw.height; - - cam->params.roi.colStart += cam->vc.x >> 3; - cam->params.roi.colEnd = cam->params.roi.colStart + - (cam->vc.width >> 3); - cam->params.roi.rowStart += cam->vc.y >> 2; - cam->params.roi.rowEnd = cam->params.roi.rowStart + - (cam->vc.height >> 2); - - return; -} - -static int allocate_frame_buf(struct cam_data *cam) -{ - int i; - - cam->frame_buf = rvmalloc(FRAME_NUM * CPIA_MAX_FRAME_SIZE); - if (!cam->frame_buf) - return -ENOBUFS; - - for (i = 0; i < FRAME_NUM; i++) - cam->frame[i].data = cam->frame_buf + i * CPIA_MAX_FRAME_SIZE; - - return 0; -} - -static int free_frame_buf(struct cam_data *cam) -{ - int i; - - rvfree(cam->frame_buf, FRAME_NUM*CPIA_MAX_FRAME_SIZE); - cam->frame_buf = NULL; - for (i=0; i < FRAME_NUM; i++) - cam->frame[i].data = NULL; - - return 0; -} - - -static inline void free_frames(struct cpia_frame frame[FRAME_NUM]) -{ - int i; - - for (i=0; i < FRAME_NUM; i++) - frame[i].state = FRAME_UNUSED; - return; -} - -/********************************************************************** - * - * General functions - * - **********************************************************************/ -/* send an arbitrary command to the camera */ -static int do_command(struct cam_data *cam, u16 command, u8 a, u8 b, u8 c, u8 d) -{ - int retval, datasize; - u8 cmd[8], data[8]; - - switch(command) { - case CPIA_COMMAND_GetCPIAVersion: - case CPIA_COMMAND_GetPnPID: - case CPIA_COMMAND_GetCameraStatus: - case CPIA_COMMAND_GetVPVersion: - datasize=8; - break; - case CPIA_COMMAND_GetColourParams: - case CPIA_COMMAND_GetColourBalance: - case CPIA_COMMAND_GetExposure: - mutex_lock(&cam->param_lock); - datasize=8; - break; - case CPIA_COMMAND_ReadMCPorts: - case CPIA_COMMAND_ReadVCRegs: - datasize = 4; - break; - default: - datasize=0; - break; - } - - cmd[0] = command>>8; - cmd[1] = command&0xff; - cmd[2] = a; - cmd[3] = b; - cmd[4] = c; - cmd[5] = d; - cmd[6] = datasize; - cmd[7] = 0; - - retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data); - if (retval) { - DBG("%x - failed, retval=%d\n", command, retval); - if (command == CPIA_COMMAND_GetColourParams || - command == CPIA_COMMAND_GetColourBalance || - command == CPIA_COMMAND_GetExposure) - mutex_unlock(&cam->param_lock); - } else { - switch(command) { - case CPIA_COMMAND_GetCPIAVersion: - cam->params.version.firmwareVersion = data[0]; - cam->params.version.firmwareRevision = data[1]; - cam->params.version.vcVersion = data[2]; - cam->params.version.vcRevision = data[3]; - break; - case CPIA_COMMAND_GetPnPID: - cam->params.pnpID.vendor = data[0]+(((u16)data[1])<<8); - cam->params.pnpID.product = data[2]+(((u16)data[3])<<8); - cam->params.pnpID.deviceRevision = - data[4]+(((u16)data[5])<<8); - break; - case CPIA_COMMAND_GetCameraStatus: - cam->params.status.systemState = data[0]; - cam->params.status.grabState = data[1]; - cam->params.status.streamState = data[2]; - cam->params.status.fatalError = data[3]; - cam->params.status.cmdError = data[4]; - cam->params.status.debugFlags = data[5]; - cam->params.status.vpStatus = data[6]; - cam->params.status.errorCode = data[7]; - break; - case CPIA_COMMAND_GetVPVersion: - cam->params.vpVersion.vpVersion = data[0]; - cam->params.vpVersion.vpRevision = data[1]; - cam->params.vpVersion.cameraHeadID = - data[2]+(((u16)data[3])<<8); - break; - case CPIA_COMMAND_GetColourParams: - cam->params.colourParams.brightness = data[0]; - cam->params.colourParams.contrast = data[1]; - cam->params.colourParams.saturation = data[2]; - mutex_unlock(&cam->param_lock); - break; - case CPIA_COMMAND_GetColourBalance: - cam->params.colourBalance.redGain = data[0]; - cam->params.colourBalance.greenGain = data[1]; - cam->params.colourBalance.blueGain = data[2]; - mutex_unlock(&cam->param_lock); - break; - case CPIA_COMMAND_GetExposure: - cam->params.exposure.gain = data[0]; - cam->params.exposure.fineExp = data[1]; - cam->params.exposure.coarseExpLo = data[2]; - cam->params.exposure.coarseExpHi = data[3]; - cam->params.exposure.redComp = data[4]; - cam->params.exposure.green1Comp = data[5]; - cam->params.exposure.green2Comp = data[6]; - cam->params.exposure.blueComp = data[7]; - mutex_unlock(&cam->param_lock); - break; - - case CPIA_COMMAND_ReadMCPorts: - if (!cam->params.qx3.qx3_detected) - break; - /* test button press */ - cam->params.qx3.button = ((data[1] & 0x02) == 0); - if (cam->params.qx3.button) { - /* button pressed - unlock the latch */ - do_command(cam,CPIA_COMMAND_WriteMCPort,3,0xDF,0xDF,0); - do_command(cam,CPIA_COMMAND_WriteMCPort,3,0xFF,0xFF,0); - } - - /* test whether microscope is cradled */ - cam->params.qx3.cradled = ((data[2] & 0x40) == 0); - break; - - default: - break; - } - } - return retval; -} - -/* send a command to the camera with an additional data transaction */ -static int do_command_extended(struct cam_data *cam, u16 command, - u8 a, u8 b, u8 c, u8 d, - u8 e, u8 f, u8 g, u8 h, - u8 i, u8 j, u8 k, u8 l) -{ - int retval; - u8 cmd[8], data[8]; - - cmd[0] = command>>8; - cmd[1] = command&0xff; - cmd[2] = a; - cmd[3] = b; - cmd[4] = c; - cmd[5] = d; - cmd[6] = 8; - cmd[7] = 0; - data[0] = e; - data[1] = f; - data[2] = g; - data[3] = h; - data[4] = i; - data[5] = j; - data[6] = k; - data[7] = l; - - retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data); - if (retval) - DBG("%x - failed\n", command); - - return retval; -} - -/********************************************************************** - * - * Colorspace conversion - * - **********************************************************************/ -#define LIMIT(x) ((((x)>0xffffff)?0xff0000:(((x)<=0xffff)?0:(x)&0xff0000))>>16) - -static int convert420(unsigned char *yuv, unsigned char *rgb, int out_fmt, - int linesize, int mmap_kludge) -{ - int y, u, v, r, g, b, y1; - - /* Odd lines use the same u and v as the previous line. - * Because of compression, it is necessary to get this - * information from the decoded image. */ - switch(out_fmt) { - case VIDEO_PALETTE_RGB555: - y = (*yuv++ - 16) * 76310; - y1 = (*yuv - 16) * 76310; - r = ((*(rgb+1-linesize)) & 0x7c) << 1; - g = ((*(rgb-linesize)) & 0xe0) >> 4 | - ((*(rgb+1-linesize)) & 0x03) << 6; - b = ((*(rgb-linesize)) & 0x1f) << 3; - u = (-53294 * r - 104635 * g + 157929 * b) / 5756495; - v = (157968 * r - 132278 * g - 25690 * b) / 5366159; - r = 104635 * v; - g = -25690 * u - 53294 * v; - b = 132278 * u; - *rgb++ = ((LIMIT(g+y) & 0xf8) << 2) | (LIMIT(b+y) >> 3); - *rgb++ = ((LIMIT(r+y) & 0xf8) >> 1) | (LIMIT(g+y) >> 6); - *rgb++ = ((LIMIT(g+y1) & 0xf8) << 2) | (LIMIT(b+y1) >> 3); - *rgb = ((LIMIT(r+y1) & 0xf8) >> 1) | (LIMIT(g+y1) >> 6); - return 4; - case VIDEO_PALETTE_RGB565: - y = (*yuv++ - 16) * 76310; - y1 = (*yuv - 16) * 76310; - r = (*(rgb+1-linesize)) & 0xf8; - g = ((*(rgb-linesize)) & 0xe0) >> 3 | - ((*(rgb+1-linesize)) & 0x07) << 5; - b = ((*(rgb-linesize)) & 0x1f) << 3; - u = (-53294 * r - 104635 * g + 157929 * b) / 5756495; - v = (157968 * r - 132278 * g - 25690 * b) / 5366159; - r = 104635 * v; - g = -25690 * u - 53294 * v; - b = 132278 * u; - *rgb++ = ((LIMIT(g+y) & 0xfc) << 3) | (LIMIT(b+y) >> 3); - *rgb++ = (LIMIT(r+y) & 0xf8) | (LIMIT(g+y) >> 5); - *rgb++ = ((LIMIT(g+y1) & 0xfc) << 3) | (LIMIT(b+y1) >> 3); - *rgb = (LIMIT(r+y1) & 0xf8) | (LIMIT(g+y1) >> 5); - return 4; - break; - case VIDEO_PALETTE_RGB24: - case VIDEO_PALETTE_RGB32: - y = (*yuv++ - 16) * 76310; - y1 = (*yuv - 16) * 76310; - if (mmap_kludge) { - r = *(rgb+2-linesize); - g = *(rgb+1-linesize); - b = *(rgb-linesize); - } else { - r = *(rgb-linesize); - g = *(rgb+1-linesize); - b = *(rgb+2-linesize); - } - u = (-53294 * r - 104635 * g + 157929 * b) / 5756495; - v = (157968 * r - 132278 * g - 25690 * b) / 5366159; - r = 104635 * v; - g = -25690 * u + -53294 * v; - b = 132278 * u; - if (mmap_kludge) { - *rgb++ = LIMIT(b+y); - *rgb++ = LIMIT(g+y); - *rgb++ = LIMIT(r+y); - if(out_fmt == VIDEO_PALETTE_RGB32) - rgb++; - *rgb++ = LIMIT(b+y1); - *rgb++ = LIMIT(g+y1); - *rgb = LIMIT(r+y1); - } else { - *rgb++ = LIMIT(r+y); - *rgb++ = LIMIT(g+y); - *rgb++ = LIMIT(b+y); - if(out_fmt == VIDEO_PALETTE_RGB32) - rgb++; - *rgb++ = LIMIT(r+y1); - *rgb++ = LIMIT(g+y1); - *rgb = LIMIT(b+y1); - } - if(out_fmt == VIDEO_PALETTE_RGB32) - return 8; - return 6; - case VIDEO_PALETTE_YUV422: - case VIDEO_PALETTE_YUYV: - y = *yuv++; - u = *(rgb+1-linesize); - y1 = *yuv; - v = *(rgb+3-linesize); - *rgb++ = y; - *rgb++ = u; - *rgb++ = y1; - *rgb = v; - return 4; - case VIDEO_PALETTE_UYVY: - u = *(rgb-linesize); - y = *yuv++; - v = *(rgb+2-linesize); - y1 = *yuv; - *rgb++ = u; - *rgb++ = y; - *rgb++ = v; - *rgb = y1; - return 4; - case VIDEO_PALETTE_GREY: - *rgb++ = *yuv++; - *rgb = *yuv; - return 2; - default: - DBG("Empty: %d\n", out_fmt); - return 0; - } -} - - -static int yuvconvert(unsigned char *yuv, unsigned char *rgb, int out_fmt, - int in_uyvy, int mmap_kludge) -{ - int y, u, v, r, g, b, y1; - - switch(out_fmt) { - case VIDEO_PALETTE_RGB555: - case VIDEO_PALETTE_RGB565: - case VIDEO_PALETTE_RGB24: - case VIDEO_PALETTE_RGB32: - if (in_uyvy) { - u = *yuv++ - 128; - y = (*yuv++ - 16) * 76310; - v = *yuv++ - 128; - y1 = (*yuv - 16) * 76310; - } else { - y = (*yuv++ - 16) * 76310; - u = *yuv++ - 128; - y1 = (*yuv++ - 16) * 76310; - v = *yuv - 128; - } - r = 104635 * v; - g = -25690 * u + -53294 * v; - b = 132278 * u; - break; - default: - y = *yuv++; - u = *yuv++; - y1 = *yuv++; - v = *yuv; - /* Just to avoid compiler warnings */ - r = 0; - g = 0; - b = 0; - break; - } - switch(out_fmt) { - case VIDEO_PALETTE_RGB555: - *rgb++ = ((LIMIT(g+y) & 0xf8) << 2) | (LIMIT(b+y) >> 3); - *rgb++ = ((LIMIT(r+y) & 0xf8) >> 1) | (LIMIT(g+y) >> 6); - *rgb++ = ((LIMIT(g+y1) & 0xf8) << 2) | (LIMIT(b+y1) >> 3); - *rgb = ((LIMIT(r+y1) & 0xf8) >> 1) | (LIMIT(g+y1) >> 6); - return 4; - case VIDEO_PALETTE_RGB565: - *rgb++ = ((LIMIT(g+y) & 0xfc) << 3) | (LIMIT(b+y) >> 3); - *rgb++ = (LIMIT(r+y) & 0xf8) | (LIMIT(g+y) >> 5); - *rgb++ = ((LIMIT(g+y1) & 0xfc) << 3) | (LIMIT(b+y1) >> 3); - *rgb = (LIMIT(r+y1) & 0xf8) | (LIMIT(g+y1) >> 5); - return 4; - case VIDEO_PALETTE_RGB24: - if (mmap_kludge) { - *rgb++ = LIMIT(b+y); - *rgb++ = LIMIT(g+y); - *rgb++ = LIMIT(r+y); - *rgb++ = LIMIT(b+y1); - *rgb++ = LIMIT(g+y1); - *rgb = LIMIT(r+y1); - } else { - *rgb++ = LIMIT(r+y); - *rgb++ = LIMIT(g+y); - *rgb++ = LIMIT(b+y); - *rgb++ = LIMIT(r+y1); - *rgb++ = LIMIT(g+y1); - *rgb = LIMIT(b+y1); - } - return 6; - case VIDEO_PALETTE_RGB32: - if (mmap_kludge) { - *rgb++ = LIMIT(b+y); - *rgb++ = LIMIT(g+y); - *rgb++ = LIMIT(r+y); - rgb++; - *rgb++ = LIMIT(b+y1); - *rgb++ = LIMIT(g+y1); - *rgb = LIMIT(r+y1); - } else { - *rgb++ = LIMIT(r+y); - *rgb++ = LIMIT(g+y); - *rgb++ = LIMIT(b+y); - rgb++; - *rgb++ = LIMIT(r+y1); - *rgb++ = LIMIT(g+y1); - *rgb = LIMIT(b+y1); - } - return 8; - case VIDEO_PALETTE_GREY: - *rgb++ = y; - *rgb = y1; - return 2; - case VIDEO_PALETTE_YUV422: - case VIDEO_PALETTE_YUYV: - *rgb++ = y; - *rgb++ = u; - *rgb++ = y1; - *rgb = v; - return 4; - case VIDEO_PALETTE_UYVY: - *rgb++ = u; - *rgb++ = y; - *rgb++ = v; - *rgb = y1; - return 4; - default: - DBG("Empty: %d\n", out_fmt); - return 0; - } -} - -static int skipcount(int count, int fmt) -{ - switch(fmt) { - case VIDEO_PALETTE_GREY: - return count; - case VIDEO_PALETTE_RGB555: - case VIDEO_PALETTE_RGB565: - case VIDEO_PALETTE_YUV422: - case VIDEO_PALETTE_YUYV: - case VIDEO_PALETTE_UYVY: - return 2*count; - case VIDEO_PALETTE_RGB24: - return 3*count; - case VIDEO_PALETTE_RGB32: - return 4*count; - default: - return 0; - } -} - -static int parse_picture(struct cam_data *cam, int size) -{ - u8 *obuf, *ibuf, *end_obuf; - int ll, in_uyvy, compressed, decimation, even_line, origsize, out_fmt; - int rows, cols, linesize, subsample_422; - - /* make sure params don't change while we are decoding */ - mutex_lock(&cam->param_lock); - - obuf = cam->decompressed_frame.data; - end_obuf = obuf+CPIA_MAX_FRAME_SIZE; - ibuf = cam->raw_image; - origsize = size; - out_fmt = cam->vp.palette; - - if ((ibuf[0] != MAGIC_0) || (ibuf[1] != MAGIC_1)) { - LOG("header not found\n"); - mutex_unlock(&cam->param_lock); - return -1; - } - - if ((ibuf[16] != VIDEOSIZE_QCIF) && (ibuf[16] != VIDEOSIZE_CIF)) { - LOG("wrong video size\n"); - mutex_unlock(&cam->param_lock); - return -1; - } - - if (ibuf[17] != SUBSAMPLE_420 && ibuf[17] != SUBSAMPLE_422) { - LOG("illegal subtype %d\n",ibuf[17]); - mutex_unlock(&cam->param_lock); - return -1; - } - subsample_422 = ibuf[17] == SUBSAMPLE_422; - - if (ibuf[18] != YUVORDER_YUYV && ibuf[18] != YUVORDER_UYVY) { - LOG("illegal yuvorder %d\n",ibuf[18]); - mutex_unlock(&cam->param_lock); - return -1; - } - in_uyvy = ibuf[18] == YUVORDER_UYVY; - - if ((ibuf[24] != cam->params.roi.colStart) || - (ibuf[25] != cam->params.roi.colEnd) || - (ibuf[26] != cam->params.roi.rowStart) || - (ibuf[27] != cam->params.roi.rowEnd)) { - LOG("ROI mismatch\n"); - mutex_unlock(&cam->param_lock); - return -1; - } - cols = 8*(ibuf[25] - ibuf[24]); - rows = 4*(ibuf[27] - ibuf[26]); - - - if ((ibuf[28] != NOT_COMPRESSED) && (ibuf[28] != COMPRESSED)) { - LOG("illegal compression %d\n",ibuf[28]); - mutex_unlock(&cam->param_lock); - return -1; - } - compressed = (ibuf[28] == COMPRESSED); - - if (ibuf[29] != NO_DECIMATION && ibuf[29] != DECIMATION_ENAB) { - LOG("illegal decimation %d\n",ibuf[29]); - mutex_unlock(&cam->param_lock); - return -1; - } - decimation = (ibuf[29] == DECIMATION_ENAB); - - cam->params.yuvThreshold.yThreshold = ibuf[30]; - cam->params.yuvThreshold.uvThreshold = ibuf[31]; - cam->params.status.systemState = ibuf[32]; - cam->params.status.grabState = ibuf[33]; - cam->params.status.streamState = ibuf[34]; - cam->params.status.fatalError = ibuf[35]; - cam->params.status.cmdError = ibuf[36]; - cam->params.status.debugFlags = ibuf[37]; - cam->params.status.vpStatus = ibuf[38]; - cam->params.status.errorCode = ibuf[39]; - cam->fps = ibuf[41]; - mutex_unlock(&cam->param_lock); - - linesize = skipcount(cols, out_fmt); - ibuf += FRAME_HEADER_SIZE; - size -= FRAME_HEADER_SIZE; - ll = ibuf[0] | (ibuf[1] << 8); - ibuf += 2; - even_line = 1; - - while (size > 0) { - size -= (ll+2); - if (size < 0) { - LOG("Insufficient data in buffer\n"); - return -1; - } - - while (ll > 1) { - if (!compressed || (compressed && !(*ibuf & 1))) { - if(subsample_422 || even_line) { - obuf += yuvconvert(ibuf, obuf, out_fmt, - in_uyvy, cam->mmap_kludge); - ibuf += 4; - ll -= 4; - } else { - /* SUBSAMPLE_420 on an odd line */ - obuf += convert420(ibuf, obuf, - out_fmt, linesize, - cam->mmap_kludge); - ibuf += 2; - ll -= 2; - } - } else { - /*skip compressed interval from previous frame*/ - obuf += skipcount(*ibuf >> 1, out_fmt); - if (obuf > end_obuf) { - LOG("Insufficient buffer size\n"); - return -1; - } - ++ibuf; - ll--; - } - } - if (ll == 1) { - if (*ibuf != EOL) { - DBG("EOL not found giving up after %d/%d" - " bytes\n", origsize-size, origsize); - return -1; - } - - ++ibuf; /* skip over EOL */ - - if ((size > 3) && (ibuf[0] == EOI) && (ibuf[1] == EOI) && - (ibuf[2] == EOI) && (ibuf[3] == EOI)) { - size -= 4; - break; - } - - if(decimation) { - /* skip the odd lines for now */ - obuf += linesize; - } - - if (size > 1) { - ll = ibuf[0] | (ibuf[1] << 8); - ibuf += 2; /* skip over line length */ - } - if(!decimation) - even_line = !even_line; - } else { - LOG("line length was not 1 but %d after %d/%d bytes\n", - ll, origsize-size, origsize); - return -1; - } - } - - if(decimation) { - /* interpolate odd rows */ - int i, j; - u8 *prev, *next; - prev = cam->decompressed_frame.data; - obuf = prev+linesize; - next = obuf+linesize; - for(i=1; idecompressed_frame.count = obuf-cam->decompressed_frame.data; - - return cam->decompressed_frame.count; -} - -/* InitStreamCap wrapper to select correct start line */ -static inline int init_stream_cap(struct cam_data *cam) -{ - return do_command(cam, CPIA_COMMAND_InitStreamCap, - 0, cam->params.streamStartLine, 0, 0); -} - - -/* find_over_exposure - * Finds a suitable value of OverExposure for use with SetFlickerCtrl - * Some calculation is required because this value changes with the brightness - * set with SetColourParameters - * - * Parameters: Brightness - last brightness value set with SetColourParameters - * - * Returns: OverExposure value to use with SetFlickerCtrl - */ -#define FLICKER_MAX_EXPOSURE 250 -#define FLICKER_ALLOWABLE_OVER_EXPOSURE 146 -#define FLICKER_BRIGHTNESS_CONSTANT 59 -static int find_over_exposure(int brightness) -{ - int MaxAllowableOverExposure, OverExposure; - - MaxAllowableOverExposure = FLICKER_MAX_EXPOSURE - brightness - - FLICKER_BRIGHTNESS_CONSTANT; - - if (MaxAllowableOverExposure < FLICKER_ALLOWABLE_OVER_EXPOSURE) { - OverExposure = MaxAllowableOverExposure; - } else { - OverExposure = FLICKER_ALLOWABLE_OVER_EXPOSURE; - } - - return OverExposure; -} -#undef FLICKER_MAX_EXPOSURE -#undef FLICKER_ALLOWABLE_OVER_EXPOSURE -#undef FLICKER_BRIGHTNESS_CONSTANT - -/* update various camera modes and settings */ -static void dispatch_commands(struct cam_data *cam) -{ - mutex_lock(&cam->param_lock); - if (cam->cmd_queue==COMMAND_NONE) { - mutex_unlock(&cam->param_lock); - return; - } - DEB_BYTE(cam->cmd_queue); - DEB_BYTE(cam->cmd_queue>>8); - if (cam->cmd_queue & COMMAND_SETFORMAT) { - do_command(cam, CPIA_COMMAND_SetFormat, - cam->params.format.videoSize, - cam->params.format.subSample, - cam->params.format.yuvOrder, 0); - do_command(cam, CPIA_COMMAND_SetROI, - cam->params.roi.colStart, cam->params.roi.colEnd, - cam->params.roi.rowStart, cam->params.roi.rowEnd); - cam->first_frame = 1; - } - - if (cam->cmd_queue & COMMAND_SETCOLOURPARAMS) - do_command(cam, CPIA_COMMAND_SetColourParams, - cam->params.colourParams.brightness, - cam->params.colourParams.contrast, - cam->params.colourParams.saturation, 0); - - if (cam->cmd_queue & COMMAND_SETAPCOR) - do_command(cam, CPIA_COMMAND_SetApcor, - cam->params.apcor.gain1, - cam->params.apcor.gain2, - cam->params.apcor.gain4, - cam->params.apcor.gain8); - - if (cam->cmd_queue & COMMAND_SETVLOFFSET) - do_command(cam, CPIA_COMMAND_SetVLOffset, - cam->params.vlOffset.gain1, - cam->params.vlOffset.gain2, - cam->params.vlOffset.gain4, - cam->params.vlOffset.gain8); - - if (cam->cmd_queue & COMMAND_SETEXPOSURE) { - do_command_extended(cam, CPIA_COMMAND_SetExposure, - cam->params.exposure.gainMode, - 1, - cam->params.exposure.compMode, - cam->params.exposure.centreWeight, - cam->params.exposure.gain, - cam->params.exposure.fineExp, - cam->params.exposure.coarseExpLo, - cam->params.exposure.coarseExpHi, - cam->params.exposure.redComp, - cam->params.exposure.green1Comp, - cam->params.exposure.green2Comp, - cam->params.exposure.blueComp); - if(cam->params.exposure.expMode != 1) { - do_command_extended(cam, CPIA_COMMAND_SetExposure, - 0, - cam->params.exposure.expMode, - 0, 0, - cam->params.exposure.gain, - cam->params.exposure.fineExp, - cam->params.exposure.coarseExpLo, - cam->params.exposure.coarseExpHi, - 0, 0, 0, 0); - } - } - - if (cam->cmd_queue & COMMAND_SETCOLOURBALANCE) { - if (cam->params.colourBalance.balanceMode == 1) { - do_command(cam, CPIA_COMMAND_SetColourBalance, - 1, - cam->params.colourBalance.redGain, - cam->params.colourBalance.greenGain, - cam->params.colourBalance.blueGain); - do_command(cam, CPIA_COMMAND_SetColourBalance, - 3, 0, 0, 0); - } - if (cam->params.colourBalance.balanceMode == 2) { - do_command(cam, CPIA_COMMAND_SetColourBalance, - 2, 0, 0, 0); - } - if (cam->params.colourBalance.balanceMode == 3) { - do_command(cam, CPIA_COMMAND_SetColourBalance, - 3, 0, 0, 0); - } - } - - if (cam->cmd_queue & COMMAND_SETCOMPRESSIONTARGET) - do_command(cam, CPIA_COMMAND_SetCompressionTarget, - cam->params.compressionTarget.frTargeting, - cam->params.compressionTarget.targetFR, - cam->params.compressionTarget.targetQ, 0); - - if (cam->cmd_queue & COMMAND_SETYUVTHRESH) - do_command(cam, CPIA_COMMAND_SetYUVThresh, - cam->params.yuvThreshold.yThreshold, - cam->params.yuvThreshold.uvThreshold, 0, 0); - - if (cam->cmd_queue & COMMAND_SETCOMPRESSIONPARAMS) - do_command_extended(cam, CPIA_COMMAND_SetCompressionParams, - 0, 0, 0, 0, - cam->params.compressionParams.hysteresis, - cam->params.compressionParams.threshMax, - cam->params.compressionParams.smallStep, - cam->params.compressionParams.largeStep, - cam->params.compressionParams.decimationHysteresis, - cam->params.compressionParams.frDiffStepThresh, - cam->params.compressionParams.qDiffStepThresh, - cam->params.compressionParams.decimationThreshMod); - - if (cam->cmd_queue & COMMAND_SETCOMPRESSION) - do_command(cam, CPIA_COMMAND_SetCompression, - cam->params.compression.mode, - cam->params.compression.decimation, 0, 0); - - if (cam->cmd_queue & COMMAND_SETSENSORFPS) - do_command(cam, CPIA_COMMAND_SetSensorFPS, - cam->params.sensorFps.divisor, - cam->params.sensorFps.baserate, 0, 0); - - if (cam->cmd_queue & COMMAND_SETFLICKERCTRL) - do_command(cam, CPIA_COMMAND_SetFlickerCtrl, - cam->params.flickerControl.flickerMode, - cam->params.flickerControl.coarseJump, - abs(cam->params.flickerControl.allowableOverExposure), - 0); - - if (cam->cmd_queue & COMMAND_SETECPTIMING) - do_command(cam, CPIA_COMMAND_SetECPTiming, - cam->params.ecpTiming, 0, 0, 0); - - if (cam->cmd_queue & COMMAND_PAUSE) - do_command(cam, CPIA_COMMAND_EndStreamCap, 0, 0, 0, 0); - - if (cam->cmd_queue & COMMAND_RESUME) - init_stream_cap(cam); - - if (cam->cmd_queue & COMMAND_SETLIGHTS && cam->params.qx3.qx3_detected) - { - int p1 = (cam->params.qx3.bottomlight == 0) << 1; - int p2 = (cam->params.qx3.toplight == 0) << 3; - do_command(cam, CPIA_COMMAND_WriteVCReg, 0x90, 0x8F, 0x50, 0); - do_command(cam, CPIA_COMMAND_WriteMCPort, 2, 0, (p1|p2|0xE0), 0); - } - - cam->cmd_queue = COMMAND_NONE; - mutex_unlock(&cam->param_lock); - return; -} - - - -static void set_flicker(struct cam_params *params, volatile u32 *command_flags, - int on) -{ - /* Everything in here is from the Windows driver */ -#define FIRMWARE_VERSION(x,y) (params->version.firmwareVersion == (x) && \ - params->version.firmwareRevision == (y)) -/* define for compgain calculation */ -#if 0 -#define COMPGAIN(base, curexp, newexp) \ - (u8) ((((float) base - 128.0) * ((float) curexp / (float) newexp)) + 128.5) -#define EXP_FROM_COMP(basecomp, curcomp, curexp) \ - (u16)((float)curexp * (float)(u8)(curcomp + 128) / (float)(u8)(basecomp - 128)) -#else - /* equivalent functions without floating point math */ -#define COMPGAIN(base, curexp, newexp) \ - (u8)(128 + (((u32)(2*(base-128)*curexp + newexp)) / (2* newexp)) ) -#define EXP_FROM_COMP(basecomp, curcomp, curexp) \ - (u16)(((u32)(curexp * (u8)(curcomp + 128)) / (u8)(basecomp - 128))) -#endif - - - int currentexp = params->exposure.coarseExpLo + - params->exposure.coarseExpHi*256; - int startexp; - if (on) { - int cj = params->flickerControl.coarseJump; - params->flickerControl.flickerMode = 1; - params->flickerControl.disabled = 0; - if(params->exposure.expMode != 2) - *command_flags |= COMMAND_SETEXPOSURE; - params->exposure.expMode = 2; - currentexp = currentexp << params->exposure.gain; - params->exposure.gain = 0; - /* round down current exposure to nearest value */ - startexp = (currentexp + ROUND_UP_EXP_FOR_FLICKER) / cj; - if(startexp < 1) - startexp = 1; - startexp = (startexp * cj) - 1; - if(FIRMWARE_VERSION(1,2)) - while(startexp > MAX_EXP_102) - startexp -= cj; - else - while(startexp > MAX_EXP) - startexp -= cj; - params->exposure.coarseExpLo = startexp & 0xff; - params->exposure.coarseExpHi = startexp >> 8; - if (currentexp > startexp) { - if (currentexp > (2 * startexp)) - currentexp = 2 * startexp; - params->exposure.redComp = COMPGAIN (COMP_RED, currentexp, startexp); - params->exposure.green1Comp = COMPGAIN (COMP_GREEN1, currentexp, startexp); - params->exposure.green2Comp = COMPGAIN (COMP_GREEN2, currentexp, startexp); - params->exposure.blueComp = COMPGAIN (COMP_BLUE, currentexp, startexp); - } else { - params->exposure.redComp = COMP_RED; - params->exposure.green1Comp = COMP_GREEN1; - params->exposure.green2Comp = COMP_GREEN2; - params->exposure.blueComp = COMP_BLUE; - } - if(FIRMWARE_VERSION(1,2)) - params->exposure.compMode = 0; - else - params->exposure.compMode = 1; - - params->apcor.gain1 = 0x18; - params->apcor.gain2 = 0x18; - params->apcor.gain4 = 0x16; - params->apcor.gain8 = 0x14; - *command_flags |= COMMAND_SETAPCOR; - } else { - params->flickerControl.flickerMode = 0; - params->flickerControl.disabled = 1; - /* Coarse = average of equivalent coarse for each comp channel */ - startexp = EXP_FROM_COMP(COMP_RED, params->exposure.redComp, currentexp); - startexp += EXP_FROM_COMP(COMP_GREEN1, params->exposure.green1Comp, currentexp); - startexp += EXP_FROM_COMP(COMP_GREEN2, params->exposure.green2Comp, currentexp); - startexp += EXP_FROM_COMP(COMP_BLUE, params->exposure.blueComp, currentexp); - startexp = startexp >> 2; - while(startexp > MAX_EXP && - params->exposure.gain < params->exposure.gainMode-1) { - startexp = startexp >> 1; - ++params->exposure.gain; - } - if(FIRMWARE_VERSION(1,2) && startexp > MAX_EXP_102) - startexp = MAX_EXP_102; - if(startexp > MAX_EXP) - startexp = MAX_EXP; - params->exposure.coarseExpLo = startexp&0xff; - params->exposure.coarseExpHi = startexp >> 8; - params->exposure.redComp = COMP_RED; - params->exposure.green1Comp = COMP_GREEN1; - params->exposure.green2Comp = COMP_GREEN2; - params->exposure.blueComp = COMP_BLUE; - params->exposure.compMode = 1; - *command_flags |= COMMAND_SETEXPOSURE; - params->apcor.gain1 = 0x18; - params->apcor.gain2 = 0x16; - params->apcor.gain4 = 0x24; - params->apcor.gain8 = 0x34; - *command_flags |= COMMAND_SETAPCOR; - } - params->vlOffset.gain1 = 20; - params->vlOffset.gain2 = 24; - params->vlOffset.gain4 = 26; - params->vlOffset.gain8 = 26; - *command_flags |= COMMAND_SETVLOFFSET; -#undef FIRMWARE_VERSION -#undef EXP_FROM_COMP -#undef COMPGAIN -} - -#define FIRMWARE_VERSION(x,y) (cam->params.version.firmwareVersion == (x) && \ - cam->params.version.firmwareRevision == (y)) -/* monitor the exposure and adjust the sensor frame rate if needed */ -static void monitor_exposure(struct cam_data *cam) -{ - u8 exp_acc, bcomp, gain, coarseL, cmd[8], data[8]; - int retval, light_exp, dark_exp, very_dark_exp; - int old_exposure, new_exposure, framerate; - - /* get necessary stats and register settings from camera */ - /* do_command can't handle this, so do it ourselves */ - cmd[0] = CPIA_COMMAND_ReadVPRegs>>8; - cmd[1] = CPIA_COMMAND_ReadVPRegs&0xff; - cmd[2] = 30; - cmd[3] = 4; - cmd[4] = 9; - cmd[5] = 8; - cmd[6] = 8; - cmd[7] = 0; - retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data); - if (retval) { - LOG("ReadVPRegs(30,4,9,8) - failed, retval=%d\n", - retval); - return; - } - exp_acc = data[0]; - bcomp = data[1]; - gain = data[2]; - coarseL = data[3]; - - mutex_lock(&cam->param_lock); - light_exp = cam->params.colourParams.brightness + - TC - 50 + EXP_ACC_LIGHT; - if(light_exp > 255) - light_exp = 255; - dark_exp = cam->params.colourParams.brightness + - TC - 50 - EXP_ACC_DARK; - if(dark_exp < 0) - dark_exp = 0; - very_dark_exp = dark_exp/2; - - old_exposure = cam->params.exposure.coarseExpHi * 256 + - cam->params.exposure.coarseExpLo; - - if(!cam->params.flickerControl.disabled) { - /* Flicker control on */ - int max_comp = FIRMWARE_VERSION(1,2) ? MAX_COMP : HIGH_COMP_102; - bcomp += 128; /* decode */ - if(bcomp >= max_comp && exp_acc < dark_exp) { - /* dark */ - if(exp_acc < very_dark_exp) { - /* very dark */ - if(cam->exposure_status == EXPOSURE_VERY_DARK) - ++cam->exposure_count; - else { - cam->exposure_status = EXPOSURE_VERY_DARK; - cam->exposure_count = 1; - } - } else { - /* just dark */ - if(cam->exposure_status == EXPOSURE_DARK) - ++cam->exposure_count; - else { - cam->exposure_status = EXPOSURE_DARK; - cam->exposure_count = 1; - } - } - } else if(old_exposure <= LOW_EXP || exp_acc > light_exp) { - /* light */ - if(old_exposure <= VERY_LOW_EXP) { - /* very light */ - if(cam->exposure_status == EXPOSURE_VERY_LIGHT) - ++cam->exposure_count; - else { - cam->exposure_status = EXPOSURE_VERY_LIGHT; - cam->exposure_count = 1; - } - } else { - /* just light */ - if(cam->exposure_status == EXPOSURE_LIGHT) - ++cam->exposure_count; - else { - cam->exposure_status = EXPOSURE_LIGHT; - cam->exposure_count = 1; - } - } - } else { - /* not dark or light */ - cam->exposure_status = EXPOSURE_NORMAL; - } - } else { - /* Flicker control off */ - if(old_exposure >= MAX_EXP && exp_acc < dark_exp) { - /* dark */ - if(exp_acc < very_dark_exp) { - /* very dark */ - if(cam->exposure_status == EXPOSURE_VERY_DARK) - ++cam->exposure_count; - else { - cam->exposure_status = EXPOSURE_VERY_DARK; - cam->exposure_count = 1; - } - } else { - /* just dark */ - if(cam->exposure_status == EXPOSURE_DARK) - ++cam->exposure_count; - else { - cam->exposure_status = EXPOSURE_DARK; - cam->exposure_count = 1; - } - } - } else if(old_exposure <= LOW_EXP || exp_acc > light_exp) { - /* light */ - if(old_exposure <= VERY_LOW_EXP) { - /* very light */ - if(cam->exposure_status == EXPOSURE_VERY_LIGHT) - ++cam->exposure_count; - else { - cam->exposure_status = EXPOSURE_VERY_LIGHT; - cam->exposure_count = 1; - } - } else { - /* just light */ - if(cam->exposure_status == EXPOSURE_LIGHT) - ++cam->exposure_count; - else { - cam->exposure_status = EXPOSURE_LIGHT; - cam->exposure_count = 1; - } - } - } else { - /* not dark or light */ - cam->exposure_status = EXPOSURE_NORMAL; - } - } - - framerate = cam->fps; - if(framerate > 30 || framerate < 1) - framerate = 1; - - if(!cam->params.flickerControl.disabled) { - /* Flicker control on */ - if((cam->exposure_status == EXPOSURE_VERY_DARK || - cam->exposure_status == EXPOSURE_DARK) && - cam->exposure_count >= DARK_TIME*framerate && - cam->params.sensorFps.divisor < 3) { - - /* dark for too long */ - ++cam->params.sensorFps.divisor; - cam->cmd_queue |= COMMAND_SETSENSORFPS; - - cam->params.flickerControl.coarseJump = - flicker_jumps[cam->mainsFreq] - [cam->params.sensorFps.baserate] - [cam->params.sensorFps.divisor]; - cam->cmd_queue |= COMMAND_SETFLICKERCTRL; - - new_exposure = cam->params.flickerControl.coarseJump-1; - while(new_exposure < old_exposure/2) - new_exposure += cam->params.flickerControl.coarseJump; - cam->params.exposure.coarseExpLo = new_exposure & 0xff; - cam->params.exposure.coarseExpHi = new_exposure >> 8; - cam->cmd_queue |= COMMAND_SETEXPOSURE; - cam->exposure_status = EXPOSURE_NORMAL; - LOG("Automatically decreasing sensor_fps\n"); - - } else if((cam->exposure_status == EXPOSURE_VERY_LIGHT || - cam->exposure_status == EXPOSURE_LIGHT) && - cam->exposure_count >= LIGHT_TIME*framerate && - cam->params.sensorFps.divisor > 0) { - - /* light for too long */ - int max_exp = FIRMWARE_VERSION(1,2) ? MAX_EXP_102 : MAX_EXP ; - - --cam->params.sensorFps.divisor; - cam->cmd_queue |= COMMAND_SETSENSORFPS; - - cam->params.flickerControl.coarseJump = - flicker_jumps[cam->mainsFreq] - [cam->params.sensorFps.baserate] - [cam->params.sensorFps.divisor]; - cam->cmd_queue |= COMMAND_SETFLICKERCTRL; - - new_exposure = cam->params.flickerControl.coarseJump-1; - while(new_exposure < 2*old_exposure && - new_exposure+ - cam->params.flickerControl.coarseJump < max_exp) - new_exposure += cam->params.flickerControl.coarseJump; - cam->params.exposure.coarseExpLo = new_exposure & 0xff; - cam->params.exposure.coarseExpHi = new_exposure >> 8; - cam->cmd_queue |= COMMAND_SETEXPOSURE; - cam->exposure_status = EXPOSURE_NORMAL; - LOG("Automatically increasing sensor_fps\n"); - } - } else { - /* Flicker control off */ - if((cam->exposure_status == EXPOSURE_VERY_DARK || - cam->exposure_status == EXPOSURE_DARK) && - cam->exposure_count >= DARK_TIME*framerate && - cam->params.sensorFps.divisor < 3) { - - /* dark for too long */ - ++cam->params.sensorFps.divisor; - cam->cmd_queue |= COMMAND_SETSENSORFPS; - - if(cam->params.exposure.gain > 0) { - --cam->params.exposure.gain; - cam->cmd_queue |= COMMAND_SETEXPOSURE; - } - cam->exposure_status = EXPOSURE_NORMAL; - LOG("Automatically decreasing sensor_fps\n"); - - } else if((cam->exposure_status == EXPOSURE_VERY_LIGHT || - cam->exposure_status == EXPOSURE_LIGHT) && - cam->exposure_count >= LIGHT_TIME*framerate && - cam->params.sensorFps.divisor > 0) { - - /* light for too long */ - --cam->params.sensorFps.divisor; - cam->cmd_queue |= COMMAND_SETSENSORFPS; - - if(cam->params.exposure.gain < - cam->params.exposure.gainMode-1) { - ++cam->params.exposure.gain; - cam->cmd_queue |= COMMAND_SETEXPOSURE; - } - cam->exposure_status = EXPOSURE_NORMAL; - LOG("Automatically increasing sensor_fps\n"); - } - } - mutex_unlock(&cam->param_lock); -} - -/*-----------------------------------------------------------------*/ -/* if flicker is switched off, this function switches it back on.It checks, - however, that conditions are suitable before restarting it. - This should only be called for firmware version 1.2. - - It also adjust the colour balance when an exposure step is detected - as - long as flicker is running -*/ -static void restart_flicker(struct cam_data *cam) -{ - int cam_exposure, old_exp; - if(!FIRMWARE_VERSION(1,2)) - return; - mutex_lock(&cam->param_lock); - if(cam->params.flickerControl.flickerMode == 0 || - cam->raw_image[39] == 0) { - mutex_unlock(&cam->param_lock); - return; - } - cam_exposure = cam->raw_image[39]*2; - old_exp = cam->params.exposure.coarseExpLo + - cam->params.exposure.coarseExpHi*256; - /* - see how far away camera exposure is from a valid - flicker exposure value - */ - cam_exposure %= cam->params.flickerControl.coarseJump; - if(!cam->params.flickerControl.disabled && - cam_exposure <= cam->params.flickerControl.coarseJump - 3) { - /* Flicker control auto-disabled */ - cam->params.flickerControl.disabled = 1; - } - - if(cam->params.flickerControl.disabled && - cam->params.flickerControl.flickerMode && - old_exp > cam->params.flickerControl.coarseJump + - ROUND_UP_EXP_FOR_FLICKER) { - /* exposure is now high enough to switch - flicker control back on */ - set_flicker(&cam->params, &cam->cmd_queue, 1); - if((cam->cmd_queue & COMMAND_SETEXPOSURE) && - cam->params.exposure.expMode == 2) - cam->exposure_status = EXPOSURE_NORMAL; - - } - mutex_unlock(&cam->param_lock); -} -#undef FIRMWARE_VERSION - -static int clear_stall(struct cam_data *cam) -{ - /* FIXME: Does this actually work? */ - LOG("Clearing stall\n"); - - cam->ops->streamRead(cam->lowlevel_data, cam->raw_image, 0); - do_command(cam, CPIA_COMMAND_GetCameraStatus,0,0,0,0); - return cam->params.status.streamState != STREAM_PAUSED; -} - -/* kernel thread function to read image from camera */ -static int fetch_frame(void *data) -{ - int image_size, retry; - struct cam_data *cam = (struct cam_data *)data; - unsigned long oldjif, rate, diff; - - /* Allow up to two bad images in a row to be read and - * ignored before an error is reported */ - for (retry = 0; retry < 3; ++retry) { - if (retry) - DBG("retry=%d\n", retry); - - if (!cam->ops) - continue; - - /* load first frame always uncompressed */ - if (cam->first_frame && - cam->params.compression.mode != CPIA_COMPRESSION_NONE) { - do_command(cam, CPIA_COMMAND_SetCompression, - CPIA_COMPRESSION_NONE, - NO_DECIMATION, 0, 0); - /* Trial & error - Discarding a frame prevents the - first frame from having an error in the data. */ - do_command(cam, CPIA_COMMAND_DiscardFrame, 0, 0, 0, 0); - } - - /* init camera upload */ - if (do_command(cam, CPIA_COMMAND_GrabFrame, 0, - cam->params.streamStartLine, 0, 0)) - continue; - - if (cam->ops->wait_for_stream_ready) { - /* loop until image ready */ - int count = 0; - do_command(cam, CPIA_COMMAND_GetCameraStatus,0,0,0,0); - while (cam->params.status.streamState != STREAM_READY) { - if(++count > READY_TIMEOUT) - break; - if(cam->params.status.streamState == - STREAM_PAUSED) { - /* Bad news */ - if(!clear_stall(cam)) - return -EIO; - } - - cond_resched(); - - /* sleep for 10 ms, hopefully ;) */ - msleep_interruptible(10); - if (signal_pending(current)) - return -EINTR; - - do_command(cam, CPIA_COMMAND_GetCameraStatus, - 0, 0, 0, 0); - } - if(cam->params.status.streamState != STREAM_READY) { - continue; - } - } - - cond_resched(); - - /* grab image from camera */ - oldjif = jiffies; - image_size = cam->ops->streamRead(cam->lowlevel_data, - cam->raw_image, 0); - if (image_size <= 0) { - DBG("streamRead failed: %d\n", image_size); - continue; - } - - rate = image_size * HZ / 1024; - diff = jiffies-oldjif; - cam->transfer_rate = diff==0 ? rate : rate/diff; - /* diff==0 ? unlikely but possible */ - - /* Switch flicker control back on if it got turned off */ - restart_flicker(cam); - - /* If AEC is enabled, monitor the exposure and - adjust the sensor frame rate if needed */ - if(cam->params.exposure.expMode == 2) - monitor_exposure(cam); - - /* camera idle now so dispatch queued commands */ - dispatch_commands(cam); - - /* Update our knowledge of the camera state */ - do_command(cam, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0); - do_command(cam, CPIA_COMMAND_GetExposure, 0, 0, 0, 0); - do_command(cam, CPIA_COMMAND_ReadMCPorts, 0, 0, 0, 0); - - /* decompress and convert image to by copying it from - * raw_image to decompressed_frame - */ - - cond_resched(); - - cam->image_size = parse_picture(cam, image_size); - if (cam->image_size <= 0) { - DBG("parse_picture failed %d\n", cam->image_size); - if(cam->params.compression.mode != - CPIA_COMPRESSION_NONE) { - /* Compression may not work right if we - had a bad frame, get the next one - uncompressed. */ - cam->first_frame = 1; - do_command(cam, CPIA_COMMAND_SetGrabMode, - CPIA_GRAB_SINGLE, 0, 0, 0); - /* FIXME: Trial & error - need up to 70ms for - the grab mode change to complete ? */ - msleep_interruptible(70); - if (signal_pending(current)) - return -EINTR; - } - } else - break; - } - - if (retry < 3) { - /* FIXME: this only works for double buffering */ - if (cam->frame[cam->curframe].state == FRAME_READY) { - memcpy(cam->frame[cam->curframe].data, - cam->decompressed_frame.data, - cam->decompressed_frame.count); - cam->frame[cam->curframe].state = FRAME_DONE; - } else - cam->decompressed_frame.state = FRAME_DONE; - - if (cam->first_frame) { - cam->first_frame = 0; - do_command(cam, CPIA_COMMAND_SetCompression, - cam->params.compression.mode, - cam->params.compression.decimation, 0, 0); - - /* Switch from single-grab to continuous grab */ - do_command(cam, CPIA_COMMAND_SetGrabMode, - CPIA_GRAB_CONTINUOUS, 0, 0, 0); - } - return 0; - } - return -EIO; -} - -static int capture_frame(struct cam_data *cam, struct video_mmap *vm) -{ - if (!cam->frame_buf) { - /* we do lazy allocation */ - int err; - if ((err = allocate_frame_buf(cam))) - return err; - } - - cam->curframe = vm->frame; - cam->frame[cam->curframe].state = FRAME_READY; - return fetch_frame(cam); -} - -static int goto_high_power(struct cam_data *cam) -{ - if (do_command(cam, CPIA_COMMAND_GotoHiPower, 0, 0, 0, 0)) - return -EIO; - msleep_interruptible(40); /* windows driver does it too */ - if(signal_pending(current)) - return -EINTR; - if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0)) - return -EIO; - if (cam->params.status.systemState == HI_POWER_STATE) { - DBG("camera now in HIGH power state\n"); - return 0; - } - printstatus(cam); - return -EIO; -} - -static int goto_low_power(struct cam_data *cam) -{ - if (do_command(cam, CPIA_COMMAND_GotoLoPower, 0, 0, 0, 0)) - return -1; - if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0)) - return -1; - if (cam->params.status.systemState == LO_POWER_STATE) { - DBG("camera now in LOW power state\n"); - return 0; - } - printstatus(cam); - return -1; -} - -static void save_camera_state(struct cam_data *cam) -{ - if(!(cam->cmd_queue & COMMAND_SETCOLOURBALANCE)) - do_command(cam, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0); - if(!(cam->cmd_queue & COMMAND_SETEXPOSURE)) - do_command(cam, CPIA_COMMAND_GetExposure, 0, 0, 0, 0); - - DBG("%d/%d/%d/%d/%d/%d/%d/%d\n", - cam->params.exposure.gain, - cam->params.exposure.fineExp, - cam->params.exposure.coarseExpLo, - cam->params.exposure.coarseExpHi, - cam->params.exposure.redComp, - cam->params.exposure.green1Comp, - cam->params.exposure.green2Comp, - cam->params.exposure.blueComp); - DBG("%d/%d/%d\n", - cam->params.colourBalance.redGain, - cam->params.colourBalance.greenGain, - cam->params.colourBalance.blueGain); -} - -static int set_camera_state(struct cam_data *cam) -{ - cam->cmd_queue = COMMAND_SETCOMPRESSION | - COMMAND_SETCOMPRESSIONTARGET | - COMMAND_SETCOLOURPARAMS | - COMMAND_SETFORMAT | - COMMAND_SETYUVTHRESH | - COMMAND_SETECPTIMING | - COMMAND_SETCOMPRESSIONPARAMS | - COMMAND_SETEXPOSURE | - COMMAND_SETCOLOURBALANCE | - COMMAND_SETSENSORFPS | - COMMAND_SETAPCOR | - COMMAND_SETFLICKERCTRL | - COMMAND_SETVLOFFSET; - - do_command(cam, CPIA_COMMAND_SetGrabMode, CPIA_GRAB_SINGLE,0,0,0); - dispatch_commands(cam); - - /* Wait 6 frames for the sensor to get all settings and - AEC/ACB to settle */ - msleep_interruptible(6*(cam->params.sensorFps.baserate ? 33 : 40) * - (1 << cam->params.sensorFps.divisor) + 10); - - if(signal_pending(current)) - return -EINTR; - - save_camera_state(cam); - - return 0; -} - -static void get_version_information(struct cam_data *cam) -{ - /* GetCPIAVersion */ - do_command(cam, CPIA_COMMAND_GetCPIAVersion, 0, 0, 0, 0); - - /* GetPnPID */ - do_command(cam, CPIA_COMMAND_GetPnPID, 0, 0, 0, 0); -} - -/* initialize camera */ -static int reset_camera(struct cam_data *cam) -{ - int err; - /* Start the camera in low power mode */ - if (goto_low_power(cam)) { - if (cam->params.status.systemState != WARM_BOOT_STATE) - return -ENODEV; - - /* FIXME: this is just dirty trial and error */ - err = goto_high_power(cam); - if(err) - return err; - do_command(cam, CPIA_COMMAND_DiscardFrame, 0, 0, 0, 0); - if (goto_low_power(cam)) - return -ENODEV; - } - - /* procedure described in developer's guide p3-28 */ - - /* Check the firmware version. */ - cam->params.version.firmwareVersion = 0; - get_version_information(cam); - if (cam->params.version.firmwareVersion != 1) - return -ENODEV; - - /* A bug in firmware 1-02 limits gainMode to 2 */ - if(cam->params.version.firmwareRevision <= 2 && - cam->params.exposure.gainMode > 2) { - cam->params.exposure.gainMode = 2; - } - - /* set QX3 detected flag */ - cam->params.qx3.qx3_detected = (cam->params.pnpID.vendor == 0x0813 && - cam->params.pnpID.product == 0x0001); - - /* The fatal error checking should be done after - * the camera powers up (developer's guide p 3-38) */ - - /* Set streamState before transition to high power to avoid bug - * in firmware 1-02 */ - do_command(cam, CPIA_COMMAND_ModifyCameraStatus, STREAMSTATE, 0, - STREAM_NOT_READY, 0); - - /* GotoHiPower */ - err = goto_high_power(cam); - if (err) - return err; - - /* Check the camera status */ - if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0)) - return -EIO; - - if (cam->params.status.fatalError) { - DBG("fatal_error: %#04x\n", - cam->params.status.fatalError); - DBG("vp_status: %#04x\n", - cam->params.status.vpStatus); - if (cam->params.status.fatalError & ~(COM_FLAG|CPIA_FLAG)) { - /* Fatal error in camera */ - return -EIO; - } else if (cam->params.status.fatalError & (COM_FLAG|CPIA_FLAG)) { - /* Firmware 1-02 may do this for parallel port cameras, - * just clear the flags (developer's guide p 3-38) */ - do_command(cam, CPIA_COMMAND_ModifyCameraStatus, - FATALERROR, ~(COM_FLAG|CPIA_FLAG), 0, 0); - } - } - - /* Check the camera status again */ - if (cam->params.status.fatalError) { - if (cam->params.status.fatalError) - return -EIO; - } - - /* VPVersion can't be retrieved before the camera is in HiPower, - * so get it here instead of in get_version_information. */ - do_command(cam, CPIA_COMMAND_GetVPVersion, 0, 0, 0, 0); - - /* set camera to a known state */ - return set_camera_state(cam); -} - -static void put_cam(struct cpia_camera_ops* ops) -{ - module_put(ops->owner); -} - -/* ------------------------- V4L interface --------------------- */ -static int cpia_open(struct file *file) -{ - struct video_device *dev = video_devdata(file); - struct cam_data *cam = video_get_drvdata(dev); - int err; - - if (!cam) { - DBG("Internal error, cam_data not found!\n"); - return -ENODEV; - } - - if (cam->open_count > 0) { - DBG("Camera already open\n"); - return -EBUSY; - } - - if (!try_module_get(cam->ops->owner)) - return -ENODEV; - - mutex_lock(&cam->busy_lock); - err = -ENOMEM; - if (!cam->raw_image) { - cam->raw_image = rvmalloc(CPIA_MAX_IMAGE_SIZE); - if (!cam->raw_image) - goto oops; - } - - if (!cam->decompressed_frame.data) { - cam->decompressed_frame.data = rvmalloc(CPIA_MAX_FRAME_SIZE); - if (!cam->decompressed_frame.data) - goto oops; - } - - /* open cpia */ - err = -ENODEV; - if (cam->ops->open(cam->lowlevel_data)) - goto oops; - - /* reset the camera */ - if ((err = reset_camera(cam)) != 0) { - cam->ops->close(cam->lowlevel_data); - goto oops; - } - - err = -EINTR; - if(signal_pending(current)) - goto oops; - - /* Set ownership of /proc/cpia/videoX to current user */ - if(cam->proc_entry) - cam->proc_entry->uid = current_uid(); - - /* set mark for loading first frame uncompressed */ - cam->first_frame = 1; - - /* init it to something */ - cam->mmap_kludge = 0; - - ++cam->open_count; - file->private_data = dev; - mutex_unlock(&cam->busy_lock); - return 0; - - oops: - if (cam->decompressed_frame.data) { - rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE); - cam->decompressed_frame.data = NULL; - } - if (cam->raw_image) { - rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE); - cam->raw_image = NULL; - } - mutex_unlock(&cam->busy_lock); - put_cam(cam->ops); - return err; -} - -static int cpia_close(struct file *file) -{ - struct video_device *dev = file->private_data; - struct cam_data *cam = video_get_drvdata(dev); - - if (cam->ops) { - /* Return ownership of /proc/cpia/videoX to root */ - if(cam->proc_entry) - cam->proc_entry->uid = 0; - - /* save camera state for later open (developers guide ch 3.5.3) */ - save_camera_state(cam); - - /* GotoLoPower */ - goto_low_power(cam); - - /* Update the camera status */ - do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0); - - /* cleanup internal state stuff */ - free_frames(cam->frame); - - /* close cpia */ - cam->ops->close(cam->lowlevel_data); - - put_cam(cam->ops); - } - - if (--cam->open_count == 0) { - /* clean up capture-buffers */ - if (cam->raw_image) { - rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE); - cam->raw_image = NULL; - } - - if (cam->decompressed_frame.data) { - rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE); - cam->decompressed_frame.data = NULL; - } - - if (cam->frame_buf) - free_frame_buf(cam); - - if (!cam->ops) - kfree(cam); - } - file->private_data = NULL; - - return 0; -} - -static ssize_t cpia_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos) -{ - struct video_device *dev = file->private_data; - struct cam_data *cam = video_get_drvdata(dev); - int err; - - /* make this _really_ smp and multithread-safe */ - if (mutex_lock_interruptible(&cam->busy_lock)) - return -EINTR; - - if (!buf) { - DBG("buf NULL\n"); - mutex_unlock(&cam->busy_lock); - return -EINVAL; - } - - if (!count) { - DBG("count 0\n"); - mutex_unlock(&cam->busy_lock); - return 0; - } - - if (!cam->ops) { - DBG("ops NULL\n"); - mutex_unlock(&cam->busy_lock); - return -ENODEV; - } - - /* upload frame */ - cam->decompressed_frame.state = FRAME_READY; - cam->mmap_kludge=0; - if((err = fetch_frame(cam)) != 0) { - DBG("ERROR from fetch_frame: %d\n", err); - mutex_unlock(&cam->busy_lock); - return err; - } - cam->decompressed_frame.state = FRAME_UNUSED; - - /* copy data to user space */ - if (cam->decompressed_frame.count > count) { - DBG("count wrong: %d, %lu\n", cam->decompressed_frame.count, - (unsigned long) count); - mutex_unlock(&cam->busy_lock); - return -EFAULT; - } - if (copy_to_user(buf, cam->decompressed_frame.data, - cam->decompressed_frame.count)) { - DBG("copy_to_user failed\n"); - mutex_unlock(&cam->busy_lock); - return -EFAULT; - } - - mutex_unlock(&cam->busy_lock); - return cam->decompressed_frame.count; -} - -static long cpia_do_ioctl(struct file *file, unsigned int cmd, void *arg) -{ - struct video_device *dev = file->private_data; - struct cam_data *cam = video_get_drvdata(dev); - int retval = 0; - - if (!cam || !cam->ops) - return -ENODEV; - - /* make this _really_ smp-safe */ - if (mutex_lock_interruptible(&cam->busy_lock)) - return -EINTR; - - /* DBG("cpia_ioctl: %u\n", cmd); */ - - switch (cmd) { - /* query capabilities */ - case VIDIOCGCAP: - { - struct video_capability *b = arg; - - DBG("VIDIOCGCAP\n"); - strcpy(b->name, "CPiA Camera"); - b->type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE; - b->channels = 1; - b->audios = 0; - b->maxwidth = 352; /* VIDEOSIZE_CIF */ - b->maxheight = 288; - b->minwidth = 48; /* VIDEOSIZE_48_48 */ - b->minheight = 48; - break; - } - - /* get/set video source - we are a camera and nothing else */ - case VIDIOCGCHAN: - { - struct video_channel *v = arg; - - DBG("VIDIOCGCHAN\n"); - if (v->channel != 0) { - retval = -EINVAL; - break; - } - - v->channel = 0; - strcpy(v->name, "Camera"); - v->tuners = 0; - v->flags = 0; - v->type = VIDEO_TYPE_CAMERA; - v->norm = 0; - break; - } - - case VIDIOCSCHAN: - { - struct video_channel *v = arg; - - DBG("VIDIOCSCHAN\n"); - if (v->channel != 0) - retval = -EINVAL; - break; - } - - /* image properties */ - case VIDIOCGPICT: - { - struct video_picture *pic = arg; - DBG("VIDIOCGPICT\n"); - *pic = cam->vp; - break; - } - - case VIDIOCSPICT: - { - struct video_picture *vp = arg; - - DBG("VIDIOCSPICT\n"); - - /* check validity */ - DBG("palette: %d\n", vp->palette); - DBG("depth: %d\n", vp->depth); - if (!valid_mode(vp->palette, vp->depth)) { - retval = -EINVAL; - break; - } - - mutex_lock(&cam->param_lock); - /* brightness, colour, contrast need no check 0-65535 */ - cam->vp = *vp; - /* update cam->params.colourParams */ - cam->params.colourParams.brightness = vp->brightness*100/65535; - cam->params.colourParams.contrast = vp->contrast*100/65535; - cam->params.colourParams.saturation = vp->colour*100/65535; - /* contrast is in steps of 8, so round */ - cam->params.colourParams.contrast = - ((cam->params.colourParams.contrast + 3) / 8) * 8; - if (cam->params.version.firmwareVersion == 1 && - cam->params.version.firmwareRevision == 2 && - cam->params.colourParams.contrast > 80) { - /* 1-02 firmware limits contrast to 80 */ - cam->params.colourParams.contrast = 80; - } - - /* Adjust flicker control if necessary */ - if(cam->params.flickerControl.allowableOverExposure < 0) - cam->params.flickerControl.allowableOverExposure = - -find_over_exposure(cam->params.colourParams.brightness); - if(cam->params.flickerControl.flickerMode != 0) - cam->cmd_queue |= COMMAND_SETFLICKERCTRL; - - - /* queue command to update camera */ - cam->cmd_queue |= COMMAND_SETCOLOURPARAMS; - mutex_unlock(&cam->param_lock); - DBG("VIDIOCSPICT: %d / %d // %d / %d / %d / %d\n", - vp->depth, vp->palette, vp->brightness, vp->hue, vp->colour, - vp->contrast); - break; - } - - /* get/set capture window */ - case VIDIOCGWIN: - { - struct video_window *vw = arg; - DBG("VIDIOCGWIN\n"); - - *vw = cam->vw; - break; - } - - case VIDIOCSWIN: - { - /* copy_from_user, check validity, copy to internal structure */ - struct video_window *vw = arg; - DBG("VIDIOCSWIN\n"); - - if (vw->clipcount != 0) { /* clipping not supported */ - retval = -EINVAL; - break; - } - if (vw->clips != NULL) { /* clipping not supported */ - retval = -EINVAL; - break; - } - - /* we set the video window to something smaller or equal to what - * is requested by the user??? - */ - mutex_lock(&cam->param_lock); - if (vw->width != cam->vw.width || vw->height != cam->vw.height) { - int video_size = match_videosize(vw->width, vw->height); - - if (video_size < 0) { - retval = -EINVAL; - mutex_unlock(&cam->param_lock); - break; - } - cam->video_size = video_size; - - /* video size is changing, reset the subcapture area */ - memset(&cam->vc, 0, sizeof(cam->vc)); - - set_vw_size(cam); - DBG("%d / %d\n", cam->vw.width, cam->vw.height); - cam->cmd_queue |= COMMAND_SETFORMAT; - } - - mutex_unlock(&cam->param_lock); - - /* setformat ignored by camera during streaming, - * so stop/dispatch/start */ - if (cam->cmd_queue & COMMAND_SETFORMAT) { - DBG("\n"); - dispatch_commands(cam); - } - DBG("%d/%d:%d\n", cam->video_size, - cam->vw.width, cam->vw.height); - break; - } - - /* mmap interface */ - case VIDIOCGMBUF: - { - struct video_mbuf *vm = arg; - int i; - - DBG("VIDIOCGMBUF\n"); - memset(vm, 0, sizeof(*vm)); - vm->size = CPIA_MAX_FRAME_SIZE*FRAME_NUM; - vm->frames = FRAME_NUM; - for (i = 0; i < FRAME_NUM; i++) - vm->offsets[i] = CPIA_MAX_FRAME_SIZE * i; - break; - } - - case VIDIOCMCAPTURE: - { - struct video_mmap *vm = arg; - int video_size; - - DBG("VIDIOCMCAPTURE: %d / %d / %dx%d\n", vm->format, vm->frame, - vm->width, vm->height); - if (vm->frame<0||vm->frame>=FRAME_NUM) { - retval = -EINVAL; - break; - } - - /* set video format */ - cam->vp.palette = vm->format; - switch(vm->format) { - case VIDEO_PALETTE_GREY: - cam->vp.depth=8; - break; - case VIDEO_PALETTE_RGB555: - case VIDEO_PALETTE_RGB565: - case VIDEO_PALETTE_YUV422: - case VIDEO_PALETTE_YUYV: - case VIDEO_PALETTE_UYVY: - cam->vp.depth = 16; - break; - case VIDEO_PALETTE_RGB24: - cam->vp.depth = 24; - break; - case VIDEO_PALETTE_RGB32: - cam->vp.depth = 32; - break; - default: - retval = -EINVAL; - break; - } - if (retval) - break; - - /* set video size */ - video_size = match_videosize(vm->width, vm->height); - if (video_size < 0) { - retval = -EINVAL; - break; - } - if (video_size != cam->video_size) { - cam->video_size = video_size; - - /* video size is changing, reset the subcapture area */ - memset(&cam->vc, 0, sizeof(cam->vc)); - - set_vw_size(cam); - cam->cmd_queue |= COMMAND_SETFORMAT; - dispatch_commands(cam); - } - /* according to v4l-spec we must start streaming here */ - cam->mmap_kludge = 1; - retval = capture_frame(cam, vm); - - break; - } - - case VIDIOCSYNC: - { - int *frame = arg; - - //DBG("VIDIOCSYNC: %d\n", *frame); - - if (*frame<0 || *frame >= FRAME_NUM) { - retval = -EINVAL; - break; - } - - switch (cam->frame[*frame].state) { - case FRAME_UNUSED: - case FRAME_READY: - case FRAME_GRABBING: - DBG("sync to unused frame %d\n", *frame); - retval = -EINVAL; - break; - - case FRAME_DONE: - cam->frame[*frame].state = FRAME_UNUSED; - //DBG("VIDIOCSYNC: %d synced\n", *frame); - break; - } - if (retval == -EINTR) { - /* FIXME - xawtv does not handle this nice */ - retval = 0; - } - break; - } - - case VIDIOCGCAPTURE: - { - struct video_capture *vc = arg; - - DBG("VIDIOCGCAPTURE\n"); - - *vc = cam->vc; - - break; - } - - case VIDIOCSCAPTURE: - { - struct video_capture *vc = arg; - - DBG("VIDIOCSCAPTURE\n"); - - if (vc->decimation != 0) { /* How should this be used? */ - retval = -EINVAL; - break; - } - if (vc->flags != 0) { /* Even/odd grab not supported */ - retval = -EINVAL; - break; - } - - /* Clip to the resolution we can set for the ROI - (every 8 columns and 4 rows) */ - vc->x = vc->x & ~(__u32)7; - vc->y = vc->y & ~(__u32)3; - vc->width = vc->width & ~(__u32)7; - vc->height = vc->height & ~(__u32)3; - - if(vc->width == 0 || vc->height == 0 || - vc->x + vc->width > cam->vw.width || - vc->y + vc->height > cam->vw.height) { - retval = -EINVAL; - break; - } - - DBG("%d,%d/%dx%d\n", vc->x,vc->y,vc->width, vc->height); - - mutex_lock(&cam->param_lock); - - cam->vc.x = vc->x; - cam->vc.y = vc->y; - cam->vc.width = vc->width; - cam->vc.height = vc->height; - - set_vw_size(cam); - cam->cmd_queue |= COMMAND_SETFORMAT; - - mutex_unlock(&cam->param_lock); - - /* setformat ignored by camera during streaming, - * so stop/dispatch/start */ - dispatch_commands(cam); - break; - } - - case VIDIOCGUNIT: - { - struct video_unit *vu = arg; - - DBG("VIDIOCGUNIT\n"); - - vu->video = cam->vdev.minor; - vu->vbi = VIDEO_NO_UNIT; - vu->radio = VIDEO_NO_UNIT; - vu->audio = VIDEO_NO_UNIT; - vu->teletext = VIDEO_NO_UNIT; - - break; - } - - - /* pointless to implement overlay with this camera */ - case VIDIOCCAPTURE: - case VIDIOCGFBUF: - case VIDIOCSFBUF: - case VIDIOCKEY: - /* tuner interface - we have none */ - case VIDIOCGTUNER: - case VIDIOCSTUNER: - case VIDIOCGFREQ: - case VIDIOCSFREQ: - /* audio interface - we have none */ - case VIDIOCGAUDIO: - case VIDIOCSAUDIO: - retval = -EINVAL; - break; - default: - retval = -ENOIOCTLCMD; - break; - } - - mutex_unlock(&cam->busy_lock); - return retval; -} - -static long cpia_ioctl(struct file *file, - unsigned int cmd, unsigned long arg) -{ - return video_usercopy(file, cmd, arg, cpia_do_ioctl); -} - - -/* FIXME */ -static int cpia_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct video_device *dev = file->private_data; - unsigned long start = vma->vm_start; - unsigned long size = vma->vm_end - vma->vm_start; - unsigned long page, pos; - struct cam_data *cam = video_get_drvdata(dev); - int retval; - - if (!cam || !cam->ops) - return -ENODEV; - - DBG("cpia_mmap: %ld\n", size); - - if (size > FRAME_NUM*CPIA_MAX_FRAME_SIZE) - return -EINVAL; - - /* make this _really_ smp-safe */ - if (mutex_lock_interruptible(&cam->busy_lock)) - return -EINTR; - - if (!cam->frame_buf) { /* we do lazy allocation */ - if ((retval = allocate_frame_buf(cam))) { - mutex_unlock(&cam->busy_lock); - return retval; - } - } - - pos = (unsigned long)(cam->frame_buf); - while (size > 0) { - page = vmalloc_to_pfn((void *)pos); - if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) { - mutex_unlock(&cam->busy_lock); - return -EAGAIN; - } - start += PAGE_SIZE; - pos += PAGE_SIZE; - if (size > PAGE_SIZE) - size -= PAGE_SIZE; - else - size = 0; - } - - DBG("cpia_mmap: %ld\n", size); - mutex_unlock(&cam->busy_lock); - - return 0; -} - -static const struct v4l2_file_operations cpia_fops = { - .owner = THIS_MODULE, - .open = cpia_open, - .release = cpia_close, - .read = cpia_read, - .mmap = cpia_mmap, - .ioctl = cpia_ioctl, -}; - -static struct video_device cpia_template = { - .name = "CPiA Camera", - .fops = &cpia_fops, - .release = video_device_release_empty, -}; - -/* initialise cam_data structure */ -static void reset_camera_struct(struct cam_data *cam) -{ - /* The following parameter values are the defaults from - * "Software Developer's Guide for CPiA Cameras". Any changes - * to the defaults are noted in comments. */ - cam->params.colourParams.brightness = 50; - cam->params.colourParams.contrast = 48; - cam->params.colourParams.saturation = 50; - cam->params.exposure.gainMode = 4; - cam->params.exposure.expMode = 2; /* AEC */ - cam->params.exposure.compMode = 1; - cam->params.exposure.centreWeight = 1; - cam->params.exposure.gain = 0; - cam->params.exposure.fineExp = 0; - cam->params.exposure.coarseExpLo = 185; - cam->params.exposure.coarseExpHi = 0; - cam->params.exposure.redComp = COMP_RED; - cam->params.exposure.green1Comp = COMP_GREEN1; - cam->params.exposure.green2Comp = COMP_GREEN2; - cam->params.exposure.blueComp = COMP_BLUE; - cam->params.colourBalance.balanceMode = 2; /* ACB */ - cam->params.colourBalance.redGain = 32; - cam->params.colourBalance.greenGain = 6; - cam->params.colourBalance.blueGain = 92; - cam->params.apcor.gain1 = 0x18; - cam->params.apcor.gain2 = 0x16; - cam->params.apcor.gain4 = 0x24; - cam->params.apcor.gain8 = 0x34; - cam->params.flickerControl.flickerMode = 0; - cam->params.flickerControl.disabled = 1; - - cam->params.flickerControl.coarseJump = - flicker_jumps[cam->mainsFreq] - [cam->params.sensorFps.baserate] - [cam->params.sensorFps.divisor]; - cam->params.flickerControl.allowableOverExposure = - -find_over_exposure(cam->params.colourParams.brightness); - cam->params.vlOffset.gain1 = 20; - cam->params.vlOffset.gain2 = 24; - cam->params.vlOffset.gain4 = 26; - cam->params.vlOffset.gain8 = 26; - cam->params.compressionParams.hysteresis = 3; - cam->params.compressionParams.threshMax = 11; - cam->params.compressionParams.smallStep = 1; - cam->params.compressionParams.largeStep = 3; - cam->params.compressionParams.decimationHysteresis = 2; - cam->params.compressionParams.frDiffStepThresh = 5; - cam->params.compressionParams.qDiffStepThresh = 3; - cam->params.compressionParams.decimationThreshMod = 2; - /* End of default values from Software Developer's Guide */ - - cam->transfer_rate = 0; - cam->exposure_status = EXPOSURE_NORMAL; - - /* Set Sensor FPS to 15fps. This seems better than 30fps - * for indoor lighting. */ - cam->params.sensorFps.divisor = 1; - cam->params.sensorFps.baserate = 1; - - cam->params.yuvThreshold.yThreshold = 6; /* From windows driver */ - cam->params.yuvThreshold.uvThreshold = 6; /* From windows driver */ - - cam->params.format.subSample = SUBSAMPLE_422; - cam->params.format.yuvOrder = YUVORDER_YUYV; - - cam->params.compression.mode = CPIA_COMPRESSION_AUTO; - cam->params.compressionTarget.frTargeting = - CPIA_COMPRESSION_TARGET_QUALITY; - cam->params.compressionTarget.targetFR = 15; /* From windows driver */ - cam->params.compressionTarget.targetQ = 5; /* From windows driver */ - - cam->params.qx3.qx3_detected = 0; - cam->params.qx3.toplight = 0; - cam->params.qx3.bottomlight = 0; - cam->params.qx3.button = 0; - cam->params.qx3.cradled = 0; - - cam->video_size = VIDEOSIZE_CIF; - - cam->vp.colour = 32768; /* 50% */ - cam->vp.hue = 32768; /* 50% */ - cam->vp.brightness = 32768; /* 50% */ - cam->vp.contrast = 32768; /* 50% */ - cam->vp.whiteness = 0; /* not used -> grayscale only */ - cam->vp.depth = 24; /* to be set by user */ - cam->vp.palette = VIDEO_PALETTE_RGB24; /* to be set by user */ - - cam->vc.x = 0; - cam->vc.y = 0; - cam->vc.width = 0; - cam->vc.height = 0; - - cam->vw.x = 0; - cam->vw.y = 0; - set_vw_size(cam); - cam->vw.chromakey = 0; - cam->vw.flags = 0; - cam->vw.clipcount = 0; - cam->vw.clips = NULL; - - cam->cmd_queue = COMMAND_NONE; - cam->first_frame = 1; - - return; -} - -/* initialize cam_data structure */ -static void init_camera_struct(struct cam_data *cam, - struct cpia_camera_ops *ops ) -{ - int i; - - /* Default everything to 0 */ - memset(cam, 0, sizeof(struct cam_data)); - - cam->ops = ops; - mutex_init(&cam->param_lock); - mutex_init(&cam->busy_lock); - - reset_camera_struct(cam); - - cam->proc_entry = NULL; - - memcpy(&cam->vdev, &cpia_template, sizeof(cpia_template)); - video_set_drvdata(&cam->vdev, cam); - - cam->curframe = 0; - for (i = 0; i < FRAME_NUM; i++) { - cam->frame[i].width = 0; - cam->frame[i].height = 0; - cam->frame[i].state = FRAME_UNUSED; - cam->frame[i].data = NULL; - } - cam->decompressed_frame.width = 0; - cam->decompressed_frame.height = 0; - cam->decompressed_frame.state = FRAME_UNUSED; - cam->decompressed_frame.data = NULL; -} - -struct cam_data *cpia_register_camera(struct cpia_camera_ops *ops, void *lowlevel) -{ - struct cam_data *camera; - - if ((camera = kmalloc(sizeof(struct cam_data), GFP_KERNEL)) == NULL) - return NULL; - - - init_camera_struct( camera, ops ); - camera->lowlevel_data = lowlevel; - - /* register v4l device */ - if (video_register_device(&camera->vdev, VFL_TYPE_GRABBER, video_nr) < 0) { - kfree(camera); - printk(KERN_DEBUG "video_register_device failed\n"); - return NULL; - } - - /* get version information from camera: open/reset/close */ - - /* open cpia */ - if (camera->ops->open(camera->lowlevel_data)) - return camera; - - /* reset the camera */ - if (reset_camera(camera) != 0) { - camera->ops->close(camera->lowlevel_data); - return camera; - } - - /* close cpia */ - camera->ops->close(camera->lowlevel_data); - -#ifdef CONFIG_PROC_FS - create_proc_cpia_cam(camera); -#endif - - printk(KERN_INFO " CPiA Version: %d.%02d (%d.%d)\n", - camera->params.version.firmwareVersion, - camera->params.version.firmwareRevision, - camera->params.version.vcVersion, - camera->params.version.vcRevision); - printk(KERN_INFO " CPiA PnP-ID: %04x:%04x:%04x\n", - camera->params.pnpID.vendor, - camera->params.pnpID.product, - camera->params.pnpID.deviceRevision); - printk(KERN_INFO " VP-Version: %d.%d %04x\n", - camera->params.vpVersion.vpVersion, - camera->params.vpVersion.vpRevision, - camera->params.vpVersion.cameraHeadID); - - return camera; -} - -void cpia_unregister_camera(struct cam_data *cam) -{ - DBG("unregistering video\n"); - video_unregister_device(&cam->vdev); - if (cam->open_count) { - put_cam(cam->ops); - DBG("camera open -- setting ops to NULL\n"); - cam->ops = NULL; - } - -#ifdef CONFIG_PROC_FS - DBG("destroying /proc/cpia/%s\n", video_device_node_name(&cam->vdev)); - destroy_proc_cpia_cam(cam); -#endif - if (!cam->open_count) { - DBG("freeing camera\n"); - kfree(cam); - } -} - -static int __init cpia_init(void) -{ - printk(KERN_INFO "%s v%d.%d.%d\n", ABOUT, - CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER); - - printk(KERN_WARNING "Since in-kernel colorspace conversion is not " - "allowed, it is disabled by default now. Users should fix the " - "applications in case they don't work without conversion " - "reenabled by setting the 'colorspace_conv' module " - "parameter to 1\n"); - -#ifdef CONFIG_PROC_FS - proc_cpia_create(); -#endif - - return 0; -} - -static void __exit cpia_exit(void) -{ -#ifdef CONFIG_PROC_FS - proc_cpia_destroy(); -#endif -} - -module_init(cpia_init); -module_exit(cpia_exit); - -/* Exported symbols for modules. */ - -EXPORT_SYMBOL(cpia_register_camera); -EXPORT_SYMBOL(cpia_unregister_camera); diff --git a/drivers/media/video/cpia.h b/drivers/media/video/cpia.h deleted file mode 100644 index 8f0cfee4b8a1..000000000000 --- a/drivers/media/video/cpia.h +++ /dev/null @@ -1,432 +0,0 @@ -#ifndef cpia_h -#define cpia_h - -/* - * CPiA Parallel Port Video4Linux driver - * - * Supports CPiA based parallel port Video Camera's. - * - * (C) Copyright 1999 Bas Huisman, - * Peter Pregler, - * Scott J. Bertin, - * VLSI Vision Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; 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. - */ - -#define CPIA_MAJ_VER 1 -#define CPIA_MIN_VER 2 -#define CPIA_PATCH_VER 3 - -#define CPIA_PP_MAJ_VER CPIA_MAJ_VER -#define CPIA_PP_MIN_VER CPIA_MIN_VER -#define CPIA_PP_PATCH_VER CPIA_PATCH_VER - -#define CPIA_USB_MAJ_VER CPIA_MAJ_VER -#define CPIA_USB_MIN_VER CPIA_MIN_VER -#define CPIA_USB_PATCH_VER CPIA_PATCH_VER - -#define CPIA_MAX_FRAME_SIZE_UNALIGNED (352 * 288 * 4) /* CIF at RGB32 */ -#define CPIA_MAX_FRAME_SIZE ((CPIA_MAX_FRAME_SIZE_UNALIGNED + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)) /* align above to PAGE_SIZE */ - -#ifdef __KERNEL__ - -#include -#include -#include -#include -#include -#include - -struct cpia_camera_ops -{ - /* open sets privdata to point to structure for this camera. - * Returns negative value on error, otherwise 0. - */ - int (*open)(void *privdata); - - /* Registers callback function cb to be called with cbdata - * when an image is ready. If cb is NULL, only single image grabs - * should be used. cb should immediately call streamRead to read - * the data or data may be lost. Returns negative value on error, - * otherwise 0. - */ - int (*registerCallback)(void *privdata, void (*cb)(void *cbdata), - void *cbdata); - - /* transferCmd sends commands to the camera. command MUST point to - * an 8 byte buffer in kernel space. data can be NULL if no extra - * data is needed. The size of the data is given by the last 2 - * bytes of command. data must also point to memory in kernel space. - * Returns negative value on error, otherwise 0. - */ - int (*transferCmd)(void *privdata, u8 *command, u8 *data); - - /* streamStart initiates stream capture mode. - * Returns negative value on error, otherwise 0. - */ - int (*streamStart)(void *privdata); - - /* streamStop terminates stream capture mode. - * Returns negative value on error, otherwise 0. - */ - int (*streamStop)(void *privdata); - - /* streamRead reads a frame from the camera. buffer points to a - * buffer large enough to hold a complete frame in kernel space. - * noblock indicates if this should be a non blocking read. - * Returns the number of bytes read, or negative value on error. - */ - int (*streamRead)(void *privdata, u8 *buffer, int noblock); - - /* close disables the device until open() is called again. - * Returns negative value on error, otherwise 0. - */ - int (*close)(void *privdata); - - /* If wait_for_stream_ready is non-zero, wait until the streamState - * is STREAM_READY before calling streamRead. - */ - int wait_for_stream_ready; - - /* - * Used to maintain lowlevel module usage counts - */ - struct module *owner; -}; - -struct cpia_frame { - u8 *data; - int count; - int width; - int height; - volatile int state; -}; - -struct cam_params { - struct { - u8 firmwareVersion; - u8 firmwareRevision; - u8 vcVersion; - u8 vcRevision; - } version; - struct { - u16 vendor; - u16 product; - u16 deviceRevision; - } pnpID; - struct { - u8 vpVersion; - u8 vpRevision; - u16 cameraHeadID; - } vpVersion; - struct { - u8 systemState; - u8 grabState; - u8 streamState; - u8 fatalError; - u8 cmdError; - u8 debugFlags; - u8 vpStatus; - u8 errorCode; - } status; - struct { - u8 brightness; - u8 contrast; - u8 saturation; - } colourParams; - struct { - u8 gainMode; - u8 expMode; - u8 compMode; - u8 centreWeight; - u8 gain; - u8 fineExp; - u8 coarseExpLo; - u8 coarseExpHi; - u8 redComp; - u8 green1Comp; - u8 green2Comp; - u8 blueComp; - } exposure; - struct { - u8 balanceMode; - u8 redGain; - u8 greenGain; - u8 blueGain; - } colourBalance; - struct { - u8 divisor; - u8 baserate; - } sensorFps; - struct { - u8 gain1; - u8 gain2; - u8 gain4; - u8 gain8; - } apcor; - struct { - u8 disabled; - u8 flickerMode; - u8 coarseJump; - int allowableOverExposure; - } flickerControl; - struct { - u8 gain1; - u8 gain2; - u8 gain4; - u8 gain8; - } vlOffset; - struct { - u8 mode; - u8 decimation; - } compression; - struct { - u8 frTargeting; - u8 targetFR; - u8 targetQ; - } compressionTarget; - struct { - u8 yThreshold; - u8 uvThreshold; - } yuvThreshold; - struct { - u8 hysteresis; - u8 threshMax; - u8 smallStep; - u8 largeStep; - u8 decimationHysteresis; - u8 frDiffStepThresh; - u8 qDiffStepThresh; - u8 decimationThreshMod; - } compressionParams; - struct { - u8 videoSize; /* CIF/QCIF */ - u8 subSample; - u8 yuvOrder; - } format; - struct { /* Intel QX3 specific data */ - u8 qx3_detected; /* a QX3 is present */ - u8 toplight; /* top light lit , R/W */ - u8 bottomlight; /* bottom light lit, R/W */ - u8 button; /* snapshot button pressed (R/O) */ - u8 cradled; /* microscope is in cradle (R/O) */ - } qx3; - struct { - u8 colStart; /* skip first 8*colStart pixels */ - u8 colEnd; /* finish at 8*colEnd pixels */ - u8 rowStart; /* skip first 4*rowStart lines */ - u8 rowEnd; /* finish at 4*rowEnd lines */ - } roi; - u8 ecpTiming; - u8 streamStartLine; -}; - -enum v4l_camstates { - CPIA_V4L_IDLE = 0, - CPIA_V4L_ERROR, - CPIA_V4L_COMMAND, - CPIA_V4L_GRABBING, - CPIA_V4L_STREAMING, - CPIA_V4L_STREAMING_PAUSED, -}; - -#define FRAME_NUM 2 /* double buffering for now */ - -struct cam_data { - struct list_head cam_data_list; - - struct mutex busy_lock; /* guard against SMP multithreading */ - struct cpia_camera_ops *ops; /* lowlevel driver operations */ - void *lowlevel_data; /* private data for lowlevel driver */ - u8 *raw_image; /* buffer for raw image data */ - struct cpia_frame decompressed_frame; - /* buffer to hold decompressed frame */ - int image_size; /* sizeof last decompressed image */ - int open_count; /* # of process that have camera open */ - - /* camera status */ - int fps; /* actual fps reported by the camera */ - int transfer_rate; /* transfer rate from camera in kB/s */ - u8 mainsFreq; /* for flicker control */ - - /* proc interface */ - struct mutex param_lock; /* params lock for this camera */ - struct cam_params params; /* camera settings */ - struct proc_dir_entry *proc_entry; /* /proc/cpia/videoX */ - - /* v4l */ - int video_size; /* VIDEO_SIZE_ */ - volatile enum v4l_camstates camstate; /* v4l layer status */ - struct video_device vdev; /* v4l videodev */ - struct video_picture vp; /* v4l camera settings */ - struct video_window vw; /* v4l capture area */ - struct video_capture vc; /* v4l subcapture area */ - - /* mmap interface */ - int curframe; /* the current frame to grab into */ - u8 *frame_buf; /* frame buffer data */ - struct cpia_frame frame[FRAME_NUM]; - /* FRAME_NUM-buffering, so we need a array */ - - int first_frame; - int mmap_kludge; /* 'wrong' byte order for mmap */ - volatile u32 cmd_queue; /* queued commands */ - int exposure_status; /* EXPOSURE_* */ - int exposure_count; /* number of frames at this status */ -}; - -/* cpia_register_camera is called by low level driver for each camera. - * A unique camera number is returned, or a negative value on error */ -struct cam_data *cpia_register_camera(struct cpia_camera_ops *ops, void *lowlevel); - -/* cpia_unregister_camera is called by low level driver when a camera - * is removed. This must not fail. */ -void cpia_unregister_camera(struct cam_data *cam); - -/* raw CIF + 64 byte header + (2 bytes line_length + EOL) per line + 4*EOI + - * one byte 16bit DMA alignment - */ -#define CPIA_MAX_IMAGE_SIZE ((352*288*2)+64+(288*3)+5) - -/* constant value's */ -#define MAGIC_0 0x19 -#define MAGIC_1 0x68 -#define DATA_IN 0xC0 -#define DATA_OUT 0x40 -#define VIDEOSIZE_QCIF 0 /* 176x144 */ -#define VIDEOSIZE_CIF 1 /* 352x288 */ -#define VIDEOSIZE_SIF 2 /* 320x240 */ -#define VIDEOSIZE_QSIF 3 /* 160x120 */ -#define VIDEOSIZE_48_48 4 /* where no one has gone before, iconsize! */ -#define VIDEOSIZE_64_48 5 -#define VIDEOSIZE_128_96 6 -#define VIDEOSIZE_160_120 VIDEOSIZE_QSIF -#define VIDEOSIZE_176_144 VIDEOSIZE_QCIF -#define VIDEOSIZE_192_144 7 -#define VIDEOSIZE_224_168 8 -#define VIDEOSIZE_256_192 9 -#define VIDEOSIZE_288_216 10 -#define VIDEOSIZE_320_240 VIDEOSIZE_SIF -#define VIDEOSIZE_352_288 VIDEOSIZE_CIF -#define VIDEOSIZE_88_72 11 /* quarter CIF */ -#define SUBSAMPLE_420 0 -#define SUBSAMPLE_422 1 -#define YUVORDER_YUYV 0 -#define YUVORDER_UYVY 1 -#define NOT_COMPRESSED 0 -#define COMPRESSED 1 -#define NO_DECIMATION 0 -#define DECIMATION_ENAB 1 -#define EOI 0xff /* End Of Image */ -#define EOL 0xfd /* End Of Line */ -#define FRAME_HEADER_SIZE 64 - -/* Image grab modes */ -#define CPIA_GRAB_SINGLE 0 -#define CPIA_GRAB_CONTINUOUS 1 - -/* Compression parameters */ -#define CPIA_COMPRESSION_NONE 0 -#define CPIA_COMPRESSION_AUTO 1 -#define CPIA_COMPRESSION_MANUAL 2 -#define CPIA_COMPRESSION_TARGET_QUALITY 0 -#define CPIA_COMPRESSION_TARGET_FRAMERATE 1 - -/* Return offsets for GetCameraState */ -#define SYSTEMSTATE 0 -#define GRABSTATE 1 -#define STREAMSTATE 2 -#define FATALERROR 3 -#define CMDERROR 4 -#define DEBUGFLAGS 5 -#define VPSTATUS 6 -#define ERRORCODE 7 - -/* SystemState */ -#define UNINITIALISED_STATE 0 -#define PASS_THROUGH_STATE 1 -#define LO_POWER_STATE 2 -#define HI_POWER_STATE 3 -#define WARM_BOOT_STATE 4 - -/* GrabState */ -#define GRAB_IDLE 0 -#define GRAB_ACTIVE 1 -#define GRAB_DONE 2 - -/* StreamState */ -#define STREAM_NOT_READY 0 -#define STREAM_READY 1 -#define STREAM_OPEN 2 -#define STREAM_PAUSED 3 -#define STREAM_FINISHED 4 - -/* Fatal Error, CmdError, and DebugFlags */ -#define CPIA_FLAG 1 -#define SYSTEM_FLAG 2 -#define INT_CTRL_FLAG 4 -#define PROCESS_FLAG 8 -#define COM_FLAG 16 -#define VP_CTRL_FLAG 32 -#define CAPTURE_FLAG 64 -#define DEBUG_FLAG 128 - -/* VPStatus */ -#define VP_STATE_OK 0x00 - -#define VP_STATE_FAILED_VIDEOINIT 0x01 -#define VP_STATE_FAILED_AECACBINIT 0x02 -#define VP_STATE_AEC_MAX 0x04 -#define VP_STATE_ACB_BMAX 0x08 - -#define VP_STATE_ACB_RMIN 0x10 -#define VP_STATE_ACB_GMIN 0x20 -#define VP_STATE_ACB_RMAX 0x40 -#define VP_STATE_ACB_GMAX 0x80 - -/* default (minimum) compensation values */ -#define COMP_RED 220 -#define COMP_GREEN1 214 -#define COMP_GREEN2 COMP_GREEN1 -#define COMP_BLUE 230 - -/* exposure status */ -#define EXPOSURE_VERY_LIGHT 0 -#define EXPOSURE_LIGHT 1 -#define EXPOSURE_NORMAL 2 -#define EXPOSURE_DARK 3 -#define EXPOSURE_VERY_DARK 4 - -/* ErrorCode */ -#define ERROR_FLICKER_BELOW_MIN_EXP 0x01 /*flicker exposure got below minimum exposure */ -#define ALOG(fmt,args...) printk(fmt, ##args) -#define LOG(fmt,args...) ALOG(KERN_INFO __FILE__ ":%s(%d):" fmt, __func__ , __LINE__ , ##args) - -#ifdef _CPIA_DEBUG_ -#define ADBG(fmt,args...) printk(fmt, jiffies, ##args) -#define DBG(fmt,args...) ADBG(KERN_DEBUG __FILE__" (%ld):%s(%d):" fmt, __func__, __LINE__ , ##args) -#else -#define DBG(fmn,args...) do {} while(0) -#endif - -#define DEB_BYTE(p)\ - DBG("%1d %1d %1d %1d %1d %1d %1d %1d \n",\ - (p)&0x80?1:0, (p)&0x40?1:0, (p)&0x20?1:0, (p)&0x10?1:0,\ - (p)&0x08?1:0, (p)&0x04?1:0, (p)&0x02?1:0, (p)&0x01?1:0); - -#endif /* __KERNEL__ */ - -#endif /* cpia_h */ diff --git a/drivers/media/video/cpia_pp.c b/drivers/media/video/cpia_pp.c deleted file mode 100644 index f5604c16a092..000000000000 --- a/drivers/media/video/cpia_pp.c +++ /dev/null @@ -1,869 +0,0 @@ -/* - * cpia_pp CPiA Parallel Port driver - * - * Supports CPiA based parallel port Video Camera's. - * - * (C) Copyright 1999 Bas Huisman - * (C) Copyright 1999-2000 Scott J. Bertin , - * (C) Copyright 1999-2000 Peter Pregler - * - * 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. - */ - -/* define _CPIA_DEBUG_ for verbose debug output (see cpia.h) */ -/* #define _CPIA_DEBUG_ 1 */ - - -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include - -/* #define _CPIA_DEBUG_ define for verbose debug output */ -#include "cpia.h" - -static int cpia_pp_open(void *privdata); -static int cpia_pp_registerCallback(void *privdata, void (*cb) (void *cbdata), - void *cbdata); -static int cpia_pp_transferCmd(void *privdata, u8 *command, u8 *data); -static int cpia_pp_streamStart(void *privdata); -static int cpia_pp_streamStop(void *privdata); -static int cpia_pp_streamRead(void *privdata, u8 *buffer, int noblock); -static int cpia_pp_close(void *privdata); - - -#define ABOUT "Parallel port driver for Vision CPiA based cameras" - -#define PACKET_LENGTH 8 - -/* Magic numbers for defining port-device mappings */ -#define PPCPIA_PARPORT_UNSPEC -4 -#define PPCPIA_PARPORT_AUTO -3 -#define PPCPIA_PARPORT_OFF -2 -#define PPCPIA_PARPORT_NONE -1 - -static int parport_nr[PARPORT_MAX] = {[0 ... PARPORT_MAX - 1] = PPCPIA_PARPORT_UNSPEC}; -static char *parport[PARPORT_MAX] = {NULL,}; - -MODULE_AUTHOR("B. Huisman & Peter Pregler "); -MODULE_DESCRIPTION("Parallel port driver for Vision CPiA based cameras"); -MODULE_LICENSE("GPL"); - -module_param_array(parport, charp, NULL, 0); -MODULE_PARM_DESC(parport, "'auto' or a list of parallel port numbers. Just like lp."); - -struct pp_cam_entry { - struct pardevice *pdev; - struct parport *port; - struct work_struct cb_task; - void (*cb_func)(void *cbdata); - void *cb_data; - int open_count; - wait_queue_head_t wq_stream; - /* image state flags */ - int image_ready; /* we got an interrupt */ - int image_complete; /* we have seen 4 EOI */ - - int streaming; /* we are in streaming mode */ - int stream_irq; -}; - -static struct cpia_camera_ops cpia_pp_ops = -{ - cpia_pp_open, - cpia_pp_registerCallback, - cpia_pp_transferCmd, - cpia_pp_streamStart, - cpia_pp_streamStop, - cpia_pp_streamRead, - cpia_pp_close, - 1, - THIS_MODULE -}; - -static LIST_HEAD(cam_list); -static spinlock_t cam_list_lock_pp; - -/* FIXME */ -static void cpia_parport_enable_irq( struct parport *port ) { - parport_enable_irq(port); - mdelay(10); - return; -} - -static void cpia_parport_disable_irq( struct parport *port ) { - parport_disable_irq(port); - mdelay(10); - return; -} - -/* Special CPiA PPC modes: These are invoked by using the 1284 Extensibility - * Link Flag during negotiation */ -#define UPLOAD_FLAG 0x08 -#define NIBBLE_TRANSFER 0x01 -#define ECP_TRANSFER 0x03 - -#define PARPORT_CHUNK_SIZE PAGE_SIZE - - -static void cpia_pp_run_callback(struct work_struct *work) -{ - void (*cb_func)(void *cbdata); - void *cb_data; - struct pp_cam_entry *cam; - - cam = container_of(work, struct pp_cam_entry, cb_task); - cb_func = cam->cb_func; - cb_data = cam->cb_data; - - cb_func(cb_data); -} - -/**************************************************************************** - * - * CPiA-specific low-level parport functions for nibble uploads - * - ***************************************************************************/ -/* CPiA nonstandard "Nibble" mode (no nDataAvail signal after each byte). */ -/* The standard kernel parport_ieee1284_read_nibble() fails with the CPiA... */ - -static size_t cpia_read_nibble (struct parport *port, - void *buffer, size_t len, - int flags) -{ - /* adapted verbatim, with one change, from - parport_ieee1284_read_nibble() in drivers/parport/ieee1284-ops.c */ - - unsigned char *buf = buffer; - int i; - unsigned char byte = 0; - - len *= 2; /* in nibbles */ - for (i=0; i < len; i++) { - unsigned char nibble; - - /* The CPiA firmware suppresses the use of nDataAvail (nFault LO) - * after every second nibble to signal that more - * data is available. (the total number of Bytes that - * should be sent is known; if too few are received, an error - * will be recorded after a timeout). - * This is incompatible with parport_ieee1284_read_nibble(), - * which expects to find nFault LO after every second nibble. - */ - - /* Solution: modify cpia_read_nibble to only check for - * nDataAvail before the first nibble is sent. - */ - - /* Does the error line indicate end of data? */ - if (((i /*& 1*/) == 0) && - (parport_read_status(port) & PARPORT_STATUS_ERROR)) { - DBG("%s: No more nibble data (%d bytes)\n", - port->name, i/2); - goto end_of_data; - } - - /* Event 7: Set nAutoFd low. */ - parport_frob_control (port, - PARPORT_CONTROL_AUTOFD, - PARPORT_CONTROL_AUTOFD); - - /* Event 9: nAck goes low. */ - port->ieee1284.phase = IEEE1284_PH_REV_DATA; - if (parport_wait_peripheral (port, - PARPORT_STATUS_ACK, 0)) { - /* Timeout -- no more data? */ - DBG("%s: Nibble timeout at event 9 (%d bytes)\n", - port->name, i/2); - parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0); - break; - } - - - /* Read a nibble. */ - nibble = parport_read_status (port) >> 3; - nibble &= ~8; - if ((nibble & 0x10) == 0) - nibble |= 8; - nibble &= 0xf; - - /* Event 10: Set nAutoFd high. */ - parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0); - - /* Event 11: nAck goes high. */ - if (parport_wait_peripheral (port, - PARPORT_STATUS_ACK, - PARPORT_STATUS_ACK)) { - /* Timeout -- no more data? */ - DBG("%s: Nibble timeout at event 11\n", - port->name); - break; - } - - if (i & 1) { - /* Second nibble */ - byte |= nibble << 4; - *buf++ = byte; - } else - byte = nibble; - } - - if (i == len) { - /* Read the last nibble without checking data avail. */ - if (parport_read_status (port) & PARPORT_STATUS_ERROR) { - end_of_data: - /* Go to reverse idle phase. */ - parport_frob_control (port, - PARPORT_CONTROL_AUTOFD, - PARPORT_CONTROL_AUTOFD); - port->physport->ieee1284.phase = IEEE1284_PH_REV_IDLE; - } - else - port->physport->ieee1284.phase = IEEE1284_PH_HBUSY_DAVAIL; - } - - return i/2; -} - -/* CPiA nonstandard "Nibble Stream" mode (2 nibbles per cycle, instead of 1) - * (See CPiA Data sheet p. 31) - * - * "Nibble Stream" mode used by CPiA for uploads to non-ECP ports is a - * nonstandard variant of nibble mode which allows the same (mediocre) - * data flow of 8 bits per cycle as software-enabled ECP by TRISTATE-capable - * parallel ports, but works also for non-TRISTATE-capable ports. - * (Standard nibble mode only send 4 bits per cycle) - * - */ - -static size_t cpia_read_nibble_stream(struct parport *port, - void *buffer, size_t len, - int flags) -{ - int i; - unsigned char *buf = buffer; - int endseen = 0; - - for (i=0; i < len; i++) { - unsigned char nibble[2], byte = 0; - int j; - - /* Image Data is complete when 4 consecutive EOI bytes (0xff) are seen */ - if (endseen > 3 ) - break; - - /* Event 7: Set nAutoFd low. */ - parport_frob_control (port, - PARPORT_CONTROL_AUTOFD, - PARPORT_CONTROL_AUTOFD); - - /* Event 9: nAck goes low. */ - port->ieee1284.phase = IEEE1284_PH_REV_DATA; - if (parport_wait_peripheral (port, - PARPORT_STATUS_ACK, 0)) { - /* Timeout -- no more data? */ - DBG("%s: Nibble timeout at event 9 (%d bytes)\n", - port->name, i/2); - parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0); - break; - } - - /* Read lower nibble */ - nibble[0] = parport_read_status (port) >>3; - - /* Event 10: Set nAutoFd high. */ - parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0); - - /* Event 11: nAck goes high. */ - if (parport_wait_peripheral (port, - PARPORT_STATUS_ACK, - PARPORT_STATUS_ACK)) { - /* Timeout -- no more data? */ - DBG("%s: Nibble timeout at event 11\n", - port->name); - break; - } - - /* Read upper nibble */ - nibble[1] = parport_read_status (port) >>3; - - /* reassemble the byte */ - for (j = 0; j < 2 ; j++ ) { - nibble[j] &= ~8; - if ((nibble[j] & 0x10) == 0) - nibble[j] |= 8; - nibble[j] &= 0xf; - } - byte = (nibble[0] |(nibble[1] << 4)); - *buf++ = byte; - - if(byte == EOI) - endseen++; - else - endseen = 0; - } - return i; -} - -/**************************************************************************** - * - * EndTransferMode - * - ***************************************************************************/ -static void EndTransferMode(struct pp_cam_entry *cam) -{ - parport_negotiate(cam->port, IEEE1284_MODE_COMPAT); -} - -/**************************************************************************** - * - * ForwardSetup - * - ***************************************************************************/ -static int ForwardSetup(struct pp_cam_entry *cam) -{ - int retry; - - /* The CPiA uses ECP protocol for Downloads from the Host to the camera. - * This will be software-emulated if ECP hardware is not present - */ - - /* the usual camera maximum response time is 10ms, but after receiving - * some commands, it needs up to 40ms. (Data Sheet p. 32)*/ - - for(retry = 0; retry < 4; ++retry) { - if(!parport_negotiate(cam->port, IEEE1284_MODE_ECP)) { - break; - } - mdelay(10); - } - if(retry == 4) { - DBG("Unable to negotiate IEEE1284 ECP Download mode\n"); - return -1; - } - return 0; -} -/**************************************************************************** - * - * ReverseSetup - * - ***************************************************************************/ -static int ReverseSetup(struct pp_cam_entry *cam, int extensibility) -{ - int retry; - int upload_mode, mode = IEEE1284_MODE_ECP; - int transfer_mode = ECP_TRANSFER; - - if (!(cam->port->modes & PARPORT_MODE_ECP) && - !(cam->port->modes & PARPORT_MODE_TRISTATE)) { - mode = IEEE1284_MODE_NIBBLE; - transfer_mode = NIBBLE_TRANSFER; - } - - upload_mode = mode; - if(extensibility) mode = UPLOAD_FLAG|transfer_mode|IEEE1284_EXT_LINK; - - /* the usual camera maximum response time is 10ms, but after - * receiving some commands, it needs up to 40ms. */ - - for(retry = 0; retry < 4; ++retry) { - if(!parport_negotiate(cam->port, mode)) { - break; - } - mdelay(10); - } - if(retry == 4) { - if(extensibility) - DBG("Unable to negotiate upload extensibility mode\n"); - else - DBG("Unable to negotiate upload mode\n"); - return -1; - } - if(extensibility) cam->port->ieee1284.mode = upload_mode; - return 0; -} - -/**************************************************************************** - * - * WritePacket - * - ***************************************************************************/ -static int WritePacket(struct pp_cam_entry *cam, const u8 *packet, size_t size) -{ - int retval=0; - int size_written; - - if (packet == NULL) { - return -EINVAL; - } - if (ForwardSetup(cam)) { - DBG("Write failed in setup\n"); - return -EIO; - } - size_written = parport_write(cam->port, packet, size); - if(size_written != size) { - DBG("Write failed, wrote %d/%d\n", size_written, size); - retval = -EIO; - } - EndTransferMode(cam); - return retval; -} - -/**************************************************************************** - * - * ReadPacket - * - ***************************************************************************/ -static int ReadPacket(struct pp_cam_entry *cam, u8 *packet, size_t size) -{ - int retval=0; - - if (packet == NULL) { - return -EINVAL; - } - if (ReverseSetup(cam, 0)) { - return -EIO; - } - - /* support for CPiA variant nibble reads */ - if(cam->port->ieee1284.mode == IEEE1284_MODE_NIBBLE) { - if(cpia_read_nibble(cam->port, packet, size, 0) != size) - retval = -EIO; - } else { - if(parport_read(cam->port, packet, size) != size) - retval = -EIO; - } - EndTransferMode(cam); - return retval; -} - -/**************************************************************************** - * - * cpia_pp_streamStart - * - ***************************************************************************/ -static int cpia_pp_streamStart(void *privdata) -{ - struct pp_cam_entry *cam = privdata; - DBG("\n"); - cam->streaming=1; - cam->image_ready=0; - //if (ReverseSetup(cam,1)) return -EIO; - if(cam->stream_irq) cpia_parport_enable_irq(cam->port); - return 0; -} - -/**************************************************************************** - * - * cpia_pp_streamStop - * - ***************************************************************************/ -static int cpia_pp_streamStop(void *privdata) -{ - struct pp_cam_entry *cam = privdata; - - DBG("\n"); - cam->streaming=0; - cpia_parport_disable_irq(cam->port); - //EndTransferMode(cam); - - return 0; -} - -/**************************************************************************** - * - * cpia_pp_streamRead - * - ***************************************************************************/ -static int cpia_pp_read(struct parport *port, u8 *buffer, int len) -{ - int bytes_read; - - /* support for CPiA variant "nibble stream" reads */ - if(port->ieee1284.mode == IEEE1284_MODE_NIBBLE) - bytes_read = cpia_read_nibble_stream(port,buffer,len,0); - else { - int new_bytes; - for(bytes_read=0; bytes_readstreaming) DBG("%d / %d\n", cam->image_ready, noblock); - if( cam->stream_irq ) { - DBG("%d\n", cam->image_ready); - cam->image_ready--; - } - cam->image_complete=0; - if (0/*cam->streaming*/) { - if(!cam->image_ready) { - if(noblock) return -EWOULDBLOCK; - interruptible_sleep_on(&cam->wq_stream); - if( signal_pending(current) ) return -EINTR; - DBG("%d\n", cam->image_ready); - } - } else { - if (ReverseSetup(cam, 1)) { - DBG("unable to ReverseSetup\n"); - return -EIO; - } - } - endseen = 0; - block_size = PARPORT_CHUNK_SIZE; - while( !cam->image_complete ) { - cond_resched(); - - new_bytes = cpia_pp_read(cam->port, buffer, block_size ); - if( new_bytes <= 0 ) { - break; - } - i=-1; - while(++iimage_complete=1; - break; - } - if( CPIA_MAX_IMAGE_SIZE-read_bytes <= PARPORT_CHUNK_SIZE ) { - block_size=CPIA_MAX_IMAGE_SIZE-read_bytes; - } - } - EndTransferMode(cam); - return cam->image_complete ? read_bytes : -EIO; -} -/**************************************************************************** - * - * cpia_pp_transferCmd - * - ***************************************************************************/ -static int cpia_pp_transferCmd(void *privdata, u8 *command, u8 *data) -{ - int err; - int retval=0; - int databytes; - struct pp_cam_entry *cam = privdata; - - if(cam == NULL) { - DBG("Internal driver error: cam is NULL\n"); - return -EINVAL; - } - if(command == NULL) { - DBG("Internal driver error: command is NULL\n"); - return -EINVAL; - } - databytes = (((int)command[7])<<8) | command[6]; - if ((err = WritePacket(cam, command, PACKET_LENGTH)) < 0) { - DBG("Error writing command\n"); - return err; - } - if(command[0] == DATA_IN) { - u8 buffer[8]; - if(data == NULL) { - DBG("Internal driver error: data is NULL\n"); - return -EINVAL; - } - if((err = ReadPacket(cam, buffer, 8)) < 0) { - DBG("Error reading command result\n"); - return err; - } - memcpy(data, buffer, databytes); - } else if(command[0] == DATA_OUT) { - if(databytes > 0) { - if(data == NULL) { - DBG("Internal driver error: data is NULL\n"); - retval = -EINVAL; - } else { - if((err=WritePacket(cam, data, databytes)) < 0){ - DBG("Error writing command data\n"); - return err; - } - } - } - } else { - DBG("Unexpected first byte of command: %x\n", command[0]); - retval = -EINVAL; - } - return retval; -} - -/**************************************************************************** - * - * cpia_pp_open - * - ***************************************************************************/ -static int cpia_pp_open(void *privdata) -{ - struct pp_cam_entry *cam = (struct pp_cam_entry *)privdata; - - if (cam == NULL) - return -EINVAL; - - if(cam->open_count == 0) { - if (parport_claim(cam->pdev)) { - DBG("failed to claim the port\n"); - return -EBUSY; - } - parport_negotiate(cam->port, IEEE1284_MODE_COMPAT); - parport_data_forward(cam->port); - parport_write_control(cam->port, PARPORT_CONTROL_SELECT); - udelay(50); - parport_write_control(cam->port, - PARPORT_CONTROL_SELECT - | PARPORT_CONTROL_INIT); - } - - ++cam->open_count; - - return 0; -} - -/**************************************************************************** - * - * cpia_pp_registerCallback - * - ***************************************************************************/ -static int cpia_pp_registerCallback(void *privdata, void (*cb)(void *cbdata), void *cbdata) -{ - struct pp_cam_entry *cam = privdata; - int retval = 0; - - if(cam->port->irq != PARPORT_IRQ_NONE) { - cam->cb_func = cb; - cam->cb_data = cbdata; - INIT_WORK(&cam->cb_task, cpia_pp_run_callback); - } else { - retval = -1; - } - return retval; -} - -/**************************************************************************** - * - * cpia_pp_close - * - ***************************************************************************/ -static int cpia_pp_close(void *privdata) -{ - struct pp_cam_entry *cam = privdata; - if (--cam->open_count == 0) { - parport_release(cam->pdev); - } - return 0; -} - -/**************************************************************************** - * - * cpia_pp_register - * - ***************************************************************************/ -static int cpia_pp_register(struct parport *port) -{ - struct pardevice *pdev = NULL; - struct pp_cam_entry *cam; - struct cam_data *cpia; - - if (!(port->modes & PARPORT_MODE_PCSPP)) { - LOG("port is not supported by CPiA driver\n"); - return -ENXIO; - } - - cam = kzalloc(sizeof(struct pp_cam_entry), GFP_KERNEL); - if (cam == NULL) { - LOG("failed to allocate camera structure\n"); - return -ENOMEM; - } - - pdev = parport_register_device(port, "cpia_pp", NULL, NULL, - NULL, 0, cam); - - if (!pdev) { - LOG("failed to parport_register_device\n"); - kfree(cam); - return -ENXIO; - } - - cam->pdev = pdev; - cam->port = port; - init_waitqueue_head(&cam->wq_stream); - - cam->streaming = 0; - cam->stream_irq = 0; - - if((cpia = cpia_register_camera(&cpia_pp_ops, cam)) == NULL) { - LOG("failed to cpia_register_camera\n"); - parport_unregister_device(pdev); - kfree(cam); - return -ENXIO; - } - spin_lock( &cam_list_lock_pp ); - list_add( &cpia->cam_data_list, &cam_list ); - spin_unlock( &cam_list_lock_pp ); - - return 0; -} - -static void cpia_pp_detach (struct parport *port) -{ - struct list_head *tmp; - struct cam_data *cpia = NULL; - struct pp_cam_entry *cam; - - spin_lock( &cam_list_lock_pp ); - list_for_each (tmp, &cam_list) { - cpia = list_entry(tmp, struct cam_data, cam_data_list); - cam = (struct pp_cam_entry *) cpia->lowlevel_data; - if (cam && cam->port->number == port->number) { - list_del(&cpia->cam_data_list); - break; - } - cpia = NULL; - } - spin_unlock( &cam_list_lock_pp ); - - if (!cpia) { - DBG("cpia_pp_detach failed to find cam_data in cam_list\n"); - return; - } - - cam = (struct pp_cam_entry *) cpia->lowlevel_data; - cpia_unregister_camera(cpia); - if(cam->open_count > 0) - cpia_pp_close(cam); - parport_unregister_device(cam->pdev); - cpia->lowlevel_data = NULL; - kfree(cam); -} - -static void cpia_pp_attach (struct parport *port) -{ - unsigned int i; - - switch (parport_nr[0]) - { - case PPCPIA_PARPORT_UNSPEC: - case PPCPIA_PARPORT_AUTO: - if (port->probe_info[0].class != PARPORT_CLASS_MEDIA || - port->probe_info[0].cmdset == NULL || - strncmp(port->probe_info[0].cmdset, "CPIA_1", 6) != 0) - return; - - cpia_pp_register(port); - - break; - - default: - for (i = 0; i < PARPORT_MAX; ++i) { - if (port->number == parport_nr[i]) { - cpia_pp_register(port); - break; - } - } - break; - } -} - -static struct parport_driver cpia_pp_driver = { - .name = "cpia_pp", - .attach = cpia_pp_attach, - .detach = cpia_pp_detach, -}; - -static int __init cpia_pp_init(void) -{ - printk(KERN_INFO "%s v%d.%d.%d\n",ABOUT, - CPIA_PP_MAJ_VER,CPIA_PP_MIN_VER,CPIA_PP_PATCH_VER); - - if(parport_nr[0] == PPCPIA_PARPORT_OFF) { - printk(" disabled\n"); - return 0; - } - - spin_lock_init( &cam_list_lock_pp ); - - if (parport_register_driver (&cpia_pp_driver)) { - LOG ("unable to register with parport\n"); - return -EIO; - } - return 0; -} - -static int __init cpia_init(void) -{ - if (parport[0]) { - /* The user gave some parameters. Let's see what they were. */ - if (!strncmp(parport[0], "auto", 4)) { - parport_nr[0] = PPCPIA_PARPORT_AUTO; - } else { - int n; - for (n = 0; n < PARPORT_MAX && parport[n]; n++) { - if (!strncmp(parport[n], "none", 4)) { - parport_nr[n] = PPCPIA_PARPORT_NONE; - } else { - char *ep; - unsigned long r = simple_strtoul(parport[n], &ep, 0); - if (ep != parport[n]) { - parport_nr[n] = r; - } else { - LOG("bad port specifier `%s'\n", parport[n]); - return -ENODEV; - } - } - } - } - } - return cpia_pp_init(); -} - -static void __exit cpia_cleanup(void) -{ - parport_unregister_driver(&cpia_pp_driver); - return; -} - -module_init(cpia_init); -module_exit(cpia_cleanup); diff --git a/drivers/media/video/cpia_usb.c b/drivers/media/video/cpia_usb.c deleted file mode 100644 index 58d193ff591c..000000000000 --- a/drivers/media/video/cpia_usb.c +++ /dev/null @@ -1,640 +0,0 @@ -/* - * cpia_usb CPiA USB driver - * - * Supports CPiA based parallel port Video Camera's. - * - * Copyright (C) 1999 Jochen Scharrlach - * Copyright (C) 1999, 2000 Johannes Erdfelt - * - * 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. - */ - -/* define _CPIA_DEBUG_ for verbose debug output (see cpia.h) */ -/* #define _CPIA_DEBUG_ 1 */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "cpia.h" - -#define USB_REQ_CPIA_GRAB_FRAME 0xC1 -#define USB_REQ_CPIA_UPLOAD_FRAME 0xC2 -#define WAIT_FOR_NEXT_FRAME 0 -#define FORCE_FRAME_UPLOAD 1 - -#define FRAMES_PER_DESC 10 -#define FRAME_SIZE_PER_DESC 960 /* Shouldn't be hardcoded */ -#define CPIA_NUMSBUF 2 -#define STREAM_BUF_SIZE (PAGE_SIZE * 4) -#define SCRATCH_BUF_SIZE (STREAM_BUF_SIZE * 2) - -struct cpia_sbuf { - char *data; - struct urb *urb; -}; - -#define FRAMEBUF_LEN (CPIA_MAX_FRAME_SIZE+100) -enum framebuf_status { - FRAME_EMPTY, - FRAME_READING, - FRAME_READY, - FRAME_ERROR, -}; - -struct framebuf { - int length; - enum framebuf_status status; - u8 data[FRAMEBUF_LEN]; - struct framebuf *next; -}; - -struct usb_cpia { - /* Device structure */ - struct usb_device *dev; - - unsigned char iface; - wait_queue_head_t wq_stream; - - int cursbuf; /* Current receiving sbuf */ - struct cpia_sbuf sbuf[CPIA_NUMSBUF]; /* Double buffering */ - - int streaming; - int open; - int present; - struct framebuf *buffers[3]; - struct framebuf *curbuff, *workbuff; -}; - -static int cpia_usb_open(void *privdata); -static int cpia_usb_registerCallback(void *privdata, void (*cb) (void *cbdata), - void *cbdata); -static int cpia_usb_transferCmd(void *privdata, u8 *command, u8 *data); -static int cpia_usb_streamStart(void *privdata); -static int cpia_usb_streamStop(void *privdata); -static int cpia_usb_streamRead(void *privdata, u8 *frame, int noblock); -static int cpia_usb_close(void *privdata); - -#define ABOUT "USB driver for Vision CPiA based cameras" - -static struct cpia_camera_ops cpia_usb_ops = { - cpia_usb_open, - cpia_usb_registerCallback, - cpia_usb_transferCmd, - cpia_usb_streamStart, - cpia_usb_streamStop, - cpia_usb_streamRead, - cpia_usb_close, - 0, - THIS_MODULE -}; - -static LIST_HEAD(cam_list); -static spinlock_t cam_list_lock_usb; - -static void cpia_usb_complete(struct urb *urb) -{ - int i; - char *cdata; - struct usb_cpia *ucpia; - - if (!urb || !urb->context) - return; - - ucpia = (struct usb_cpia *) urb->context; - - if (!ucpia->dev || !ucpia->streaming || !ucpia->present || !ucpia->open) - return; - - if (ucpia->workbuff->status == FRAME_EMPTY) { - ucpia->workbuff->status = FRAME_READING; - ucpia->workbuff->length = 0; - } - - for (i = 0; i < urb->number_of_packets; i++) { - int n = urb->iso_frame_desc[i].actual_length; - int st = urb->iso_frame_desc[i].status; - - cdata = urb->transfer_buffer + urb->iso_frame_desc[i].offset; - - if (st) - printk(KERN_DEBUG "cpia data error: [%d] len=%d, status=%X\n", i, n, st); - - if (FRAMEBUF_LEN < ucpia->workbuff->length + n) { - printk(KERN_DEBUG "cpia: scratch buf overflow!scr_len: %d, n: %d\n", ucpia->workbuff->length, n); - return; - } - - if (n) { - if ((ucpia->workbuff->length > 0) || - (0x19 == cdata[0] && 0x68 == cdata[1])) { - memcpy(ucpia->workbuff->data + ucpia->workbuff->length, cdata, n); - ucpia->workbuff->length += n; - } else - DBG("Ignoring packet!\n"); - } else { - if (ucpia->workbuff->length > 4 && - 0xff == ucpia->workbuff->data[ucpia->workbuff->length-1] && - 0xff == ucpia->workbuff->data[ucpia->workbuff->length-2] && - 0xff == ucpia->workbuff->data[ucpia->workbuff->length-3] && - 0xff == ucpia->workbuff->data[ucpia->workbuff->length-4]) { - ucpia->workbuff->status = FRAME_READY; - ucpia->curbuff = ucpia->workbuff; - ucpia->workbuff = ucpia->workbuff->next; - ucpia->workbuff->status = FRAME_EMPTY; - ucpia->workbuff->length = 0; - - if (waitqueue_active(&ucpia->wq_stream)) - wake_up_interruptible(&ucpia->wq_stream); - } - } - } - - /* resubmit */ - urb->dev = ucpia->dev; - if ((i = usb_submit_urb(urb, GFP_ATOMIC)) != 0) - printk(KERN_ERR "%s: usb_submit_urb ret %d\n", __func__, i); -} - -static int cpia_usb_open(void *privdata) -{ - struct usb_cpia *ucpia = (struct usb_cpia *) privdata; - struct urb *urb; - int ret, retval = 0, fx, err; - - if (!ucpia) - return -EINVAL; - - ucpia->sbuf[0].data = kmalloc(FRAMES_PER_DESC * FRAME_SIZE_PER_DESC, GFP_KERNEL); - if (!ucpia->sbuf[0].data) - return -EINVAL; - - ucpia->sbuf[1].data = kmalloc(FRAMES_PER_DESC * FRAME_SIZE_PER_DESC, GFP_KERNEL); - if (!ucpia->sbuf[1].data) { - retval = -EINVAL; - goto error_0; - } - - ret = usb_set_interface(ucpia->dev, ucpia->iface, 3); - if (ret < 0) { - printk(KERN_ERR "cpia_usb_open: usb_set_interface error (ret = %d)\n", ret); - retval = -EBUSY; - goto error_1; - } - - ucpia->buffers[0]->status = FRAME_EMPTY; - ucpia->buffers[0]->length = 0; - ucpia->buffers[1]->status = FRAME_EMPTY; - ucpia->buffers[1]->length = 0; - ucpia->buffers[2]->status = FRAME_EMPTY; - ucpia->buffers[2]->length = 0; - ucpia->curbuff = ucpia->buffers[0]; - ucpia->workbuff = ucpia->buffers[1]; - - /* We double buffer the Iso lists, and also know the polling - * interval is every frame (1 == (1 << (bInterval -1))). - */ - urb = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL); - if (!urb) { - printk(KERN_ERR "cpia_init_isoc: usb_alloc_urb 0\n"); - retval = -ENOMEM; - goto error_1; - } - - ucpia->sbuf[0].urb = urb; - urb->dev = ucpia->dev; - urb->context = ucpia; - urb->pipe = usb_rcvisocpipe(ucpia->dev, 1); - urb->transfer_flags = URB_ISO_ASAP; - urb->transfer_buffer = ucpia->sbuf[0].data; - urb->complete = cpia_usb_complete; - urb->number_of_packets = FRAMES_PER_DESC; - urb->interval = 1; - urb->transfer_buffer_length = FRAME_SIZE_PER_DESC * FRAMES_PER_DESC; - for (fx = 0; fx < FRAMES_PER_DESC; fx++) { - urb->iso_frame_desc[fx].offset = FRAME_SIZE_PER_DESC * fx; - urb->iso_frame_desc[fx].length = FRAME_SIZE_PER_DESC; - } - - urb = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL); - if (!urb) { - printk(KERN_ERR "cpia_init_isoc: usb_alloc_urb 1\n"); - retval = -ENOMEM; - goto error_urb0; - } - - ucpia->sbuf[1].urb = urb; - urb->dev = ucpia->dev; - urb->context = ucpia; - urb->pipe = usb_rcvisocpipe(ucpia->dev, 1); - urb->transfer_flags = URB_ISO_ASAP; - urb->transfer_buffer = ucpia->sbuf[1].data; - urb->complete = cpia_usb_complete; - urb->number_of_packets = FRAMES_PER_DESC; - urb->interval = 1; - urb->transfer_buffer_length = FRAME_SIZE_PER_DESC * FRAMES_PER_DESC; - for (fx = 0; fx < FRAMES_PER_DESC; fx++) { - urb->iso_frame_desc[fx].offset = FRAME_SIZE_PER_DESC * fx; - urb->iso_frame_desc[fx].length = FRAME_SIZE_PER_DESC; - } - - /* queue the ISO urbs, and resubmit in the completion handler */ - err = usb_submit_urb(ucpia->sbuf[0].urb, GFP_KERNEL); - if (err) { - printk(KERN_ERR "cpia_init_isoc: usb_submit_urb 0 ret %d\n", - err); - goto error_urb1; - } - err = usb_submit_urb(ucpia->sbuf[1].urb, GFP_KERNEL); - if (err) { - printk(KERN_ERR "cpia_init_isoc: usb_submit_urb 1 ret %d\n", - err); - goto error_urb1; - } - - ucpia->streaming = 1; - ucpia->open = 1; - - return 0; - -error_urb1: /* free urb 1 */ - usb_free_urb(ucpia->sbuf[1].urb); - ucpia->sbuf[1].urb = NULL; -error_urb0: /* free urb 0 */ - usb_free_urb(ucpia->sbuf[0].urb); - ucpia->sbuf[0].urb = NULL; -error_1: - kfree (ucpia->sbuf[1].data); - ucpia->sbuf[1].data = NULL; -error_0: - kfree (ucpia->sbuf[0].data); - ucpia->sbuf[0].data = NULL; - - return retval; -} - -// -// convenience functions -// - -/**************************************************************************** - * - * WritePacket - * - ***************************************************************************/ -static int WritePacket(struct usb_device *udev, const u8 *packet, u8 *buf, size_t size) -{ - if (!packet) - return -EINVAL; - - return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - packet[1] + (packet[0] << 8), - USB_TYPE_VENDOR | USB_RECIP_DEVICE, - packet[2] + (packet[3] << 8), - packet[4] + (packet[5] << 8), buf, size, 1000); -} - -/**************************************************************************** - * - * ReadPacket - * - ***************************************************************************/ -static int ReadPacket(struct usb_device *udev, u8 *packet, u8 *buf, size_t size) -{ - if (!packet || size <= 0) - return -EINVAL; - - return usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), - packet[1] + (packet[0] << 8), - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - packet[2] + (packet[3] << 8), - packet[4] + (packet[5] << 8), buf, size, 1000); -} - -static int cpia_usb_transferCmd(void *privdata, u8 *command, u8 *data) -{ - int err = 0; - int databytes; - struct usb_cpia *ucpia = (struct usb_cpia *)privdata; - struct usb_device *udev = ucpia->dev; - - if (!udev) { - DBG("Internal driver error: udev is NULL\n"); - return -EINVAL; - } - - if (!command) { - DBG("Internal driver error: command is NULL\n"); - return -EINVAL; - } - - databytes = (((int)command[7])<<8) | command[6]; - - if (command[0] == DATA_IN) { - u8 buffer[8]; - - if (!data) { - DBG("Internal driver error: data is NULL\n"); - return -EINVAL; - } - - err = ReadPacket(udev, command, buffer, 8); - if (err < 0) - return err; - - memcpy(data, buffer, databytes); - } else if(command[0] == DATA_OUT) - WritePacket(udev, command, data, databytes); - else { - DBG("Unexpected first byte of command: %x\n", command[0]); - err = -EINVAL; - } - - return 0; -} - -static int cpia_usb_registerCallback(void *privdata, void (*cb) (void *cbdata), - void *cbdata) -{ - return -ENODEV; -} - -static int cpia_usb_streamStart(void *privdata) -{ - return -ENODEV; -} - -static int cpia_usb_streamStop(void *privdata) -{ - return -ENODEV; -} - -static int cpia_usb_streamRead(void *privdata, u8 *frame, int noblock) -{ - struct usb_cpia *ucpia = (struct usb_cpia *) privdata; - struct framebuf *mybuff; - - if (!ucpia || !ucpia->present) - return -1; - - if (ucpia->curbuff->status != FRAME_READY) - interruptible_sleep_on(&ucpia->wq_stream); - else - DBG("Frame already waiting!\n"); - - mybuff = ucpia->curbuff; - - if (!mybuff) - return -1; - - if (mybuff->status != FRAME_READY || mybuff->length < 4) { - DBG("Something went wrong!\n"); - return -1; - } - - memcpy(frame, mybuff->data, mybuff->length); - mybuff->status = FRAME_EMPTY; - -/* DBG("read done, %d bytes, Header: %x/%x, Footer: %x%x%x%x\n", */ -/* mybuff->length, frame[0], frame[1], */ -/* frame[mybuff->length-4], frame[mybuff->length-3], */ -/* frame[mybuff->length-2], frame[mybuff->length-1]); */ - - return mybuff->length; -} - -static void cpia_usb_free_resources(struct usb_cpia *ucpia, int try) -{ - if (!ucpia->streaming) - return; - - ucpia->streaming = 0; - - /* Set packet size to 0 */ - if (try) { - int ret; - - ret = usb_set_interface(ucpia->dev, ucpia->iface, 0); - if (ret < 0) { - printk(KERN_ERR "usb_set_interface error (ret = %d)\n", ret); - return; - } - } - - /* Unschedule all of the iso td's */ - if (ucpia->sbuf[1].urb) { - usb_kill_urb(ucpia->sbuf[1].urb); - usb_free_urb(ucpia->sbuf[1].urb); - ucpia->sbuf[1].urb = NULL; - } - - kfree(ucpia->sbuf[1].data); - ucpia->sbuf[1].data = NULL; - - if (ucpia->sbuf[0].urb) { - usb_kill_urb(ucpia->sbuf[0].urb); - usb_free_urb(ucpia->sbuf[0].urb); - ucpia->sbuf[0].urb = NULL; - } - - kfree(ucpia->sbuf[0].data); - ucpia->sbuf[0].data = NULL; -} - -static int cpia_usb_close(void *privdata) -{ - struct usb_cpia *ucpia = (struct usb_cpia *) privdata; - - if(!ucpia) - return -ENODEV; - - ucpia->open = 0; - - /* ucpia->present = 0 protects against trying to reset the - * alt setting if camera is physically disconnected while open */ - cpia_usb_free_resources(ucpia, ucpia->present); - - return 0; -} - -/* Probing and initializing */ - -static int cpia_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - struct usb_device *udev = interface_to_usbdev(intf); - struct usb_host_interface *interface; - struct usb_cpia *ucpia; - struct cam_data *cam; - int ret; - - /* A multi-config CPiA camera? */ - if (udev->descriptor.bNumConfigurations != 1) - return -ENODEV; - - interface = intf->cur_altsetting; - - printk(KERN_INFO "USB CPiA camera found\n"); - - ucpia = kzalloc(sizeof(*ucpia), GFP_KERNEL); - if (!ucpia) { - printk(KERN_ERR "couldn't kmalloc cpia struct\n"); - return -ENOMEM; - } - - ucpia->dev = udev; - ucpia->iface = interface->desc.bInterfaceNumber; - init_waitqueue_head(&ucpia->wq_stream); - - ucpia->buffers[0] = vmalloc(sizeof(*ucpia->buffers[0])); - if (!ucpia->buffers[0]) { - printk(KERN_ERR "couldn't vmalloc frame buffer 0\n"); - goto fail_alloc_0; - } - - ucpia->buffers[1] = vmalloc(sizeof(*ucpia->buffers[1])); - if (!ucpia->buffers[1]) { - printk(KERN_ERR "couldn't vmalloc frame buffer 1\n"); - goto fail_alloc_1; - } - - ucpia->buffers[2] = vmalloc(sizeof(*ucpia->buffers[2])); - if (!ucpia->buffers[2]) { - printk(KERN_ERR "couldn't vmalloc frame buffer 2\n"); - goto fail_alloc_2; - } - - ucpia->buffers[0]->next = ucpia->buffers[1]; - ucpia->buffers[1]->next = ucpia->buffers[2]; - ucpia->buffers[2]->next = ucpia->buffers[0]; - - ret = usb_set_interface(udev, ucpia->iface, 0); - if (ret < 0) { - printk(KERN_ERR "cpia_probe: usb_set_interface error (ret = %d)\n", ret); - /* goto fail_all; */ - } - - /* Before register_camera, important */ - ucpia->present = 1; - - cam = cpia_register_camera(&cpia_usb_ops, ucpia); - if (!cam) { - LOG("failed to cpia_register_camera\n"); - goto fail_all; - } - - spin_lock( &cam_list_lock_usb ); - list_add( &cam->cam_data_list, &cam_list ); - spin_unlock( &cam_list_lock_usb ); - - usb_set_intfdata(intf, cam); - return 0; - -fail_all: - vfree(ucpia->buffers[2]); - ucpia->buffers[2] = NULL; -fail_alloc_2: - vfree(ucpia->buffers[1]); - ucpia->buffers[1] = NULL; -fail_alloc_1: - vfree(ucpia->buffers[0]); - ucpia->buffers[0] = NULL; -fail_alloc_0: - kfree(ucpia); - return -EIO; -} - -static void cpia_disconnect(struct usb_interface *intf); - -static struct usb_device_id cpia_id_table [] = { - { USB_DEVICE(0x0553, 0x0002) }, - { USB_DEVICE(0x0813, 0x0001) }, - { } /* Terminating entry */ -}; - -MODULE_DEVICE_TABLE (usb, cpia_id_table); -MODULE_LICENSE("GPL"); - - -static struct usb_driver cpia_driver = { - .name = "cpia", - .probe = cpia_probe, - .disconnect = cpia_disconnect, - .id_table = cpia_id_table, -}; - -static void cpia_disconnect(struct usb_interface *intf) -{ - struct cam_data *cam = usb_get_intfdata(intf); - struct usb_cpia *ucpia; - - usb_set_intfdata(intf, NULL); - if (!cam) - return; - - ucpia = (struct usb_cpia *) cam->lowlevel_data; - spin_lock( &cam_list_lock_usb ); - list_del(&cam->cam_data_list); - spin_unlock( &cam_list_lock_usb ); - - ucpia->present = 0; - - cpia_unregister_camera(cam); - if(ucpia->open) - cpia_usb_close(cam->lowlevel_data); - - ucpia->curbuff->status = FRAME_ERROR; - - if (waitqueue_active(&ucpia->wq_stream)) - wake_up_interruptible(&ucpia->wq_stream); - - ucpia->curbuff = ucpia->workbuff = NULL; - - vfree(ucpia->buffers[2]); - ucpia->buffers[2] = NULL; - - vfree(ucpia->buffers[1]); - ucpia->buffers[1] = NULL; - - vfree(ucpia->buffers[0]); - ucpia->buffers[0] = NULL; - - cam->lowlevel_data = NULL; - kfree(ucpia); -} - -static int __init usb_cpia_init(void) -{ - printk(KERN_INFO "%s v%d.%d.%d\n",ABOUT, - CPIA_USB_MAJ_VER,CPIA_USB_MIN_VER,CPIA_USB_PATCH_VER); - - spin_lock_init(&cam_list_lock_usb); - return usb_register(&cpia_driver); -} - -static void __exit usb_cpia_cleanup(void) -{ - usb_deregister(&cpia_driver); -} - - -module_init (usb_cpia_init); -module_exit (usb_cpia_cleanup); - diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 335311a98fdc..303c52cfe915 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -51,6 +51,8 @@ source "drivers/staging/cx25821/Kconfig" source "drivers/staging/tm6000/Kconfig" +source "drivers/staging/cpia/Kconfig" + source "drivers/staging/usbip/Kconfig" source "drivers/staging/winbond/Kconfig" diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index e3f1e1b6095e..ddcac245c99a 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_SLICOSS) += slicoss/ obj-$(CONFIG_VIDEO_GO7007) += go7007/ obj-$(CONFIG_VIDEO_CX25821) += cx25821/ obj-$(CONFIG_VIDEO_TM6000) += tm6000/ +obj-$(CONFIG_VIDEO_CPIA) += cpia/ obj-$(CONFIG_LIRC_STAGING) += lirc/ obj-$(CONFIG_USB_IP_COMMON) += usbip/ obj-$(CONFIG_W35UND) += winbond/ diff --git a/drivers/staging/cpia/Kconfig b/drivers/staging/cpia/Kconfig new file mode 100644 index 000000000000..205d247ad373 --- /dev/null +++ b/drivers/staging/cpia/Kconfig @@ -0,0 +1,39 @@ +config VIDEO_CPIA + tristate "CPiA Video For Linux (DEPRECATED)" + depends on VIDEO_V4L1 + default n + ---help--- + This driver is DEPRECATED please use the gspca cpia1 module + instead. Note that you need atleast version 0.6.4 of libv4l for + the cpia1 gspca module. + + This is the video4linux driver for cameras based on Vision's CPiA + (Colour Processor Interface ASIC), such as the Creative Labs Video + Blaster Webcam II. If you have one of these cameras, say Y here + and select parallel port and/or USB lowlevel support below, + otherwise say N. This will not work with the Creative Webcam III. + + Please read for more + information. + + This driver is also available as a module (cpia). + +config VIDEO_CPIA_PP + tristate "CPiA Parallel Port Lowlevel Support" + depends on PARPORT_1284 && VIDEO_CPIA && PARPORT + help + This is the lowlevel parallel port support for cameras based on + Vision's CPiA (Colour Processor Interface ASIC), such as the + Creative Webcam II. If you have the parallel port version of one + of these cameras, say Y here, otherwise say N. It is also available + as a module (cpia_pp). + +config VIDEO_CPIA_USB + tristate "CPiA USB Lowlevel Support" + depends on VIDEO_CPIA && USB + help + This is the lowlevel USB support for cameras based on Vision's CPiA + (Colour Processor Interface ASIC), such as the Creative Webcam II. + If you have the USB version of one of these cameras, say Y here, + otherwise say N. This will not work with the Creative Webcam III. + It is also available as a module (cpia_usb). diff --git a/drivers/staging/cpia/Makefile b/drivers/staging/cpia/Makefile new file mode 100644 index 000000000000..89e52f10d739 --- /dev/null +++ b/drivers/staging/cpia/Makefile @@ -0,0 +1,5 @@ +obj-$(CONFIG_VIDEO_CPIA) += cpia.o +obj-$(CONFIG_VIDEO_CPIA_PP) += cpia_pp.o +obj-$(CONFIG_VIDEO_CPIA_USB) += cpia_usb.o + +EXTRA_CFLAGS += -Idrivers/media/video diff --git a/drivers/staging/cpia/TODO b/drivers/staging/cpia/TODO new file mode 100644 index 000000000000..ccb1c0775eec --- /dev/null +++ b/drivers/staging/cpia/TODO @@ -0,0 +1,8 @@ +This is an obsolete driver for some cpia-based webcams that use the parallel port. +We couldn't find anyone with this hardware in order to port it to use V4L2. + +Also, parallel-port webcams are obsolete nowadays. + +If nobody take care on it, the driver will be removed for 2.6.38. + +Please send patches to linux-media@vger.kernel.org diff --git a/drivers/staging/cpia/cpia.c b/drivers/staging/cpia/cpia.c new file mode 100644 index 000000000000..933ae4c8cb9a --- /dev/null +++ b/drivers/staging/cpia/cpia.c @@ -0,0 +1,4032 @@ +/* + * cpia CPiA driver + * + * Supports CPiA based Video Camera's. + * + * (C) Copyright 1999-2000 Peter Pregler + * (C) Copyright 1999-2000 Scott J. Bertin + * (C) Copyright 1999-2000 Johannes Erdfelt + * (C) Copyright 2000 STMicroelectronics + * + * 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. + */ + +/* define _CPIA_DEBUG_ for verbose debug output (see cpia.h) */ +/* #define _CPIA_DEBUG_ 1 */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cpia.h" + +static int video_nr = -1; + +#ifdef MODULE +module_param(video_nr, int, 0); +MODULE_AUTHOR("Scott J. Bertin & Peter Pregler & Johannes Erdfelt "); +MODULE_DESCRIPTION("V4L-driver for Vision CPiA based cameras"); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("video"); +#endif + +static unsigned short colorspace_conv; +module_param(colorspace_conv, ushort, 0444); +MODULE_PARM_DESC(colorspace_conv, + " Colorspace conversion:" + "\n 0 = disable, 1 = enable" + "\n Default value is 0" + ); + +#define ABOUT "V4L-Driver for Vision CPiA based cameras" + +#define CPIA_MODULE_CPIA (0<<5) +#define CPIA_MODULE_SYSTEM (1<<5) +#define CPIA_MODULE_VP_CTRL (5<<5) +#define CPIA_MODULE_CAPTURE (6<<5) +#define CPIA_MODULE_DEBUG (7<<5) + +#define INPUT (DATA_IN << 8) +#define OUTPUT (DATA_OUT << 8) + +#define CPIA_COMMAND_GetCPIAVersion (INPUT | CPIA_MODULE_CPIA | 1) +#define CPIA_COMMAND_GetPnPID (INPUT | CPIA_MODULE_CPIA | 2) +#define CPIA_COMMAND_GetCameraStatus (INPUT | CPIA_MODULE_CPIA | 3) +#define CPIA_COMMAND_GotoHiPower (OUTPUT | CPIA_MODULE_CPIA | 4) +#define CPIA_COMMAND_GotoLoPower (OUTPUT | CPIA_MODULE_CPIA | 5) +#define CPIA_COMMAND_GotoSuspend (OUTPUT | CPIA_MODULE_CPIA | 7) +#define CPIA_COMMAND_GotoPassThrough (OUTPUT | CPIA_MODULE_CPIA | 8) +#define CPIA_COMMAND_ModifyCameraStatus (OUTPUT | CPIA_MODULE_CPIA | 10) + +#define CPIA_COMMAND_ReadVCRegs (INPUT | CPIA_MODULE_SYSTEM | 1) +#define CPIA_COMMAND_WriteVCReg (OUTPUT | CPIA_MODULE_SYSTEM | 2) +#define CPIA_COMMAND_ReadMCPorts (INPUT | CPIA_MODULE_SYSTEM | 3) +#define CPIA_COMMAND_WriteMCPort (OUTPUT | CPIA_MODULE_SYSTEM | 4) +#define CPIA_COMMAND_SetBaudRate (OUTPUT | CPIA_MODULE_SYSTEM | 5) +#define CPIA_COMMAND_SetECPTiming (OUTPUT | CPIA_MODULE_SYSTEM | 6) +#define CPIA_COMMAND_ReadIDATA (INPUT | CPIA_MODULE_SYSTEM | 7) +#define CPIA_COMMAND_WriteIDATA (OUTPUT | CPIA_MODULE_SYSTEM | 8) +#define CPIA_COMMAND_GenericCall (OUTPUT | CPIA_MODULE_SYSTEM | 9) +#define CPIA_COMMAND_I2CStart (OUTPUT | CPIA_MODULE_SYSTEM | 10) +#define CPIA_COMMAND_I2CStop (OUTPUT | CPIA_MODULE_SYSTEM | 11) +#define CPIA_COMMAND_I2CWrite (OUTPUT | CPIA_MODULE_SYSTEM | 12) +#define CPIA_COMMAND_I2CRead (INPUT | CPIA_MODULE_SYSTEM | 13) + +#define CPIA_COMMAND_GetVPVersion (INPUT | CPIA_MODULE_VP_CTRL | 1) +#define CPIA_COMMAND_ResetFrameCounter (INPUT | CPIA_MODULE_VP_CTRL | 2) +#define CPIA_COMMAND_SetColourParams (OUTPUT | CPIA_MODULE_VP_CTRL | 3) +#define CPIA_COMMAND_SetExposure (OUTPUT | CPIA_MODULE_VP_CTRL | 4) +#define CPIA_COMMAND_SetColourBalance (OUTPUT | CPIA_MODULE_VP_CTRL | 6) +#define CPIA_COMMAND_SetSensorFPS (OUTPUT | CPIA_MODULE_VP_CTRL | 7) +#define CPIA_COMMAND_SetVPDefaults (OUTPUT | CPIA_MODULE_VP_CTRL | 8) +#define CPIA_COMMAND_SetApcor (OUTPUT | CPIA_MODULE_VP_CTRL | 9) +#define CPIA_COMMAND_SetFlickerCtrl (OUTPUT | CPIA_MODULE_VP_CTRL | 10) +#define CPIA_COMMAND_SetVLOffset (OUTPUT | CPIA_MODULE_VP_CTRL | 11) +#define CPIA_COMMAND_GetColourParams (INPUT | CPIA_MODULE_VP_CTRL | 16) +#define CPIA_COMMAND_GetColourBalance (INPUT | CPIA_MODULE_VP_CTRL | 17) +#define CPIA_COMMAND_GetExposure (INPUT | CPIA_MODULE_VP_CTRL | 18) +#define CPIA_COMMAND_SetSensorMatrix (OUTPUT | CPIA_MODULE_VP_CTRL | 19) +#define CPIA_COMMAND_ColourBars (OUTPUT | CPIA_MODULE_VP_CTRL | 25) +#define CPIA_COMMAND_ReadVPRegs (INPUT | CPIA_MODULE_VP_CTRL | 30) +#define CPIA_COMMAND_WriteVPReg (OUTPUT | CPIA_MODULE_VP_CTRL | 31) + +#define CPIA_COMMAND_GrabFrame (OUTPUT | CPIA_MODULE_CAPTURE | 1) +#define CPIA_COMMAND_UploadFrame (OUTPUT | CPIA_MODULE_CAPTURE | 2) +#define CPIA_COMMAND_SetGrabMode (OUTPUT | CPIA_MODULE_CAPTURE | 3) +#define CPIA_COMMAND_InitStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 4) +#define CPIA_COMMAND_FiniStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 5) +#define CPIA_COMMAND_StartStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 6) +#define CPIA_COMMAND_EndStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 7) +#define CPIA_COMMAND_SetFormat (OUTPUT | CPIA_MODULE_CAPTURE | 8) +#define CPIA_COMMAND_SetROI (OUTPUT | CPIA_MODULE_CAPTURE | 9) +#define CPIA_COMMAND_SetCompression (OUTPUT | CPIA_MODULE_CAPTURE | 10) +#define CPIA_COMMAND_SetCompressionTarget (OUTPUT | CPIA_MODULE_CAPTURE | 11) +#define CPIA_COMMAND_SetYUVThresh (OUTPUT | CPIA_MODULE_CAPTURE | 12) +#define CPIA_COMMAND_SetCompressionParams (OUTPUT | CPIA_MODULE_CAPTURE | 13) +#define CPIA_COMMAND_DiscardFrame (OUTPUT | CPIA_MODULE_CAPTURE | 14) +#define CPIA_COMMAND_GrabReset (OUTPUT | CPIA_MODULE_CAPTURE | 15) + +#define CPIA_COMMAND_OutputRS232 (OUTPUT | CPIA_MODULE_DEBUG | 1) +#define CPIA_COMMAND_AbortProcess (OUTPUT | CPIA_MODULE_DEBUG | 4) +#define CPIA_COMMAND_SetDramPage (OUTPUT | CPIA_MODULE_DEBUG | 5) +#define CPIA_COMMAND_StartDramUpload (OUTPUT | CPIA_MODULE_DEBUG | 6) +#define CPIA_COMMAND_StartDummyDtream (OUTPUT | CPIA_MODULE_DEBUG | 8) +#define CPIA_COMMAND_AbortStream (OUTPUT | CPIA_MODULE_DEBUG | 9) +#define CPIA_COMMAND_DownloadDRAM (OUTPUT | CPIA_MODULE_DEBUG | 10) +#define CPIA_COMMAND_Null (OUTPUT | CPIA_MODULE_DEBUG | 11) + +enum { + FRAME_READY, /* Ready to grab into */ + FRAME_GRABBING, /* In the process of being grabbed into */ + FRAME_DONE, /* Finished grabbing, but not been synced yet */ + FRAME_UNUSED, /* Unused (no MCAPTURE) */ +}; + +#define COMMAND_NONE 0x0000 +#define COMMAND_SETCOMPRESSION 0x0001 +#define COMMAND_SETCOMPRESSIONTARGET 0x0002 +#define COMMAND_SETCOLOURPARAMS 0x0004 +#define COMMAND_SETFORMAT 0x0008 +#define COMMAND_PAUSE 0x0010 +#define COMMAND_RESUME 0x0020 +#define COMMAND_SETYUVTHRESH 0x0040 +#define COMMAND_SETECPTIMING 0x0080 +#define COMMAND_SETCOMPRESSIONPARAMS 0x0100 +#define COMMAND_SETEXPOSURE 0x0200 +#define COMMAND_SETCOLOURBALANCE 0x0400 +#define COMMAND_SETSENSORFPS 0x0800 +#define COMMAND_SETAPCOR 0x1000 +#define COMMAND_SETFLICKERCTRL 0x2000 +#define COMMAND_SETVLOFFSET 0x4000 +#define COMMAND_SETLIGHTS 0x8000 + +#define ROUND_UP_EXP_FOR_FLICKER 15 + +/* Constants for automatic frame rate adjustment */ +#define MAX_EXP 302 +#define MAX_EXP_102 255 +#define LOW_EXP 140 +#define VERY_LOW_EXP 70 +#define TC 94 +#define EXP_ACC_DARK 50 +#define EXP_ACC_LIGHT 90 +#define HIGH_COMP_102 160 +#define MAX_COMP 239 +#define DARK_TIME 3 +#define LIGHT_TIME 3 + +/* Maximum number of 10ms loops to wait for the stream to become ready */ +#define READY_TIMEOUT 100 + +/* Developer's Guide Table 5 p 3-34 + * indexed by [mains][sensorFps.baserate][sensorFps.divisor]*/ +static u8 flicker_jumps[2][2][4] = +{ { { 76, 38, 19, 9 }, { 92, 46, 23, 11 } }, + { { 64, 32, 16, 8 }, { 76, 38, 19, 9} } +}; + +/* forward declaration of local function */ +static void reset_camera_struct(struct cam_data *cam); +static int find_over_exposure(int brightness); +static void set_flicker(struct cam_params *params, volatile u32 *command_flags, + int on); + + +/********************************************************************** + * + * Memory management + * + **********************************************************************/ +static void *rvmalloc(unsigned long size) +{ + void *mem; + unsigned long adr; + + size = PAGE_ALIGN(size); + mem = vmalloc_32(size); + if (!mem) + return NULL; + + memset(mem, 0, size); /* Clear the ram out, no junk to the user */ + adr = (unsigned long) mem; + while (size > 0) { + SetPageReserved(vmalloc_to_page((void *)adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + + return mem; +} + +static void rvfree(void *mem, unsigned long size) +{ + unsigned long adr; + + if (!mem) + return; + + adr = (unsigned long) mem; + while ((long) size > 0) { + ClearPageReserved(vmalloc_to_page((void *)adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + vfree(mem); +} + +/********************************************************************** + * + * /proc interface + * + **********************************************************************/ +#ifdef CONFIG_PROC_FS +static struct proc_dir_entry *cpia_proc_root=NULL; + +static int cpia_proc_show(struct seq_file *m, void *v) +{ + struct cam_data *cam = m->private; + int tmp; + char tmpstr[29]; + + seq_printf(m, "read-only\n-----------------------\n"); + seq_printf(m, "V4L Driver version: %d.%d.%d\n", + CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER); + seq_printf(m, "CPIA Version: %d.%02d (%d.%d)\n", + cam->params.version.firmwareVersion, + cam->params.version.firmwareRevision, + cam->params.version.vcVersion, + cam->params.version.vcRevision); + seq_printf(m, "CPIA PnP-ID: %04x:%04x:%04x\n", + cam->params.pnpID.vendor, cam->params.pnpID.product, + cam->params.pnpID.deviceRevision); + seq_printf(m, "VP-Version: %d.%d %04x\n", + cam->params.vpVersion.vpVersion, + cam->params.vpVersion.vpRevision, + cam->params.vpVersion.cameraHeadID); + + seq_printf(m, "system_state: %#04x\n", + cam->params.status.systemState); + seq_printf(m, "grab_state: %#04x\n", + cam->params.status.grabState); + seq_printf(m, "stream_state: %#04x\n", + cam->params.status.streamState); + seq_printf(m, "fatal_error: %#04x\n", + cam->params.status.fatalError); + seq_printf(m, "cmd_error: %#04x\n", + cam->params.status.cmdError); + seq_printf(m, "debug_flags: %#04x\n", + cam->params.status.debugFlags); + seq_printf(m, "vp_status: %#04x\n", + cam->params.status.vpStatus); + seq_printf(m, "error_code: %#04x\n", + cam->params.status.errorCode); + /* QX3 specific entries */ + if (cam->params.qx3.qx3_detected) { + seq_printf(m, "button: %4d\n", + cam->params.qx3.button); + seq_printf(m, "cradled: %4d\n", + cam->params.qx3.cradled); + } + seq_printf(m, "video_size: %s\n", + cam->params.format.videoSize == VIDEOSIZE_CIF ? + "CIF " : "QCIF"); + seq_printf(m, "roi: (%3d, %3d) to (%3d, %3d)\n", + cam->params.roi.colStart*8, + cam->params.roi.rowStart*4, + cam->params.roi.colEnd*8, + cam->params.roi.rowEnd*4); + seq_printf(m, "actual_fps: %3d\n", cam->fps); + seq_printf(m, "transfer_rate: %4dkB/s\n", + cam->transfer_rate); + + seq_printf(m, "\nread-write\n"); + seq_printf(m, "----------------------- current min" + " max default comment\n"); + seq_printf(m, "brightness: %8d %8d %8d %8d\n", + cam->params.colourParams.brightness, 0, 100, 50); + if (cam->params.version.firmwareVersion == 1 && + cam->params.version.firmwareRevision == 2) + /* 1-02 firmware limits contrast to 80 */ + tmp = 80; + else + tmp = 96; + + seq_printf(m, "contrast: %8d %8d %8d %8d" + " steps of 8\n", + cam->params.colourParams.contrast, 0, tmp, 48); + seq_printf(m, "saturation: %8d %8d %8d %8d\n", + cam->params.colourParams.saturation, 0, 100, 50); + tmp = (25000+5000*cam->params.sensorFps.baserate)/ + (1<params.sensorFps.divisor); + seq_printf(m, "sensor_fps: %4d.%03d %8d %8d %8d\n", + tmp/1000, tmp%1000, 3, 30, 15); + seq_printf(m, "stream_start_line: %8d %8d %8d %8d\n", + 2*cam->params.streamStartLine, 0, + cam->params.format.videoSize == VIDEOSIZE_CIF ? 288:144, + cam->params.format.videoSize == VIDEOSIZE_CIF ? 240:120); + seq_printf(m, "sub_sample: %8s %8s %8s %8s\n", + cam->params.format.subSample == SUBSAMPLE_420 ? + "420" : "422", "420", "422", "422"); + seq_printf(m, "yuv_order: %8s %8s %8s %8s\n", + cam->params.format.yuvOrder == YUVORDER_YUYV ? + "YUYV" : "UYVY", "YUYV" , "UYVY", "YUYV"); + seq_printf(m, "ecp_timing: %8s %8s %8s %8s\n", + cam->params.ecpTiming ? "slow" : "normal", "slow", + "normal", "normal"); + + if (cam->params.colourBalance.balanceMode == 2) { + sprintf(tmpstr, "auto"); + } else { + sprintf(tmpstr, "manual"); + } + seq_printf(m, "color_balance_mode: %8s %8s %8s" + " %8s\n", tmpstr, "manual", "auto", "auto"); + seq_printf(m, "red_gain: %8d %8d %8d %8d\n", + cam->params.colourBalance.redGain, 0, 212, 32); + seq_printf(m, "green_gain: %8d %8d %8d %8d\n", + cam->params.colourBalance.greenGain, 0, 212, 6); + seq_printf(m, "blue_gain: %8d %8d %8d %8d\n", + cam->params.colourBalance.blueGain, 0, 212, 92); + + if (cam->params.version.firmwareVersion == 1 && + cam->params.version.firmwareRevision == 2) + /* 1-02 firmware limits gain to 2 */ + sprintf(tmpstr, "%8d %8d %8d", 1, 2, 2); + else + sprintf(tmpstr, "%8d %8d %8d", 1, 8, 2); + + if (cam->params.exposure.gainMode == 0) + seq_printf(m, "max_gain: unknown %28s" + " powers of 2\n", tmpstr); + else + seq_printf(m, "max_gain: %8d %28s" + " 1,2,4 or 8 \n", + 1<<(cam->params.exposure.gainMode-1), tmpstr); + + switch(cam->params.exposure.expMode) { + case 1: + case 3: + sprintf(tmpstr, "manual"); + break; + case 2: + sprintf(tmpstr, "auto"); + break; + default: + sprintf(tmpstr, "unknown"); + break; + } + seq_printf(m, "exposure_mode: %8s %8s %8s" + " %8s\n", tmpstr, "manual", "auto", "auto"); + seq_printf(m, "centre_weight: %8s %8s %8s %8s\n", + (2-cam->params.exposure.centreWeight) ? "on" : "off", + "off", "on", "on"); + seq_printf(m, "gain: %8d %8d max_gain %8d 1,2,4,8 possible\n", + 1<params.exposure.gain, 1, 1); + if (cam->params.version.firmwareVersion == 1 && + cam->params.version.firmwareRevision == 2) + /* 1-02 firmware limits fineExp/2 to 127 */ + tmp = 254; + else + tmp = 510; + + seq_printf(m, "fine_exp: %8d %8d %8d %8d\n", + cam->params.exposure.fineExp*2, 0, tmp, 0); + if (cam->params.version.firmwareVersion == 1 && + cam->params.version.firmwareRevision == 2) + /* 1-02 firmware limits coarseExpHi to 0 */ + tmp = MAX_EXP_102; + else + tmp = MAX_EXP; + + seq_printf(m, "coarse_exp: %8d %8d %8d" + " %8d\n", cam->params.exposure.coarseExpLo+ + 256*cam->params.exposure.coarseExpHi, 0, tmp, 185); + seq_printf(m, "red_comp: %8d %8d %8d %8d\n", + cam->params.exposure.redComp, COMP_RED, 255, COMP_RED); + seq_printf(m, "green1_comp: %8d %8d %8d %8d\n", + cam->params.exposure.green1Comp, COMP_GREEN1, 255, + COMP_GREEN1); + seq_printf(m, "green2_comp: %8d %8d %8d %8d\n", + cam->params.exposure.green2Comp, COMP_GREEN2, 255, + COMP_GREEN2); + seq_printf(m, "blue_comp: %8d %8d %8d %8d\n", + cam->params.exposure.blueComp, COMP_BLUE, 255, COMP_BLUE); + + seq_printf(m, "apcor_gain1: %#8x %#8x %#8x %#8x\n", + cam->params.apcor.gain1, 0, 0xff, 0x1c); + seq_printf(m, "apcor_gain2: %#8x %#8x %#8x %#8x\n", + cam->params.apcor.gain2, 0, 0xff, 0x1a); + seq_printf(m, "apcor_gain4: %#8x %#8x %#8x %#8x\n", + cam->params.apcor.gain4, 0, 0xff, 0x2d); + seq_printf(m, "apcor_gain8: %#8x %#8x %#8x %#8x\n", + cam->params.apcor.gain8, 0, 0xff, 0x2a); + seq_printf(m, "vl_offset_gain1: %8d %8d %8d %8d\n", + cam->params.vlOffset.gain1, 0, 255, 24); + seq_printf(m, "vl_offset_gain2: %8d %8d %8d %8d\n", + cam->params.vlOffset.gain2, 0, 255, 28); + seq_printf(m, "vl_offset_gain4: %8d %8d %8d %8d\n", + cam->params.vlOffset.gain4, 0, 255, 30); + seq_printf(m, "vl_offset_gain8: %8d %8d %8d %8d\n", + cam->params.vlOffset.gain8, 0, 255, 30); + seq_printf(m, "flicker_control: %8s %8s %8s %8s\n", + cam->params.flickerControl.flickerMode ? "on" : "off", + "off", "on", "off"); + seq_printf(m, "mains_frequency: %8d %8d %8d %8d" + " only 50/60\n", + cam->mainsFreq ? 60 : 50, 50, 60, 50); + if(cam->params.flickerControl.allowableOverExposure < 0) + seq_printf(m, "allowable_overexposure: %4dauto auto %8d auto\n", + -cam->params.flickerControl.allowableOverExposure, + 255); + else + seq_printf(m, "allowable_overexposure: %8d auto %8d auto\n", + cam->params.flickerControl.allowableOverExposure, + 255); + seq_printf(m, "compression_mode: "); + switch(cam->params.compression.mode) { + case CPIA_COMPRESSION_NONE: + seq_printf(m, "%8s", "none"); + break; + case CPIA_COMPRESSION_AUTO: + seq_printf(m, "%8s", "auto"); + break; + case CPIA_COMPRESSION_MANUAL: + seq_printf(m, "%8s", "manual"); + break; + default: + seq_printf(m, "%8s", "unknown"); + break; + } + seq_printf(m, " none,auto,manual auto\n"); + seq_printf(m, "decimation_enable: %8s %8s %8s %8s\n", + cam->params.compression.decimation == + DECIMATION_ENAB ? "on":"off", "off", "on", + "off"); + seq_printf(m, "compression_target: %9s %9s %9s %9s\n", + cam->params.compressionTarget.frTargeting == + CPIA_COMPRESSION_TARGET_FRAMERATE ? + "framerate":"quality", + "framerate", "quality", "quality"); + seq_printf(m, "target_framerate: %8d %8d %8d %8d\n", + cam->params.compressionTarget.targetFR, 1, 30, 15); + seq_printf(m, "target_quality: %8d %8d %8d %8d\n", + cam->params.compressionTarget.targetQ, 1, 64, 5); + seq_printf(m, "y_threshold: %8d %8d %8d %8d\n", + cam->params.yuvThreshold.yThreshold, 0, 31, 6); + seq_printf(m, "uv_threshold: %8d %8d %8d %8d\n", + cam->params.yuvThreshold.uvThreshold, 0, 31, 6); + seq_printf(m, "hysteresis: %8d %8d %8d %8d\n", + cam->params.compressionParams.hysteresis, 0, 255, 3); + seq_printf(m, "threshold_max: %8d %8d %8d %8d\n", + cam->params.compressionParams.threshMax, 0, 255, 11); + seq_printf(m, "small_step: %8d %8d %8d %8d\n", + cam->params.compressionParams.smallStep, 0, 255, 1); + seq_printf(m, "large_step: %8d %8d %8d %8d\n", + cam->params.compressionParams.largeStep, 0, 255, 3); + seq_printf(m, "decimation_hysteresis: %8d %8d %8d %8d\n", + cam->params.compressionParams.decimationHysteresis, + 0, 255, 2); + seq_printf(m, "fr_diff_step_thresh: %8d %8d %8d %8d\n", + cam->params.compressionParams.frDiffStepThresh, + 0, 255, 5); + seq_printf(m, "q_diff_step_thresh: %8d %8d %8d %8d\n", + cam->params.compressionParams.qDiffStepThresh, + 0, 255, 3); + seq_printf(m, "decimation_thresh_mod: %8d %8d %8d %8d\n", + cam->params.compressionParams.decimationThreshMod, + 0, 255, 2); + /* QX3 specific entries */ + if (cam->params.qx3.qx3_detected) { + seq_printf(m, "toplight: %8s %8s %8s %8s\n", + cam->params.qx3.toplight ? "on" : "off", + "off", "on", "off"); + seq_printf(m, "bottomlight: %8s %8s %8s %8s\n", + cam->params.qx3.bottomlight ? "on" : "off", + "off", "on", "off"); + } + + return 0; +} + +static int cpia_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, cpia_proc_show, PDE(inode)->data); +} + +static int match(char *checkstr, char **buffer, size_t *count, + int *find_colon, int *err) +{ + int ret, colon_found = 1; + int len = strlen(checkstr); + ret = (len <= *count && strncmp(*buffer, checkstr, len) == 0); + if (ret) { + *buffer += len; + *count -= len; + if (*find_colon) { + colon_found = 0; + while (*count && (**buffer == ' ' || **buffer == '\t' || + (!colon_found && **buffer == ':'))) { + if (**buffer == ':') + colon_found = 1; + --*count; + ++*buffer; + } + if (!*count || !colon_found) + *err = -EINVAL; + *find_colon = 0; + } + } + return ret; +} + +static unsigned long int value(char **buffer, size_t *count, int *err) +{ + char *p; + unsigned long int ret; + ret = simple_strtoul(*buffer, &p, 0); + if (p == *buffer) + *err = -EINVAL; + else { + *count -= p - *buffer; + *buffer = p; + } + return ret; +} + +static ssize_t cpia_proc_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + struct cam_data *cam = PDE(file->f_path.dentry->d_inode)->data; + struct cam_params new_params; + char *page, *buffer; + int retval, find_colon; + int size = count; + unsigned long val = 0; + u32 command_flags = 0; + u8 new_mains; + + /* + * This code to copy from buf to page is shamelessly copied + * from the comx driver + */ + if (count > PAGE_SIZE) { + printk(KERN_ERR "count is %zu > %d!!!\n", count, (int)PAGE_SIZE); + return -ENOSPC; + } + + if (!(page = (char *)__get_free_page(GFP_KERNEL))) return -ENOMEM; + + if(copy_from_user(page, buf, count)) + { + retval = -EFAULT; + goto out; + } + + if (page[count-1] == '\n') + page[count-1] = '\0'; + else if (count < PAGE_SIZE) + page[count] = '\0'; + else if (page[count]) { + retval = -EINVAL; + goto out; + } + + buffer = page; + + if (mutex_lock_interruptible(&cam->param_lock)) + return -ERESTARTSYS; + + /* + * Skip over leading whitespace + */ + while (count && isspace(*buffer)) { + --count; + ++buffer; + } + + memcpy(&new_params, &cam->params, sizeof(struct cam_params)); + new_mains = cam->mainsFreq; + +#define MATCH(x) (match(x, &buffer, &count, &find_colon, &retval)) +#define VALUE (value(&buffer,&count, &retval)) +#define FIRMWARE_VERSION(x,y) (new_params.version.firmwareVersion == (x) && \ + new_params.version.firmwareRevision == (y)) + + retval = 0; + while (count && !retval) { + find_colon = 1; + if (MATCH("brightness")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 100) + new_params.colourParams.brightness = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOLOURPARAMS; + if(new_params.flickerControl.allowableOverExposure < 0) + new_params.flickerControl.allowableOverExposure = + -find_over_exposure(new_params.colourParams.brightness); + if(new_params.flickerControl.flickerMode != 0) + command_flags |= COMMAND_SETFLICKERCTRL; + + } else if (MATCH("contrast")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 100) { + /* contrast is in steps of 8, so round*/ + val = ((val + 3) / 8) * 8; + /* 1-02 firmware limits contrast to 80*/ + if (FIRMWARE_VERSION(1,2) && val > 80) + val = 80; + + new_params.colourParams.contrast = val; + } else + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOLOURPARAMS; + } else if (MATCH("saturation")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 100) + new_params.colourParams.saturation = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOLOURPARAMS; + } else if (MATCH("sensor_fps")) { + if (!retval) + val = VALUE; + + if (!retval) { + /* find values so that sensorFPS is minimized, + * but >= val */ + if (val > 30) + retval = -EINVAL; + else if (val > 25) { + new_params.sensorFps.divisor = 0; + new_params.sensorFps.baserate = 1; + } else if (val > 15) { + new_params.sensorFps.divisor = 0; + new_params.sensorFps.baserate = 0; + } else if (val > 12) { + new_params.sensorFps.divisor = 1; + new_params.sensorFps.baserate = 1; + } else if (val > 7) { + new_params.sensorFps.divisor = 1; + new_params.sensorFps.baserate = 0; + } else if (val > 6) { + new_params.sensorFps.divisor = 2; + new_params.sensorFps.baserate = 1; + } else if (val > 3) { + new_params.sensorFps.divisor = 2; + new_params.sensorFps.baserate = 0; + } else { + new_params.sensorFps.divisor = 3; + /* Either base rate would work here */ + new_params.sensorFps.baserate = 1; + } + new_params.flickerControl.coarseJump = + flicker_jumps[new_mains] + [new_params.sensorFps.baserate] + [new_params.sensorFps.divisor]; + if (new_params.flickerControl.flickerMode) + command_flags |= COMMAND_SETFLICKERCTRL; + } + command_flags |= COMMAND_SETSENSORFPS; + cam->exposure_status = EXPOSURE_NORMAL; + } else if (MATCH("stream_start_line")) { + if (!retval) + val = VALUE; + + if (!retval) { + int max_line = 288; + + if (new_params.format.videoSize == VIDEOSIZE_QCIF) + max_line = 144; + if (val <= max_line) + new_params.streamStartLine = val/2; + else + retval = -EINVAL; + } + } else if (MATCH("sub_sample")) { + if (!retval && MATCH("420")) + new_params.format.subSample = SUBSAMPLE_420; + else if (!retval && MATCH("422")) + new_params.format.subSample = SUBSAMPLE_422; + else + retval = -EINVAL; + + command_flags |= COMMAND_SETFORMAT; + } else if (MATCH("yuv_order")) { + if (!retval && MATCH("YUYV")) + new_params.format.yuvOrder = YUVORDER_YUYV; + else if (!retval && MATCH("UYVY")) + new_params.format.yuvOrder = YUVORDER_UYVY; + else + retval = -EINVAL; + + command_flags |= COMMAND_SETFORMAT; + } else if (MATCH("ecp_timing")) { + if (!retval && MATCH("normal")) + new_params.ecpTiming = 0; + else if (!retval && MATCH("slow")) + new_params.ecpTiming = 1; + else + retval = -EINVAL; + + command_flags |= COMMAND_SETECPTIMING; + } else if (MATCH("color_balance_mode")) { + if (!retval && MATCH("manual")) + new_params.colourBalance.balanceMode = 3; + else if (!retval && MATCH("auto")) + new_params.colourBalance.balanceMode = 2; + else + retval = -EINVAL; + + command_flags |= COMMAND_SETCOLOURBALANCE; + } else if (MATCH("red_gain")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 212) { + new_params.colourBalance.redGain = val; + new_params.colourBalance.balanceMode = 1; + } else + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOLOURBALANCE; + } else if (MATCH("green_gain")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 212) { + new_params.colourBalance.greenGain = val; + new_params.colourBalance.balanceMode = 1; + } else + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOLOURBALANCE; + } else if (MATCH("blue_gain")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 212) { + new_params.colourBalance.blueGain = val; + new_params.colourBalance.balanceMode = 1; + } else + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOLOURBALANCE; + } else if (MATCH("max_gain")) { + if (!retval) + val = VALUE; + + if (!retval) { + /* 1-02 firmware limits gain to 2 */ + if (FIRMWARE_VERSION(1,2) && val > 2) + val = 2; + switch(val) { + case 1: + new_params.exposure.gainMode = 1; + break; + case 2: + new_params.exposure.gainMode = 2; + break; + case 4: + new_params.exposure.gainMode = 3; + break; + case 8: + new_params.exposure.gainMode = 4; + break; + default: + retval = -EINVAL; + break; + } + } + command_flags |= COMMAND_SETEXPOSURE; + } else if (MATCH("exposure_mode")) { + if (!retval && MATCH("auto")) + new_params.exposure.expMode = 2; + else if (!retval && MATCH("manual")) { + if (new_params.exposure.expMode == 2) + new_params.exposure.expMode = 3; + if(new_params.flickerControl.flickerMode != 0) + command_flags |= COMMAND_SETFLICKERCTRL; + new_params.flickerControl.flickerMode = 0; + } else + retval = -EINVAL; + + command_flags |= COMMAND_SETEXPOSURE; + } else if (MATCH("centre_weight")) { + if (!retval && MATCH("on")) + new_params.exposure.centreWeight = 1; + else if (!retval && MATCH("off")) + new_params.exposure.centreWeight = 2; + else + retval = -EINVAL; + + command_flags |= COMMAND_SETEXPOSURE; + } else if (MATCH("gain")) { + if (!retval) + val = VALUE; + + if (!retval) { + switch(val) { + case 1: + new_params.exposure.gain = 0; + break; + case 2: + new_params.exposure.gain = 1; + break; + case 4: + new_params.exposure.gain = 2; + break; + case 8: + new_params.exposure.gain = 3; + break; + default: + retval = -EINVAL; + break; + } + new_params.exposure.expMode = 1; + if(new_params.flickerControl.flickerMode != 0) + command_flags |= COMMAND_SETFLICKERCTRL; + new_params.flickerControl.flickerMode = 0; + command_flags |= COMMAND_SETEXPOSURE; + if (new_params.exposure.gain > + new_params.exposure.gainMode-1) + retval = -EINVAL; + } + } else if (MATCH("fine_exp")) { + if (!retval) + val = VALUE/2; + + if (!retval) { + if (val < 256) { + /* 1-02 firmware limits fineExp/2 to 127*/ + if (FIRMWARE_VERSION(1,2) && val > 127) + val = 127; + new_params.exposure.fineExp = val; + new_params.exposure.expMode = 1; + command_flags |= COMMAND_SETEXPOSURE; + if(new_params.flickerControl.flickerMode != 0) + command_flags |= COMMAND_SETFLICKERCTRL; + new_params.flickerControl.flickerMode = 0; + command_flags |= COMMAND_SETFLICKERCTRL; + } else + retval = -EINVAL; + } + } else if (MATCH("coarse_exp")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= MAX_EXP) { + if (FIRMWARE_VERSION(1,2) && + val > MAX_EXP_102) + val = MAX_EXP_102; + new_params.exposure.coarseExpLo = + val & 0xff; + new_params.exposure.coarseExpHi = + val >> 8; + new_params.exposure.expMode = 1; + command_flags |= COMMAND_SETEXPOSURE; + if(new_params.flickerControl.flickerMode != 0) + command_flags |= COMMAND_SETFLICKERCTRL; + new_params.flickerControl.flickerMode = 0; + command_flags |= COMMAND_SETFLICKERCTRL; + } else + retval = -EINVAL; + } + } else if (MATCH("red_comp")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val >= COMP_RED && val <= 255) { + new_params.exposure.redComp = val; + new_params.exposure.compMode = 1; + command_flags |= COMMAND_SETEXPOSURE; + } else + retval = -EINVAL; + } + } else if (MATCH("green1_comp")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val >= COMP_GREEN1 && val <= 255) { + new_params.exposure.green1Comp = val; + new_params.exposure.compMode = 1; + command_flags |= COMMAND_SETEXPOSURE; + } else + retval = -EINVAL; + } + } else if (MATCH("green2_comp")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val >= COMP_GREEN2 && val <= 255) { + new_params.exposure.green2Comp = val; + new_params.exposure.compMode = 1; + command_flags |= COMMAND_SETEXPOSURE; + } else + retval = -EINVAL; + } + } else if (MATCH("blue_comp")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val >= COMP_BLUE && val <= 255) { + new_params.exposure.blueComp = val; + new_params.exposure.compMode = 1; + command_flags |= COMMAND_SETEXPOSURE; + } else + retval = -EINVAL; + } + } else if (MATCH("apcor_gain1")) { + if (!retval) + val = VALUE; + + if (!retval) { + command_flags |= COMMAND_SETAPCOR; + if (val <= 0xff) + new_params.apcor.gain1 = val; + else + retval = -EINVAL; + } + } else if (MATCH("apcor_gain2")) { + if (!retval) + val = VALUE; + + if (!retval) { + command_flags |= COMMAND_SETAPCOR; + if (val <= 0xff) + new_params.apcor.gain2 = val; + else + retval = -EINVAL; + } + } else if (MATCH("apcor_gain4")) { + if (!retval) + val = VALUE; + + if (!retval) { + command_flags |= COMMAND_SETAPCOR; + if (val <= 0xff) + new_params.apcor.gain4 = val; + else + retval = -EINVAL; + } + } else if (MATCH("apcor_gain8")) { + if (!retval) + val = VALUE; + + if (!retval) { + command_flags |= COMMAND_SETAPCOR; + if (val <= 0xff) + new_params.apcor.gain8 = val; + else + retval = -EINVAL; + } + } else if (MATCH("vl_offset_gain1")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.vlOffset.gain1 = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETVLOFFSET; + } else if (MATCH("vl_offset_gain2")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.vlOffset.gain2 = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETVLOFFSET; + } else if (MATCH("vl_offset_gain4")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.vlOffset.gain4 = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETVLOFFSET; + } else if (MATCH("vl_offset_gain8")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.vlOffset.gain8 = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETVLOFFSET; + } else if (MATCH("flicker_control")) { + if (!retval && MATCH("on")) { + set_flicker(&new_params, &command_flags, 1); + } else if (!retval && MATCH("off")) { + set_flicker(&new_params, &command_flags, 0); + } else + retval = -EINVAL; + + command_flags |= COMMAND_SETFLICKERCTRL; + } else if (MATCH("mains_frequency")) { + if (!retval && MATCH("50")) { + new_mains = 0; + new_params.flickerControl.coarseJump = + flicker_jumps[new_mains] + [new_params.sensorFps.baserate] + [new_params.sensorFps.divisor]; + if (new_params.flickerControl.flickerMode) + command_flags |= COMMAND_SETFLICKERCTRL; + } else if (!retval && MATCH("60")) { + new_mains = 1; + new_params.flickerControl.coarseJump = + flicker_jumps[new_mains] + [new_params.sensorFps.baserate] + [new_params.sensorFps.divisor]; + if (new_params.flickerControl.flickerMode) + command_flags |= COMMAND_SETFLICKERCTRL; + } else + retval = -EINVAL; + } else if (MATCH("allowable_overexposure")) { + if (!retval && MATCH("auto")) { + new_params.flickerControl.allowableOverExposure = + -find_over_exposure(new_params.colourParams.brightness); + if(new_params.flickerControl.flickerMode != 0) + command_flags |= COMMAND_SETFLICKERCTRL; + } else { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) { + new_params.flickerControl. + allowableOverExposure = val; + if(new_params.flickerControl.flickerMode != 0) + command_flags |= COMMAND_SETFLICKERCTRL; + } else + retval = -EINVAL; + } + } + } else if (MATCH("compression_mode")) { + if (!retval && MATCH("none")) + new_params.compression.mode = + CPIA_COMPRESSION_NONE; + else if (!retval && MATCH("auto")) + new_params.compression.mode = + CPIA_COMPRESSION_AUTO; + else if (!retval && MATCH("manual")) + new_params.compression.mode = + CPIA_COMPRESSION_MANUAL; + else + retval = -EINVAL; + + command_flags |= COMMAND_SETCOMPRESSION; + } else if (MATCH("decimation_enable")) { + if (!retval && MATCH("off")) + new_params.compression.decimation = 0; + else if (!retval && MATCH("on")) + new_params.compression.decimation = 1; + else + retval = -EINVAL; + + command_flags |= COMMAND_SETCOMPRESSION; + } else if (MATCH("compression_target")) { + if (!retval && MATCH("quality")) + new_params.compressionTarget.frTargeting = + CPIA_COMPRESSION_TARGET_QUALITY; + else if (!retval && MATCH("framerate")) + new_params.compressionTarget.frTargeting = + CPIA_COMPRESSION_TARGET_FRAMERATE; + else + retval = -EINVAL; + + command_flags |= COMMAND_SETCOMPRESSIONTARGET; + } else if (MATCH("target_framerate")) { + if (!retval) + val = VALUE; + + if (!retval) { + if(val > 0 && val <= 30) + new_params.compressionTarget.targetFR = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOMPRESSIONTARGET; + } else if (MATCH("target_quality")) { + if (!retval) + val = VALUE; + + if (!retval) { + if(val > 0 && val <= 64) + new_params.compressionTarget.targetQ = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOMPRESSIONTARGET; + } else if (MATCH("y_threshold")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val < 32) + new_params.yuvThreshold.yThreshold = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETYUVTHRESH; + } else if (MATCH("uv_threshold")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val < 32) + new_params.yuvThreshold.uvThreshold = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETYUVTHRESH; + } else if (MATCH("hysteresis")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.compressionParams.hysteresis = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOMPRESSIONPARAMS; + } else if (MATCH("threshold_max")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.compressionParams.threshMax = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOMPRESSIONPARAMS; + } else if (MATCH("small_step")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.compressionParams.smallStep = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOMPRESSIONPARAMS; + } else if (MATCH("large_step")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.compressionParams.largeStep = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOMPRESSIONPARAMS; + } else if (MATCH("decimation_hysteresis")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.compressionParams.decimationHysteresis = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOMPRESSIONPARAMS; + } else if (MATCH("fr_diff_step_thresh")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.compressionParams.frDiffStepThresh = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOMPRESSIONPARAMS; + } else if (MATCH("q_diff_step_thresh")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.compressionParams.qDiffStepThresh = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOMPRESSIONPARAMS; + } else if (MATCH("decimation_thresh_mod")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.compressionParams.decimationThreshMod = val; + else + retval = -EINVAL; + } + command_flags |= COMMAND_SETCOMPRESSIONPARAMS; + } else if (MATCH("toplight")) { + if (!retval && MATCH("on")) + new_params.qx3.toplight = 1; + else if (!retval && MATCH("off")) + new_params.qx3.toplight = 0; + else + retval = -EINVAL; + command_flags |= COMMAND_SETLIGHTS; + } else if (MATCH("bottomlight")) { + if (!retval && MATCH("on")) + new_params.qx3.bottomlight = 1; + else if (!retval && MATCH("off")) + new_params.qx3.bottomlight = 0; + else + retval = -EINVAL; + command_flags |= COMMAND_SETLIGHTS; + } else { + DBG("No match found\n"); + retval = -EINVAL; + } + + if (!retval) { + while (count && isspace(*buffer) && *buffer != '\n') { + --count; + ++buffer; + } + if (count) { + if (*buffer == '\0' && count != 1) + retval = -EINVAL; + else if (*buffer != '\n' && *buffer != ';' && + *buffer != '\0') + retval = -EINVAL; + else { + --count; + ++buffer; + } + } + } + } +#undef MATCH +#undef VALUE +#undef FIRMWARE_VERSION + if (!retval) { + if (command_flags & COMMAND_SETCOLOURPARAMS) { + /* Adjust cam->vp to reflect these changes */ + cam->vp.brightness = + new_params.colourParams.brightness*65535/100; + cam->vp.contrast = + new_params.colourParams.contrast*65535/100; + cam->vp.colour = + new_params.colourParams.saturation*65535/100; + } + if((command_flags & COMMAND_SETEXPOSURE) && + new_params.exposure.expMode == 2) + cam->exposure_status = EXPOSURE_NORMAL; + + memcpy(&cam->params, &new_params, sizeof(struct cam_params)); + cam->mainsFreq = new_mains; + cam->cmd_queue |= command_flags; + retval = size; + } else + DBG("error: %d\n", retval); + + mutex_unlock(&cam->param_lock); + +out: + free_page((unsigned long)page); + return retval; +} + +static const struct file_operations cpia_proc_fops = { + .owner = THIS_MODULE, + .open = cpia_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .write = cpia_proc_write, +}; + +static void create_proc_cpia_cam(struct cam_data *cam) +{ + struct proc_dir_entry *ent; + + if (!cpia_proc_root || !cam) + return; + + ent = proc_create_data(video_device_node_name(&cam->vdev), + S_IRUGO|S_IWUSR, cpia_proc_root, + &cpia_proc_fops, cam); + if (!ent) + return; + + /* + size of the proc entry is 3736 bytes for the standard webcam; + the extra features of the QX3 microscope add 189 bytes. + (we have not yet probed the camera to see which type it is). + */ + ent->size = 3736 + 189; + cam->proc_entry = ent; +} + +static void destroy_proc_cpia_cam(struct cam_data *cam) +{ + if (!cam || !cam->proc_entry) + return; + + remove_proc_entry(video_device_node_name(&cam->vdev), cpia_proc_root); + cam->proc_entry = NULL; +} + +static void proc_cpia_create(void) +{ + cpia_proc_root = proc_mkdir("cpia", NULL); + + if (!cpia_proc_root) + LOG("Unable to initialise /proc/cpia\n"); +} + +static void __exit proc_cpia_destroy(void) +{ + remove_proc_entry("cpia", NULL); +} +#endif /* CONFIG_PROC_FS */ + +/* ----------------------- debug functions ---------------------- */ + +#define printstatus(cam) \ + DBG("%02x %02x %02x %02x %02x %02x %02x %02x\n",\ + cam->params.status.systemState, cam->params.status.grabState, \ + cam->params.status.streamState, cam->params.status.fatalError, \ + cam->params.status.cmdError, cam->params.status.debugFlags, \ + cam->params.status.vpStatus, cam->params.status.errorCode); + +/* ----------------------- v4l helpers -------------------------- */ + +/* supported frame palettes and depths */ +static inline int valid_mode(u16 palette, u16 depth) +{ + if ((palette == VIDEO_PALETTE_YUV422 && depth == 16) || + (palette == VIDEO_PALETTE_YUYV && depth == 16)) + return 1; + + if (colorspace_conv) + return (palette == VIDEO_PALETTE_GREY && depth == 8) || + (palette == VIDEO_PALETTE_RGB555 && depth == 16) || + (palette == VIDEO_PALETTE_RGB565 && depth == 16) || + (palette == VIDEO_PALETTE_RGB24 && depth == 24) || + (palette == VIDEO_PALETTE_RGB32 && depth == 32) || + (palette == VIDEO_PALETTE_UYVY && depth == 16); + + return 0; +} + +static int match_videosize( int width, int height ) +{ + /* return the best match, where 'best' is as always + * the largest that is not bigger than what is requested. */ + if (width>=352 && height>=288) + return VIDEOSIZE_352_288; /* CIF */ + + if (width>=320 && height>=240) + return VIDEOSIZE_320_240; /* SIF */ + + if (width>=288 && height>=216) + return VIDEOSIZE_288_216; + + if (width>=256 && height>=192) + return VIDEOSIZE_256_192; + + if (width>=224 && height>=168) + return VIDEOSIZE_224_168; + + if (width>=192 && height>=144) + return VIDEOSIZE_192_144; + + if (width>=176 && height>=144) + return VIDEOSIZE_176_144; /* QCIF */ + + if (width>=160 && height>=120) + return VIDEOSIZE_160_120; /* QSIF */ + + if (width>=128 && height>=96) + return VIDEOSIZE_128_96; + + if (width>=88 && height>=72) + return VIDEOSIZE_88_72; + + if (width>=64 && height>=48) + return VIDEOSIZE_64_48; + + if (width>=48 && height>=48) + return VIDEOSIZE_48_48; + + return -1; +} + +/* these are the capture sizes we support */ +static void set_vw_size(struct cam_data *cam) +{ + /* the col/row/start/end values are the result of simple math */ + /* study the SetROI-command in cpia developers guide p 2-22 */ + /* streamStartLine is set to the recommended value in the cpia */ + /* developers guide p 3-37 */ + switch(cam->video_size) { + case VIDEOSIZE_CIF: + cam->vw.width = 352; + cam->vw.height = 288; + cam->params.format.videoSize=VIDEOSIZE_CIF; + cam->params.roi.colStart=0; + cam->params.roi.rowStart=0; + cam->params.streamStartLine = 120; + break; + case VIDEOSIZE_SIF: + cam->vw.width = 320; + cam->vw.height = 240; + cam->params.format.videoSize=VIDEOSIZE_CIF; + cam->params.roi.colStart=2; + cam->params.roi.rowStart=6; + cam->params.streamStartLine = 120; + break; + case VIDEOSIZE_288_216: + cam->vw.width = 288; + cam->vw.height = 216; + cam->params.format.videoSize=VIDEOSIZE_CIF; + cam->params.roi.colStart=4; + cam->params.roi.rowStart=9; + cam->params.streamStartLine = 120; + break; + case VIDEOSIZE_256_192: + cam->vw.width = 256; + cam->vw.height = 192; + cam->params.format.videoSize=VIDEOSIZE_CIF; + cam->params.roi.colStart=6; + cam->params.roi.rowStart=12; + cam->params.streamStartLine = 120; + break; + case VIDEOSIZE_224_168: + cam->vw.width = 224; + cam->vw.height = 168; + cam->params.format.videoSize=VIDEOSIZE_CIF; + cam->params.roi.colStart=8; + cam->params.roi.rowStart=15; + cam->params.streamStartLine = 120; + break; + case VIDEOSIZE_192_144: + cam->vw.width = 192; + cam->vw.height = 144; + cam->params.format.videoSize=VIDEOSIZE_CIF; + cam->params.roi.colStart=10; + cam->params.roi.rowStart=18; + cam->params.streamStartLine = 120; + break; + case VIDEOSIZE_QCIF: + cam->vw.width = 176; + cam->vw.height = 144; + cam->params.format.videoSize=VIDEOSIZE_QCIF; + cam->params.roi.colStart=0; + cam->params.roi.rowStart=0; + cam->params.streamStartLine = 60; + break; + case VIDEOSIZE_QSIF: + cam->vw.width = 160; + cam->vw.height = 120; + cam->params.format.videoSize=VIDEOSIZE_QCIF; + cam->params.roi.colStart=1; + cam->params.roi.rowStart=3; + cam->params.streamStartLine = 60; + break; + case VIDEOSIZE_128_96: + cam->vw.width = 128; + cam->vw.height = 96; + cam->params.format.videoSize=VIDEOSIZE_QCIF; + cam->params.roi.colStart=3; + cam->params.roi.rowStart=6; + cam->params.streamStartLine = 60; + break; + case VIDEOSIZE_88_72: + cam->vw.width = 88; + cam->vw.height = 72; + cam->params.format.videoSize=VIDEOSIZE_QCIF; + cam->params.roi.colStart=5; + cam->params.roi.rowStart=9; + cam->params.streamStartLine = 60; + break; + case VIDEOSIZE_64_48: + cam->vw.width = 64; + cam->vw.height = 48; + cam->params.format.videoSize=VIDEOSIZE_QCIF; + cam->params.roi.colStart=7; + cam->params.roi.rowStart=12; + cam->params.streamStartLine = 60; + break; + case VIDEOSIZE_48_48: + cam->vw.width = 48; + cam->vw.height = 48; + cam->params.format.videoSize=VIDEOSIZE_QCIF; + cam->params.roi.colStart=8; + cam->params.roi.rowStart=6; + cam->params.streamStartLine = 60; + break; + default: + LOG("bad videosize value: %d\n", cam->video_size); + return; + } + + if(cam->vc.width == 0) + cam->vc.width = cam->vw.width; + if(cam->vc.height == 0) + cam->vc.height = cam->vw.height; + + cam->params.roi.colStart += cam->vc.x >> 3; + cam->params.roi.colEnd = cam->params.roi.colStart + + (cam->vc.width >> 3); + cam->params.roi.rowStart += cam->vc.y >> 2; + cam->params.roi.rowEnd = cam->params.roi.rowStart + + (cam->vc.height >> 2); + + return; +} + +static int allocate_frame_buf(struct cam_data *cam) +{ + int i; + + cam->frame_buf = rvmalloc(FRAME_NUM * CPIA_MAX_FRAME_SIZE); + if (!cam->frame_buf) + return -ENOBUFS; + + for (i = 0; i < FRAME_NUM; i++) + cam->frame[i].data = cam->frame_buf + i * CPIA_MAX_FRAME_SIZE; + + return 0; +} + +static int free_frame_buf(struct cam_data *cam) +{ + int i; + + rvfree(cam->frame_buf, FRAME_NUM*CPIA_MAX_FRAME_SIZE); + cam->frame_buf = NULL; + for (i=0; i < FRAME_NUM; i++) + cam->frame[i].data = NULL; + + return 0; +} + + +static inline void free_frames(struct cpia_frame frame[FRAME_NUM]) +{ + int i; + + for (i=0; i < FRAME_NUM; i++) + frame[i].state = FRAME_UNUSED; + return; +} + +/********************************************************************** + * + * General functions + * + **********************************************************************/ +/* send an arbitrary command to the camera */ +static int do_command(struct cam_data *cam, u16 command, u8 a, u8 b, u8 c, u8 d) +{ + int retval, datasize; + u8 cmd[8], data[8]; + + switch(command) { + case CPIA_COMMAND_GetCPIAVersion: + case CPIA_COMMAND_GetPnPID: + case CPIA_COMMAND_GetCameraStatus: + case CPIA_COMMAND_GetVPVersion: + datasize=8; + break; + case CPIA_COMMAND_GetColourParams: + case CPIA_COMMAND_GetColourBalance: + case CPIA_COMMAND_GetExposure: + mutex_lock(&cam->param_lock); + datasize=8; + break; + case CPIA_COMMAND_ReadMCPorts: + case CPIA_COMMAND_ReadVCRegs: + datasize = 4; + break; + default: + datasize=0; + break; + } + + cmd[0] = command>>8; + cmd[1] = command&0xff; + cmd[2] = a; + cmd[3] = b; + cmd[4] = c; + cmd[5] = d; + cmd[6] = datasize; + cmd[7] = 0; + + retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data); + if (retval) { + DBG("%x - failed, retval=%d\n", command, retval); + if (command == CPIA_COMMAND_GetColourParams || + command == CPIA_COMMAND_GetColourBalance || + command == CPIA_COMMAND_GetExposure) + mutex_unlock(&cam->param_lock); + } else { + switch(command) { + case CPIA_COMMAND_GetCPIAVersion: + cam->params.version.firmwareVersion = data[0]; + cam->params.version.firmwareRevision = data[1]; + cam->params.version.vcVersion = data[2]; + cam->params.version.vcRevision = data[3]; + break; + case CPIA_COMMAND_GetPnPID: + cam->params.pnpID.vendor = data[0]+(((u16)data[1])<<8); + cam->params.pnpID.product = data[2]+(((u16)data[3])<<8); + cam->params.pnpID.deviceRevision = + data[4]+(((u16)data[5])<<8); + break; + case CPIA_COMMAND_GetCameraStatus: + cam->params.status.systemState = data[0]; + cam->params.status.grabState = data[1]; + cam->params.status.streamState = data[2]; + cam->params.status.fatalError = data[3]; + cam->params.status.cmdError = data[4]; + cam->params.status.debugFlags = data[5]; + cam->params.status.vpStatus = data[6]; + cam->params.status.errorCode = data[7]; + break; + case CPIA_COMMAND_GetVPVersion: + cam->params.vpVersion.vpVersion = data[0]; + cam->params.vpVersion.vpRevision = data[1]; + cam->params.vpVersion.cameraHeadID = + data[2]+(((u16)data[3])<<8); + break; + case CPIA_COMMAND_GetColourParams: + cam->params.colourParams.brightness = data[0]; + cam->params.colourParams.contrast = data[1]; + cam->params.colourParams.saturation = data[2]; + mutex_unlock(&cam->param_lock); + break; + case CPIA_COMMAND_GetColourBalance: + cam->params.colourBalance.redGain = data[0]; + cam->params.colourBalance.greenGain = data[1]; + cam->params.colourBalance.blueGain = data[2]; + mutex_unlock(&cam->param_lock); + break; + case CPIA_COMMAND_GetExposure: + cam->params.exposure.gain = data[0]; + cam->params.exposure.fineExp = data[1]; + cam->params.exposure.coarseExpLo = data[2]; + cam->params.exposure.coarseExpHi = data[3]; + cam->params.exposure.redComp = data[4]; + cam->params.exposure.green1Comp = data[5]; + cam->params.exposure.green2Comp = data[6]; + cam->params.exposure.blueComp = data[7]; + mutex_unlock(&cam->param_lock); + break; + + case CPIA_COMMAND_ReadMCPorts: + if (!cam->params.qx3.qx3_detected) + break; + /* test button press */ + cam->params.qx3.button = ((data[1] & 0x02) == 0); + if (cam->params.qx3.button) { + /* button pressed - unlock the latch */ + do_command(cam,CPIA_COMMAND_WriteMCPort,3,0xDF,0xDF,0); + do_command(cam,CPIA_COMMAND_WriteMCPort,3,0xFF,0xFF,0); + } + + /* test whether microscope is cradled */ + cam->params.qx3.cradled = ((data[2] & 0x40) == 0); + break; + + default: + break; + } + } + return retval; +} + +/* send a command to the camera with an additional data transaction */ +static int do_command_extended(struct cam_data *cam, u16 command, + u8 a, u8 b, u8 c, u8 d, + u8 e, u8 f, u8 g, u8 h, + u8 i, u8 j, u8 k, u8 l) +{ + int retval; + u8 cmd[8], data[8]; + + cmd[0] = command>>8; + cmd[1] = command&0xff; + cmd[2] = a; + cmd[3] = b; + cmd[4] = c; + cmd[5] = d; + cmd[6] = 8; + cmd[7] = 0; + data[0] = e; + data[1] = f; + data[2] = g; + data[3] = h; + data[4] = i; + data[5] = j; + data[6] = k; + data[7] = l; + + retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data); + if (retval) + DBG("%x - failed\n", command); + + return retval; +} + +/********************************************************************** + * + * Colorspace conversion + * + **********************************************************************/ +#define LIMIT(x) ((((x)>0xffffff)?0xff0000:(((x)<=0xffff)?0:(x)&0xff0000))>>16) + +static int convert420(unsigned char *yuv, unsigned char *rgb, int out_fmt, + int linesize, int mmap_kludge) +{ + int y, u, v, r, g, b, y1; + + /* Odd lines use the same u and v as the previous line. + * Because of compression, it is necessary to get this + * information from the decoded image. */ + switch(out_fmt) { + case VIDEO_PALETTE_RGB555: + y = (*yuv++ - 16) * 76310; + y1 = (*yuv - 16) * 76310; + r = ((*(rgb+1-linesize)) & 0x7c) << 1; + g = ((*(rgb-linesize)) & 0xe0) >> 4 | + ((*(rgb+1-linesize)) & 0x03) << 6; + b = ((*(rgb-linesize)) & 0x1f) << 3; + u = (-53294 * r - 104635 * g + 157929 * b) / 5756495; + v = (157968 * r - 132278 * g - 25690 * b) / 5366159; + r = 104635 * v; + g = -25690 * u - 53294 * v; + b = 132278 * u; + *rgb++ = ((LIMIT(g+y) & 0xf8) << 2) | (LIMIT(b+y) >> 3); + *rgb++ = ((LIMIT(r+y) & 0xf8) >> 1) | (LIMIT(g+y) >> 6); + *rgb++ = ((LIMIT(g+y1) & 0xf8) << 2) | (LIMIT(b+y1) >> 3); + *rgb = ((LIMIT(r+y1) & 0xf8) >> 1) | (LIMIT(g+y1) >> 6); + return 4; + case VIDEO_PALETTE_RGB565: + y = (*yuv++ - 16) * 76310; + y1 = (*yuv - 16) * 76310; + r = (*(rgb+1-linesize)) & 0xf8; + g = ((*(rgb-linesize)) & 0xe0) >> 3 | + ((*(rgb+1-linesize)) & 0x07) << 5; + b = ((*(rgb-linesize)) & 0x1f) << 3; + u = (-53294 * r - 104635 * g + 157929 * b) / 5756495; + v = (157968 * r - 132278 * g - 25690 * b) / 5366159; + r = 104635 * v; + g = -25690 * u - 53294 * v; + b = 132278 * u; + *rgb++ = ((LIMIT(g+y) & 0xfc) << 3) | (LIMIT(b+y) >> 3); + *rgb++ = (LIMIT(r+y) & 0xf8) | (LIMIT(g+y) >> 5); + *rgb++ = ((LIMIT(g+y1) & 0xfc) << 3) | (LIMIT(b+y1) >> 3); + *rgb = (LIMIT(r+y1) & 0xf8) | (LIMIT(g+y1) >> 5); + return 4; + break; + case VIDEO_PALETTE_RGB24: + case VIDEO_PALETTE_RGB32: + y = (*yuv++ - 16) * 76310; + y1 = (*yuv - 16) * 76310; + if (mmap_kludge) { + r = *(rgb+2-linesize); + g = *(rgb+1-linesize); + b = *(rgb-linesize); + } else { + r = *(rgb-linesize); + g = *(rgb+1-linesize); + b = *(rgb+2-linesize); + } + u = (-53294 * r - 104635 * g + 157929 * b) / 5756495; + v = (157968 * r - 132278 * g - 25690 * b) / 5366159; + r = 104635 * v; + g = -25690 * u + -53294 * v; + b = 132278 * u; + if (mmap_kludge) { + *rgb++ = LIMIT(b+y); + *rgb++ = LIMIT(g+y); + *rgb++ = LIMIT(r+y); + if(out_fmt == VIDEO_PALETTE_RGB32) + rgb++; + *rgb++ = LIMIT(b+y1); + *rgb++ = LIMIT(g+y1); + *rgb = LIMIT(r+y1); + } else { + *rgb++ = LIMIT(r+y); + *rgb++ = LIMIT(g+y); + *rgb++ = LIMIT(b+y); + if(out_fmt == VIDEO_PALETTE_RGB32) + rgb++; + *rgb++ = LIMIT(r+y1); + *rgb++ = LIMIT(g+y1); + *rgb = LIMIT(b+y1); + } + if(out_fmt == VIDEO_PALETTE_RGB32) + return 8; + return 6; + case VIDEO_PALETTE_YUV422: + case VIDEO_PALETTE_YUYV: + y = *yuv++; + u = *(rgb+1-linesize); + y1 = *yuv; + v = *(rgb+3-linesize); + *rgb++ = y; + *rgb++ = u; + *rgb++ = y1; + *rgb = v; + return 4; + case VIDEO_PALETTE_UYVY: + u = *(rgb-linesize); + y = *yuv++; + v = *(rgb+2-linesize); + y1 = *yuv; + *rgb++ = u; + *rgb++ = y; + *rgb++ = v; + *rgb = y1; + return 4; + case VIDEO_PALETTE_GREY: + *rgb++ = *yuv++; + *rgb = *yuv; + return 2; + default: + DBG("Empty: %d\n", out_fmt); + return 0; + } +} + + +static int yuvconvert(unsigned char *yuv, unsigned char *rgb, int out_fmt, + int in_uyvy, int mmap_kludge) +{ + int y, u, v, r, g, b, y1; + + switch(out_fmt) { + case VIDEO_PALETTE_RGB555: + case VIDEO_PALETTE_RGB565: + case VIDEO_PALETTE_RGB24: + case VIDEO_PALETTE_RGB32: + if (in_uyvy) { + u = *yuv++ - 128; + y = (*yuv++ - 16) * 76310; + v = *yuv++ - 128; + y1 = (*yuv - 16) * 76310; + } else { + y = (*yuv++ - 16) * 76310; + u = *yuv++ - 128; + y1 = (*yuv++ - 16) * 76310; + v = *yuv - 128; + } + r = 104635 * v; + g = -25690 * u + -53294 * v; + b = 132278 * u; + break; + default: + y = *yuv++; + u = *yuv++; + y1 = *yuv++; + v = *yuv; + /* Just to avoid compiler warnings */ + r = 0; + g = 0; + b = 0; + break; + } + switch(out_fmt) { + case VIDEO_PALETTE_RGB555: + *rgb++ = ((LIMIT(g+y) & 0xf8) << 2) | (LIMIT(b+y) >> 3); + *rgb++ = ((LIMIT(r+y) & 0xf8) >> 1) | (LIMIT(g+y) >> 6); + *rgb++ = ((LIMIT(g+y1) & 0xf8) << 2) | (LIMIT(b+y1) >> 3); + *rgb = ((LIMIT(r+y1) & 0xf8) >> 1) | (LIMIT(g+y1) >> 6); + return 4; + case VIDEO_PALETTE_RGB565: + *rgb++ = ((LIMIT(g+y) & 0xfc) << 3) | (LIMIT(b+y) >> 3); + *rgb++ = (LIMIT(r+y) & 0xf8) | (LIMIT(g+y) >> 5); + *rgb++ = ((LIMIT(g+y1) & 0xfc) << 3) | (LIMIT(b+y1) >> 3); + *rgb = (LIMIT(r+y1) & 0xf8) | (LIMIT(g+y1) >> 5); + return 4; + case VIDEO_PALETTE_RGB24: + if (mmap_kludge) { + *rgb++ = LIMIT(b+y); + *rgb++ = LIMIT(g+y); + *rgb++ = LIMIT(r+y); + *rgb++ = LIMIT(b+y1); + *rgb++ = LIMIT(g+y1); + *rgb = LIMIT(r+y1); + } else { + *rgb++ = LIMIT(r+y); + *rgb++ = LIMIT(g+y); + *rgb++ = LIMIT(b+y); + *rgb++ = LIMIT(r+y1); + *rgb++ = LIMIT(g+y1); + *rgb = LIMIT(b+y1); + } + return 6; + case VIDEO_PALETTE_RGB32: + if (mmap_kludge) { + *rgb++ = LIMIT(b+y); + *rgb++ = LIMIT(g+y); + *rgb++ = LIMIT(r+y); + rgb++; + *rgb++ = LIMIT(b+y1); + *rgb++ = LIMIT(g+y1); + *rgb = LIMIT(r+y1); + } else { + *rgb++ = LIMIT(r+y); + *rgb++ = LIMIT(g+y); + *rgb++ = LIMIT(b+y); + rgb++; + *rgb++ = LIMIT(r+y1); + *rgb++ = LIMIT(g+y1); + *rgb = LIMIT(b+y1); + } + return 8; + case VIDEO_PALETTE_GREY: + *rgb++ = y; + *rgb = y1; + return 2; + case VIDEO_PALETTE_YUV422: + case VIDEO_PALETTE_YUYV: + *rgb++ = y; + *rgb++ = u; + *rgb++ = y1; + *rgb = v; + return 4; + case VIDEO_PALETTE_UYVY: + *rgb++ = u; + *rgb++ = y; + *rgb++ = v; + *rgb = y1; + return 4; + default: + DBG("Empty: %d\n", out_fmt); + return 0; + } +} + +static int skipcount(int count, int fmt) +{ + switch(fmt) { + case VIDEO_PALETTE_GREY: + return count; + case VIDEO_PALETTE_RGB555: + case VIDEO_PALETTE_RGB565: + case VIDEO_PALETTE_YUV422: + case VIDEO_PALETTE_YUYV: + case VIDEO_PALETTE_UYVY: + return 2*count; + case VIDEO_PALETTE_RGB24: + return 3*count; + case VIDEO_PALETTE_RGB32: + return 4*count; + default: + return 0; + } +} + +static int parse_picture(struct cam_data *cam, int size) +{ + u8 *obuf, *ibuf, *end_obuf; + int ll, in_uyvy, compressed, decimation, even_line, origsize, out_fmt; + int rows, cols, linesize, subsample_422; + + /* make sure params don't change while we are decoding */ + mutex_lock(&cam->param_lock); + + obuf = cam->decompressed_frame.data; + end_obuf = obuf+CPIA_MAX_FRAME_SIZE; + ibuf = cam->raw_image; + origsize = size; + out_fmt = cam->vp.palette; + + if ((ibuf[0] != MAGIC_0) || (ibuf[1] != MAGIC_1)) { + LOG("header not found\n"); + mutex_unlock(&cam->param_lock); + return -1; + } + + if ((ibuf[16] != VIDEOSIZE_QCIF) && (ibuf[16] != VIDEOSIZE_CIF)) { + LOG("wrong video size\n"); + mutex_unlock(&cam->param_lock); + return -1; + } + + if (ibuf[17] != SUBSAMPLE_420 && ibuf[17] != SUBSAMPLE_422) { + LOG("illegal subtype %d\n",ibuf[17]); + mutex_unlock(&cam->param_lock); + return -1; + } + subsample_422 = ibuf[17] == SUBSAMPLE_422; + + if (ibuf[18] != YUVORDER_YUYV && ibuf[18] != YUVORDER_UYVY) { + LOG("illegal yuvorder %d\n",ibuf[18]); + mutex_unlock(&cam->param_lock); + return -1; + } + in_uyvy = ibuf[18] == YUVORDER_UYVY; + + if ((ibuf[24] != cam->params.roi.colStart) || + (ibuf[25] != cam->params.roi.colEnd) || + (ibuf[26] != cam->params.roi.rowStart) || + (ibuf[27] != cam->params.roi.rowEnd)) { + LOG("ROI mismatch\n"); + mutex_unlock(&cam->param_lock); + return -1; + } + cols = 8*(ibuf[25] - ibuf[24]); + rows = 4*(ibuf[27] - ibuf[26]); + + + if ((ibuf[28] != NOT_COMPRESSED) && (ibuf[28] != COMPRESSED)) { + LOG("illegal compression %d\n",ibuf[28]); + mutex_unlock(&cam->param_lock); + return -1; + } + compressed = (ibuf[28] == COMPRESSED); + + if (ibuf[29] != NO_DECIMATION && ibuf[29] != DECIMATION_ENAB) { + LOG("illegal decimation %d\n",ibuf[29]); + mutex_unlock(&cam->param_lock); + return -1; + } + decimation = (ibuf[29] == DECIMATION_ENAB); + + cam->params.yuvThreshold.yThreshold = ibuf[30]; + cam->params.yuvThreshold.uvThreshold = ibuf[31]; + cam->params.status.systemState = ibuf[32]; + cam->params.status.grabState = ibuf[33]; + cam->params.status.streamState = ibuf[34]; + cam->params.status.fatalError = ibuf[35]; + cam->params.status.cmdError = ibuf[36]; + cam->params.status.debugFlags = ibuf[37]; + cam->params.status.vpStatus = ibuf[38]; + cam->params.status.errorCode = ibuf[39]; + cam->fps = ibuf[41]; + mutex_unlock(&cam->param_lock); + + linesize = skipcount(cols, out_fmt); + ibuf += FRAME_HEADER_SIZE; + size -= FRAME_HEADER_SIZE; + ll = ibuf[0] | (ibuf[1] << 8); + ibuf += 2; + even_line = 1; + + while (size > 0) { + size -= (ll+2); + if (size < 0) { + LOG("Insufficient data in buffer\n"); + return -1; + } + + while (ll > 1) { + if (!compressed || (compressed && !(*ibuf & 1))) { + if(subsample_422 || even_line) { + obuf += yuvconvert(ibuf, obuf, out_fmt, + in_uyvy, cam->mmap_kludge); + ibuf += 4; + ll -= 4; + } else { + /* SUBSAMPLE_420 on an odd line */ + obuf += convert420(ibuf, obuf, + out_fmt, linesize, + cam->mmap_kludge); + ibuf += 2; + ll -= 2; + } + } else { + /*skip compressed interval from previous frame*/ + obuf += skipcount(*ibuf >> 1, out_fmt); + if (obuf > end_obuf) { + LOG("Insufficient buffer size\n"); + return -1; + } + ++ibuf; + ll--; + } + } + if (ll == 1) { + if (*ibuf != EOL) { + DBG("EOL not found giving up after %d/%d" + " bytes\n", origsize-size, origsize); + return -1; + } + + ++ibuf; /* skip over EOL */ + + if ((size > 3) && (ibuf[0] == EOI) && (ibuf[1] == EOI) && + (ibuf[2] == EOI) && (ibuf[3] == EOI)) { + size -= 4; + break; + } + + if(decimation) { + /* skip the odd lines for now */ + obuf += linesize; + } + + if (size > 1) { + ll = ibuf[0] | (ibuf[1] << 8); + ibuf += 2; /* skip over line length */ + } + if(!decimation) + even_line = !even_line; + } else { + LOG("line length was not 1 but %d after %d/%d bytes\n", + ll, origsize-size, origsize); + return -1; + } + } + + if(decimation) { + /* interpolate odd rows */ + int i, j; + u8 *prev, *next; + prev = cam->decompressed_frame.data; + obuf = prev+linesize; + next = obuf+linesize; + for(i=1; idecompressed_frame.count = obuf-cam->decompressed_frame.data; + + return cam->decompressed_frame.count; +} + +/* InitStreamCap wrapper to select correct start line */ +static inline int init_stream_cap(struct cam_data *cam) +{ + return do_command(cam, CPIA_COMMAND_InitStreamCap, + 0, cam->params.streamStartLine, 0, 0); +} + + +/* find_over_exposure + * Finds a suitable value of OverExposure for use with SetFlickerCtrl + * Some calculation is required because this value changes with the brightness + * set with SetColourParameters + * + * Parameters: Brightness - last brightness value set with SetColourParameters + * + * Returns: OverExposure value to use with SetFlickerCtrl + */ +#define FLICKER_MAX_EXPOSURE 250 +#define FLICKER_ALLOWABLE_OVER_EXPOSURE 146 +#define FLICKER_BRIGHTNESS_CONSTANT 59 +static int find_over_exposure(int brightness) +{ + int MaxAllowableOverExposure, OverExposure; + + MaxAllowableOverExposure = FLICKER_MAX_EXPOSURE - brightness - + FLICKER_BRIGHTNESS_CONSTANT; + + if (MaxAllowableOverExposure < FLICKER_ALLOWABLE_OVER_EXPOSURE) { + OverExposure = MaxAllowableOverExposure; + } else { + OverExposure = FLICKER_ALLOWABLE_OVER_EXPOSURE; + } + + return OverExposure; +} +#undef FLICKER_MAX_EXPOSURE +#undef FLICKER_ALLOWABLE_OVER_EXPOSURE +#undef FLICKER_BRIGHTNESS_CONSTANT + +/* update various camera modes and settings */ +static void dispatch_commands(struct cam_data *cam) +{ + mutex_lock(&cam->param_lock); + if (cam->cmd_queue==COMMAND_NONE) { + mutex_unlock(&cam->param_lock); + return; + } + DEB_BYTE(cam->cmd_queue); + DEB_BYTE(cam->cmd_queue>>8); + if (cam->cmd_queue & COMMAND_SETFORMAT) { + do_command(cam, CPIA_COMMAND_SetFormat, + cam->params.format.videoSize, + cam->params.format.subSample, + cam->params.format.yuvOrder, 0); + do_command(cam, CPIA_COMMAND_SetROI, + cam->params.roi.colStart, cam->params.roi.colEnd, + cam->params.roi.rowStart, cam->params.roi.rowEnd); + cam->first_frame = 1; + } + + if (cam->cmd_queue & COMMAND_SETCOLOURPARAMS) + do_command(cam, CPIA_COMMAND_SetColourParams, + cam->params.colourParams.brightness, + cam->params.colourParams.contrast, + cam->params.colourParams.saturation, 0); + + if (cam->cmd_queue & COMMAND_SETAPCOR) + do_command(cam, CPIA_COMMAND_SetApcor, + cam->params.apcor.gain1, + cam->params.apcor.gain2, + cam->params.apcor.gain4, + cam->params.apcor.gain8); + + if (cam->cmd_queue & COMMAND_SETVLOFFSET) + do_command(cam, CPIA_COMMAND_SetVLOffset, + cam->params.vlOffset.gain1, + cam->params.vlOffset.gain2, + cam->params.vlOffset.gain4, + cam->params.vlOffset.gain8); + + if (cam->cmd_queue & COMMAND_SETEXPOSURE) { + do_command_extended(cam, CPIA_COMMAND_SetExposure, + cam->params.exposure.gainMode, + 1, + cam->params.exposure.compMode, + cam->params.exposure.centreWeight, + cam->params.exposure.gain, + cam->params.exposure.fineExp, + cam->params.exposure.coarseExpLo, + cam->params.exposure.coarseExpHi, + cam->params.exposure.redComp, + cam->params.exposure.green1Comp, + cam->params.exposure.green2Comp, + cam->params.exposure.blueComp); + if(cam->params.exposure.expMode != 1) { + do_command_extended(cam, CPIA_COMMAND_SetExposure, + 0, + cam->params.exposure.expMode, + 0, 0, + cam->params.exposure.gain, + cam->params.exposure.fineExp, + cam->params.exposure.coarseExpLo, + cam->params.exposure.coarseExpHi, + 0, 0, 0, 0); + } + } + + if (cam->cmd_queue & COMMAND_SETCOLOURBALANCE) { + if (cam->params.colourBalance.balanceMode == 1) { + do_command(cam, CPIA_COMMAND_SetColourBalance, + 1, + cam->params.colourBalance.redGain, + cam->params.colourBalance.greenGain, + cam->params.colourBalance.blueGain); + do_command(cam, CPIA_COMMAND_SetColourBalance, + 3, 0, 0, 0); + } + if (cam->params.colourBalance.balanceMode == 2) { + do_command(cam, CPIA_COMMAND_SetColourBalance, + 2, 0, 0, 0); + } + if (cam->params.colourBalance.balanceMode == 3) { + do_command(cam, CPIA_COMMAND_SetColourBalance, + 3, 0, 0, 0); + } + } + + if (cam->cmd_queue & COMMAND_SETCOMPRESSIONTARGET) + do_command(cam, CPIA_COMMAND_SetCompressionTarget, + cam->params.compressionTarget.frTargeting, + cam->params.compressionTarget.targetFR, + cam->params.compressionTarget.targetQ, 0); + + if (cam->cmd_queue & COMMAND_SETYUVTHRESH) + do_command(cam, CPIA_COMMAND_SetYUVThresh, + cam->params.yuvThreshold.yThreshold, + cam->params.yuvThreshold.uvThreshold, 0, 0); + + if (cam->cmd_queue & COMMAND_SETCOMPRESSIONPARAMS) + do_command_extended(cam, CPIA_COMMAND_SetCompressionParams, + 0, 0, 0, 0, + cam->params.compressionParams.hysteresis, + cam->params.compressionParams.threshMax, + cam->params.compressionParams.smallStep, + cam->params.compressionParams.largeStep, + cam->params.compressionParams.decimationHysteresis, + cam->params.compressionParams.frDiffStepThresh, + cam->params.compressionParams.qDiffStepThresh, + cam->params.compressionParams.decimationThreshMod); + + if (cam->cmd_queue & COMMAND_SETCOMPRESSION) + do_command(cam, CPIA_COMMAND_SetCompression, + cam->params.compression.mode, + cam->params.compression.decimation, 0, 0); + + if (cam->cmd_queue & COMMAND_SETSENSORFPS) + do_command(cam, CPIA_COMMAND_SetSensorFPS, + cam->params.sensorFps.divisor, + cam->params.sensorFps.baserate, 0, 0); + + if (cam->cmd_queue & COMMAND_SETFLICKERCTRL) + do_command(cam, CPIA_COMMAND_SetFlickerCtrl, + cam->params.flickerControl.flickerMode, + cam->params.flickerControl.coarseJump, + abs(cam->params.flickerControl.allowableOverExposure), + 0); + + if (cam->cmd_queue & COMMAND_SETECPTIMING) + do_command(cam, CPIA_COMMAND_SetECPTiming, + cam->params.ecpTiming, 0, 0, 0); + + if (cam->cmd_queue & COMMAND_PAUSE) + do_command(cam, CPIA_COMMAND_EndStreamCap, 0, 0, 0, 0); + + if (cam->cmd_queue & COMMAND_RESUME) + init_stream_cap(cam); + + if (cam->cmd_queue & COMMAND_SETLIGHTS && cam->params.qx3.qx3_detected) + { + int p1 = (cam->params.qx3.bottomlight == 0) << 1; + int p2 = (cam->params.qx3.toplight == 0) << 3; + do_command(cam, CPIA_COMMAND_WriteVCReg, 0x90, 0x8F, 0x50, 0); + do_command(cam, CPIA_COMMAND_WriteMCPort, 2, 0, (p1|p2|0xE0), 0); + } + + cam->cmd_queue = COMMAND_NONE; + mutex_unlock(&cam->param_lock); + return; +} + + + +static void set_flicker(struct cam_params *params, volatile u32 *command_flags, + int on) +{ + /* Everything in here is from the Windows driver */ +#define FIRMWARE_VERSION(x,y) (params->version.firmwareVersion == (x) && \ + params->version.firmwareRevision == (y)) +/* define for compgain calculation */ +#if 0 +#define COMPGAIN(base, curexp, newexp) \ + (u8) ((((float) base - 128.0) * ((float) curexp / (float) newexp)) + 128.5) +#define EXP_FROM_COMP(basecomp, curcomp, curexp) \ + (u16)((float)curexp * (float)(u8)(curcomp + 128) / (float)(u8)(basecomp - 128)) +#else + /* equivalent functions without floating point math */ +#define COMPGAIN(base, curexp, newexp) \ + (u8)(128 + (((u32)(2*(base-128)*curexp + newexp)) / (2* newexp)) ) +#define EXP_FROM_COMP(basecomp, curcomp, curexp) \ + (u16)(((u32)(curexp * (u8)(curcomp + 128)) / (u8)(basecomp - 128))) +#endif + + + int currentexp = params->exposure.coarseExpLo + + params->exposure.coarseExpHi*256; + int startexp; + if (on) { + int cj = params->flickerControl.coarseJump; + params->flickerControl.flickerMode = 1; + params->flickerControl.disabled = 0; + if(params->exposure.expMode != 2) + *command_flags |= COMMAND_SETEXPOSURE; + params->exposure.expMode = 2; + currentexp = currentexp << params->exposure.gain; + params->exposure.gain = 0; + /* round down current exposure to nearest value */ + startexp = (currentexp + ROUND_UP_EXP_FOR_FLICKER) / cj; + if(startexp < 1) + startexp = 1; + startexp = (startexp * cj) - 1; + if(FIRMWARE_VERSION(1,2)) + while(startexp > MAX_EXP_102) + startexp -= cj; + else + while(startexp > MAX_EXP) + startexp -= cj; + params->exposure.coarseExpLo = startexp & 0xff; + params->exposure.coarseExpHi = startexp >> 8; + if (currentexp > startexp) { + if (currentexp > (2 * startexp)) + currentexp = 2 * startexp; + params->exposure.redComp = COMPGAIN (COMP_RED, currentexp, startexp); + params->exposure.green1Comp = COMPGAIN (COMP_GREEN1, currentexp, startexp); + params->exposure.green2Comp = COMPGAIN (COMP_GREEN2, currentexp, startexp); + params->exposure.blueComp = COMPGAIN (COMP_BLUE, currentexp, startexp); + } else { + params->exposure.redComp = COMP_RED; + params->exposure.green1Comp = COMP_GREEN1; + params->exposure.green2Comp = COMP_GREEN2; + params->exposure.blueComp = COMP_BLUE; + } + if(FIRMWARE_VERSION(1,2)) + params->exposure.compMode = 0; + else + params->exposure.compMode = 1; + + params->apcor.gain1 = 0x18; + params->apcor.gain2 = 0x18; + params->apcor.gain4 = 0x16; + params->apcor.gain8 = 0x14; + *command_flags |= COMMAND_SETAPCOR; + } else { + params->flickerControl.flickerMode = 0; + params->flickerControl.disabled = 1; + /* Coarse = average of equivalent coarse for each comp channel */ + startexp = EXP_FROM_COMP(COMP_RED, params->exposure.redComp, currentexp); + startexp += EXP_FROM_COMP(COMP_GREEN1, params->exposure.green1Comp, currentexp); + startexp += EXP_FROM_COMP(COMP_GREEN2, params->exposure.green2Comp, currentexp); + startexp += EXP_FROM_COMP(COMP_BLUE, params->exposure.blueComp, currentexp); + startexp = startexp >> 2; + while(startexp > MAX_EXP && + params->exposure.gain < params->exposure.gainMode-1) { + startexp = startexp >> 1; + ++params->exposure.gain; + } + if(FIRMWARE_VERSION(1,2) && startexp > MAX_EXP_102) + startexp = MAX_EXP_102; + if(startexp > MAX_EXP) + startexp = MAX_EXP; + params->exposure.coarseExpLo = startexp&0xff; + params->exposure.coarseExpHi = startexp >> 8; + params->exposure.redComp = COMP_RED; + params->exposure.green1Comp = COMP_GREEN1; + params->exposure.green2Comp = COMP_GREEN2; + params->exposure.blueComp = COMP_BLUE; + params->exposure.compMode = 1; + *command_flags |= COMMAND_SETEXPOSURE; + params->apcor.gain1 = 0x18; + params->apcor.gain2 = 0x16; + params->apcor.gain4 = 0x24; + params->apcor.gain8 = 0x34; + *command_flags |= COMMAND_SETAPCOR; + } + params->vlOffset.gain1 = 20; + params->vlOffset.gain2 = 24; + params->vlOffset.gain4 = 26; + params->vlOffset.gain8 = 26; + *command_flags |= COMMAND_SETVLOFFSET; +#undef FIRMWARE_VERSION +#undef EXP_FROM_COMP +#undef COMPGAIN +} + +#define FIRMWARE_VERSION(x,y) (cam->params.version.firmwareVersion == (x) && \ + cam->params.version.firmwareRevision == (y)) +/* monitor the exposure and adjust the sensor frame rate if needed */ +static void monitor_exposure(struct cam_data *cam) +{ + u8 exp_acc, bcomp, gain, coarseL, cmd[8], data[8]; + int retval, light_exp, dark_exp, very_dark_exp; + int old_exposure, new_exposure, framerate; + + /* get necessary stats and register settings from camera */ + /* do_command can't handle this, so do it ourselves */ + cmd[0] = CPIA_COMMAND_ReadVPRegs>>8; + cmd[1] = CPIA_COMMAND_ReadVPRegs&0xff; + cmd[2] = 30; + cmd[3] = 4; + cmd[4] = 9; + cmd[5] = 8; + cmd[6] = 8; + cmd[7] = 0; + retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data); + if (retval) { + LOG("ReadVPRegs(30,4,9,8) - failed, retval=%d\n", + retval); + return; + } + exp_acc = data[0]; + bcomp = data[1]; + gain = data[2]; + coarseL = data[3]; + + mutex_lock(&cam->param_lock); + light_exp = cam->params.colourParams.brightness + + TC - 50 + EXP_ACC_LIGHT; + if(light_exp > 255) + light_exp = 255; + dark_exp = cam->params.colourParams.brightness + + TC - 50 - EXP_ACC_DARK; + if(dark_exp < 0) + dark_exp = 0; + very_dark_exp = dark_exp/2; + + old_exposure = cam->params.exposure.coarseExpHi * 256 + + cam->params.exposure.coarseExpLo; + + if(!cam->params.flickerControl.disabled) { + /* Flicker control on */ + int max_comp = FIRMWARE_VERSION(1,2) ? MAX_COMP : HIGH_COMP_102; + bcomp += 128; /* decode */ + if(bcomp >= max_comp && exp_acc < dark_exp) { + /* dark */ + if(exp_acc < very_dark_exp) { + /* very dark */ + if(cam->exposure_status == EXPOSURE_VERY_DARK) + ++cam->exposure_count; + else { + cam->exposure_status = EXPOSURE_VERY_DARK; + cam->exposure_count = 1; + } + } else { + /* just dark */ + if(cam->exposure_status == EXPOSURE_DARK) + ++cam->exposure_count; + else { + cam->exposure_status = EXPOSURE_DARK; + cam->exposure_count = 1; + } + } + } else if(old_exposure <= LOW_EXP || exp_acc > light_exp) { + /* light */ + if(old_exposure <= VERY_LOW_EXP) { + /* very light */ + if(cam->exposure_status == EXPOSURE_VERY_LIGHT) + ++cam->exposure_count; + else { + cam->exposure_status = EXPOSURE_VERY_LIGHT; + cam->exposure_count = 1; + } + } else { + /* just light */ + if(cam->exposure_status == EXPOSURE_LIGHT) + ++cam->exposure_count; + else { + cam->exposure_status = EXPOSURE_LIGHT; + cam->exposure_count = 1; + } + } + } else { + /* not dark or light */ + cam->exposure_status = EXPOSURE_NORMAL; + } + } else { + /* Flicker control off */ + if(old_exposure >= MAX_EXP && exp_acc < dark_exp) { + /* dark */ + if(exp_acc < very_dark_exp) { + /* very dark */ + if(cam->exposure_status == EXPOSURE_VERY_DARK) + ++cam->exposure_count; + else { + cam->exposure_status = EXPOSURE_VERY_DARK; + cam->exposure_count = 1; + } + } else { + /* just dark */ + if(cam->exposure_status == EXPOSURE_DARK) + ++cam->exposure_count; + else { + cam->exposure_status = EXPOSURE_DARK; + cam->exposure_count = 1; + } + } + } else if(old_exposure <= LOW_EXP || exp_acc > light_exp) { + /* light */ + if(old_exposure <= VERY_LOW_EXP) { + /* very light */ + if(cam->exposure_status == EXPOSURE_VERY_LIGHT) + ++cam->exposure_count; + else { + cam->exposure_status = EXPOSURE_VERY_LIGHT; + cam->exposure_count = 1; + } + } else { + /* just light */ + if(cam->exposure_status == EXPOSURE_LIGHT) + ++cam->exposure_count; + else { + cam->exposure_status = EXPOSURE_LIGHT; + cam->exposure_count = 1; + } + } + } else { + /* not dark or light */ + cam->exposure_status = EXPOSURE_NORMAL; + } + } + + framerate = cam->fps; + if(framerate > 30 || framerate < 1) + framerate = 1; + + if(!cam->params.flickerControl.disabled) { + /* Flicker control on */ + if((cam->exposure_status == EXPOSURE_VERY_DARK || + cam->exposure_status == EXPOSURE_DARK) && + cam->exposure_count >= DARK_TIME*framerate && + cam->params.sensorFps.divisor < 3) { + + /* dark for too long */ + ++cam->params.sensorFps.divisor; + cam->cmd_queue |= COMMAND_SETSENSORFPS; + + cam->params.flickerControl.coarseJump = + flicker_jumps[cam->mainsFreq] + [cam->params.sensorFps.baserate] + [cam->params.sensorFps.divisor]; + cam->cmd_queue |= COMMAND_SETFLICKERCTRL; + + new_exposure = cam->params.flickerControl.coarseJump-1; + while(new_exposure < old_exposure/2) + new_exposure += cam->params.flickerControl.coarseJump; + cam->params.exposure.coarseExpLo = new_exposure & 0xff; + cam->params.exposure.coarseExpHi = new_exposure >> 8; + cam->cmd_queue |= COMMAND_SETEXPOSURE; + cam->exposure_status = EXPOSURE_NORMAL; + LOG("Automatically decreasing sensor_fps\n"); + + } else if((cam->exposure_status == EXPOSURE_VERY_LIGHT || + cam->exposure_status == EXPOSURE_LIGHT) && + cam->exposure_count >= LIGHT_TIME*framerate && + cam->params.sensorFps.divisor > 0) { + + /* light for too long */ + int max_exp = FIRMWARE_VERSION(1,2) ? MAX_EXP_102 : MAX_EXP ; + + --cam->params.sensorFps.divisor; + cam->cmd_queue |= COMMAND_SETSENSORFPS; + + cam->params.flickerControl.coarseJump = + flicker_jumps[cam->mainsFreq] + [cam->params.sensorFps.baserate] + [cam->params.sensorFps.divisor]; + cam->cmd_queue |= COMMAND_SETFLICKERCTRL; + + new_exposure = cam->params.flickerControl.coarseJump-1; + while(new_exposure < 2*old_exposure && + new_exposure+ + cam->params.flickerControl.coarseJump < max_exp) + new_exposure += cam->params.flickerControl.coarseJump; + cam->params.exposure.coarseExpLo = new_exposure & 0xff; + cam->params.exposure.coarseExpHi = new_exposure >> 8; + cam->cmd_queue |= COMMAND_SETEXPOSURE; + cam->exposure_status = EXPOSURE_NORMAL; + LOG("Automatically increasing sensor_fps\n"); + } + } else { + /* Flicker control off */ + if((cam->exposure_status == EXPOSURE_VERY_DARK || + cam->exposure_status == EXPOSURE_DARK) && + cam->exposure_count >= DARK_TIME*framerate && + cam->params.sensorFps.divisor < 3) { + + /* dark for too long */ + ++cam->params.sensorFps.divisor; + cam->cmd_queue |= COMMAND_SETSENSORFPS; + + if(cam->params.exposure.gain > 0) { + --cam->params.exposure.gain; + cam->cmd_queue |= COMMAND_SETEXPOSURE; + } + cam->exposure_status = EXPOSURE_NORMAL; + LOG("Automatically decreasing sensor_fps\n"); + + } else if((cam->exposure_status == EXPOSURE_VERY_LIGHT || + cam->exposure_status == EXPOSURE_LIGHT) && + cam->exposure_count >= LIGHT_TIME*framerate && + cam->params.sensorFps.divisor > 0) { + + /* light for too long */ + --cam->params.sensorFps.divisor; + cam->cmd_queue |= COMMAND_SETSENSORFPS; + + if(cam->params.exposure.gain < + cam->params.exposure.gainMode-1) { + ++cam->params.exposure.gain; + cam->cmd_queue |= COMMAND_SETEXPOSURE; + } + cam->exposure_status = EXPOSURE_NORMAL; + LOG("Automatically increasing sensor_fps\n"); + } + } + mutex_unlock(&cam->param_lock); +} + +/*-----------------------------------------------------------------*/ +/* if flicker is switched off, this function switches it back on.It checks, + however, that conditions are suitable before restarting it. + This should only be called for firmware version 1.2. + + It also adjust the colour balance when an exposure step is detected - as + long as flicker is running +*/ +static void restart_flicker(struct cam_data *cam) +{ + int cam_exposure, old_exp; + if(!FIRMWARE_VERSION(1,2)) + return; + mutex_lock(&cam->param_lock); + if(cam->params.flickerControl.flickerMode == 0 || + cam->raw_image[39] == 0) { + mutex_unlock(&cam->param_lock); + return; + } + cam_exposure = cam->raw_image[39]*2; + old_exp = cam->params.exposure.coarseExpLo + + cam->params.exposure.coarseExpHi*256; + /* + see how far away camera exposure is from a valid + flicker exposure value + */ + cam_exposure %= cam->params.flickerControl.coarseJump; + if(!cam->params.flickerControl.disabled && + cam_exposure <= cam->params.flickerControl.coarseJump - 3) { + /* Flicker control auto-disabled */ + cam->params.flickerControl.disabled = 1; + } + + if(cam->params.flickerControl.disabled && + cam->params.flickerControl.flickerMode && + old_exp > cam->params.flickerControl.coarseJump + + ROUND_UP_EXP_FOR_FLICKER) { + /* exposure is now high enough to switch + flicker control back on */ + set_flicker(&cam->params, &cam->cmd_queue, 1); + if((cam->cmd_queue & COMMAND_SETEXPOSURE) && + cam->params.exposure.expMode == 2) + cam->exposure_status = EXPOSURE_NORMAL; + + } + mutex_unlock(&cam->param_lock); +} +#undef FIRMWARE_VERSION + +static int clear_stall(struct cam_data *cam) +{ + /* FIXME: Does this actually work? */ + LOG("Clearing stall\n"); + + cam->ops->streamRead(cam->lowlevel_data, cam->raw_image, 0); + do_command(cam, CPIA_COMMAND_GetCameraStatus,0,0,0,0); + return cam->params.status.streamState != STREAM_PAUSED; +} + +/* kernel thread function to read image from camera */ +static int fetch_frame(void *data) +{ + int image_size, retry; + struct cam_data *cam = (struct cam_data *)data; + unsigned long oldjif, rate, diff; + + /* Allow up to two bad images in a row to be read and + * ignored before an error is reported */ + for (retry = 0; retry < 3; ++retry) { + if (retry) + DBG("retry=%d\n", retry); + + if (!cam->ops) + continue; + + /* load first frame always uncompressed */ + if (cam->first_frame && + cam->params.compression.mode != CPIA_COMPRESSION_NONE) { + do_command(cam, CPIA_COMMAND_SetCompression, + CPIA_COMPRESSION_NONE, + NO_DECIMATION, 0, 0); + /* Trial & error - Discarding a frame prevents the + first frame from having an error in the data. */ + do_command(cam, CPIA_COMMAND_DiscardFrame, 0, 0, 0, 0); + } + + /* init camera upload */ + if (do_command(cam, CPIA_COMMAND_GrabFrame, 0, + cam->params.streamStartLine, 0, 0)) + continue; + + if (cam->ops->wait_for_stream_ready) { + /* loop until image ready */ + int count = 0; + do_command(cam, CPIA_COMMAND_GetCameraStatus,0,0,0,0); + while (cam->params.status.streamState != STREAM_READY) { + if(++count > READY_TIMEOUT) + break; + if(cam->params.status.streamState == + STREAM_PAUSED) { + /* Bad news */ + if(!clear_stall(cam)) + return -EIO; + } + + cond_resched(); + + /* sleep for 10 ms, hopefully ;) */ + msleep_interruptible(10); + if (signal_pending(current)) + return -EINTR; + + do_command(cam, CPIA_COMMAND_GetCameraStatus, + 0, 0, 0, 0); + } + if(cam->params.status.streamState != STREAM_READY) { + continue; + } + } + + cond_resched(); + + /* grab image from camera */ + oldjif = jiffies; + image_size = cam->ops->streamRead(cam->lowlevel_data, + cam->raw_image, 0); + if (image_size <= 0) { + DBG("streamRead failed: %d\n", image_size); + continue; + } + + rate = image_size * HZ / 1024; + diff = jiffies-oldjif; + cam->transfer_rate = diff==0 ? rate : rate/diff; + /* diff==0 ? unlikely but possible */ + + /* Switch flicker control back on if it got turned off */ + restart_flicker(cam); + + /* If AEC is enabled, monitor the exposure and + adjust the sensor frame rate if needed */ + if(cam->params.exposure.expMode == 2) + monitor_exposure(cam); + + /* camera idle now so dispatch queued commands */ + dispatch_commands(cam); + + /* Update our knowledge of the camera state */ + do_command(cam, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0); + do_command(cam, CPIA_COMMAND_GetExposure, 0, 0, 0, 0); + do_command(cam, CPIA_COMMAND_ReadMCPorts, 0, 0, 0, 0); + + /* decompress and convert image to by copying it from + * raw_image to decompressed_frame + */ + + cond_resched(); + + cam->image_size = parse_picture(cam, image_size); + if (cam->image_size <= 0) { + DBG("parse_picture failed %d\n", cam->image_size); + if(cam->params.compression.mode != + CPIA_COMPRESSION_NONE) { + /* Compression may not work right if we + had a bad frame, get the next one + uncompressed. */ + cam->first_frame = 1; + do_command(cam, CPIA_COMMAND_SetGrabMode, + CPIA_GRAB_SINGLE, 0, 0, 0); + /* FIXME: Trial & error - need up to 70ms for + the grab mode change to complete ? */ + msleep_interruptible(70); + if (signal_pending(current)) + return -EINTR; + } + } else + break; + } + + if (retry < 3) { + /* FIXME: this only works for double buffering */ + if (cam->frame[cam->curframe].state == FRAME_READY) { + memcpy(cam->frame[cam->curframe].data, + cam->decompressed_frame.data, + cam->decompressed_frame.count); + cam->frame[cam->curframe].state = FRAME_DONE; + } else + cam->decompressed_frame.state = FRAME_DONE; + + if (cam->first_frame) { + cam->first_frame = 0; + do_command(cam, CPIA_COMMAND_SetCompression, + cam->params.compression.mode, + cam->params.compression.decimation, 0, 0); + + /* Switch from single-grab to continuous grab */ + do_command(cam, CPIA_COMMAND_SetGrabMode, + CPIA_GRAB_CONTINUOUS, 0, 0, 0); + } + return 0; + } + return -EIO; +} + +static int capture_frame(struct cam_data *cam, struct video_mmap *vm) +{ + if (!cam->frame_buf) { + /* we do lazy allocation */ + int err; + if ((err = allocate_frame_buf(cam))) + return err; + } + + cam->curframe = vm->frame; + cam->frame[cam->curframe].state = FRAME_READY; + return fetch_frame(cam); +} + +static int goto_high_power(struct cam_data *cam) +{ + if (do_command(cam, CPIA_COMMAND_GotoHiPower, 0, 0, 0, 0)) + return -EIO; + msleep_interruptible(40); /* windows driver does it too */ + if(signal_pending(current)) + return -EINTR; + if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0)) + return -EIO; + if (cam->params.status.systemState == HI_POWER_STATE) { + DBG("camera now in HIGH power state\n"); + return 0; + } + printstatus(cam); + return -EIO; +} + +static int goto_low_power(struct cam_data *cam) +{ + if (do_command(cam, CPIA_COMMAND_GotoLoPower, 0, 0, 0, 0)) + return -1; + if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0)) + return -1; + if (cam->params.status.systemState == LO_POWER_STATE) { + DBG("camera now in LOW power state\n"); + return 0; + } + printstatus(cam); + return -1; +} + +static void save_camera_state(struct cam_data *cam) +{ + if(!(cam->cmd_queue & COMMAND_SETCOLOURBALANCE)) + do_command(cam, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0); + if(!(cam->cmd_queue & COMMAND_SETEXPOSURE)) + do_command(cam, CPIA_COMMAND_GetExposure, 0, 0, 0, 0); + + DBG("%d/%d/%d/%d/%d/%d/%d/%d\n", + cam->params.exposure.gain, + cam->params.exposure.fineExp, + cam->params.exposure.coarseExpLo, + cam->params.exposure.coarseExpHi, + cam->params.exposure.redComp, + cam->params.exposure.green1Comp, + cam->params.exposure.green2Comp, + cam->params.exposure.blueComp); + DBG("%d/%d/%d\n", + cam->params.colourBalance.redGain, + cam->params.colourBalance.greenGain, + cam->params.colourBalance.blueGain); +} + +static int set_camera_state(struct cam_data *cam) +{ + cam->cmd_queue = COMMAND_SETCOMPRESSION | + COMMAND_SETCOMPRESSIONTARGET | + COMMAND_SETCOLOURPARAMS | + COMMAND_SETFORMAT | + COMMAND_SETYUVTHRESH | + COMMAND_SETECPTIMING | + COMMAND_SETCOMPRESSIONPARAMS | + COMMAND_SETEXPOSURE | + COMMAND_SETCOLOURBALANCE | + COMMAND_SETSENSORFPS | + COMMAND_SETAPCOR | + COMMAND_SETFLICKERCTRL | + COMMAND_SETVLOFFSET; + + do_command(cam, CPIA_COMMAND_SetGrabMode, CPIA_GRAB_SINGLE,0,0,0); + dispatch_commands(cam); + + /* Wait 6 frames for the sensor to get all settings and + AEC/ACB to settle */ + msleep_interruptible(6*(cam->params.sensorFps.baserate ? 33 : 40) * + (1 << cam->params.sensorFps.divisor) + 10); + + if(signal_pending(current)) + return -EINTR; + + save_camera_state(cam); + + return 0; +} + +static void get_version_information(struct cam_data *cam) +{ + /* GetCPIAVersion */ + do_command(cam, CPIA_COMMAND_GetCPIAVersion, 0, 0, 0, 0); + + /* GetPnPID */ + do_command(cam, CPIA_COMMAND_GetPnPID, 0, 0, 0, 0); +} + +/* initialize camera */ +static int reset_camera(struct cam_data *cam) +{ + int err; + /* Start the camera in low power mode */ + if (goto_low_power(cam)) { + if (cam->params.status.systemState != WARM_BOOT_STATE) + return -ENODEV; + + /* FIXME: this is just dirty trial and error */ + err = goto_high_power(cam); + if(err) + return err; + do_command(cam, CPIA_COMMAND_DiscardFrame, 0, 0, 0, 0); + if (goto_low_power(cam)) + return -ENODEV; + } + + /* procedure described in developer's guide p3-28 */ + + /* Check the firmware version. */ + cam->params.version.firmwareVersion = 0; + get_version_information(cam); + if (cam->params.version.firmwareVersion != 1) + return -ENODEV; + + /* A bug in firmware 1-02 limits gainMode to 2 */ + if(cam->params.version.firmwareRevision <= 2 && + cam->params.exposure.gainMode > 2) { + cam->params.exposure.gainMode = 2; + } + + /* set QX3 detected flag */ + cam->params.qx3.qx3_detected = (cam->params.pnpID.vendor == 0x0813 && + cam->params.pnpID.product == 0x0001); + + /* The fatal error checking should be done after + * the camera powers up (developer's guide p 3-38) */ + + /* Set streamState before transition to high power to avoid bug + * in firmware 1-02 */ + do_command(cam, CPIA_COMMAND_ModifyCameraStatus, STREAMSTATE, 0, + STREAM_NOT_READY, 0); + + /* GotoHiPower */ + err = goto_high_power(cam); + if (err) + return err; + + /* Check the camera status */ + if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0)) + return -EIO; + + if (cam->params.status.fatalError) { + DBG("fatal_error: %#04x\n", + cam->params.status.fatalError); + DBG("vp_status: %#04x\n", + cam->params.status.vpStatus); + if (cam->params.status.fatalError & ~(COM_FLAG|CPIA_FLAG)) { + /* Fatal error in camera */ + return -EIO; + } else if (cam->params.status.fatalError & (COM_FLAG|CPIA_FLAG)) { + /* Firmware 1-02 may do this for parallel port cameras, + * just clear the flags (developer's guide p 3-38) */ + do_command(cam, CPIA_COMMAND_ModifyCameraStatus, + FATALERROR, ~(COM_FLAG|CPIA_FLAG), 0, 0); + } + } + + /* Check the camera status again */ + if (cam->params.status.fatalError) { + if (cam->params.status.fatalError) + return -EIO; + } + + /* VPVersion can't be retrieved before the camera is in HiPower, + * so get it here instead of in get_version_information. */ + do_command(cam, CPIA_COMMAND_GetVPVersion, 0, 0, 0, 0); + + /* set camera to a known state */ + return set_camera_state(cam); +} + +static void put_cam(struct cpia_camera_ops* ops) +{ + module_put(ops->owner); +} + +/* ------------------------- V4L interface --------------------- */ +static int cpia_open(struct file *file) +{ + struct video_device *dev = video_devdata(file); + struct cam_data *cam = video_get_drvdata(dev); + int err; + + if (!cam) { + DBG("Internal error, cam_data not found!\n"); + return -ENODEV; + } + + if (cam->open_count > 0) { + DBG("Camera already open\n"); + return -EBUSY; + } + + if (!try_module_get(cam->ops->owner)) + return -ENODEV; + + mutex_lock(&cam->busy_lock); + err = -ENOMEM; + if (!cam->raw_image) { + cam->raw_image = rvmalloc(CPIA_MAX_IMAGE_SIZE); + if (!cam->raw_image) + goto oops; + } + + if (!cam->decompressed_frame.data) { + cam->decompressed_frame.data = rvmalloc(CPIA_MAX_FRAME_SIZE); + if (!cam->decompressed_frame.data) + goto oops; + } + + /* open cpia */ + err = -ENODEV; + if (cam->ops->open(cam->lowlevel_data)) + goto oops; + + /* reset the camera */ + if ((err = reset_camera(cam)) != 0) { + cam->ops->close(cam->lowlevel_data); + goto oops; + } + + err = -EINTR; + if(signal_pending(current)) + goto oops; + + /* Set ownership of /proc/cpia/videoX to current user */ + if(cam->proc_entry) + cam->proc_entry->uid = current_uid(); + + /* set mark for loading first frame uncompressed */ + cam->first_frame = 1; + + /* init it to something */ + cam->mmap_kludge = 0; + + ++cam->open_count; + file->private_data = dev; + mutex_unlock(&cam->busy_lock); + return 0; + + oops: + if (cam->decompressed_frame.data) { + rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE); + cam->decompressed_frame.data = NULL; + } + if (cam->raw_image) { + rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE); + cam->raw_image = NULL; + } + mutex_unlock(&cam->busy_lock); + put_cam(cam->ops); + return err; +} + +static int cpia_close(struct file *file) +{ + struct video_device *dev = file->private_data; + struct cam_data *cam = video_get_drvdata(dev); + + if (cam->ops) { + /* Return ownership of /proc/cpia/videoX to root */ + if(cam->proc_entry) + cam->proc_entry->uid = 0; + + /* save camera state for later open (developers guide ch 3.5.3) */ + save_camera_state(cam); + + /* GotoLoPower */ + goto_low_power(cam); + + /* Update the camera status */ + do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0); + + /* cleanup internal state stuff */ + free_frames(cam->frame); + + /* close cpia */ + cam->ops->close(cam->lowlevel_data); + + put_cam(cam->ops); + } + + if (--cam->open_count == 0) { + /* clean up capture-buffers */ + if (cam->raw_image) { + rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE); + cam->raw_image = NULL; + } + + if (cam->decompressed_frame.data) { + rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE); + cam->decompressed_frame.data = NULL; + } + + if (cam->frame_buf) + free_frame_buf(cam); + + if (!cam->ops) + kfree(cam); + } + file->private_data = NULL; + + return 0; +} + +static ssize_t cpia_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct video_device *dev = file->private_data; + struct cam_data *cam = video_get_drvdata(dev); + int err; + + /* make this _really_ smp and multithread-safe */ + if (mutex_lock_interruptible(&cam->busy_lock)) + return -EINTR; + + if (!buf) { + DBG("buf NULL\n"); + mutex_unlock(&cam->busy_lock); + return -EINVAL; + } + + if (!count) { + DBG("count 0\n"); + mutex_unlock(&cam->busy_lock); + return 0; + } + + if (!cam->ops) { + DBG("ops NULL\n"); + mutex_unlock(&cam->busy_lock); + return -ENODEV; + } + + /* upload frame */ + cam->decompressed_frame.state = FRAME_READY; + cam->mmap_kludge=0; + if((err = fetch_frame(cam)) != 0) { + DBG("ERROR from fetch_frame: %d\n", err); + mutex_unlock(&cam->busy_lock); + return err; + } + cam->decompressed_frame.state = FRAME_UNUSED; + + /* copy data to user space */ + if (cam->decompressed_frame.count > count) { + DBG("count wrong: %d, %lu\n", cam->decompressed_frame.count, + (unsigned long) count); + mutex_unlock(&cam->busy_lock); + return -EFAULT; + } + if (copy_to_user(buf, cam->decompressed_frame.data, + cam->decompressed_frame.count)) { + DBG("copy_to_user failed\n"); + mutex_unlock(&cam->busy_lock); + return -EFAULT; + } + + mutex_unlock(&cam->busy_lock); + return cam->decompressed_frame.count; +} + +static long cpia_do_ioctl(struct file *file, unsigned int cmd, void *arg) +{ + struct video_device *dev = file->private_data; + struct cam_data *cam = video_get_drvdata(dev); + int retval = 0; + + if (!cam || !cam->ops) + return -ENODEV; + + /* make this _really_ smp-safe */ + if (mutex_lock_interruptible(&cam->busy_lock)) + return -EINTR; + + /* DBG("cpia_ioctl: %u\n", cmd); */ + + switch (cmd) { + /* query capabilities */ + case VIDIOCGCAP: + { + struct video_capability *b = arg; + + DBG("VIDIOCGCAP\n"); + strcpy(b->name, "CPiA Camera"); + b->type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE; + b->channels = 1; + b->audios = 0; + b->maxwidth = 352; /* VIDEOSIZE_CIF */ + b->maxheight = 288; + b->minwidth = 48; /* VIDEOSIZE_48_48 */ + b->minheight = 48; + break; + } + + /* get/set video source - we are a camera and nothing else */ + case VIDIOCGCHAN: + { + struct video_channel *v = arg; + + DBG("VIDIOCGCHAN\n"); + if (v->channel != 0) { + retval = -EINVAL; + break; + } + + v->channel = 0; + strcpy(v->name, "Camera"); + v->tuners = 0; + v->flags = 0; + v->type = VIDEO_TYPE_CAMERA; + v->norm = 0; + break; + } + + case VIDIOCSCHAN: + { + struct video_channel *v = arg; + + DBG("VIDIOCSCHAN\n"); + if (v->channel != 0) + retval = -EINVAL; + break; + } + + /* image properties */ + case VIDIOCGPICT: + { + struct video_picture *pic = arg; + DBG("VIDIOCGPICT\n"); + *pic = cam->vp; + break; + } + + case VIDIOCSPICT: + { + struct video_picture *vp = arg; + + DBG("VIDIOCSPICT\n"); + + /* check validity */ + DBG("palette: %d\n", vp->palette); + DBG("depth: %d\n", vp->depth); + if (!valid_mode(vp->palette, vp->depth)) { + retval = -EINVAL; + break; + } + + mutex_lock(&cam->param_lock); + /* brightness, colour, contrast need no check 0-65535 */ + cam->vp = *vp; + /* update cam->params.colourParams */ + cam->params.colourParams.brightness = vp->brightness*100/65535; + cam->params.colourParams.contrast = vp->contrast*100/65535; + cam->params.colourParams.saturation = vp->colour*100/65535; + /* contrast is in steps of 8, so round */ + cam->params.colourParams.contrast = + ((cam->params.colourParams.contrast + 3) / 8) * 8; + if (cam->params.version.firmwareVersion == 1 && + cam->params.version.firmwareRevision == 2 && + cam->params.colourParams.contrast > 80) { + /* 1-02 firmware limits contrast to 80 */ + cam->params.colourParams.contrast = 80; + } + + /* Adjust flicker control if necessary */ + if(cam->params.flickerControl.allowableOverExposure < 0) + cam->params.flickerControl.allowableOverExposure = + -find_over_exposure(cam->params.colourParams.brightness); + if(cam->params.flickerControl.flickerMode != 0) + cam->cmd_queue |= COMMAND_SETFLICKERCTRL; + + + /* queue command to update camera */ + cam->cmd_queue |= COMMAND_SETCOLOURPARAMS; + mutex_unlock(&cam->param_lock); + DBG("VIDIOCSPICT: %d / %d // %d / %d / %d / %d\n", + vp->depth, vp->palette, vp->brightness, vp->hue, vp->colour, + vp->contrast); + break; + } + + /* get/set capture window */ + case VIDIOCGWIN: + { + struct video_window *vw = arg; + DBG("VIDIOCGWIN\n"); + + *vw = cam->vw; + break; + } + + case VIDIOCSWIN: + { + /* copy_from_user, check validity, copy to internal structure */ + struct video_window *vw = arg; + DBG("VIDIOCSWIN\n"); + + if (vw->clipcount != 0) { /* clipping not supported */ + retval = -EINVAL; + break; + } + if (vw->clips != NULL) { /* clipping not supported */ + retval = -EINVAL; + break; + } + + /* we set the video window to something smaller or equal to what + * is requested by the user??? + */ + mutex_lock(&cam->param_lock); + if (vw->width != cam->vw.width || vw->height != cam->vw.height) { + int video_size = match_videosize(vw->width, vw->height); + + if (video_size < 0) { + retval = -EINVAL; + mutex_unlock(&cam->param_lock); + break; + } + cam->video_size = video_size; + + /* video size is changing, reset the subcapture area */ + memset(&cam->vc, 0, sizeof(cam->vc)); + + set_vw_size(cam); + DBG("%d / %d\n", cam->vw.width, cam->vw.height); + cam->cmd_queue |= COMMAND_SETFORMAT; + } + + mutex_unlock(&cam->param_lock); + + /* setformat ignored by camera during streaming, + * so stop/dispatch/start */ + if (cam->cmd_queue & COMMAND_SETFORMAT) { + DBG("\n"); + dispatch_commands(cam); + } + DBG("%d/%d:%d\n", cam->video_size, + cam->vw.width, cam->vw.height); + break; + } + + /* mmap interface */ + case VIDIOCGMBUF: + { + struct video_mbuf *vm = arg; + int i; + + DBG("VIDIOCGMBUF\n"); + memset(vm, 0, sizeof(*vm)); + vm->size = CPIA_MAX_FRAME_SIZE*FRAME_NUM; + vm->frames = FRAME_NUM; + for (i = 0; i < FRAME_NUM; i++) + vm->offsets[i] = CPIA_MAX_FRAME_SIZE * i; + break; + } + + case VIDIOCMCAPTURE: + { + struct video_mmap *vm = arg; + int video_size; + + DBG("VIDIOCMCAPTURE: %d / %d / %dx%d\n", vm->format, vm->frame, + vm->width, vm->height); + if (vm->frame<0||vm->frame>=FRAME_NUM) { + retval = -EINVAL; + break; + } + + /* set video format */ + cam->vp.palette = vm->format; + switch(vm->format) { + case VIDEO_PALETTE_GREY: + cam->vp.depth=8; + break; + case VIDEO_PALETTE_RGB555: + case VIDEO_PALETTE_RGB565: + case VIDEO_PALETTE_YUV422: + case VIDEO_PALETTE_YUYV: + case VIDEO_PALETTE_UYVY: + cam->vp.depth = 16; + break; + case VIDEO_PALETTE_RGB24: + cam->vp.depth = 24; + break; + case VIDEO_PALETTE_RGB32: + cam->vp.depth = 32; + break; + default: + retval = -EINVAL; + break; + } + if (retval) + break; + + /* set video size */ + video_size = match_videosize(vm->width, vm->height); + if (video_size < 0) { + retval = -EINVAL; + break; + } + if (video_size != cam->video_size) { + cam->video_size = video_size; + + /* video size is changing, reset the subcapture area */ + memset(&cam->vc, 0, sizeof(cam->vc)); + + set_vw_size(cam); + cam->cmd_queue |= COMMAND_SETFORMAT; + dispatch_commands(cam); + } + /* according to v4l-spec we must start streaming here */ + cam->mmap_kludge = 1; + retval = capture_frame(cam, vm); + + break; + } + + case VIDIOCSYNC: + { + int *frame = arg; + + //DBG("VIDIOCSYNC: %d\n", *frame); + + if (*frame<0 || *frame >= FRAME_NUM) { + retval = -EINVAL; + break; + } + + switch (cam->frame[*frame].state) { + case FRAME_UNUSED: + case FRAME_READY: + case FRAME_GRABBING: + DBG("sync to unused frame %d\n", *frame); + retval = -EINVAL; + break; + + case FRAME_DONE: + cam->frame[*frame].state = FRAME_UNUSED; + //DBG("VIDIOCSYNC: %d synced\n", *frame); + break; + } + if (retval == -EINTR) { + /* FIXME - xawtv does not handle this nice */ + retval = 0; + } + break; + } + + case VIDIOCGCAPTURE: + { + struct video_capture *vc = arg; + + DBG("VIDIOCGCAPTURE\n"); + + *vc = cam->vc; + + break; + } + + case VIDIOCSCAPTURE: + { + struct video_capture *vc = arg; + + DBG("VIDIOCSCAPTURE\n"); + + if (vc->decimation != 0) { /* How should this be used? */ + retval = -EINVAL; + break; + } + if (vc->flags != 0) { /* Even/odd grab not supported */ + retval = -EINVAL; + break; + } + + /* Clip to the resolution we can set for the ROI + (every 8 columns and 4 rows) */ + vc->x = vc->x & ~(__u32)7; + vc->y = vc->y & ~(__u32)3; + vc->width = vc->width & ~(__u32)7; + vc->height = vc->height & ~(__u32)3; + + if(vc->width == 0 || vc->height == 0 || + vc->x + vc->width > cam->vw.width || + vc->y + vc->height > cam->vw.height) { + retval = -EINVAL; + break; + } + + DBG("%d,%d/%dx%d\n", vc->x,vc->y,vc->width, vc->height); + + mutex_lock(&cam->param_lock); + + cam->vc.x = vc->x; + cam->vc.y = vc->y; + cam->vc.width = vc->width; + cam->vc.height = vc->height; + + set_vw_size(cam); + cam->cmd_queue |= COMMAND_SETFORMAT; + + mutex_unlock(&cam->param_lock); + + /* setformat ignored by camera during streaming, + * so stop/dispatch/start */ + dispatch_commands(cam); + break; + } + + case VIDIOCGUNIT: + { + struct video_unit *vu = arg; + + DBG("VIDIOCGUNIT\n"); + + vu->video = cam->vdev.minor; + vu->vbi = VIDEO_NO_UNIT; + vu->radio = VIDEO_NO_UNIT; + vu->audio = VIDEO_NO_UNIT; + vu->teletext = VIDEO_NO_UNIT; + + break; + } + + + /* pointless to implement overlay with this camera */ + case VIDIOCCAPTURE: + case VIDIOCGFBUF: + case VIDIOCSFBUF: + case VIDIOCKEY: + /* tuner interface - we have none */ + case VIDIOCGTUNER: + case VIDIOCSTUNER: + case VIDIOCGFREQ: + case VIDIOCSFREQ: + /* audio interface - we have none */ + case VIDIOCGAUDIO: + case VIDIOCSAUDIO: + retval = -EINVAL; + break; + default: + retval = -ENOIOCTLCMD; + break; + } + + mutex_unlock(&cam->busy_lock); + return retval; +} + +static long cpia_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + return video_usercopy(file, cmd, arg, cpia_do_ioctl); +} + + +/* FIXME */ +static int cpia_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct video_device *dev = file->private_data; + unsigned long start = vma->vm_start; + unsigned long size = vma->vm_end - vma->vm_start; + unsigned long page, pos; + struct cam_data *cam = video_get_drvdata(dev); + int retval; + + if (!cam || !cam->ops) + return -ENODEV; + + DBG("cpia_mmap: %ld\n", size); + + if (size > FRAME_NUM*CPIA_MAX_FRAME_SIZE) + return -EINVAL; + + /* make this _really_ smp-safe */ + if (mutex_lock_interruptible(&cam->busy_lock)) + return -EINTR; + + if (!cam->frame_buf) { /* we do lazy allocation */ + if ((retval = allocate_frame_buf(cam))) { + mutex_unlock(&cam->busy_lock); + return retval; + } + } + + pos = (unsigned long)(cam->frame_buf); + while (size > 0) { + page = vmalloc_to_pfn((void *)pos); + if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) { + mutex_unlock(&cam->busy_lock); + return -EAGAIN; + } + start += PAGE_SIZE; + pos += PAGE_SIZE; + if (size > PAGE_SIZE) + size -= PAGE_SIZE; + else + size = 0; + } + + DBG("cpia_mmap: %ld\n", size); + mutex_unlock(&cam->busy_lock); + + return 0; +} + +static const struct v4l2_file_operations cpia_fops = { + .owner = THIS_MODULE, + .open = cpia_open, + .release = cpia_close, + .read = cpia_read, + .mmap = cpia_mmap, + .ioctl = cpia_ioctl, +}; + +static struct video_device cpia_template = { + .name = "CPiA Camera", + .fops = &cpia_fops, + .release = video_device_release_empty, +}; + +/* initialise cam_data structure */ +static void reset_camera_struct(struct cam_data *cam) +{ + /* The following parameter values are the defaults from + * "Software Developer's Guide for CPiA Cameras". Any changes + * to the defaults are noted in comments. */ + cam->params.colourParams.brightness = 50; + cam->params.colourParams.contrast = 48; + cam->params.colourParams.saturation = 50; + cam->params.exposure.gainMode = 4; + cam->params.exposure.expMode = 2; /* AEC */ + cam->params.exposure.compMode = 1; + cam->params.exposure.centreWeight = 1; + cam->params.exposure.gain = 0; + cam->params.exposure.fineExp = 0; + cam->params.exposure.coarseExpLo = 185; + cam->params.exposure.coarseExpHi = 0; + cam->params.exposure.redComp = COMP_RED; + cam->params.exposure.green1Comp = COMP_GREEN1; + cam->params.exposure.green2Comp = COMP_GREEN2; + cam->params.exposure.blueComp = COMP_BLUE; + cam->params.colourBalance.balanceMode = 2; /* ACB */ + cam->params.colourBalance.redGain = 32; + cam->params.colourBalance.greenGain = 6; + cam->params.colourBalance.blueGain = 92; + cam->params.apcor.gain1 = 0x18; + cam->params.apcor.gain2 = 0x16; + cam->params.apcor.gain4 = 0x24; + cam->params.apcor.gain8 = 0x34; + cam->params.flickerControl.flickerMode = 0; + cam->params.flickerControl.disabled = 1; + + cam->params.flickerControl.coarseJump = + flicker_jumps[cam->mainsFreq] + [cam->params.sensorFps.baserate] + [cam->params.sensorFps.divisor]; + cam->params.flickerControl.allowableOverExposure = + -find_over_exposure(cam->params.colourParams.brightness); + cam->params.vlOffset.gain1 = 20; + cam->params.vlOffset.gain2 = 24; + cam->params.vlOffset.gain4 = 26; + cam->params.vlOffset.gain8 = 26; + cam->params.compressionParams.hysteresis = 3; + cam->params.compressionParams.threshMax = 11; + cam->params.compressionParams.smallStep = 1; + cam->params.compressionParams.largeStep = 3; + cam->params.compressionParams.decimationHysteresis = 2; + cam->params.compressionParams.frDiffStepThresh = 5; + cam->params.compressionParams.qDiffStepThresh = 3; + cam->params.compressionParams.decimationThreshMod = 2; + /* End of default values from Software Developer's Guide */ + + cam->transfer_rate = 0; + cam->exposure_status = EXPOSURE_NORMAL; + + /* Set Sensor FPS to 15fps. This seems better than 30fps + * for indoor lighting. */ + cam->params.sensorFps.divisor = 1; + cam->params.sensorFps.baserate = 1; + + cam->params.yuvThreshold.yThreshold = 6; /* From windows driver */ + cam->params.yuvThreshold.uvThreshold = 6; /* From windows driver */ + + cam->params.format.subSample = SUBSAMPLE_422; + cam->params.format.yuvOrder = YUVORDER_YUYV; + + cam->params.compression.mode = CPIA_COMPRESSION_AUTO; + cam->params.compressionTarget.frTargeting = + CPIA_COMPRESSION_TARGET_QUALITY; + cam->params.compressionTarget.targetFR = 15; /* From windows driver */ + cam->params.compressionTarget.targetQ = 5; /* From windows driver */ + + cam->params.qx3.qx3_detected = 0; + cam->params.qx3.toplight = 0; + cam->params.qx3.bottomlight = 0; + cam->params.qx3.button = 0; + cam->params.qx3.cradled = 0; + + cam->video_size = VIDEOSIZE_CIF; + + cam->vp.colour = 32768; /* 50% */ + cam->vp.hue = 32768; /* 50% */ + cam->vp.brightness = 32768; /* 50% */ + cam->vp.contrast = 32768; /* 50% */ + cam->vp.whiteness = 0; /* not used -> grayscale only */ + cam->vp.depth = 24; /* to be set by user */ + cam->vp.palette = VIDEO_PALETTE_RGB24; /* to be set by user */ + + cam->vc.x = 0; + cam->vc.y = 0; + cam->vc.width = 0; + cam->vc.height = 0; + + cam->vw.x = 0; + cam->vw.y = 0; + set_vw_size(cam); + cam->vw.chromakey = 0; + cam->vw.flags = 0; + cam->vw.clipcount = 0; + cam->vw.clips = NULL; + + cam->cmd_queue = COMMAND_NONE; + cam->first_frame = 1; + + return; +} + +/* initialize cam_data structure */ +static void init_camera_struct(struct cam_data *cam, + struct cpia_camera_ops *ops ) +{ + int i; + + /* Default everything to 0 */ + memset(cam, 0, sizeof(struct cam_data)); + + cam->ops = ops; + mutex_init(&cam->param_lock); + mutex_init(&cam->busy_lock); + + reset_camera_struct(cam); + + cam->proc_entry = NULL; + + memcpy(&cam->vdev, &cpia_template, sizeof(cpia_template)); + video_set_drvdata(&cam->vdev, cam); + + cam->curframe = 0; + for (i = 0; i < FRAME_NUM; i++) { + cam->frame[i].width = 0; + cam->frame[i].height = 0; + cam->frame[i].state = FRAME_UNUSED; + cam->frame[i].data = NULL; + } + cam->decompressed_frame.width = 0; + cam->decompressed_frame.height = 0; + cam->decompressed_frame.state = FRAME_UNUSED; + cam->decompressed_frame.data = NULL; +} + +struct cam_data *cpia_register_camera(struct cpia_camera_ops *ops, void *lowlevel) +{ + struct cam_data *camera; + + if ((camera = kmalloc(sizeof(struct cam_data), GFP_KERNEL)) == NULL) + return NULL; + + + init_camera_struct( camera, ops ); + camera->lowlevel_data = lowlevel; + + /* register v4l device */ + if (video_register_device(&camera->vdev, VFL_TYPE_GRABBER, video_nr) < 0) { + kfree(camera); + printk(KERN_DEBUG "video_register_device failed\n"); + return NULL; + } + + /* get version information from camera: open/reset/close */ + + /* open cpia */ + if (camera->ops->open(camera->lowlevel_data)) + return camera; + + /* reset the camera */ + if (reset_camera(camera) != 0) { + camera->ops->close(camera->lowlevel_data); + return camera; + } + + /* close cpia */ + camera->ops->close(camera->lowlevel_data); + +#ifdef CONFIG_PROC_FS + create_proc_cpia_cam(camera); +#endif + + printk(KERN_INFO " CPiA Version: %d.%02d (%d.%d)\n", + camera->params.version.firmwareVersion, + camera->params.version.firmwareRevision, + camera->params.version.vcVersion, + camera->params.version.vcRevision); + printk(KERN_INFO " CPiA PnP-ID: %04x:%04x:%04x\n", + camera->params.pnpID.vendor, + camera->params.pnpID.product, + camera->params.pnpID.deviceRevision); + printk(KERN_INFO " VP-Version: %d.%d %04x\n", + camera->params.vpVersion.vpVersion, + camera->params.vpVersion.vpRevision, + camera->params.vpVersion.cameraHeadID); + + return camera; +} + +void cpia_unregister_camera(struct cam_data *cam) +{ + DBG("unregistering video\n"); + video_unregister_device(&cam->vdev); + if (cam->open_count) { + put_cam(cam->ops); + DBG("camera open -- setting ops to NULL\n"); + cam->ops = NULL; + } + +#ifdef CONFIG_PROC_FS + DBG("destroying /proc/cpia/%s\n", video_device_node_name(&cam->vdev)); + destroy_proc_cpia_cam(cam); +#endif + if (!cam->open_count) { + DBG("freeing camera\n"); + kfree(cam); + } +} + +static int __init cpia_init(void) +{ + printk(KERN_INFO "%s v%d.%d.%d\n", ABOUT, + CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER); + + printk(KERN_WARNING "Since in-kernel colorspace conversion is not " + "allowed, it is disabled by default now. Users should fix the " + "applications in case they don't work without conversion " + "reenabled by setting the 'colorspace_conv' module " + "parameter to 1\n"); + +#ifdef CONFIG_PROC_FS + proc_cpia_create(); +#endif + + return 0; +} + +static void __exit cpia_exit(void) +{ +#ifdef CONFIG_PROC_FS + proc_cpia_destroy(); +#endif +} + +module_init(cpia_init); +module_exit(cpia_exit); + +/* Exported symbols for modules. */ + +EXPORT_SYMBOL(cpia_register_camera); +EXPORT_SYMBOL(cpia_unregister_camera); diff --git a/drivers/staging/cpia/cpia.h b/drivers/staging/cpia/cpia.h new file mode 100644 index 000000000000..8f0cfee4b8a1 --- /dev/null +++ b/drivers/staging/cpia/cpia.h @@ -0,0 +1,432 @@ +#ifndef cpia_h +#define cpia_h + +/* + * CPiA Parallel Port Video4Linux driver + * + * Supports CPiA based parallel port Video Camera's. + * + * (C) Copyright 1999 Bas Huisman, + * Peter Pregler, + * Scott J. Bertin, + * VLSI Vision Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; 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. + */ + +#define CPIA_MAJ_VER 1 +#define CPIA_MIN_VER 2 +#define CPIA_PATCH_VER 3 + +#define CPIA_PP_MAJ_VER CPIA_MAJ_VER +#define CPIA_PP_MIN_VER CPIA_MIN_VER +#define CPIA_PP_PATCH_VER CPIA_PATCH_VER + +#define CPIA_USB_MAJ_VER CPIA_MAJ_VER +#define CPIA_USB_MIN_VER CPIA_MIN_VER +#define CPIA_USB_PATCH_VER CPIA_PATCH_VER + +#define CPIA_MAX_FRAME_SIZE_UNALIGNED (352 * 288 * 4) /* CIF at RGB32 */ +#define CPIA_MAX_FRAME_SIZE ((CPIA_MAX_FRAME_SIZE_UNALIGNED + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)) /* align above to PAGE_SIZE */ + +#ifdef __KERNEL__ + +#include +#include +#include +#include +#include +#include + +struct cpia_camera_ops +{ + /* open sets privdata to point to structure for this camera. + * Returns negative value on error, otherwise 0. + */ + int (*open)(void *privdata); + + /* Registers callback function cb to be called with cbdata + * when an image is ready. If cb is NULL, only single image grabs + * should be used. cb should immediately call streamRead to read + * the data or data may be lost. Returns negative value on error, + * otherwise 0. + */ + int (*registerCallback)(void *privdata, void (*cb)(void *cbdata), + void *cbdata); + + /* transferCmd sends commands to the camera. command MUST point to + * an 8 byte buffer in kernel space. data can be NULL if no extra + * data is needed. The size of the data is given by the last 2 + * bytes of command. data must also point to memory in kernel space. + * Returns negative value on error, otherwise 0. + */ + int (*transferCmd)(void *privdata, u8 *command, u8 *data); + + /* streamStart initiates stream capture mode. + * Returns negative value on error, otherwise 0. + */ + int (*streamStart)(void *privdata); + + /* streamStop terminates stream capture mode. + * Returns negative value on error, otherwise 0. + */ + int (*streamStop)(void *privdata); + + /* streamRead reads a frame from the camera. buffer points to a + * buffer large enough to hold a complete frame in kernel space. + * noblock indicates if this should be a non blocking read. + * Returns the number of bytes read, or negative value on error. + */ + int (*streamRead)(void *privdata, u8 *buffer, int noblock); + + /* close disables the device until open() is called again. + * Returns negative value on error, otherwise 0. + */ + int (*close)(void *privdata); + + /* If wait_for_stream_ready is non-zero, wait until the streamState + * is STREAM_READY before calling streamRead. + */ + int wait_for_stream_ready; + + /* + * Used to maintain lowlevel module usage counts + */ + struct module *owner; +}; + +struct cpia_frame { + u8 *data; + int count; + int width; + int height; + volatile int state; +}; + +struct cam_params { + struct { + u8 firmwareVersion; + u8 firmwareRevision; + u8 vcVersion; + u8 vcRevision; + } version; + struct { + u16 vendor; + u16 product; + u16 deviceRevision; + } pnpID; + struct { + u8 vpVersion; + u8 vpRevision; + u16 cameraHeadID; + } vpVersion; + struct { + u8 systemState; + u8 grabState; + u8 streamState; + u8 fatalError; + u8 cmdError; + u8 debugFlags; + u8 vpStatus; + u8 errorCode; + } status; + struct { + u8 brightness; + u8 contrast; + u8 saturation; + } colourParams; + struct { + u8 gainMode; + u8 expMode; + u8 compMode; + u8 centreWeight; + u8 gain; + u8 fineExp; + u8 coarseExpLo; + u8 coarseExpHi; + u8 redComp; + u8 green1Comp; + u8 green2Comp; + u8 blueComp; + } exposure; + struct { + u8 balanceMode; + u8 redGain; + u8 greenGain; + u8 blueGain; + } colourBalance; + struct { + u8 divisor; + u8 baserate; + } sensorFps; + struct { + u8 gain1; + u8 gain2; + u8 gain4; + u8 gain8; + } apcor; + struct { + u8 disabled; + u8 flickerMode; + u8 coarseJump; + int allowableOverExposure; + } flickerControl; + struct { + u8 gain1; + u8 gain2; + u8 gain4; + u8 gain8; + } vlOffset; + struct { + u8 mode; + u8 decimation; + } compression; + struct { + u8 frTargeting; + u8 targetFR; + u8 targetQ; + } compressionTarget; + struct { + u8 yThreshold; + u8 uvThreshold; + } yuvThreshold; + struct { + u8 hysteresis; + u8 threshMax; + u8 smallStep; + u8 largeStep; + u8 decimationHysteresis; + u8 frDiffStepThresh; + u8 qDiffStepThresh; + u8 decimationThreshMod; + } compressionParams; + struct { + u8 videoSize; /* CIF/QCIF */ + u8 subSample; + u8 yuvOrder; + } format; + struct { /* Intel QX3 specific data */ + u8 qx3_detected; /* a QX3 is present */ + u8 toplight; /* top light lit , R/W */ + u8 bottomlight; /* bottom light lit, R/W */ + u8 button; /* snapshot button pressed (R/O) */ + u8 cradled; /* microscope is in cradle (R/O) */ + } qx3; + struct { + u8 colStart; /* skip first 8*colStart pixels */ + u8 colEnd; /* finish at 8*colEnd pixels */ + u8 rowStart; /* skip first 4*rowStart lines */ + u8 rowEnd; /* finish at 4*rowEnd lines */ + } roi; + u8 ecpTiming; + u8 streamStartLine; +}; + +enum v4l_camstates { + CPIA_V4L_IDLE = 0, + CPIA_V4L_ERROR, + CPIA_V4L_COMMAND, + CPIA_V4L_GRABBING, + CPIA_V4L_STREAMING, + CPIA_V4L_STREAMING_PAUSED, +}; + +#define FRAME_NUM 2 /* double buffering for now */ + +struct cam_data { + struct list_head cam_data_list; + + struct mutex busy_lock; /* guard against SMP multithreading */ + struct cpia_camera_ops *ops; /* lowlevel driver operations */ + void *lowlevel_data; /* private data for lowlevel driver */ + u8 *raw_image; /* buffer for raw image data */ + struct cpia_frame decompressed_frame; + /* buffer to hold decompressed frame */ + int image_size; /* sizeof last decompressed image */ + int open_count; /* # of process that have camera open */ + + /* camera status */ + int fps; /* actual fps reported by the camera */ + int transfer_rate; /* transfer rate from camera in kB/s */ + u8 mainsFreq; /* for flicker control */ + + /* proc interface */ + struct mutex param_lock; /* params lock for this camera */ + struct cam_params params; /* camera settings */ + struct proc_dir_entry *proc_entry; /* /proc/cpia/videoX */ + + /* v4l */ + int video_size; /* VIDEO_SIZE_ */ + volatile enum v4l_camstates camstate; /* v4l layer status */ + struct video_device vdev; /* v4l videodev */ + struct video_picture vp; /* v4l camera settings */ + struct video_window vw; /* v4l capture area */ + struct video_capture vc; /* v4l subcapture area */ + + /* mmap interface */ + int curframe; /* the current frame to grab into */ + u8 *frame_buf; /* frame buffer data */ + struct cpia_frame frame[FRAME_NUM]; + /* FRAME_NUM-buffering, so we need a array */ + + int first_frame; + int mmap_kludge; /* 'wrong' byte order for mmap */ + volatile u32 cmd_queue; /* queued commands */ + int exposure_status; /* EXPOSURE_* */ + int exposure_count; /* number of frames at this status */ +}; + +/* cpia_register_camera is called by low level driver for each camera. + * A unique camera number is returned, or a negative value on error */ +struct cam_data *cpia_register_camera(struct cpia_camera_ops *ops, void *lowlevel); + +/* cpia_unregister_camera is called by low level driver when a camera + * is removed. This must not fail. */ +void cpia_unregister_camera(struct cam_data *cam); + +/* raw CIF + 64 byte header + (2 bytes line_length + EOL) per line + 4*EOI + + * one byte 16bit DMA alignment + */ +#define CPIA_MAX_IMAGE_SIZE ((352*288*2)+64+(288*3)+5) + +/* constant value's */ +#define MAGIC_0 0x19 +#define MAGIC_1 0x68 +#define DATA_IN 0xC0 +#define DATA_OUT 0x40 +#define VIDEOSIZE_QCIF 0 /* 176x144 */ +#define VIDEOSIZE_CIF 1 /* 352x288 */ +#define VIDEOSIZE_SIF 2 /* 320x240 */ +#define VIDEOSIZE_QSIF 3 /* 160x120 */ +#define VIDEOSIZE_48_48 4 /* where no one has gone before, iconsize! */ +#define VIDEOSIZE_64_48 5 +#define VIDEOSIZE_128_96 6 +#define VIDEOSIZE_160_120 VIDEOSIZE_QSIF +#define VIDEOSIZE_176_144 VIDEOSIZE_QCIF +#define VIDEOSIZE_192_144 7 +#define VIDEOSIZE_224_168 8 +#define VIDEOSIZE_256_192 9 +#define VIDEOSIZE_288_216 10 +#define VIDEOSIZE_320_240 VIDEOSIZE_SIF +#define VIDEOSIZE_352_288 VIDEOSIZE_CIF +#define VIDEOSIZE_88_72 11 /* quarter CIF */ +#define SUBSAMPLE_420 0 +#define SUBSAMPLE_422 1 +#define YUVORDER_YUYV 0 +#define YUVORDER_UYVY 1 +#define NOT_COMPRESSED 0 +#define COMPRESSED 1 +#define NO_DECIMATION 0 +#define DECIMATION_ENAB 1 +#define EOI 0xff /* End Of Image */ +#define EOL 0xfd /* End Of Line */ +#define FRAME_HEADER_SIZE 64 + +/* Image grab modes */ +#define CPIA_GRAB_SINGLE 0 +#define CPIA_GRAB_CONTINUOUS 1 + +/* Compression parameters */ +#define CPIA_COMPRESSION_NONE 0 +#define CPIA_COMPRESSION_AUTO 1 +#define CPIA_COMPRESSION_MANUAL 2 +#define CPIA_COMPRESSION_TARGET_QUALITY 0 +#define CPIA_COMPRESSION_TARGET_FRAMERATE 1 + +/* Return offsets for GetCameraState */ +#define SYSTEMSTATE 0 +#define GRABSTATE 1 +#define STREAMSTATE 2 +#define FATALERROR 3 +#define CMDERROR 4 +#define DEBUGFLAGS 5 +#define VPSTATUS 6 +#define ERRORCODE 7 + +/* SystemState */ +#define UNINITIALISED_STATE 0 +#define PASS_THROUGH_STATE 1 +#define LO_POWER_STATE 2 +#define HI_POWER_STATE 3 +#define WARM_BOOT_STATE 4 + +/* GrabState */ +#define GRAB_IDLE 0 +#define GRAB_ACTIVE 1 +#define GRAB_DONE 2 + +/* StreamState */ +#define STREAM_NOT_READY 0 +#define STREAM_READY 1 +#define STREAM_OPEN 2 +#define STREAM_PAUSED 3 +#define STREAM_FINISHED 4 + +/* Fatal Error, CmdError, and DebugFlags */ +#define CPIA_FLAG 1 +#define SYSTEM_FLAG 2 +#define INT_CTRL_FLAG 4 +#define PROCESS_FLAG 8 +#define COM_FLAG 16 +#define VP_CTRL_FLAG 32 +#define CAPTURE_FLAG 64 +#define DEBUG_FLAG 128 + +/* VPStatus */ +#define VP_STATE_OK 0x00 + +#define VP_STATE_FAILED_VIDEOINIT 0x01 +#define VP_STATE_FAILED_AECACBINIT 0x02 +#define VP_STATE_AEC_MAX 0x04 +#define VP_STATE_ACB_BMAX 0x08 + +#define VP_STATE_ACB_RMIN 0x10 +#define VP_STATE_ACB_GMIN 0x20 +#define VP_STATE_ACB_RMAX 0x40 +#define VP_STATE_ACB_GMAX 0x80 + +/* default (minimum) compensation values */ +#define COMP_RED 220 +#define COMP_GREEN1 214 +#define COMP_GREEN2 COMP_GREEN1 +#define COMP_BLUE 230 + +/* exposure status */ +#define EXPOSURE_VERY_LIGHT 0 +#define EXPOSURE_LIGHT 1 +#define EXPOSURE_NORMAL 2 +#define EXPOSURE_DARK 3 +#define EXPOSURE_VERY_DARK 4 + +/* ErrorCode */ +#define ERROR_FLICKER_BELOW_MIN_EXP 0x01 /*flicker exposure got below minimum exposure */ +#define ALOG(fmt,args...) printk(fmt, ##args) +#define LOG(fmt,args...) ALOG(KERN_INFO __FILE__ ":%s(%d):" fmt, __func__ , __LINE__ , ##args) + +#ifdef _CPIA_DEBUG_ +#define ADBG(fmt,args...) printk(fmt, jiffies, ##args) +#define DBG(fmt,args...) ADBG(KERN_DEBUG __FILE__" (%ld):%s(%d):" fmt, __func__, __LINE__ , ##args) +#else +#define DBG(fmn,args...) do {} while(0) +#endif + +#define DEB_BYTE(p)\ + DBG("%1d %1d %1d %1d %1d %1d %1d %1d \n",\ + (p)&0x80?1:0, (p)&0x40?1:0, (p)&0x20?1:0, (p)&0x10?1:0,\ + (p)&0x08?1:0, (p)&0x04?1:0, (p)&0x02?1:0, (p)&0x01?1:0); + +#endif /* __KERNEL__ */ + +#endif /* cpia_h */ diff --git a/drivers/staging/cpia/cpia_pp.c b/drivers/staging/cpia/cpia_pp.c new file mode 100644 index 000000000000..f5604c16a092 --- /dev/null +++ b/drivers/staging/cpia/cpia_pp.c @@ -0,0 +1,869 @@ +/* + * cpia_pp CPiA Parallel Port driver + * + * Supports CPiA based parallel port Video Camera's. + * + * (C) Copyright 1999 Bas Huisman + * (C) Copyright 1999-2000 Scott J. Bertin , + * (C) Copyright 1999-2000 Peter Pregler + * + * 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. + */ + +/* define _CPIA_DEBUG_ for verbose debug output (see cpia.h) */ +/* #define _CPIA_DEBUG_ 1 */ + + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +/* #define _CPIA_DEBUG_ define for verbose debug output */ +#include "cpia.h" + +static int cpia_pp_open(void *privdata); +static int cpia_pp_registerCallback(void *privdata, void (*cb) (void *cbdata), + void *cbdata); +static int cpia_pp_transferCmd(void *privdata, u8 *command, u8 *data); +static int cpia_pp_streamStart(void *privdata); +static int cpia_pp_streamStop(void *privdata); +static int cpia_pp_streamRead(void *privdata, u8 *buffer, int noblock); +static int cpia_pp_close(void *privdata); + + +#define ABOUT "Parallel port driver for Vision CPiA based cameras" + +#define PACKET_LENGTH 8 + +/* Magic numbers for defining port-device mappings */ +#define PPCPIA_PARPORT_UNSPEC -4 +#define PPCPIA_PARPORT_AUTO -3 +#define PPCPIA_PARPORT_OFF -2 +#define PPCPIA_PARPORT_NONE -1 + +static int parport_nr[PARPORT_MAX] = {[0 ... PARPORT_MAX - 1] = PPCPIA_PARPORT_UNSPEC}; +static char *parport[PARPORT_MAX] = {NULL,}; + +MODULE_AUTHOR("B. Huisman & Peter Pregler "); +MODULE_DESCRIPTION("Parallel port driver for Vision CPiA based cameras"); +MODULE_LICENSE("GPL"); + +module_param_array(parport, charp, NULL, 0); +MODULE_PARM_DESC(parport, "'auto' or a list of parallel port numbers. Just like lp."); + +struct pp_cam_entry { + struct pardevice *pdev; + struct parport *port; + struct work_struct cb_task; + void (*cb_func)(void *cbdata); + void *cb_data; + int open_count; + wait_queue_head_t wq_stream; + /* image state flags */ + int image_ready; /* we got an interrupt */ + int image_complete; /* we have seen 4 EOI */ + + int streaming; /* we are in streaming mode */ + int stream_irq; +}; + +static struct cpia_camera_ops cpia_pp_ops = +{ + cpia_pp_open, + cpia_pp_registerCallback, + cpia_pp_transferCmd, + cpia_pp_streamStart, + cpia_pp_streamStop, + cpia_pp_streamRead, + cpia_pp_close, + 1, + THIS_MODULE +}; + +static LIST_HEAD(cam_list); +static spinlock_t cam_list_lock_pp; + +/* FIXME */ +static void cpia_parport_enable_irq( struct parport *port ) { + parport_enable_irq(port); + mdelay(10); + return; +} + +static void cpia_parport_disable_irq( struct parport *port ) { + parport_disable_irq(port); + mdelay(10); + return; +} + +/* Special CPiA PPC modes: These are invoked by using the 1284 Extensibility + * Link Flag during negotiation */ +#define UPLOAD_FLAG 0x08 +#define NIBBLE_TRANSFER 0x01 +#define ECP_TRANSFER 0x03 + +#define PARPORT_CHUNK_SIZE PAGE_SIZE + + +static void cpia_pp_run_callback(struct work_struct *work) +{ + void (*cb_func)(void *cbdata); + void *cb_data; + struct pp_cam_entry *cam; + + cam = container_of(work, struct pp_cam_entry, cb_task); + cb_func = cam->cb_func; + cb_data = cam->cb_data; + + cb_func(cb_data); +} + +/**************************************************************************** + * + * CPiA-specific low-level parport functions for nibble uploads + * + ***************************************************************************/ +/* CPiA nonstandard "Nibble" mode (no nDataAvail signal after each byte). */ +/* The standard kernel parport_ieee1284_read_nibble() fails with the CPiA... */ + +static size_t cpia_read_nibble (struct parport *port, + void *buffer, size_t len, + int flags) +{ + /* adapted verbatim, with one change, from + parport_ieee1284_read_nibble() in drivers/parport/ieee1284-ops.c */ + + unsigned char *buf = buffer; + int i; + unsigned char byte = 0; + + len *= 2; /* in nibbles */ + for (i=0; i < len; i++) { + unsigned char nibble; + + /* The CPiA firmware suppresses the use of nDataAvail (nFault LO) + * after every second nibble to signal that more + * data is available. (the total number of Bytes that + * should be sent is known; if too few are received, an error + * will be recorded after a timeout). + * This is incompatible with parport_ieee1284_read_nibble(), + * which expects to find nFault LO after every second nibble. + */ + + /* Solution: modify cpia_read_nibble to only check for + * nDataAvail before the first nibble is sent. + */ + + /* Does the error line indicate end of data? */ + if (((i /*& 1*/) == 0) && + (parport_read_status(port) & PARPORT_STATUS_ERROR)) { + DBG("%s: No more nibble data (%d bytes)\n", + port->name, i/2); + goto end_of_data; + } + + /* Event 7: Set nAutoFd low. */ + parport_frob_control (port, + PARPORT_CONTROL_AUTOFD, + PARPORT_CONTROL_AUTOFD); + + /* Event 9: nAck goes low. */ + port->ieee1284.phase = IEEE1284_PH_REV_DATA; + if (parport_wait_peripheral (port, + PARPORT_STATUS_ACK, 0)) { + /* Timeout -- no more data? */ + DBG("%s: Nibble timeout at event 9 (%d bytes)\n", + port->name, i/2); + parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0); + break; + } + + + /* Read a nibble. */ + nibble = parport_read_status (port) >> 3; + nibble &= ~8; + if ((nibble & 0x10) == 0) + nibble |= 8; + nibble &= 0xf; + + /* Event 10: Set nAutoFd high. */ + parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0); + + /* Event 11: nAck goes high. */ + if (parport_wait_peripheral (port, + PARPORT_STATUS_ACK, + PARPORT_STATUS_ACK)) { + /* Timeout -- no more data? */ + DBG("%s: Nibble timeout at event 11\n", + port->name); + break; + } + + if (i & 1) { + /* Second nibble */ + byte |= nibble << 4; + *buf++ = byte; + } else + byte = nibble; + } + + if (i == len) { + /* Read the last nibble without checking data avail. */ + if (parport_read_status (port) & PARPORT_STATUS_ERROR) { + end_of_data: + /* Go to reverse idle phase. */ + parport_frob_control (port, + PARPORT_CONTROL_AUTOFD, + PARPORT_CONTROL_AUTOFD); + port->physport->ieee1284.phase = IEEE1284_PH_REV_IDLE; + } + else + port->physport->ieee1284.phase = IEEE1284_PH_HBUSY_DAVAIL; + } + + return i/2; +} + +/* CPiA nonstandard "Nibble Stream" mode (2 nibbles per cycle, instead of 1) + * (See CPiA Data sheet p. 31) + * + * "Nibble Stream" mode used by CPiA for uploads to non-ECP ports is a + * nonstandard variant of nibble mode which allows the same (mediocre) + * data flow of 8 bits per cycle as software-enabled ECP by TRISTATE-capable + * parallel ports, but works also for non-TRISTATE-capable ports. + * (Standard nibble mode only send 4 bits per cycle) + * + */ + +static size_t cpia_read_nibble_stream(struct parport *port, + void *buffer, size_t len, + int flags) +{ + int i; + unsigned char *buf = buffer; + int endseen = 0; + + for (i=0; i < len; i++) { + unsigned char nibble[2], byte = 0; + int j; + + /* Image Data is complete when 4 consecutive EOI bytes (0xff) are seen */ + if (endseen > 3 ) + break; + + /* Event 7: Set nAutoFd low. */ + parport_frob_control (port, + PARPORT_CONTROL_AUTOFD, + PARPORT_CONTROL_AUTOFD); + + /* Event 9: nAck goes low. */ + port->ieee1284.phase = IEEE1284_PH_REV_DATA; + if (parport_wait_peripheral (port, + PARPORT_STATUS_ACK, 0)) { + /* Timeout -- no more data? */ + DBG("%s: Nibble timeout at event 9 (%d bytes)\n", + port->name, i/2); + parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0); + break; + } + + /* Read lower nibble */ + nibble[0] = parport_read_status (port) >>3; + + /* Event 10: Set nAutoFd high. */ + parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0); + + /* Event 11: nAck goes high. */ + if (parport_wait_peripheral (port, + PARPORT_STATUS_ACK, + PARPORT_STATUS_ACK)) { + /* Timeout -- no more data? */ + DBG("%s: Nibble timeout at event 11\n", + port->name); + break; + } + + /* Read upper nibble */ + nibble[1] = parport_read_status (port) >>3; + + /* reassemble the byte */ + for (j = 0; j < 2 ; j++ ) { + nibble[j] &= ~8; + if ((nibble[j] & 0x10) == 0) + nibble[j] |= 8; + nibble[j] &= 0xf; + } + byte = (nibble[0] |(nibble[1] << 4)); + *buf++ = byte; + + if(byte == EOI) + endseen++; + else + endseen = 0; + } + return i; +} + +/**************************************************************************** + * + * EndTransferMode + * + ***************************************************************************/ +static void EndTransferMode(struct pp_cam_entry *cam) +{ + parport_negotiate(cam->port, IEEE1284_MODE_COMPAT); +} + +/**************************************************************************** + * + * ForwardSetup + * + ***************************************************************************/ +static int ForwardSetup(struct pp_cam_entry *cam) +{ + int retry; + + /* The CPiA uses ECP protocol for Downloads from the Host to the camera. + * This will be software-emulated if ECP hardware is not present + */ + + /* the usual camera maximum response time is 10ms, but after receiving + * some commands, it needs up to 40ms. (Data Sheet p. 32)*/ + + for(retry = 0; retry < 4; ++retry) { + if(!parport_negotiate(cam->port, IEEE1284_MODE_ECP)) { + break; + } + mdelay(10); + } + if(retry == 4) { + DBG("Unable to negotiate IEEE1284 ECP Download mode\n"); + return -1; + } + return 0; +} +/**************************************************************************** + * + * ReverseSetup + * + ***************************************************************************/ +static int ReverseSetup(struct pp_cam_entry *cam, int extensibility) +{ + int retry; + int upload_mode, mode = IEEE1284_MODE_ECP; + int transfer_mode = ECP_TRANSFER; + + if (!(cam->port->modes & PARPORT_MODE_ECP) && + !(cam->port->modes & PARPORT_MODE_TRISTATE)) { + mode = IEEE1284_MODE_NIBBLE; + transfer_mode = NIBBLE_TRANSFER; + } + + upload_mode = mode; + if(extensibility) mode = UPLOAD_FLAG|transfer_mode|IEEE1284_EXT_LINK; + + /* the usual camera maximum response time is 10ms, but after + * receiving some commands, it needs up to 40ms. */ + + for(retry = 0; retry < 4; ++retry) { + if(!parport_negotiate(cam->port, mode)) { + break; + } + mdelay(10); + } + if(retry == 4) { + if(extensibility) + DBG("Unable to negotiate upload extensibility mode\n"); + else + DBG("Unable to negotiate upload mode\n"); + return -1; + } + if(extensibility) cam->port->ieee1284.mode = upload_mode; + return 0; +} + +/**************************************************************************** + * + * WritePacket + * + ***************************************************************************/ +static int WritePacket(struct pp_cam_entry *cam, const u8 *packet, size_t size) +{ + int retval=0; + int size_written; + + if (packet == NULL) { + return -EINVAL; + } + if (ForwardSetup(cam)) { + DBG("Write failed in setup\n"); + return -EIO; + } + size_written = parport_write(cam->port, packet, size); + if(size_written != size) { + DBG("Write failed, wrote %d/%d\n", size_written, size); + retval = -EIO; + } + EndTransferMode(cam); + return retval; +} + +/**************************************************************************** + * + * ReadPacket + * + ***************************************************************************/ +static int ReadPacket(struct pp_cam_entry *cam, u8 *packet, size_t size) +{ + int retval=0; + + if (packet == NULL) { + return -EINVAL; + } + if (ReverseSetup(cam, 0)) { + return -EIO; + } + + /* support for CPiA variant nibble reads */ + if(cam->port->ieee1284.mode == IEEE1284_MODE_NIBBLE) { + if(cpia_read_nibble(cam->port, packet, size, 0) != size) + retval = -EIO; + } else { + if(parport_read(cam->port, packet, size) != size) + retval = -EIO; + } + EndTransferMode(cam); + return retval; +} + +/**************************************************************************** + * + * cpia_pp_streamStart + * + ***************************************************************************/ +static int cpia_pp_streamStart(void *privdata) +{ + struct pp_cam_entry *cam = privdata; + DBG("\n"); + cam->streaming=1; + cam->image_ready=0; + //if (ReverseSetup(cam,1)) return -EIO; + if(cam->stream_irq) cpia_parport_enable_irq(cam->port); + return 0; +} + +/**************************************************************************** + * + * cpia_pp_streamStop + * + ***************************************************************************/ +static int cpia_pp_streamStop(void *privdata) +{ + struct pp_cam_entry *cam = privdata; + + DBG("\n"); + cam->streaming=0; + cpia_parport_disable_irq(cam->port); + //EndTransferMode(cam); + + return 0; +} + +/**************************************************************************** + * + * cpia_pp_streamRead + * + ***************************************************************************/ +static int cpia_pp_read(struct parport *port, u8 *buffer, int len) +{ + int bytes_read; + + /* support for CPiA variant "nibble stream" reads */ + if(port->ieee1284.mode == IEEE1284_MODE_NIBBLE) + bytes_read = cpia_read_nibble_stream(port,buffer,len,0); + else { + int new_bytes; + for(bytes_read=0; bytes_readstreaming) DBG("%d / %d\n", cam->image_ready, noblock); + if( cam->stream_irq ) { + DBG("%d\n", cam->image_ready); + cam->image_ready--; + } + cam->image_complete=0; + if (0/*cam->streaming*/) { + if(!cam->image_ready) { + if(noblock) return -EWOULDBLOCK; + interruptible_sleep_on(&cam->wq_stream); + if( signal_pending(current) ) return -EINTR; + DBG("%d\n", cam->image_ready); + } + } else { + if (ReverseSetup(cam, 1)) { + DBG("unable to ReverseSetup\n"); + return -EIO; + } + } + endseen = 0; + block_size = PARPORT_CHUNK_SIZE; + while( !cam->image_complete ) { + cond_resched(); + + new_bytes = cpia_pp_read(cam->port, buffer, block_size ); + if( new_bytes <= 0 ) { + break; + } + i=-1; + while(++iimage_complete=1; + break; + } + if( CPIA_MAX_IMAGE_SIZE-read_bytes <= PARPORT_CHUNK_SIZE ) { + block_size=CPIA_MAX_IMAGE_SIZE-read_bytes; + } + } + EndTransferMode(cam); + return cam->image_complete ? read_bytes : -EIO; +} +/**************************************************************************** + * + * cpia_pp_transferCmd + * + ***************************************************************************/ +static int cpia_pp_transferCmd(void *privdata, u8 *command, u8 *data) +{ + int err; + int retval=0; + int databytes; + struct pp_cam_entry *cam = privdata; + + if(cam == NULL) { + DBG("Internal driver error: cam is NULL\n"); + return -EINVAL; + } + if(command == NULL) { + DBG("Internal driver error: command is NULL\n"); + return -EINVAL; + } + databytes = (((int)command[7])<<8) | command[6]; + if ((err = WritePacket(cam, command, PACKET_LENGTH)) < 0) { + DBG("Error writing command\n"); + return err; + } + if(command[0] == DATA_IN) { + u8 buffer[8]; + if(data == NULL) { + DBG("Internal driver error: data is NULL\n"); + return -EINVAL; + } + if((err = ReadPacket(cam, buffer, 8)) < 0) { + DBG("Error reading command result\n"); + return err; + } + memcpy(data, buffer, databytes); + } else if(command[0] == DATA_OUT) { + if(databytes > 0) { + if(data == NULL) { + DBG("Internal driver error: data is NULL\n"); + retval = -EINVAL; + } else { + if((err=WritePacket(cam, data, databytes)) < 0){ + DBG("Error writing command data\n"); + return err; + } + } + } + } else { + DBG("Unexpected first byte of command: %x\n", command[0]); + retval = -EINVAL; + } + return retval; +} + +/**************************************************************************** + * + * cpia_pp_open + * + ***************************************************************************/ +static int cpia_pp_open(void *privdata) +{ + struct pp_cam_entry *cam = (struct pp_cam_entry *)privdata; + + if (cam == NULL) + return -EINVAL; + + if(cam->open_count == 0) { + if (parport_claim(cam->pdev)) { + DBG("failed to claim the port\n"); + return -EBUSY; + } + parport_negotiate(cam->port, IEEE1284_MODE_COMPAT); + parport_data_forward(cam->port); + parport_write_control(cam->port, PARPORT_CONTROL_SELECT); + udelay(50); + parport_write_control(cam->port, + PARPORT_CONTROL_SELECT + | PARPORT_CONTROL_INIT); + } + + ++cam->open_count; + + return 0; +} + +/**************************************************************************** + * + * cpia_pp_registerCallback + * + ***************************************************************************/ +static int cpia_pp_registerCallback(void *privdata, void (*cb)(void *cbdata), void *cbdata) +{ + struct pp_cam_entry *cam = privdata; + int retval = 0; + + if(cam->port->irq != PARPORT_IRQ_NONE) { + cam->cb_func = cb; + cam->cb_data = cbdata; + INIT_WORK(&cam->cb_task, cpia_pp_run_callback); + } else { + retval = -1; + } + return retval; +} + +/**************************************************************************** + * + * cpia_pp_close + * + ***************************************************************************/ +static int cpia_pp_close(void *privdata) +{ + struct pp_cam_entry *cam = privdata; + if (--cam->open_count == 0) { + parport_release(cam->pdev); + } + return 0; +} + +/**************************************************************************** + * + * cpia_pp_register + * + ***************************************************************************/ +static int cpia_pp_register(struct parport *port) +{ + struct pardevice *pdev = NULL; + struct pp_cam_entry *cam; + struct cam_data *cpia; + + if (!(port->modes & PARPORT_MODE_PCSPP)) { + LOG("port is not supported by CPiA driver\n"); + return -ENXIO; + } + + cam = kzalloc(sizeof(struct pp_cam_entry), GFP_KERNEL); + if (cam == NULL) { + LOG("failed to allocate camera structure\n"); + return -ENOMEM; + } + + pdev = parport_register_device(port, "cpia_pp", NULL, NULL, + NULL, 0, cam); + + if (!pdev) { + LOG("failed to parport_register_device\n"); + kfree(cam); + return -ENXIO; + } + + cam->pdev = pdev; + cam->port = port; + init_waitqueue_head(&cam->wq_stream); + + cam->streaming = 0; + cam->stream_irq = 0; + + if((cpia = cpia_register_camera(&cpia_pp_ops, cam)) == NULL) { + LOG("failed to cpia_register_camera\n"); + parport_unregister_device(pdev); + kfree(cam); + return -ENXIO; + } + spin_lock( &cam_list_lock_pp ); + list_add( &cpia->cam_data_list, &cam_list ); + spin_unlock( &cam_list_lock_pp ); + + return 0; +} + +static void cpia_pp_detach (struct parport *port) +{ + struct list_head *tmp; + struct cam_data *cpia = NULL; + struct pp_cam_entry *cam; + + spin_lock( &cam_list_lock_pp ); + list_for_each (tmp, &cam_list) { + cpia = list_entry(tmp, struct cam_data, cam_data_list); + cam = (struct pp_cam_entry *) cpia->lowlevel_data; + if (cam && cam->port->number == port->number) { + list_del(&cpia->cam_data_list); + break; + } + cpia = NULL; + } + spin_unlock( &cam_list_lock_pp ); + + if (!cpia) { + DBG("cpia_pp_detach failed to find cam_data in cam_list\n"); + return; + } + + cam = (struct pp_cam_entry *) cpia->lowlevel_data; + cpia_unregister_camera(cpia); + if(cam->open_count > 0) + cpia_pp_close(cam); + parport_unregister_device(cam->pdev); + cpia->lowlevel_data = NULL; + kfree(cam); +} + +static void cpia_pp_attach (struct parport *port) +{ + unsigned int i; + + switch (parport_nr[0]) + { + case PPCPIA_PARPORT_UNSPEC: + case PPCPIA_PARPORT_AUTO: + if (port->probe_info[0].class != PARPORT_CLASS_MEDIA || + port->probe_info[0].cmdset == NULL || + strncmp(port->probe_info[0].cmdset, "CPIA_1", 6) != 0) + return; + + cpia_pp_register(port); + + break; + + default: + for (i = 0; i < PARPORT_MAX; ++i) { + if (port->number == parport_nr[i]) { + cpia_pp_register(port); + break; + } + } + break; + } +} + +static struct parport_driver cpia_pp_driver = { + .name = "cpia_pp", + .attach = cpia_pp_attach, + .detach = cpia_pp_detach, +}; + +static int __init cpia_pp_init(void) +{ + printk(KERN_INFO "%s v%d.%d.%d\n",ABOUT, + CPIA_PP_MAJ_VER,CPIA_PP_MIN_VER,CPIA_PP_PATCH_VER); + + if(parport_nr[0] == PPCPIA_PARPORT_OFF) { + printk(" disabled\n"); + return 0; + } + + spin_lock_init( &cam_list_lock_pp ); + + if (parport_register_driver (&cpia_pp_driver)) { + LOG ("unable to register with parport\n"); + return -EIO; + } + return 0; +} + +static int __init cpia_init(void) +{ + if (parport[0]) { + /* The user gave some parameters. Let's see what they were. */ + if (!strncmp(parport[0], "auto", 4)) { + parport_nr[0] = PPCPIA_PARPORT_AUTO; + } else { + int n; + for (n = 0; n < PARPORT_MAX && parport[n]; n++) { + if (!strncmp(parport[n], "none", 4)) { + parport_nr[n] = PPCPIA_PARPORT_NONE; + } else { + char *ep; + unsigned long r = simple_strtoul(parport[n], &ep, 0); + if (ep != parport[n]) { + parport_nr[n] = r; + } else { + LOG("bad port specifier `%s'\n", parport[n]); + return -ENODEV; + } + } + } + } + } + return cpia_pp_init(); +} + +static void __exit cpia_cleanup(void) +{ + parport_unregister_driver(&cpia_pp_driver); + return; +} + +module_init(cpia_init); +module_exit(cpia_cleanup); diff --git a/drivers/staging/cpia/cpia_usb.c b/drivers/staging/cpia/cpia_usb.c new file mode 100644 index 000000000000..58d193ff591c --- /dev/null +++ b/drivers/staging/cpia/cpia_usb.c @@ -0,0 +1,640 @@ +/* + * cpia_usb CPiA USB driver + * + * Supports CPiA based parallel port Video Camera's. + * + * Copyright (C) 1999 Jochen Scharrlach + * Copyright (C) 1999, 2000 Johannes Erdfelt + * + * 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. + */ + +/* define _CPIA_DEBUG_ for verbose debug output (see cpia.h) */ +/* #define _CPIA_DEBUG_ 1 */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cpia.h" + +#define USB_REQ_CPIA_GRAB_FRAME 0xC1 +#define USB_REQ_CPIA_UPLOAD_FRAME 0xC2 +#define WAIT_FOR_NEXT_FRAME 0 +#define FORCE_FRAME_UPLOAD 1 + +#define FRAMES_PER_DESC 10 +#define FRAME_SIZE_PER_DESC 960 /* Shouldn't be hardcoded */ +#define CPIA_NUMSBUF 2 +#define STREAM_BUF_SIZE (PAGE_SIZE * 4) +#define SCRATCH_BUF_SIZE (STREAM_BUF_SIZE * 2) + +struct cpia_sbuf { + char *data; + struct urb *urb; +}; + +#define FRAMEBUF_LEN (CPIA_MAX_FRAME_SIZE+100) +enum framebuf_status { + FRAME_EMPTY, + FRAME_READING, + FRAME_READY, + FRAME_ERROR, +}; + +struct framebuf { + int length; + enum framebuf_status status; + u8 data[FRAMEBUF_LEN]; + struct framebuf *next; +}; + +struct usb_cpia { + /* Device structure */ + struct usb_device *dev; + + unsigned char iface; + wait_queue_head_t wq_stream; + + int cursbuf; /* Current receiving sbuf */ + struct cpia_sbuf sbuf[CPIA_NUMSBUF]; /* Double buffering */ + + int streaming; + int open; + int present; + struct framebuf *buffers[3]; + struct framebuf *curbuff, *workbuff; +}; + +static int cpia_usb_open(void *privdata); +static int cpia_usb_registerCallback(void *privdata, void (*cb) (void *cbdata), + void *cbdata); +static int cpia_usb_transferCmd(void *privdata, u8 *command, u8 *data); +static int cpia_usb_streamStart(void *privdata); +static int cpia_usb_streamStop(void *privdata); +static int cpia_usb_streamRead(void *privdata, u8 *frame, int noblock); +static int cpia_usb_close(void *privdata); + +#define ABOUT "USB driver for Vision CPiA based cameras" + +static struct cpia_camera_ops cpia_usb_ops = { + cpia_usb_open, + cpia_usb_registerCallback, + cpia_usb_transferCmd, + cpia_usb_streamStart, + cpia_usb_streamStop, + cpia_usb_streamRead, + cpia_usb_close, + 0, + THIS_MODULE +}; + +static LIST_HEAD(cam_list); +static spinlock_t cam_list_lock_usb; + +static void cpia_usb_complete(struct urb *urb) +{ + int i; + char *cdata; + struct usb_cpia *ucpia; + + if (!urb || !urb->context) + return; + + ucpia = (struct usb_cpia *) urb->context; + + if (!ucpia->dev || !ucpia->streaming || !ucpia->present || !ucpia->open) + return; + + if (ucpia->workbuff->status == FRAME_EMPTY) { + ucpia->workbuff->status = FRAME_READING; + ucpia->workbuff->length = 0; + } + + for (i = 0; i < urb->number_of_packets; i++) { + int n = urb->iso_frame_desc[i].actual_length; + int st = urb->iso_frame_desc[i].status; + + cdata = urb->transfer_buffer + urb->iso_frame_desc[i].offset; + + if (st) + printk(KERN_DEBUG "cpia data error: [%d] len=%d, status=%X\n", i, n, st); + + if (FRAMEBUF_LEN < ucpia->workbuff->length + n) { + printk(KERN_DEBUG "cpia: scratch buf overflow!scr_len: %d, n: %d\n", ucpia->workbuff->length, n); + return; + } + + if (n) { + if ((ucpia->workbuff->length > 0) || + (0x19 == cdata[0] && 0x68 == cdata[1])) { + memcpy(ucpia->workbuff->data + ucpia->workbuff->length, cdata, n); + ucpia->workbuff->length += n; + } else + DBG("Ignoring packet!\n"); + } else { + if (ucpia->workbuff->length > 4 && + 0xff == ucpia->workbuff->data[ucpia->workbuff->length-1] && + 0xff == ucpia->workbuff->data[ucpia->workbuff->length-2] && + 0xff == ucpia->workbuff->data[ucpia->workbuff->length-3] && + 0xff == ucpia->workbuff->data[ucpia->workbuff->length-4]) { + ucpia->workbuff->status = FRAME_READY; + ucpia->curbuff = ucpia->workbuff; + ucpia->workbuff = ucpia->workbuff->next; + ucpia->workbuff->status = FRAME_EMPTY; + ucpia->workbuff->length = 0; + + if (waitqueue_active(&ucpia->wq_stream)) + wake_up_interruptible(&ucpia->wq_stream); + } + } + } + + /* resubmit */ + urb->dev = ucpia->dev; + if ((i = usb_submit_urb(urb, GFP_ATOMIC)) != 0) + printk(KERN_ERR "%s: usb_submit_urb ret %d\n", __func__, i); +} + +static int cpia_usb_open(void *privdata) +{ + struct usb_cpia *ucpia = (struct usb_cpia *) privdata; + struct urb *urb; + int ret, retval = 0, fx, err; + + if (!ucpia) + return -EINVAL; + + ucpia->sbuf[0].data = kmalloc(FRAMES_PER_DESC * FRAME_SIZE_PER_DESC, GFP_KERNEL); + if (!ucpia->sbuf[0].data) + return -EINVAL; + + ucpia->sbuf[1].data = kmalloc(FRAMES_PER_DESC * FRAME_SIZE_PER_DESC, GFP_KERNEL); + if (!ucpia->sbuf[1].data) { + retval = -EINVAL; + goto error_0; + } + + ret = usb_set_interface(ucpia->dev, ucpia->iface, 3); + if (ret < 0) { + printk(KERN_ERR "cpia_usb_open: usb_set_interface error (ret = %d)\n", ret); + retval = -EBUSY; + goto error_1; + } + + ucpia->buffers[0]->status = FRAME_EMPTY; + ucpia->buffers[0]->length = 0; + ucpia->buffers[1]->status = FRAME_EMPTY; + ucpia->buffers[1]->length = 0; + ucpia->buffers[2]->status = FRAME_EMPTY; + ucpia->buffers[2]->length = 0; + ucpia->curbuff = ucpia->buffers[0]; + ucpia->workbuff = ucpia->buffers[1]; + + /* We double buffer the Iso lists, and also know the polling + * interval is every frame (1 == (1 << (bInterval -1))). + */ + urb = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL); + if (!urb) { + printk(KERN_ERR "cpia_init_isoc: usb_alloc_urb 0\n"); + retval = -ENOMEM; + goto error_1; + } + + ucpia->sbuf[0].urb = urb; + urb->dev = ucpia->dev; + urb->context = ucpia; + urb->pipe = usb_rcvisocpipe(ucpia->dev, 1); + urb->transfer_flags = URB_ISO_ASAP; + urb->transfer_buffer = ucpia->sbuf[0].data; + urb->complete = cpia_usb_complete; + urb->number_of_packets = FRAMES_PER_DESC; + urb->interval = 1; + urb->transfer_buffer_length = FRAME_SIZE_PER_DESC * FRAMES_PER_DESC; + for (fx = 0; fx < FRAMES_PER_DESC; fx++) { + urb->iso_frame_desc[fx].offset = FRAME_SIZE_PER_DESC * fx; + urb->iso_frame_desc[fx].length = FRAME_SIZE_PER_DESC; + } + + urb = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL); + if (!urb) { + printk(KERN_ERR "cpia_init_isoc: usb_alloc_urb 1\n"); + retval = -ENOMEM; + goto error_urb0; + } + + ucpia->sbuf[1].urb = urb; + urb->dev = ucpia->dev; + urb->context = ucpia; + urb->pipe = usb_rcvisocpipe(ucpia->dev, 1); + urb->transfer_flags = URB_ISO_ASAP; + urb->transfer_buffer = ucpia->sbuf[1].data; + urb->complete = cpia_usb_complete; + urb->number_of_packets = FRAMES_PER_DESC; + urb->interval = 1; + urb->transfer_buffer_length = FRAME_SIZE_PER_DESC * FRAMES_PER_DESC; + for (fx = 0; fx < FRAMES_PER_DESC; fx++) { + urb->iso_frame_desc[fx].offset = FRAME_SIZE_PER_DESC * fx; + urb->iso_frame_desc[fx].length = FRAME_SIZE_PER_DESC; + } + + /* queue the ISO urbs, and resubmit in the completion handler */ + err = usb_submit_urb(ucpia->sbuf[0].urb, GFP_KERNEL); + if (err) { + printk(KERN_ERR "cpia_init_isoc: usb_submit_urb 0 ret %d\n", + err); + goto error_urb1; + } + err = usb_submit_urb(ucpia->sbuf[1].urb, GFP_KERNEL); + if (err) { + printk(KERN_ERR "cpia_init_isoc: usb_submit_urb 1 ret %d\n", + err); + goto error_urb1; + } + + ucpia->streaming = 1; + ucpia->open = 1; + + return 0; + +error_urb1: /* free urb 1 */ + usb_free_urb(ucpia->sbuf[1].urb); + ucpia->sbuf[1].urb = NULL; +error_urb0: /* free urb 0 */ + usb_free_urb(ucpia->sbuf[0].urb); + ucpia->sbuf[0].urb = NULL; +error_1: + kfree (ucpia->sbuf[1].data); + ucpia->sbuf[1].data = NULL; +error_0: + kfree (ucpia->sbuf[0].data); + ucpia->sbuf[0].data = NULL; + + return retval; +} + +// +// convenience functions +// + +/**************************************************************************** + * + * WritePacket + * + ***************************************************************************/ +static int WritePacket(struct usb_device *udev, const u8 *packet, u8 *buf, size_t size) +{ + if (!packet) + return -EINVAL; + + return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + packet[1] + (packet[0] << 8), + USB_TYPE_VENDOR | USB_RECIP_DEVICE, + packet[2] + (packet[3] << 8), + packet[4] + (packet[5] << 8), buf, size, 1000); +} + +/**************************************************************************** + * + * ReadPacket + * + ***************************************************************************/ +static int ReadPacket(struct usb_device *udev, u8 *packet, u8 *buf, size_t size) +{ + if (!packet || size <= 0) + return -EINVAL; + + return usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + packet[1] + (packet[0] << 8), + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + packet[2] + (packet[3] << 8), + packet[4] + (packet[5] << 8), buf, size, 1000); +} + +static int cpia_usb_transferCmd(void *privdata, u8 *command, u8 *data) +{ + int err = 0; + int databytes; + struct usb_cpia *ucpia = (struct usb_cpia *)privdata; + struct usb_device *udev = ucpia->dev; + + if (!udev) { + DBG("Internal driver error: udev is NULL\n"); + return -EINVAL; + } + + if (!command) { + DBG("Internal driver error: command is NULL\n"); + return -EINVAL; + } + + databytes = (((int)command[7])<<8) | command[6]; + + if (command[0] == DATA_IN) { + u8 buffer[8]; + + if (!data) { + DBG("Internal driver error: data is NULL\n"); + return -EINVAL; + } + + err = ReadPacket(udev, command, buffer, 8); + if (err < 0) + return err; + + memcpy(data, buffer, databytes); + } else if(command[0] == DATA_OUT) + WritePacket(udev, command, data, databytes); + else { + DBG("Unexpected first byte of command: %x\n", command[0]); + err = -EINVAL; + } + + return 0; +} + +static int cpia_usb_registerCallback(void *privdata, void (*cb) (void *cbdata), + void *cbdata) +{ + return -ENODEV; +} + +static int cpia_usb_streamStart(void *privdata) +{ + return -ENODEV; +} + +static int cpia_usb_streamStop(void *privdata) +{ + return -ENODEV; +} + +static int cpia_usb_streamRead(void *privdata, u8 *frame, int noblock) +{ + struct usb_cpia *ucpia = (struct usb_cpia *) privdata; + struct framebuf *mybuff; + + if (!ucpia || !ucpia->present) + return -1; + + if (ucpia->curbuff->status != FRAME_READY) + interruptible_sleep_on(&ucpia->wq_stream); + else + DBG("Frame already waiting!\n"); + + mybuff = ucpia->curbuff; + + if (!mybuff) + return -1; + + if (mybuff->status != FRAME_READY || mybuff->length < 4) { + DBG("Something went wrong!\n"); + return -1; + } + + memcpy(frame, mybuff->data, mybuff->length); + mybuff->status = FRAME_EMPTY; + +/* DBG("read done, %d bytes, Header: %x/%x, Footer: %x%x%x%x\n", */ +/* mybuff->length, frame[0], frame[1], */ +/* frame[mybuff->length-4], frame[mybuff->length-3], */ +/* frame[mybuff->length-2], frame[mybuff->length-1]); */ + + return mybuff->length; +} + +static void cpia_usb_free_resources(struct usb_cpia *ucpia, int try) +{ + if (!ucpia->streaming) + return; + + ucpia->streaming = 0; + + /* Set packet size to 0 */ + if (try) { + int ret; + + ret = usb_set_interface(ucpia->dev, ucpia->iface, 0); + if (ret < 0) { + printk(KERN_ERR "usb_set_interface error (ret = %d)\n", ret); + return; + } + } + + /* Unschedule all of the iso td's */ + if (ucpia->sbuf[1].urb) { + usb_kill_urb(ucpia->sbuf[1].urb); + usb_free_urb(ucpia->sbuf[1].urb); + ucpia->sbuf[1].urb = NULL; + } + + kfree(ucpia->sbuf[1].data); + ucpia->sbuf[1].data = NULL; + + if (ucpia->sbuf[0].urb) { + usb_kill_urb(ucpia->sbuf[0].urb); + usb_free_urb(ucpia->sbuf[0].urb); + ucpia->sbuf[0].urb = NULL; + } + + kfree(ucpia->sbuf[0].data); + ucpia->sbuf[0].data = NULL; +} + +static int cpia_usb_close(void *privdata) +{ + struct usb_cpia *ucpia = (struct usb_cpia *) privdata; + + if(!ucpia) + return -ENODEV; + + ucpia->open = 0; + + /* ucpia->present = 0 protects against trying to reset the + * alt setting if camera is physically disconnected while open */ + cpia_usb_free_resources(ucpia, ucpia->present); + + return 0; +} + +/* Probing and initializing */ + +static int cpia_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct usb_host_interface *interface; + struct usb_cpia *ucpia; + struct cam_data *cam; + int ret; + + /* A multi-config CPiA camera? */ + if (udev->descriptor.bNumConfigurations != 1) + return -ENODEV; + + interface = intf->cur_altsetting; + + printk(KERN_INFO "USB CPiA camera found\n"); + + ucpia = kzalloc(sizeof(*ucpia), GFP_KERNEL); + if (!ucpia) { + printk(KERN_ERR "couldn't kmalloc cpia struct\n"); + return -ENOMEM; + } + + ucpia->dev = udev; + ucpia->iface = interface->desc.bInterfaceNumber; + init_waitqueue_head(&ucpia->wq_stream); + + ucpia->buffers[0] = vmalloc(sizeof(*ucpia->buffers[0])); + if (!ucpia->buffers[0]) { + printk(KERN_ERR "couldn't vmalloc frame buffer 0\n"); + goto fail_alloc_0; + } + + ucpia->buffers[1] = vmalloc(sizeof(*ucpia->buffers[1])); + if (!ucpia->buffers[1]) { + printk(KERN_ERR "couldn't vmalloc frame buffer 1\n"); + goto fail_alloc_1; + } + + ucpia->buffers[2] = vmalloc(sizeof(*ucpia->buffers[2])); + if (!ucpia->buffers[2]) { + printk(KERN_ERR "couldn't vmalloc frame buffer 2\n"); + goto fail_alloc_2; + } + + ucpia->buffers[0]->next = ucpia->buffers[1]; + ucpia->buffers[1]->next = ucpia->buffers[2]; + ucpia->buffers[2]->next = ucpia->buffers[0]; + + ret = usb_set_interface(udev, ucpia->iface, 0); + if (ret < 0) { + printk(KERN_ERR "cpia_probe: usb_set_interface error (ret = %d)\n", ret); + /* goto fail_all; */ + } + + /* Before register_camera, important */ + ucpia->present = 1; + + cam = cpia_register_camera(&cpia_usb_ops, ucpia); + if (!cam) { + LOG("failed to cpia_register_camera\n"); + goto fail_all; + } + + spin_lock( &cam_list_lock_usb ); + list_add( &cam->cam_data_list, &cam_list ); + spin_unlock( &cam_list_lock_usb ); + + usb_set_intfdata(intf, cam); + return 0; + +fail_all: + vfree(ucpia->buffers[2]); + ucpia->buffers[2] = NULL; +fail_alloc_2: + vfree(ucpia->buffers[1]); + ucpia->buffers[1] = NULL; +fail_alloc_1: + vfree(ucpia->buffers[0]); + ucpia->buffers[0] = NULL; +fail_alloc_0: + kfree(ucpia); + return -EIO; +} + +static void cpia_disconnect(struct usb_interface *intf); + +static struct usb_device_id cpia_id_table [] = { + { USB_DEVICE(0x0553, 0x0002) }, + { USB_DEVICE(0x0813, 0x0001) }, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE (usb, cpia_id_table); +MODULE_LICENSE("GPL"); + + +static struct usb_driver cpia_driver = { + .name = "cpia", + .probe = cpia_probe, + .disconnect = cpia_disconnect, + .id_table = cpia_id_table, +}; + +static void cpia_disconnect(struct usb_interface *intf) +{ + struct cam_data *cam = usb_get_intfdata(intf); + struct usb_cpia *ucpia; + + usb_set_intfdata(intf, NULL); + if (!cam) + return; + + ucpia = (struct usb_cpia *) cam->lowlevel_data; + spin_lock( &cam_list_lock_usb ); + list_del(&cam->cam_data_list); + spin_unlock( &cam_list_lock_usb ); + + ucpia->present = 0; + + cpia_unregister_camera(cam); + if(ucpia->open) + cpia_usb_close(cam->lowlevel_data); + + ucpia->curbuff->status = FRAME_ERROR; + + if (waitqueue_active(&ucpia->wq_stream)) + wake_up_interruptible(&ucpia->wq_stream); + + ucpia->curbuff = ucpia->workbuff = NULL; + + vfree(ucpia->buffers[2]); + ucpia->buffers[2] = NULL; + + vfree(ucpia->buffers[1]); + ucpia->buffers[1] = NULL; + + vfree(ucpia->buffers[0]); + ucpia->buffers[0] = NULL; + + cam->lowlevel_data = NULL; + kfree(ucpia); +} + +static int __init usb_cpia_init(void) +{ + printk(KERN_INFO "%s v%d.%d.%d\n",ABOUT, + CPIA_USB_MAJ_VER,CPIA_USB_MIN_VER,CPIA_USB_PATCH_VER); + + spin_lock_init(&cam_list_lock_usb); + return usb_register(&cpia_driver); +} + +static void __exit usb_cpia_cleanup(void) +{ + usb_deregister(&cpia_driver); +} + + +module_init (usb_cpia_init); +module_exit (usb_cpia_cleanup); + -- cgit v1.2.3 From 96322b80e29802d2d3087987f6dc4e5aa19df64b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 30 Sep 2010 08:26:28 -0300 Subject: V4L/DVB: Deprecate stradis driver The driver author seems to not worked on this driver since its conversion from 2.2 to 2.4. Nobody is known to have a stradis hardware for testing. As it still uses V4L1 API, BKL and probably some other old stuff, someone would need to work on it to preserve the driver. Instead of investing time and efforts to keep porting it to work with new API's, it seems better to just drop the driver. So, let's move it to drivers/staging and label it to die at 2.6.38, if nobody cares enough to port parallel port support to gspca or to create a new driver that uses the same gspca-cpia sub-driver. Signed-off-by: Mauro Carvalho Chehab --- Documentation/feature-removal-schedule.txt | 4 +- drivers/media/video/Kconfig | 8 - drivers/media/video/Makefile | 1 - drivers/media/video/stradis.c | 2213 ---------------------------- drivers/staging/Kconfig | 2 + drivers/staging/Makefile | 1 + drivers/staging/stradis/Kconfig | 7 + drivers/staging/stradis/Makefile | 3 + drivers/staging/stradis/TODO | 6 + drivers/staging/stradis/stradis.c | 2213 ++++++++++++++++++++++++++++ 10 files changed, 2234 insertions(+), 2224 deletions(-) delete mode 100644 drivers/media/video/stradis.c create mode 100644 drivers/staging/stradis/Kconfig create mode 100644 drivers/staging/stradis/Makefile create mode 100644 drivers/staging/stradis/TODO create mode 100644 drivers/staging/stradis/stradis.c (limited to 'Documentation') diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 448722555648..d87ee1760e3a 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -118,8 +118,8 @@ Who: Mauro Carvalho Chehab What: Video4Linux obsolete drivers using V4L1 API When: kernel 2.6.38 -Files: drivers/staging/cpia/* -Check: drivers/staging/cpia/cpia.c +Files: drivers/staging/cpia/* drivers/staging/stradis/* +Check: drivers/staging/cpia/cpia.c drivers/staging/stradis/stradis.c Why: There are some drivers still using V4L1 API, despite all efforts we've done to migrate. Those drivers are for obsolete hardware that the old maintainer didn't care (or not have the hardware anymore), and that no other developer diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 4f21ef877f37..75404fc8e5c2 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -609,14 +609,6 @@ config VIDEO_VINO Say Y here to build in support for the Vino video input system found on SGI Indy machines. -config VIDEO_STRADIS - tristate "Stradis 4:2:2 MPEG-2 video driver (EXPERIMENTAL)" - depends on EXPERIMENTAL && PCI && VIDEO_V4L1 && VIRT_TO_BUS - help - Say Y here to enable support for the Stradis 4:2:2 MPEG-2 video - driver for PCI. There is a product page at - . - source "drivers/media/video/zoran/Kconfig" config VIDEO_MEYE diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index b947160a8cb0..118196c06fcd 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -91,7 +91,6 @@ obj-$(CONFIG_VIDEO_BWQCAM) += bw-qcam.o obj-$(CONFIG_VIDEO_W9966) += w9966.o obj-$(CONFIG_VIDEO_PMS) += pms.o obj-$(CONFIG_VIDEO_VINO) += vino.o -obj-$(CONFIG_VIDEO_STRADIS) += stradis.o obj-$(CONFIG_VIDEO_MEYE) += meye.o obj-$(CONFIG_VIDEO_SAA7134) += saa7134/ obj-$(CONFIG_VIDEO_CX88) += cx88/ diff --git a/drivers/media/video/stradis.c b/drivers/media/video/stradis.c deleted file mode 100644 index a057824e7ebc..000000000000 --- a/drivers/media/video/stradis.c +++ /dev/null @@ -1,2213 +0,0 @@ -/* - * stradis.c - stradis 4:2:2 mpeg decoder driver - * - * Stradis 4:2:2 MPEG-2 Decoder Driver - * Copyright (C) 1999 Nathan Laredo - * - * 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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "saa7146.h" -#include "saa7146reg.h" -#include "ibmmpeg2.h" -#include "saa7121.h" -#include "cs8420.h" - -#define DEBUG(x) /* debug driver */ -#undef IDEBUG /* debug irq handler */ -#undef MDEBUG /* debug memory management */ - -#define SAA7146_MAX 6 - -static struct saa7146 saa7146s[SAA7146_MAX]; - -static int saa_num; /* number of SAA7146s in use */ - -static int video_nr = -1; -module_param(video_nr, int, 0); -MODULE_LICENSE("GPL"); - -#define nDebNormal 0x00480000 -#define nDebNoInc 0x00480000 -#define nDebVideo 0xd0480000 -#define nDebAudio 0xd0400000 -#define nDebDMA 0x02c80000 - -#define oDebNormal 0x13c80000 -#define oDebNoInc 0x13c80000 -#define oDebVideo 0xd1080000 -#define oDebAudio 0xd1080000 -#define oDebDMA 0x03080000 - -#define NewCard (saa->boardcfg[3]) -#define ChipControl (saa->boardcfg[1]) -#define NTSCFirstActive (saa->boardcfg[4]) -#define PALFirstActive (saa->boardcfg[5]) -#define NTSCLastActive (saa->boardcfg[54]) -#define PALLastActive (saa->boardcfg[55]) -#define Have2MB (saa->boardcfg[18] & 0x40) -#define HaveCS8420 (saa->boardcfg[18] & 0x04) -#define IBMMPEGCD20 (saa->boardcfg[18] & 0x20) -#define HaveCS3310 (saa->boardcfg[18] & 0x01) -#define CS3310MaxLvl ((saa->boardcfg[30] << 8) | saa->boardcfg[31]) -#define HaveCS4341 (saa->boardcfg[40] == 2) -#define SDIType (saa->boardcfg[27]) -#define CurrentMode (saa->boardcfg[2]) - -#define debNormal (NewCard ? nDebNormal : oDebNormal) -#define debNoInc (NewCard ? nDebNoInc : oDebNoInc) -#define debVideo (NewCard ? nDebVideo : oDebVideo) -#define debAudio (NewCard ? nDebAudio : oDebAudio) -#define debDMA (NewCard ? nDebDMA : oDebDMA) - -#ifdef USE_RESCUE_EEPROM_SDM275 -static unsigned char rescue_eeprom[64] = { - 0x00, 0x01, 0x04, 0x13, 0x26, 0x0f, 0x10, 0x00, 0x00, 0x00, 0x43, 0x63, - 0x22, 0x01, 0x29, 0x15, 0x73, 0x00, 0x1f, 'd', 'e', 'c', 'x', 'l', - 'd', 'v', 'a', 0x02, 0x00, 0x01, 0x00, 0xcc, 0xa4, 0x63, 0x09, 0xe2, - 0x10, 0x00, 0x0a, 0x00, 0x02, 0x02, 'd', 'e', 'c', 'x', 'l', 'a', - 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, -}; -#endif - -/* ----------------------------------------------------------------------- */ -/* Hardware I2C functions */ -static void I2CWipe(struct saa7146 *saa) -{ - int i; - /* set i2c to ~=100kHz, abort transfer, clear busy */ - saawrite(0x600 | SAA7146_I2C_ABORT, SAA7146_I2C_STATUS); - saawrite((SAA7146_MC2_UPLD_I2C << 16) | - SAA7146_MC2_UPLD_I2C, SAA7146_MC2); - /* wait for i2c registers to be programmed */ - for (i = 0; i < 1000 && - !(saaread(SAA7146_MC2) & SAA7146_MC2_UPLD_I2C); i++) - schedule(); - saawrite(0x600, SAA7146_I2C_STATUS); - saawrite((SAA7146_MC2_UPLD_I2C << 16) | - SAA7146_MC2_UPLD_I2C, SAA7146_MC2); - /* wait for i2c registers to be programmed */ - for (i = 0; i < 1000 && - !(saaread(SAA7146_MC2) & SAA7146_MC2_UPLD_I2C); i++) - schedule(); - saawrite(0x600, SAA7146_I2C_STATUS); - saawrite((SAA7146_MC2_UPLD_I2C << 16) | - SAA7146_MC2_UPLD_I2C, SAA7146_MC2); - /* wait for i2c registers to be programmed */ - for (i = 0; i < 1000 && - !(saaread(SAA7146_MC2) & SAA7146_MC2_UPLD_I2C); i++) - schedule(); -} - -/* read I2C */ -static int I2CRead(struct saa7146 *saa, unsigned char addr, - unsigned char subaddr, int dosub) -{ - int i; - - if (saaread(SAA7146_I2C_STATUS) & 0x3c) - I2CWipe(saa); - for (i = 0; - i < 1000 && (saaread(SAA7146_I2C_STATUS) & SAA7146_I2C_BUSY); - i++) - schedule(); - if (i == 1000) - I2CWipe(saa); - if (dosub) - saawrite(((addr & 0xfe) << 24) | (((addr | 1) & 0xff) << 8) | - ((subaddr & 0xff) << 16) | 0xed, SAA7146_I2C_TRANSFER); - else - saawrite(((addr & 0xfe) << 24) | (((addr | 1) & 0xff) << 16) | - 0xf1, SAA7146_I2C_TRANSFER); - saawrite((SAA7146_MC2_UPLD_I2C << 16) | - SAA7146_MC2_UPLD_I2C, SAA7146_MC2); - /* wait for i2c registers to be programmed */ - for (i = 0; i < 1000 && - !(saaread(SAA7146_MC2) & SAA7146_MC2_UPLD_I2C); i++) - schedule(); - /* wait for valid data */ - for (i = 0; i < 1000 && - (saaread(SAA7146_I2C_STATUS) & SAA7146_I2C_BUSY); i++) - schedule(); - if (saaread(SAA7146_I2C_STATUS) & SAA7146_I2C_ERR) - return -1; - if (i == 1000) - printk("i2c setup read timeout\n"); - saawrite(0x41, SAA7146_I2C_TRANSFER); - saawrite((SAA7146_MC2_UPLD_I2C << 16) | - SAA7146_MC2_UPLD_I2C, SAA7146_MC2); - /* wait for i2c registers to be programmed */ - for (i = 0; i < 1000 && - !(saaread(SAA7146_MC2) & SAA7146_MC2_UPLD_I2C); i++) - schedule(); - /* wait for valid data */ - for (i = 0; i < 1000 && - (saaread(SAA7146_I2C_TRANSFER) & SAA7146_I2C_BUSY); i++) - schedule(); - if (saaread(SAA7146_I2C_TRANSFER) & SAA7146_I2C_ERR) - return -1; - if (i == 1000) - printk("i2c read timeout\n"); - return ((saaread(SAA7146_I2C_TRANSFER) >> 24) & 0xff); -} - -/* set both to write both bytes, reset it to write only b1 */ - -static int I2CWrite(struct saa7146 *saa, unsigned char addr, unsigned char b1, - unsigned char b2, int both) -{ - int i; - u32 data; - - if (saaread(SAA7146_I2C_STATUS) & 0x3c) - I2CWipe(saa); - for (i = 0; i < 1000 && - (saaread(SAA7146_I2C_STATUS) & SAA7146_I2C_BUSY); i++) - schedule(); - if (i == 1000) - I2CWipe(saa); - data = ((addr & 0xfe) << 24) | ((b1 & 0xff) << 16); - if (both) - data |= ((b2 & 0xff) << 8) | 0xe5; - else - data |= 0xd1; - saawrite(data, SAA7146_I2C_TRANSFER); - saawrite((SAA7146_MC2_UPLD_I2C << 16) | SAA7146_MC2_UPLD_I2C, - SAA7146_MC2); - return 0; -} - -static void attach_inform(struct saa7146 *saa, int id) -{ - int i; - - DEBUG(printk(KERN_DEBUG "stradis%d: i2c: device found=%02x\n", saa->nr, - id)); - if (id == 0xa0) { /* we have rev2 or later board, fill in info */ - for (i = 0; i < 64; i++) - saa->boardcfg[i] = I2CRead(saa, 0xa0, i, 1); -#ifdef USE_RESCUE_EEPROM_SDM275 - if (saa->boardcfg[0] != 0) { - printk("stradis%d: WARNING: EEPROM STORED VALUES HAVE " - "BEEN IGNORED\n", saa->nr); - for (i = 0; i < 64; i++) - saa->boardcfg[i] = rescue_eeprom[i]; - } -#endif - printk("stradis%d: config =", saa->nr); - for (i = 0; i < 51; i++) { - printk(" %02x", saa->boardcfg[i]); - } - printk("\n"); - } -} - -static void I2CBusScan(struct saa7146 *saa) -{ - int i; - for (i = 0; i < 0xff; i += 2) - if ((I2CRead(saa, i, 0, 0)) >= 0) - attach_inform(saa, i); -} - -static int debiwait_maxwait; - -static int wait_for_debi_done(struct saa7146 *saa) -{ - int i; - - /* wait for registers to be programmed */ - for (i = 0; i < 100000 && - !(saaread(SAA7146_MC2) & SAA7146_MC2_UPLD_DEBI); i++) - saaread(SAA7146_MC2); - /* wait for transfer to complete */ - for (i = 0; i < 500000 && - (saaread(SAA7146_PSR) & SAA7146_PSR_DEBI_S); i++) - saaread(SAA7146_MC2); - - if (i > debiwait_maxwait) - printk("wait-for-debi-done maxwait: %d\n", - debiwait_maxwait = i); - - if (i == 500000) - return -1; - - return 0; -} - -static int debiwrite(struct saa7146 *saa, u32 config, int addr, - u32 val, int count) -{ - u32 cmd; - if (count <= 0 || count > 32764) - return -1; - if (wait_for_debi_done(saa) < 0) - return -1; - saawrite(config, SAA7146_DEBI_CONFIG); - if (count <= 4) /* immediate transfer */ - saawrite(val, SAA7146_DEBI_AD); - else /* block transfer */ - saawrite(virt_to_bus(saa->dmadebi), SAA7146_DEBI_AD); - saawrite((cmd = (count << 17) | (addr & 0xffff)), SAA7146_DEBI_COMMAND); - saawrite((SAA7146_MC2_UPLD_DEBI << 16) | SAA7146_MC2_UPLD_DEBI, - SAA7146_MC2); - return 0; -} - -static u32 debiread(struct saa7146 *saa, u32 config, int addr, int count) -{ - u32 result = 0; - - if (count > 32764 || count <= 0) - return 0; - if (wait_for_debi_done(saa) < 0) - return 0; - saawrite(virt_to_bus(saa->dmadebi), SAA7146_DEBI_AD); - saawrite((count << 17) | 0x10000 | (addr & 0xffff), - SAA7146_DEBI_COMMAND); - saawrite(config, SAA7146_DEBI_CONFIG); - saawrite((SAA7146_MC2_UPLD_DEBI << 16) | SAA7146_MC2_UPLD_DEBI, - SAA7146_MC2); - if (count > 4) /* not an immediate transfer */ - return count; - wait_for_debi_done(saa); - result = saaread(SAA7146_DEBI_AD); - if (count == 1) - result &= 0xff; - if (count == 2) - result &= 0xffff; - if (count == 3) - result &= 0xffffff; - return result; -} - -static void do_irq_send_data(struct saa7146 *saa) -{ - int split, audbytes, vidbytes; - - saawrite(SAA7146_PSR_PIN1, SAA7146_IER); - /* if special feature mode in effect, disable audio sending */ - if (saa->playmode != VID_PLAY_NORMAL) - saa->audtail = saa->audhead = 0; - if (saa->audhead <= saa->audtail) - audbytes = saa->audtail - saa->audhead; - else - audbytes = 65536 - (saa->audhead - saa->audtail); - if (saa->vidhead <= saa->vidtail) - vidbytes = saa->vidtail - saa->vidhead; - else - vidbytes = 524288 - (saa->vidhead - saa->vidtail); - if (audbytes == 0 && vidbytes == 0 && saa->osdtail == saa->osdhead) { - saawrite(0, SAA7146_IER); - return; - } - /* if at least 1 block audio waiting and audio fifo isn't full */ - if (audbytes >= 2048 && (debiread(saa, debNormal, IBM_MP2_AUD_FIFO, 2) - & 0xff) < 60) { - if (saa->audhead > saa->audtail) - split = 65536 - saa->audhead; - else - split = 0; - audbytes = 2048; - if (split > 0 && split < 2048) { - memcpy(saa->dmadebi, saa->audbuf + saa->audhead, split); - saa->audhead = 0; - audbytes -= split; - } else - split = 0; - memcpy(saa->dmadebi + split, saa->audbuf + saa->audhead, - audbytes); - saa->audhead += audbytes; - saa->audhead &= 0xffff; - debiwrite(saa, debAudio, (NewCard ? IBM_MP2_AUD_FIFO : - IBM_MP2_AUD_FIFOW), 0, 2048); - wake_up_interruptible(&saa->audq); - /* if at least 1 block video waiting and video fifo isn't full */ - } else if (vidbytes >= 30720 && (debiread(saa, debNormal, - IBM_MP2_FIFO, 2)) < 16384) { - if (saa->vidhead > saa->vidtail) - split = 524288 - saa->vidhead; - else - split = 0; - vidbytes = 30720; - if (split > 0 && split < 30720) { - memcpy(saa->dmadebi, saa->vidbuf + saa->vidhead, split); - saa->vidhead = 0; - vidbytes -= split; - } else - split = 0; - memcpy(saa->dmadebi + split, saa->vidbuf + saa->vidhead, - vidbytes); - saa->vidhead += vidbytes; - saa->vidhead &= 0x7ffff; - debiwrite(saa, debVideo, (NewCard ? IBM_MP2_FIFO : - IBM_MP2_FIFOW), 0, 30720); - wake_up_interruptible(&saa->vidq); - } - saawrite(SAA7146_PSR_DEBI_S | SAA7146_PSR_PIN1, SAA7146_IER); -} - -static void send_osd_data(struct saa7146 *saa) -{ - int size = saa->osdtail - saa->osdhead; - if (size > 30720) - size = 30720; - /* ensure some multiple of 8 bytes is transferred */ - size = 8 * ((size + 8) >> 3); - if (size) { - debiwrite(saa, debNormal, IBM_MP2_OSD_ADDR, - (saa->osdhead >> 3), 2); - memcpy(saa->dmadebi, &saa->osdbuf[saa->osdhead], size); - saa->osdhead += size; - /* block transfer of next 8 bytes to ~32k bytes */ - debiwrite(saa, debNormal, IBM_MP2_OSD_DATA, 0, size); - } - if (saa->osdhead >= saa->osdtail) { - saa->osdhead = saa->osdtail = 0; - debiwrite(saa, debNormal, IBM_MP2_MASK0, 0xc00c, 2); - } -} - -static irqreturn_t saa7146_irq(int irq, void *dev_id) -{ - struct saa7146 *saa = dev_id; - u32 stat, astat; - int count; - int handled = 0; - - count = 0; - while (1) { - /* get/clear interrupt status bits */ - stat = saaread(SAA7146_ISR); - astat = stat & saaread(SAA7146_IER); - if (!astat) - break; - handled = 1; - saawrite(astat, SAA7146_ISR); - if (astat & SAA7146_PSR_DEBI_S) { - do_irq_send_data(saa); - } - if (astat & SAA7146_PSR_PIN1) { - int istat; - /* the following read will trigger DEBI_S */ - istat = debiread(saa, debNormal, IBM_MP2_HOST_INT, 2); - if (istat & 1) { - saawrite(0, SAA7146_IER); - send_osd_data(saa); - saawrite(SAA7146_PSR_DEBI_S | - SAA7146_PSR_PIN1, SAA7146_IER); - } - if (istat & 0x20) { /* Video Start */ - saa->vidinfo.frame_count++; - } - if (istat & 0x400) { /* Picture Start */ - /* update temporal reference */ - } - if (istat & 0x200) { /* Picture Resolution Change */ - /* read new resolution */ - } - if (istat & 0x100) { /* New User Data found */ - /* read new user data */ - } - if (istat & 0x1000) { /* new GOP/SMPTE */ - /* read new SMPTE */ - } - if (istat & 0x8000) { /* Sequence Start Code */ - /* reset frame counter, load sizes */ - saa->vidinfo.frame_count = 0; - saa->vidinfo.h_size = 704; - saa->vidinfo.v_size = 480; -#if 0 - if (saa->endmarkhead != saa->endmarktail) { - saa->audhead = - saa->endmark[saa->endmarkhead]; - saa->endmarkhead++; - if (saa->endmarkhead >= MAX_MARKS) - saa->endmarkhead = 0; - } -#endif - } - if (istat & 0x4000) { /* Sequence Error Code */ - if (saa->endmarkhead != saa->endmarktail) { - saa->audhead = - saa->endmark[saa->endmarkhead]; - saa->endmarkhead++; - if (saa->endmarkhead >= MAX_MARKS) - saa->endmarkhead = 0; - } - } - } -#ifdef IDEBUG - if (astat & SAA7146_PSR_PPEF) { - IDEBUG(printk("stradis%d irq: PPEF\n", saa->nr)); - } - if (astat & SAA7146_PSR_PABO) { - IDEBUG(printk("stradis%d irq: PABO\n", saa->nr)); - } - if (astat & SAA7146_PSR_PPED) { - IDEBUG(printk("stradis%d irq: PPED\n", saa->nr)); - } - if (astat & SAA7146_PSR_RPS_I1) { - IDEBUG(printk("stradis%d irq: RPS_I1\n", saa->nr)); - } - if (astat & SAA7146_PSR_RPS_I0) { - IDEBUG(printk("stradis%d irq: RPS_I0\n", saa->nr)); - } - if (astat & SAA7146_PSR_RPS_LATE1) { - IDEBUG(printk("stradis%d irq: RPS_LATE1\n", saa->nr)); - } - if (astat & SAA7146_PSR_RPS_LATE0) { - IDEBUG(printk("stradis%d irq: RPS_LATE0\n", saa->nr)); - } - if (astat & SAA7146_PSR_RPS_E1) { - IDEBUG(printk("stradis%d irq: RPS_E1\n", saa->nr)); - } - if (astat & SAA7146_PSR_RPS_E0) { - IDEBUG(printk("stradis%d irq: RPS_E0\n", saa->nr)); - } - if (astat & SAA7146_PSR_RPS_TO1) { - IDEBUG(printk("stradis%d irq: RPS_TO1\n", saa->nr)); - } - if (astat & SAA7146_PSR_RPS_TO0) { - IDEBUG(printk("stradis%d irq: RPS_TO0\n", saa->nr)); - } - if (astat & SAA7146_PSR_UPLD) { - IDEBUG(printk("stradis%d irq: UPLD\n", saa->nr)); - } - if (astat & SAA7146_PSR_DEBI_E) { - IDEBUG(printk("stradis%d irq: DEBI_E\n", saa->nr)); - } - if (astat & SAA7146_PSR_I2C_S) { - IDEBUG(printk("stradis%d irq: I2C_S\n", saa->nr)); - } - if (astat & SAA7146_PSR_I2C_E) { - IDEBUG(printk("stradis%d irq: I2C_E\n", saa->nr)); - } - if (astat & SAA7146_PSR_A2_IN) { - IDEBUG(printk("stradis%d irq: A2_IN\n", saa->nr)); - } - if (astat & SAA7146_PSR_A2_OUT) { - IDEBUG(printk("stradis%d irq: A2_OUT\n", saa->nr)); - } - if (astat & SAA7146_PSR_A1_IN) { - IDEBUG(printk("stradis%d irq: A1_IN\n", saa->nr)); - } - if (astat & SAA7146_PSR_A1_OUT) { - IDEBUG(printk("stradis%d irq: A1_OUT\n", saa->nr)); - } - if (astat & SAA7146_PSR_AFOU) { - IDEBUG(printk("stradis%d irq: AFOU\n", saa->nr)); - } - if (astat & SAA7146_PSR_V_PE) { - IDEBUG(printk("stradis%d irq: V_PE\n", saa->nr)); - } - if (astat & SAA7146_PSR_VFOU) { - IDEBUG(printk("stradis%d irq: VFOU\n", saa->nr)); - } - if (astat & SAA7146_PSR_FIDA) { - IDEBUG(printk("stradis%d irq: FIDA\n", saa->nr)); - } - if (astat & SAA7146_PSR_FIDB) { - IDEBUG(printk("stradis%d irq: FIDB\n", saa->nr)); - } - if (astat & SAA7146_PSR_PIN3) { - IDEBUG(printk("stradis%d irq: PIN3\n", saa->nr)); - } - if (astat & SAA7146_PSR_PIN2) { - IDEBUG(printk("stradis%d irq: PIN2\n", saa->nr)); - } - if (astat & SAA7146_PSR_PIN0) { - IDEBUG(printk("stradis%d irq: PIN0\n", saa->nr)); - } - if (astat & SAA7146_PSR_ECS) { - IDEBUG(printk("stradis%d irq: ECS\n", saa->nr)); - } - if (astat & SAA7146_PSR_EC3S) { - IDEBUG(printk("stradis%d irq: EC3S\n", saa->nr)); - } - if (astat & SAA7146_PSR_EC0S) { - IDEBUG(printk("stradis%d irq: EC0S\n", saa->nr)); - } -#endif - count++; - if (count > 15) - printk(KERN_WARNING "stradis%d: irq loop %d\n", - saa->nr, count); - if (count > 20) { - saawrite(0, SAA7146_IER); - printk(KERN_ERR - "stradis%d: IRQ loop cleared\n", saa->nr); - } - } - return IRQ_RETVAL(handled); -} - -static int ibm_send_command(struct saa7146 *saa, - int command, int data, int chain) -{ - int i; - - if (chain) - debiwrite(saa, debNormal, IBM_MP2_COMMAND, (command << 1)| 1,2); - else - debiwrite(saa, debNormal, IBM_MP2_COMMAND, command << 1, 2); - debiwrite(saa, debNormal, IBM_MP2_CMD_DATA, data, 2); - debiwrite(saa, debNormal, IBM_MP2_CMD_STAT, 1, 2); - for (i = 0; i < 100 && - (debiread(saa, debNormal, IBM_MP2_CMD_STAT, 2) & 1); i++) - schedule(); - if (i == 100) - return -1; - return 0; -} - -static void cs4341_setlevel(struct saa7146 *saa, int left, int right) -{ - I2CWrite(saa, 0x22, 0x03, left > 94 ? 94 : left, 2); - I2CWrite(saa, 0x22, 0x04, right > 94 ? 94 : right, 2); -} - -static void initialize_cs4341(struct saa7146 *saa) -{ - int i; - for (i = 0; i < 200; i++) { - /* auto mute off, power on, no de-emphasis */ - /* I2S data up to 24-bit 64xFs internal SCLK */ - I2CWrite(saa, 0x22, 0x01, 0x11, 2); - /* ATAPI mixer settings */ - I2CWrite(saa, 0x22, 0x02, 0x49, 2); - /* attenuation left 3db */ - I2CWrite(saa, 0x22, 0x03, 0x00, 2); - /* attenuation right 3db */ - I2CWrite(saa, 0x22, 0x04, 0x00, 2); - I2CWrite(saa, 0x22, 0x01, 0x10, 2); - if (I2CRead(saa, 0x22, 0x02, 1) == 0x49) - break; - schedule(); - } - printk("stradis%d: CS4341 initialized (%d)\n", saa->nr, i); - return; -} - -static void initialize_cs8420(struct saa7146 *saa, int pro) -{ - int i; - u8 *sequence; - if (pro) - sequence = mode8420pro; - else - sequence = mode8420con; - for (i = 0; i < INIT8420LEN; i++) - I2CWrite(saa, 0x20, init8420[i * 2], init8420[i * 2 + 1], 2); - for (i = 0; i < MODE8420LEN; i++) - I2CWrite(saa, 0x20, sequence[i * 2], sequence[i * 2 + 1], 2); - printk("stradis%d: CS8420 initialized\n", saa->nr); -} - -static void initialize_saa7121(struct saa7146 *saa, int dopal) -{ - int i, mod; - u8 *sequence; - if (dopal) - sequence = init7121pal; - else - sequence = init7121ntsc; - mod = saaread(SAA7146_PSR) & 0x08; - /* initialize PAL/NTSC video encoder */ - for (i = 0; i < INIT7121LEN; i++) { - if (NewCard) { /* handle new card encoder differences */ - if (sequence[i * 2] == 0x3a) - I2CWrite(saa, 0x88, 0x3a, 0x13, 2); - else if (sequence[i * 2] == 0x6b) - I2CWrite(saa, 0x88, 0x6b, 0x20, 2); - else if (sequence[i * 2] == 0x6c) - I2CWrite(saa, 0x88, 0x6c, - dopal ? 0x09 : 0xf5, 2); - else if (sequence[i * 2] == 0x6d) - I2CWrite(saa, 0x88, 0x6d, - dopal ? 0x20 : 0x00, 2); - else if (sequence[i * 2] == 0x7a) - I2CWrite(saa, 0x88, 0x7a, - dopal ? (PALFirstActive - 1) : - (NTSCFirstActive - 4), 2); - else if (sequence[i * 2] == 0x7b) - I2CWrite(saa, 0x88, 0x7b, - dopal ? PALLastActive : - NTSCLastActive, 2); - else - I2CWrite(saa, 0x88, sequence[i * 2], - sequence[i * 2 + 1], 2); - } else { - if (sequence[i * 2] == 0x6b && mod) - I2CWrite(saa, 0x88, 0x6b, - (sequence[i * 2 + 1] ^ 0x09), 2); - else if (sequence[i * 2] == 0x7a) - I2CWrite(saa, 0x88, 0x7a, - dopal ? (PALFirstActive - 1) : - (NTSCFirstActive - 4), 2); - else if (sequence[i * 2] == 0x7b) - I2CWrite(saa, 0x88, 0x7b, - dopal ? PALLastActive : - NTSCLastActive, 2); - else - I2CWrite(saa, 0x88, sequence[i * 2], - sequence[i * 2 + 1], 2); - } - } -} - -static void set_genlock_offset(struct saa7146 *saa, int noffset) -{ - int nCode; - int PixelsPerLine = 858; - if (CurrentMode == VIDEO_MODE_PAL) - PixelsPerLine = 864; - if (noffset > 500) - noffset = 500; - else if (noffset < -500) - noffset = -500; - nCode = noffset + 0x100; - if (nCode == 1) - nCode = 0x401; - else if (nCode < 1) - nCode = 0x400 + PixelsPerLine + nCode; - debiwrite(saa, debNormal, XILINX_GLDELAY, nCode, 2); -} - -static void set_out_format(struct saa7146 *saa, int mode) -{ - initialize_saa7121(saa, (mode == VIDEO_MODE_NTSC ? 0 : 1)); - saa->boardcfg[2] = mode; - /* do not adjust analog video parameters here, use saa7121 init */ - /* you will affect the SDI output on the new card */ - if (mode == VIDEO_MODE_PAL) { /* PAL */ - debiwrite(saa, debNormal, XILINX_CTL0, 0x0808, 2); - mdelay(50); - saawrite(0x012002c0, SAA7146_NUM_LINE_BYTE1); - if (NewCard) { - debiwrite(saa, debNormal, IBM_MP2_DISP_MODE, 0xe100, 2); - mdelay(50); - } - debiwrite(saa, debNormal, IBM_MP2_DISP_MODE, - NewCard ? 0xe500 : 0x6500, 2); - debiwrite(saa, debNormal, IBM_MP2_DISP_DLY, - (1 << 8) | - (NewCard ? PALFirstActive : PALFirstActive - 6), 2); - } else { /* NTSC */ - debiwrite(saa, debNormal, XILINX_CTL0, 0x0800, 2); - mdelay(50); - saawrite(0x00f002c0, SAA7146_NUM_LINE_BYTE1); - debiwrite(saa, debNormal, IBM_MP2_DISP_MODE, - NewCard ? 0xe100 : 0x6100, 2); - debiwrite(saa, debNormal, IBM_MP2_DISP_DLY, - (1 << 8) | - (NewCard ? NTSCFirstActive : NTSCFirstActive - 6), 2); - } -} - -/* Intialize bitmangler to map from a byte value to the mangled word that - * must be output to program the Xilinx part through the DEBI port. - * Xilinx Data Bit->DEBI Bit: 0->15 1->7 2->6 3->12 4->11 5->2 6->1 7->0 - * transfer FPGA code, init IBM chip, transfer IBM microcode - * rev2 card mangles: 0->7 1->6 2->5 3->4 4->3 5->2 6->1 7->0 - */ -static u16 bitmangler[256]; - -static int initialize_fpga(struct video_code *bitdata) -{ - int i, num, startindex, failure = 0, loadtwo, loadfile = 0; - u16 *dmabuf; - u8 *newdma; - struct saa7146 *saa; - - /* verify fpga code */ - for (startindex = 0; startindex < bitdata->datasize; startindex++) - if (bitdata->data[startindex] == 255) - break; - if (startindex == bitdata->datasize) { - printk(KERN_INFO "stradis: bad fpga code\n"); - return -1; - } - /* initialize all detected cards */ - for (num = 0; num < saa_num; num++) { - saa = &saa7146s[num]; - if (saa->boardcfg[0] > 20) - continue; /* card was programmed */ - loadtwo = (saa->boardcfg[18] & 0x10); - if (!NewCard) /* we have an old board */ - for (i = 0; i < 256; i++) - bitmangler[i] = ((i & 0x01) << 15) | - ((i & 0x02) << 6) | ((i & 0x04) << 4) | - ((i & 0x08) << 9) | ((i & 0x10) << 7) | - ((i & 0x20) >> 3) | ((i & 0x40) >> 5) | - ((i & 0x80) >> 7); - else /* else we have a new board */ - for (i = 0; i < 256; i++) - bitmangler[i] = ((i & 0x01) << 7) | - ((i & 0x02) << 5) | ((i & 0x04) << 3) | - ((i & 0x08) << 1) | ((i & 0x10) >> 1) | - ((i & 0x20) >> 3) | ((i & 0x40) >> 5) | - ((i & 0x80) >> 7); - - dmabuf = (u16 *) saa->dmadebi; - newdma = (u8 *) saa->dmadebi; - if (NewCard) { /* SDM2xxx */ - if (!strncmp(bitdata->loadwhat, "decoder2", 8)) - continue; /* fpga not for this card */ - if (!strncmp(&saa->boardcfg[42], bitdata->loadwhat, 8)) - loadfile = 1; - else if (loadtwo && !strncmp(&saa->boardcfg[19], - bitdata->loadwhat, 8)) - loadfile = 2; - else if (!saa->boardcfg[42] && !strncmp("decxl", - bitdata->loadwhat, 8)) - loadfile = 1; /* special */ - else - continue; /* fpga not for this card */ - if (loadfile != 1 && loadfile != 2) - continue; /* skip to next card */ - if (saa->boardcfg[0] && loadfile == 1) - continue; /* skip to next card */ - if (saa->boardcfg[0] != 1 && loadfile == 2) - continue; /* skip to next card */ - saa->boardcfg[0]++; /* mark fpga handled */ - printk("stradis%d: loading %s\n", saa->nr, - bitdata->loadwhat); - if (loadtwo && loadfile == 2) - goto send_fpga_stuff; - /* turn on the Audio interface to set PROG low */ - saawrite(0x00400040, SAA7146_GPIO_CTRL); - saaread(SAA7146_PSR); /* ensure posted write */ - /* wait for everyone to reset */ - mdelay(10); - saawrite(0x00400000, SAA7146_GPIO_CTRL); - } else { /* original card */ - if (strncmp(bitdata->loadwhat, "decoder2", 8)) - continue; /* fpga not for this card */ - /* Pull the Xilinx PROG signal WS3 low */ - saawrite(0x02000200, SAA7146_MC1); - /* Turn on the Audio interface so can set PROG low */ - saawrite(0x000000c0, SAA7146_ACON1); - /* Pull the Xilinx INIT signal (GPIO2) low */ - saawrite(0x00400000, SAA7146_GPIO_CTRL); - /* Make sure everybody resets */ - saaread(SAA7146_PSR); /* ensure posted write */ - mdelay(10); - /* Release the Xilinx PROG signal */ - saawrite(0x00000000, SAA7146_ACON1); - /* Turn off the Audio interface */ - saawrite(0x02000000, SAA7146_MC1); - } - /* Release Xilinx INIT signal (WS2) */ - saawrite(0x00000000, SAA7146_GPIO_CTRL); - /* Wait for the INIT to go High */ - for (i = 0; - i < 10000 && !(saaread(SAA7146_PSR) & SAA7146_PSR_PIN2); - i++) - schedule(); - if (i == 1000) { - printk(KERN_INFO "stradis%d: no fpga INIT\n", saa->nr); - return -1; - } -send_fpga_stuff: - if (NewCard) { - for (i = startindex; i < bitdata->datasize; i++) - newdma[i - startindex] = - bitmangler[bitdata->data[i]]; - debiwrite(saa, 0x01420000, 0, 0, - ((bitdata->datasize - startindex) + 5)); - if (loadtwo && loadfile == 1) { - printk("stradis%d: awaiting 2nd FPGA bitfile\n", - saa->nr); - continue; /* skip to next card */ - } - } else { - for (i = startindex; i < bitdata->datasize; i++) - dmabuf[i - startindex] = - bitmangler[bitdata->data[i]]; - debiwrite(saa, 0x014a0000, 0, 0, - ((bitdata->datasize - startindex) + 5) * 2); - } - for (i = 0; - i < 1000 && !(saaread(SAA7146_PSR) & SAA7146_PSR_PIN2); - i++) - schedule(); - if (i == 1000) { - printk(KERN_INFO "stradis%d: FPGA load failed\n", - saa->nr); - failure++; - continue; - } - if (!NewCard) { - /* Pull the Xilinx INIT signal (GPIO2) low */ - saawrite(0x00400000, SAA7146_GPIO_CTRL); - saaread(SAA7146_PSR); /* ensure posted write */ - mdelay(2); - saawrite(0x00000000, SAA7146_GPIO_CTRL); - mdelay(2); - } - printk(KERN_INFO "stradis%d: FPGA Loaded\n", saa->nr); - saa->boardcfg[0] = 26; /* mark fpga programmed */ - /* set VXCO to its lowest frequency */ - debiwrite(saa, debNormal, XILINX_PWM, 0, 2); - if (NewCard) { - /* mute CS3310 */ - if (HaveCS3310) - debiwrite(saa, debNormal, XILINX_CS3310_CMPLT, - 0, 2); - /* set VXCO to PWM mode, release reset, blank on */ - debiwrite(saa, debNormal, XILINX_CTL0, 0xffc4, 2); - mdelay(10); - /* unmute CS3310 */ - if (HaveCS3310) - debiwrite(saa, debNormal, XILINX_CTL0, - 0x2020, 2); - } - /* set source Black */ - debiwrite(saa, debNormal, XILINX_CTL0, 0x1707, 2); - saa->boardcfg[4] = 22; /* set NTSC First Active Line */ - saa->boardcfg[5] = 23; /* set PAL First Active Line */ - saa->boardcfg[54] = 2; /* set NTSC Last Active Line - 256 */ - saa->boardcfg[55] = 54; /* set PAL Last Active Line - 256 */ - set_out_format(saa, VIDEO_MODE_NTSC); - mdelay(50); - /* begin IBM chip init */ - debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, 4, 2); - saaread(SAA7146_PSR); /* wait for reset */ - mdelay(5); - debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, 0, 2); - debiread(saa, debNormal, IBM_MP2_CHIP_CONTROL, 2); - debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, 0x10, 2); - debiwrite(saa, debNormal, IBM_MP2_CMD_ADDR, 0, 2); - debiwrite(saa, debNormal, IBM_MP2_CHIP_MODE, 0x2e, 2); - if (NewCard) { - mdelay(5); - /* set i2s rate converter to 48KHz */ - debiwrite(saa, debNormal, 0x80c0, 6, 2); - /* we must init CS8420 first since rev b pulls i2s */ - /* master clock low and CS4341 needs i2s master to */ - /* run the i2c port. */ - if (HaveCS8420) - /* 0=consumer, 1=pro */ - initialize_cs8420(saa, 0); - - mdelay(5); - if (HaveCS4341) - initialize_cs4341(saa); - } - debiwrite(saa, debNormal, IBM_MP2_INFC_CTL, 0x48, 2); - debiwrite(saa, debNormal, IBM_MP2_BEEP_CTL, 0xa000, 2); - debiwrite(saa, debNormal, IBM_MP2_DISP_LBOR, 0, 2); - debiwrite(saa, debNormal, IBM_MP2_DISP_TBOR, 0, 2); - if (NewCard) - set_genlock_offset(saa, 0); - debiwrite(saa, debNormal, IBM_MP2_FRNT_ATTEN, 0, 2); -#if 0 - /* enable genlock */ - debiwrite(saa, debNormal, XILINX_CTL0, 0x8000, 2); -#else - /* disable genlock */ - debiwrite(saa, debNormal, XILINX_CTL0, 0x8080, 2); -#endif - } - - return failure; -} - -static int do_ibm_reset(struct saa7146 *saa) -{ - /* failure if decoder not previously programmed */ - if (saa->boardcfg[0] < 37) - return -EIO; - /* mute CS3310 */ - if (HaveCS3310) - debiwrite(saa, debNormal, XILINX_CS3310_CMPLT, 0, 2); - /* disable interrupts */ - saawrite(0, SAA7146_IER); - saa->audhead = saa->audtail = 0; - saa->vidhead = saa->vidtail = 0; - /* tristate debi bus, disable debi transfers */ - saawrite(0x00880000, SAA7146_MC1); - /* ensure posted write */ - saaread(SAA7146_MC1); - mdelay(50); - /* re-enable debi transfers */ - saawrite(0x00880088, SAA7146_MC1); - /* set source Black */ - debiwrite(saa, debNormal, XILINX_CTL0, 0x1707, 2); - /* begin IBM chip init */ - set_out_format(saa, CurrentMode); - debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, 4, 2); - saaread(SAA7146_PSR); /* wait for reset */ - mdelay(5); - debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, 0, 2); - debiread(saa, debNormal, IBM_MP2_CHIP_CONTROL, 2); - debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, ChipControl, 2); - debiwrite(saa, debNormal, IBM_MP2_CHIP_MODE, 0x2e, 2); - if (NewCard) { - mdelay(5); - /* set i2s rate converter to 48KHz */ - debiwrite(saa, debNormal, 0x80c0, 6, 2); - /* we must init CS8420 first since rev b pulls i2s */ - /* master clock low and CS4341 needs i2s master to */ - /* run the i2c port. */ - if (HaveCS8420) - /* 0=consumer, 1=pro */ - initialize_cs8420(saa, 1); - - mdelay(5); - if (HaveCS4341) - initialize_cs4341(saa); - } - debiwrite(saa, debNormal, IBM_MP2_INFC_CTL, 0x48, 2); - debiwrite(saa, debNormal, IBM_MP2_BEEP_CTL, 0xa000, 2); - debiwrite(saa, debNormal, IBM_MP2_DISP_LBOR, 0, 2); - debiwrite(saa, debNormal, IBM_MP2_DISP_TBOR, 0, 2); - if (NewCard) - set_genlock_offset(saa, 0); - debiwrite(saa, debNormal, IBM_MP2_FRNT_ATTEN, 0, 2); - debiwrite(saa, debNormal, IBM_MP2_OSD_SIZE, 0x2000, 2); - debiwrite(saa, debNormal, IBM_MP2_AUD_CTL, 0x4552, 2); - if (ibm_send_command(saa, IBM_MP2_CONFIG_DECODER, - (ChipControl == 0x43 ? 0xe800 : 0xe000), 1)) { - printk(KERN_ERR "stradis%d: IBM config failed\n", saa->nr); - } - if (HaveCS3310) { - int i = CS3310MaxLvl; - debiwrite(saa, debNormal, XILINX_CS3310_CMPLT, ((i << 8)| i),2); - } - /* start video decoder */ - debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, ChipControl, 2); - /* 256k vid, 3520 bytes aud */ - debiwrite(saa, debNormal, IBM_MP2_RB_THRESHOLD, 0x4037, 2); - debiwrite(saa, debNormal, IBM_MP2_AUD_CTL, 0x4573, 2); - ibm_send_command(saa, IBM_MP2_PLAY, 0, 0); - /* enable buffer threshold irq */ - debiwrite(saa, debNormal, IBM_MP2_MASK0, 0xc00c, 2); - /* clear pending interrupts */ - debiread(saa, debNormal, IBM_MP2_HOST_INT, 2); - debiwrite(saa, debNormal, XILINX_CTL0, 0x1711, 2); - - return 0; -} - -/* load the decoder microcode */ -static int initialize_ibmmpeg2(struct video_code *microcode) -{ - int i, num; - struct saa7146 *saa; - - for (num = 0; num < saa_num; num++) { - saa = &saa7146s[num]; - /* check that FPGA is loaded */ - debiwrite(saa, debNormal, IBM_MP2_OSD_SIZE, 0xa55a, 2); - i = debiread(saa, debNormal, IBM_MP2_OSD_SIZE, 2); - if (i != 0xa55a) { - printk(KERN_INFO "stradis%d: %04x != 0xa55a\n", - saa->nr, i); -#if 0 - return -1; -#endif - } - if (!strncmp(microcode->loadwhat, "decoder.vid", 11)) { - if (saa->boardcfg[0] > 27) - continue; /* skip to next card */ - /* load video control store */ - saa->boardcfg[1] = 0x13; /* no-sync default */ - debiwrite(saa, debNormal, IBM_MP2_WR_PROT, 1, 2); - debiwrite(saa, debNormal, IBM_MP2_PROC_IADDR, 0, 2); - for (i = 0; i < microcode->datasize / 2; i++) - debiwrite(saa, debNormal, IBM_MP2_PROC_IDATA, - (microcode->data[i * 2] << 8) | - microcode->data[i * 2 + 1], 2); - debiwrite(saa, debNormal, IBM_MP2_PROC_IADDR, 0, 2); - debiwrite(saa, debNormal, IBM_MP2_WR_PROT, 0, 2); - debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, - ChipControl, 2); - saa->boardcfg[0] = 28; - } - if (!strncmp(microcode->loadwhat, "decoder.aud", 11)) { - if (saa->boardcfg[0] > 35) - continue; /* skip to next card */ - /* load audio control store */ - debiwrite(saa, debNormal, IBM_MP2_WR_PROT, 1, 2); - debiwrite(saa, debNormal, IBM_MP2_AUD_IADDR, 0, 2); - for (i = 0; i < microcode->datasize; i++) - debiwrite(saa, debNormal, IBM_MP2_AUD_IDATA, - microcode->data[i], 1); - debiwrite(saa, debNormal, IBM_MP2_AUD_IADDR, 0, 2); - debiwrite(saa, debNormal, IBM_MP2_WR_PROT, 0, 2); - debiwrite(saa, debNormal, IBM_MP2_OSD_SIZE, 0x2000, 2); - debiwrite(saa, debNormal, IBM_MP2_AUD_CTL, 0x4552, 2); - if (ibm_send_command(saa, IBM_MP2_CONFIG_DECODER, - 0xe000, 1)) { - printk(KERN_ERR "stradis%d: IBM config " - "failed\n", saa->nr); - return -1; - } - /* set PWM to center value */ - if (NewCard) { - debiwrite(saa, debNormal, XILINX_PWM, - saa->boardcfg[14] + - (saa->boardcfg[13] << 8), 2); - } else - debiwrite(saa, debNormal, XILINX_PWM, 0x46, 2); - - if (HaveCS3310) { - i = CS3310MaxLvl; - debiwrite(saa, debNormal, XILINX_CS3310_CMPLT, - (i << 8) | i, 2); - } - printk(KERN_INFO "stradis%d: IBM MPEGCD%d Inited\n", - saa->nr, 18 + (debiread(saa, debNormal, - IBM_MP2_CHIP_CONTROL, 2) >> 12)); - /* start video decoder */ - debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, - ChipControl, 2); - debiwrite(saa, debNormal, IBM_MP2_RB_THRESHOLD, 0x4037, - 2); /* 256k vid, 3520 bytes aud */ - debiwrite(saa, debNormal, IBM_MP2_AUD_CTL, 0x4573, 2); - ibm_send_command(saa, IBM_MP2_PLAY, 0, 0); - /* enable buffer threshold irq */ - debiwrite(saa, debNormal, IBM_MP2_MASK0, 0xc00c, 2); - debiread(saa, debNormal, IBM_MP2_HOST_INT, 2); - /* enable gpio irq */ - saawrite(0x00002000, SAA7146_GPIO_CTRL); - /* enable decoder output to HPS */ - debiwrite(saa, debNormal, XILINX_CTL0, 0x1711, 2); - saa->boardcfg[0] = 37; - } - } - - return 0; -} - -static u32 palette2fmt[] = { /* some of these YUV translations are wrong */ - 0xffffffff, 0x86000000, 0x87000000, 0x80000000, 0x8100000, 0x82000000, - 0x83000000, 0x00000000, 0x03000000, 0x03000000, 0x0a00000, 0x03000000, - 0x06000000, 0x00000000, 0x03000000, 0x0a000000, 0x0300000 -}; -static int bpp2fmt[4] = { - VIDEO_PALETTE_HI240, VIDEO_PALETTE_RGB565, VIDEO_PALETTE_RGB24, - VIDEO_PALETTE_RGB32 -}; - -/* I wish I could find a formula to calculate these... */ -static u32 h_prescale[64] = { - 0x10000000, 0x18040202, 0x18080000, 0x380c0606, 0x38100204, 0x38140808, - 0x38180000, 0x381c0000, 0x3820161c, 0x38242a3b, 0x38281230, 0x382c4460, - 0x38301040, 0x38340080, 0x38380000, 0x383c0000, 0x3840fefe, 0x3844ee9f, - 0x3848ee9f, 0x384cee9f, 0x3850ee9f, 0x38542a3b, 0x38581230, 0x385c0000, - 0x38600000, 0x38640000, 0x38680000, 0x386c0000, 0x38700000, 0x38740000, - 0x38780000, 0x387c0000, 0x30800000, 0x38840000, 0x38880000, 0x388c0000, - 0x38900000, 0x38940000, 0x38980000, 0x389c0000, 0x38a00000, 0x38a40000, - 0x38a80000, 0x38ac0000, 0x38b00000, 0x38b40000, 0x38b80000, 0x38bc0000, - 0x38c00000, 0x38c40000, 0x38c80000, 0x38cc0000, 0x38d00000, 0x38d40000, - 0x38d80000, 0x38dc0000, 0x38e00000, 0x38e40000, 0x38e80000, 0x38ec0000, - 0x38f00000, 0x38f40000, 0x38f80000, 0x38fc0000, -}; -static u32 v_gain[64] = { - 0x016000ff, 0x016100ff, 0x016100ff, 0x016200ff, 0x016200ff, 0x016200ff, - 0x016200ff, 0x016300ff, 0x016300ff, 0x016300ff, 0x016300ff, 0x016300ff, - 0x016300ff, 0x016300ff, 0x016300ff, 0x016400ff, 0x016400ff, 0x016400ff, - 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, - 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, - 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, - 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, - 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, - 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, - 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, - 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, -}; - -static void saa7146_set_winsize(struct saa7146 *saa) -{ - u32 format; - int offset, yacl, ysci; - saa->win.color_fmt = format = - (saa->win.depth == 15) ? palette2fmt[VIDEO_PALETTE_RGB555] : - palette2fmt[bpp2fmt[(saa->win.bpp - 1) & 3]]; - offset = saa->win.x * saa->win.bpp + saa->win.y * saa->win.bpl; - saawrite(saa->win.vidadr + offset, SAA7146_BASE_EVEN1); - saawrite(saa->win.vidadr + offset + saa->win.bpl, SAA7146_BASE_ODD1); - saawrite(saa->win.bpl * 2, SAA7146_PITCH1); - saawrite(saa->win.vidadr + saa->win.bpl * saa->win.sheight, - SAA7146_PROT_ADDR1); - saawrite(0, SAA7146_PAGE1); - saawrite(format | 0x60, SAA7146_CLIP_FORMAT_CTRL); - offset = (704 / (saa->win.width - 1)) & 0x3f; - saawrite(h_prescale[offset], SAA7146_HPS_H_PRESCALE); - offset = (720896 / saa->win.width) / (offset + 1); - saawrite((offset << 12) | 0x0c, SAA7146_HPS_H_SCALE); - if (CurrentMode == VIDEO_MODE_NTSC) { - yacl = /*(480 / saa->win.height - 1) & 0x3f */ 0; - ysci = 1024 - (saa->win.height * 1024 / 480); - } else { - yacl = /*(576 / saa->win.height - 1) & 0x3f */ 0; - ysci = 1024 - (saa->win.height * 1024 / 576); - } - saawrite((1 << 31) | (ysci << 21) | (yacl << 15), SAA7146_HPS_V_SCALE); - saawrite(v_gain[yacl], SAA7146_HPS_V_GAIN); - saawrite(((SAA7146_MC2_UPLD_DMA1 | SAA7146_MC2_UPLD_HPS_V | - SAA7146_MC2_UPLD_HPS_H) << 16) | (SAA7146_MC2_UPLD_DMA1 | - SAA7146_MC2_UPLD_HPS_V | SAA7146_MC2_UPLD_HPS_H), SAA7146_MC2); -} - -/* clip_draw_rectangle(cm,x,y,w,h) -- handle clipping an area - * bitmap is fixed width, 128 bytes (1024 pixels represented) - * arranged most-sigificant-bit-left in 32-bit words - * based on saa7146 clipping hardware, it swaps bytes if LE - * much of this makes up for egcs brain damage -- so if you - * are wondering "why did he do this?" it is because the C - * was adjusted to generate the optimal asm output without - * writing non-portable __asm__ directives. - */ - -static void clip_draw_rectangle(u32 *clipmap, int x, int y, int w, int h) -{ - register int startword, endword; - register u32 bitsleft, bitsright; - u32 *temp; - if (x < 0) { - w += x; - x = 0; - } - if (y < 0) { - h += y; - y = 0; - } - if (w <= 0 || h <= 0 || x > 1023 || y > 639) - return; /* throw away bad clips */ - if (x + w > 1024) - w = 1024 - x; - if (y + h > 640) - h = 640 - y; - startword = (x >> 5); - endword = ((x + w) >> 5); - bitsleft = (0xffffffff >> (x & 31)); - bitsright = (0xffffffff << (~((x + w) - (endword << 5)))); - temp = &clipmap[(y << 5) + startword]; - w = endword - startword; - if (!w) { - bitsleft |= bitsright; - for (y = 0; y < h; y++) { - *temp |= bitsleft; - temp += 32; - } - } else { - for (y = 0; y < h; y++) { - *temp++ |= bitsleft; - for (x = 1; x < w; x++) - *temp++ = 0xffffffff; - *temp |= bitsright; - temp += (32 - w); - } - } -} - -static void make_clip_tab(struct saa7146 *saa, struct video_clip *cr, int ncr) -{ - int i, width, height; - u32 *clipmap; - - clipmap = saa->dmavid2; - if ((width = saa->win.width) > 1023) - width = 1023; /* sanity check */ - if ((height = saa->win.height) > 640) - height = 639; /* sanity check */ - if (ncr > 0) { /* rectangles pased */ - /* convert rectangular clips to a bitmap */ - memset(clipmap, 0, VIDEO_CLIPMAP_SIZE); /* clear map */ - for (i = 0; i < ncr; i++) - clip_draw_rectangle(clipmap, cr[i].x, cr[i].y, - cr[i].width, cr[i].height); - } - /* clip against viewing window AND screen - so we do not have to rely on the user program - */ - clip_draw_rectangle(clipmap, (saa->win.x + width > saa->win.swidth) ? - (saa->win.swidth - saa->win.x) : width, 0, 1024, 768); - clip_draw_rectangle(clipmap, 0, - (saa->win.y + height > saa->win.sheight) ? - (saa->win.sheight - saa->win.y) : height, 1024, 768); - if (saa->win.x < 0) - clip_draw_rectangle(clipmap, 0, 0, -saa->win.x, 768); - if (saa->win.y < 0) - clip_draw_rectangle(clipmap, 0, 0, 1024, -saa->win.y); -} - -static long saa_ioctl(struct file *file, - unsigned int cmd, unsigned long argl) -{ - struct saa7146 *saa = file->private_data; - void __user *arg = (void __user *)argl; - - switch (cmd) { - case VIDIOCGCAP: - { - struct video_capability b; - strcpy(b.name, saa->video_dev.name); - b.type = VID_TYPE_CAPTURE | VID_TYPE_OVERLAY | - VID_TYPE_CLIPPING | VID_TYPE_FRAMERAM | - VID_TYPE_SCALES; - b.channels = 1; - b.audios = 1; - b.maxwidth = 768; - b.maxheight = 576; - b.minwidth = 32; - b.minheight = 32; - if (copy_to_user(arg, &b, sizeof(b))) - return -EFAULT; - return 0; - } - case VIDIOCGPICT: - { - struct video_picture p = saa->picture; - if (saa->win.depth == 8) - p.palette = VIDEO_PALETTE_HI240; - if (saa->win.depth == 15) - p.palette = VIDEO_PALETTE_RGB555; - if (saa->win.depth == 16) - p.palette = VIDEO_PALETTE_RGB565; - if (saa->win.depth == 24) - p.palette = VIDEO_PALETTE_RGB24; - if (saa->win.depth == 32) - p.palette = VIDEO_PALETTE_RGB32; - if (copy_to_user(arg, &p, sizeof(p))) - return -EFAULT; - return 0; - } - case VIDIOCSPICT: - { - struct video_picture p; - u32 format; - if (copy_from_user(&p, arg, sizeof(p))) - return -EFAULT; - if (p.palette < ARRAY_SIZE(palette2fmt)) { - format = palette2fmt[p.palette]; - saa->win.color_fmt = format; - saawrite(format | 0x60, - SAA7146_CLIP_FORMAT_CTRL); - } - saawrite(((p.brightness & 0xff00) << 16) | - ((p.contrast & 0xfe00) << 7) | - ((p.colour & 0xfe00) >> 9), SAA7146_BCS_CTRL); - saa->picture = p; - /* upload changed registers */ - saawrite(((SAA7146_MC2_UPLD_HPS_H | - SAA7146_MC2_UPLD_HPS_V) << 16) | - SAA7146_MC2_UPLD_HPS_H | - SAA7146_MC2_UPLD_HPS_V, SAA7146_MC2); - return 0; - } - case VIDIOCSWIN: - { - struct video_window vw; - struct video_clip *vcp = NULL; - - if (copy_from_user(&vw, arg, sizeof(vw))) - return -EFAULT; - - /* stop capture */ - if (vw.flags || vw.width < 16 || vw.height < 16) { - saawrite((SAA7146_MC1_TR_E_1 << 16), - SAA7146_MC1); - return -EINVAL; - } - /* 32-bit align start and adjust width */ - if (saa->win.bpp < 4) { - int i = vw.x; - vw.x = (vw.x + 3) & ~3; - i = vw.x - i; - vw.width -= i; - } - saa->win.x = vw.x; - saa->win.y = vw.y; - saa->win.width = vw.width; - if (saa->win.width > 768) - saa->win.width = 768; - saa->win.height = vw.height; - if (CurrentMode == VIDEO_MODE_NTSC) { - if (saa->win.height > 480) - saa->win.height = 480; - } else { - if (saa->win.height > 576) - saa->win.height = 576; - } - - /* stop capture */ - saawrite((SAA7146_MC1_TR_E_1 << 16), SAA7146_MC1); - saa7146_set_winsize(saa); - - /* - * Do any clips. - */ - if (vw.clipcount < 0) { - if (copy_from_user(saa->dmavid2, vw.clips, - VIDEO_CLIPMAP_SIZE)) - return -EFAULT; - } else if (vw.clipcount > 16384) { - return -EINVAL; - } else if (vw.clipcount > 0) { - vcp = vmalloc(sizeof(struct video_clip) * - vw.clipcount); - if (vcp == NULL) - return -ENOMEM; - if (copy_from_user(vcp, vw.clips, - sizeof(struct video_clip) * - vw.clipcount)) { - vfree(vcp); - return -EFAULT; - } - } else /* nothing clipped */ - memset(saa->dmavid2, 0, VIDEO_CLIPMAP_SIZE); - - make_clip_tab(saa, vcp, vw.clipcount); - if (vw.clipcount > 0) - vfree(vcp); - - /* start capture & clip dma if we have an address */ - if ((saa->cap & 3) && saa->win.vidadr != 0) - saawrite(((SAA7146_MC1_TR_E_1 | - SAA7146_MC1_TR_E_2) << 16) | 0xffff, - SAA7146_MC1); - return 0; - } - case VIDIOCGWIN: - { - struct video_window vw; - vw.x = saa->win.x; - vw.y = saa->win.y; - vw.width = saa->win.width; - vw.height = saa->win.height; - vw.chromakey = 0; - vw.flags = 0; - if (copy_to_user(arg, &vw, sizeof(vw))) - return -EFAULT; - return 0; - } - case VIDIOCCAPTURE: - { - int v; - if (copy_from_user(&v, arg, sizeof(v))) - return -EFAULT; - if (v == 0) { - saa->cap &= ~1; - saawrite((SAA7146_MC1_TR_E_1 << 16), - SAA7146_MC1); - } else { - if (saa->win.vidadr == 0 || saa->win.width == 0 - || saa->win.height == 0) - return -EINVAL; - saa->cap |= 1; - saawrite((SAA7146_MC1_TR_E_1 << 16) | 0xffff, - SAA7146_MC1); - } - return 0; - } - case VIDIOCGFBUF: - { - struct video_buffer v; - v.base = (void *)saa->win.vidadr; - v.height = saa->win.sheight; - v.width = saa->win.swidth; - v.depth = saa->win.depth; - v.bytesperline = saa->win.bpl; - if (copy_to_user(arg, &v, sizeof(v))) - return -EFAULT; - return 0; - - } - case VIDIOCSFBUF: - { - struct video_buffer v; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - if (copy_from_user(&v, arg, sizeof(v))) - return -EFAULT; - if (v.depth != 8 && v.depth != 15 && v.depth != 16 && - v.depth != 24 && v.depth != 32 && v.width > 16 && - v.height > 16 && v.bytesperline > 16) - return -EINVAL; - if (v.base) - saa->win.vidadr = (unsigned long)v.base; - saa->win.sheight = v.height; - saa->win.swidth = v.width; - saa->win.bpp = ((v.depth + 7) & 0x38) / 8; - saa->win.depth = v.depth; - saa->win.bpl = v.bytesperline; - - DEBUG(printk("Display at %p is %d by %d, bytedepth %d, " - "bpl %d\n", v.base, v.width, v.height, - saa->win.bpp, saa->win.bpl)); - saa7146_set_winsize(saa); - return 0; - } - case VIDIOCKEY: - { - /* Will be handled higher up .. */ - return 0; - } - - case VIDIOCGAUDIO: - { - struct video_audio v; - v = saa->audio_dev; - v.flags &= ~(VIDEO_AUDIO_MUTE | VIDEO_AUDIO_MUTABLE); - v.flags |= VIDEO_AUDIO_MUTABLE | VIDEO_AUDIO_VOLUME; - strcpy(v.name, "MPEG"); - v.mode = VIDEO_SOUND_STEREO; - if (copy_to_user(arg, &v, sizeof(v))) - return -EFAULT; - return 0; - } - case VIDIOCSAUDIO: - { - struct video_audio v; - int i; - if (copy_from_user(&v, arg, sizeof(v))) - return -EFAULT; - i = (~(v.volume >> 8)) & 0xff; - if (!HaveCS4341) { - if (v.flags & VIDEO_AUDIO_MUTE) - debiwrite(saa, debNormal, - IBM_MP2_FRNT_ATTEN, 0xffff, 2); - if (!(v.flags & VIDEO_AUDIO_MUTE)) - debiwrite(saa, debNormal, - IBM_MP2_FRNT_ATTEN, 0x0000, 2); - if (v.flags & VIDEO_AUDIO_VOLUME) - debiwrite(saa, debNormal, - IBM_MP2_FRNT_ATTEN, - (i << 8) | i, 2); - } else { - if (v.flags & VIDEO_AUDIO_MUTE) - cs4341_setlevel(saa, 0xff, 0xff); - if (!(v.flags & VIDEO_AUDIO_MUTE)) - cs4341_setlevel(saa, 0, 0); - if (v.flags & VIDEO_AUDIO_VOLUME) - cs4341_setlevel(saa, i, i); - } - saa->audio_dev = v; - return 0; - } - - case VIDIOCGUNIT: - { - struct video_unit vu; - vu.video = saa->video_dev.minor; - vu.vbi = VIDEO_NO_UNIT; - vu.radio = VIDEO_NO_UNIT; - vu.audio = VIDEO_NO_UNIT; - vu.teletext = VIDEO_NO_UNIT; - if (copy_to_user(arg, &vu, sizeof(vu))) - return -EFAULT; - return 0; - } - case VIDIOCSPLAYMODE: - { - struct video_play_mode pmode; - if (copy_from_user((void *)&pmode, arg, - sizeof(struct video_play_mode))) - return -EFAULT; - switch (pmode.mode) { - case VID_PLAY_VID_OUT_MODE: - if (pmode.p1 != VIDEO_MODE_NTSC && - pmode.p1 != VIDEO_MODE_PAL) - return -EINVAL; - set_out_format(saa, pmode.p1); - return 0; - case VID_PLAY_GENLOCK: - debiwrite(saa, debNormal, XILINX_CTL0, - pmode.p1 ? 0x8000 : 0x8080, 2); - if (NewCard) - set_genlock_offset(saa, pmode.p2); - return 0; - case VID_PLAY_NORMAL: - debiwrite(saa, debNormal, - IBM_MP2_CHIP_CONTROL, ChipControl, 2); - ibm_send_command(saa, IBM_MP2_PLAY, 0, 0); - saa->playmode = pmode.mode; - return 0; - case VID_PLAY_PAUSE: - /* IBM removed the PAUSE command */ - /* they say use SINGLE_FRAME now */ - case VID_PLAY_SINGLE_FRAME: - ibm_send_command(saa, IBM_MP2_SINGLE_FRAME,0,0); - if (saa->playmode == pmode.mode) { - debiwrite(saa, debNormal, - IBM_MP2_CHIP_CONTROL, - ChipControl, 2); - } - saa->playmode = pmode.mode; - return 0; - case VID_PLAY_FAST_FORWARD: - ibm_send_command(saa, IBM_MP2_FAST_FORWARD,0,0); - saa->playmode = pmode.mode; - return 0; - case VID_PLAY_SLOW_MOTION: - ibm_send_command(saa, IBM_MP2_SLOW_MOTION, - pmode.p1, 0); - saa->playmode = pmode.mode; - return 0; - case VID_PLAY_IMMEDIATE_NORMAL: - /* ensure transfers resume */ - debiwrite(saa, debNormal, - IBM_MP2_CHIP_CONTROL, ChipControl, 2); - ibm_send_command(saa, IBM_MP2_IMED_NORM_PLAY, - 0, 0); - saa->playmode = VID_PLAY_NORMAL; - return 0; - case VID_PLAY_SWITCH_CHANNELS: - saa->audhead = saa->audtail = 0; - saa->vidhead = saa->vidtail = 0; - ibm_send_command(saa, IBM_MP2_FREEZE_FRAME,0,1); - ibm_send_command(saa, IBM_MP2_RESET_AUD_RATE, - 0, 1); - debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, - 0, 2); - ibm_send_command(saa, IBM_MP2_CHANNEL_SWITCH, - 0, 1); - debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, - ChipControl, 2); - ibm_send_command(saa, IBM_MP2_PLAY, 0, 0); - saa->playmode = VID_PLAY_NORMAL; - return 0; - case VID_PLAY_FREEZE_FRAME: - ibm_send_command(saa, IBM_MP2_FREEZE_FRAME,0,0); - saa->playmode = pmode.mode; - return 0; - case VID_PLAY_STILL_MODE: - ibm_send_command(saa, IBM_MP2_SET_STILL_MODE, - 0, 0); - saa->playmode = pmode.mode; - return 0; - case VID_PLAY_MASTER_MODE: - if (pmode.p1 == VID_PLAY_MASTER_NONE) - saa->boardcfg[1] = 0x13; - else if (pmode.p1 == VID_PLAY_MASTER_VIDEO) - saa->boardcfg[1] = 0x23; - else if (pmode.p1 == VID_PLAY_MASTER_AUDIO) - saa->boardcfg[1] = 0x43; - else - return -EINVAL; - debiwrite(saa, debNormal, - IBM_MP2_CHIP_CONTROL, ChipControl, 2); - return 0; - case VID_PLAY_ACTIVE_SCANLINES: - if (CurrentMode == VIDEO_MODE_PAL) { - if (pmode.p1 < 1 || pmode.p2 > 625) - return -EINVAL; - saa->boardcfg[5] = pmode.p1; - saa->boardcfg[55] = (pmode.p1 + - (pmode.p2 / 2) - 1) & 0xff; - } else { - if (pmode.p1 < 4 || pmode.p2 > 525) - return -EINVAL; - saa->boardcfg[4] = pmode.p1; - saa->boardcfg[54] = (pmode.p1 + - (pmode.p2 / 2) - 4) & 0xff; - } - set_out_format(saa, CurrentMode); - case VID_PLAY_RESET: - return do_ibm_reset(saa); - case VID_PLAY_END_MARK: - if (saa->endmarktail < saa->endmarkhead) { - if (saa->endmarkhead - - saa->endmarktail < 2) - return -ENOSPC; - } else if (saa->endmarkhead <=saa->endmarktail){ - if (saa->endmarktail - saa->endmarkhead - > (MAX_MARKS - 2)) - return -ENOSPC; - } else - return -ENOSPC; - saa->endmark[saa->endmarktail] = saa->audtail; - saa->endmarktail++; - if (saa->endmarktail >= MAX_MARKS) - saa->endmarktail = 0; - } - return -EINVAL; - } - case VIDIOCSWRITEMODE: - { - int mode; - if (copy_from_user((void *)&mode, arg, sizeof(int))) - return -EFAULT; - if (mode == VID_WRITE_MPEG_AUD || - mode == VID_WRITE_MPEG_VID || - mode == VID_WRITE_CC || - mode == VID_WRITE_TTX || - mode == VID_WRITE_OSD) { - saa->writemode = mode; - return 0; - } - return -EINVAL; - } - case VIDIOCSMICROCODE: - { - struct video_code ucode; - __u8 *udata; - int i; - if (copy_from_user(&ucode, arg, sizeof(ucode))) - return -EFAULT; - if (ucode.datasize > 65536 || ucode.datasize < 1024 || - strncmp(ucode.loadwhat, "dec", 3)) - return -EINVAL; - if ((udata = vmalloc(ucode.datasize)) == NULL) - return -ENOMEM; - if (copy_from_user(udata, ucode.data, ucode.datasize)) { - vfree(udata); - return -EFAULT; - } - ucode.data = udata; - if (!strncmp(ucode.loadwhat, "decoder.aud", 11) || - !strncmp(ucode.loadwhat, "decoder.vid", 11)) - i = initialize_ibmmpeg2(&ucode); - else - i = initialize_fpga(&ucode); - vfree(udata); - if (i) - return -EINVAL; - return 0; - - } - case VIDIOCGCHAN: /* this makes xawtv happy */ - { - struct video_channel v; - if (copy_from_user(&v, arg, sizeof(v))) - return -EFAULT; - v.flags = VIDEO_VC_AUDIO; - v.tuners = 0; - v.type = VID_TYPE_MPEG_DECODER; - v.norm = CurrentMode; - strcpy(v.name, "MPEG2"); - if (copy_to_user(arg, &v, sizeof(v))) - return -EFAULT; - return 0; - } - case VIDIOCSCHAN: /* this makes xawtv happy */ - { - struct video_channel v; - if (copy_from_user(&v, arg, sizeof(v))) - return -EFAULT; - /* do nothing */ - return 0; - } - default: - return -ENOIOCTLCMD; - } - return 0; -} - -static int saa_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct saa7146 *saa = file->private_data; - printk(KERN_DEBUG "stradis%d: saa_mmap called\n", saa->nr); - return -EINVAL; -} - -static ssize_t saa_read(struct file *file, char __user * buf, - size_t count, loff_t * ppos) -{ - return -EINVAL; -} - -static ssize_t saa_write(struct file *file, const char __user * buf, - size_t count, loff_t * ppos) -{ - struct saa7146 *saa = file->private_data; - unsigned long todo = count; - int blocksize, split; - unsigned long flags; - - while (todo > 0) { - if (saa->writemode == VID_WRITE_MPEG_AUD) { - spin_lock_irqsave(&saa->lock, flags); - if (saa->audhead <= saa->audtail) - blocksize = 65536 - - (saa->audtail - saa->audhead); - else - blocksize = saa->audhead - saa->audtail; - spin_unlock_irqrestore(&saa->lock, flags); - if (blocksize < 16384) { - saawrite(SAA7146_PSR_DEBI_S | - SAA7146_PSR_PIN1, SAA7146_IER); - saawrite(SAA7146_PSR_PIN1, SAA7146_PSR); - /* wait for buffer space to open */ - interruptible_sleep_on(&saa->audq); - } - spin_lock_irqsave(&saa->lock, flags); - if (saa->audhead <= saa->audtail) { - blocksize = 65536 - - (saa->audtail - saa->audhead); - split = 65536 - saa->audtail; - } else { - blocksize = saa->audhead - saa->audtail; - split = 65536; - } - spin_unlock_irqrestore(&saa->lock, flags); - blocksize--; - if (blocksize > todo) - blocksize = todo; - /* double check that we really have space */ - if (!blocksize) - return -ENOSPC; - if (split < blocksize) { - if (copy_from_user(saa->audbuf + - saa->audtail, buf, split)) - return -EFAULT; - buf += split; - todo -= split; - blocksize -= split; - saa->audtail = 0; - } - if (copy_from_user(saa->audbuf + saa->audtail, buf, - blocksize)) - return -EFAULT; - saa->audtail += blocksize; - todo -= blocksize; - buf += blocksize; - saa->audtail &= 0xffff; - } else if (saa->writemode == VID_WRITE_MPEG_VID) { - spin_lock_irqsave(&saa->lock, flags); - if (saa->vidhead <= saa->vidtail) - blocksize = 524288 - - (saa->vidtail - saa->vidhead); - else - blocksize = saa->vidhead - saa->vidtail; - spin_unlock_irqrestore(&saa->lock, flags); - if (blocksize < 65536) { - saawrite(SAA7146_PSR_DEBI_S | - SAA7146_PSR_PIN1, SAA7146_IER); - saawrite(SAA7146_PSR_PIN1, SAA7146_PSR); - /* wait for buffer space to open */ - interruptible_sleep_on(&saa->vidq); - } - spin_lock_irqsave(&saa->lock, flags); - if (saa->vidhead <= saa->vidtail) { - blocksize = 524288 - - (saa->vidtail - saa->vidhead); - split = 524288 - saa->vidtail; - } else { - blocksize = saa->vidhead - saa->vidtail; - split = 524288; - } - spin_unlock_irqrestore(&saa->lock, flags); - blocksize--; - if (blocksize > todo) - blocksize = todo; - /* double check that we really have space */ - if (!blocksize) - return -ENOSPC; - if (split < blocksize) { - if (copy_from_user(saa->vidbuf + - saa->vidtail, buf, split)) - return -EFAULT; - buf += split; - todo -= split; - blocksize -= split; - saa->vidtail = 0; - } - if (copy_from_user(saa->vidbuf + saa->vidtail, buf, - blocksize)) - return -EFAULT; - saa->vidtail += blocksize; - todo -= blocksize; - buf += blocksize; - saa->vidtail &= 0x7ffff; - } else if (saa->writemode == VID_WRITE_OSD) { - if (count > 131072) - return -ENOSPC; - if (copy_from_user(saa->osdbuf, buf, count)) - return -EFAULT; - buf += count; - saa->osdhead = 0; - saa->osdtail = count; - debiwrite(saa, debNormal, IBM_MP2_OSD_ADDR, 0, 2); - debiwrite(saa, debNormal, IBM_MP2_OSD_LINK_ADDR, 0, 2); - debiwrite(saa, debNormal, IBM_MP2_MASK0, 0xc00d, 2); - debiwrite(saa, debNormal, IBM_MP2_DISP_MODE, - debiread(saa, debNormal, - IBM_MP2_DISP_MODE, 2) | 1, 2); - /* trigger osd data transfer */ - saawrite(SAA7146_PSR_DEBI_S | - SAA7146_PSR_PIN1, SAA7146_IER); - saawrite(SAA7146_PSR_PIN1, SAA7146_PSR); - } - } - return count; -} - -static int saa_open(struct file *file) -{ - struct video_device *vdev = video_devdata(file); - struct saa7146 *saa = container_of(vdev, struct saa7146, video_dev); - - lock_kernel(); - file->private_data = saa; - - saa->user++; - if (saa->user > 1) { - unlock_kernel(); - return 0; /* device open already, don't reset */ - } - saa->writemode = VID_WRITE_MPEG_VID; /* default to video */ - unlock_kernel(); - return 0; -} - -static int saa_release(struct file *file) -{ - struct saa7146 *saa = file->private_data; - saa->user--; - - if (saa->user > 0) /* still someone using device */ - return 0; - saawrite(0x007f0000, SAA7146_MC1); /* stop all overlay dma */ - return 0; -} - -static const struct v4l2_file_operations saa_fops = { - .owner = THIS_MODULE, - .open = saa_open, - .release = saa_release, - .ioctl = saa_ioctl, - .read = saa_read, - .write = saa_write, - .mmap = saa_mmap, -}; - -/* template for video_device-structure */ -static struct video_device saa_template = { - .name = "SAA7146A", - .fops = &saa_fops, - .release = video_device_release_empty, -}; - -static int __devinit configure_saa7146(struct pci_dev *pdev, int num) -{ - int retval; - struct saa7146 *saa = pci_get_drvdata(pdev); - - saa->endmarkhead = saa->endmarktail = 0; - saa->win.x = saa->win.y = 0; - saa->win.width = saa->win.cropwidth = 720; - saa->win.height = saa->win.cropheight = 480; - saa->win.cropx = saa->win.cropy = 0; - saa->win.bpp = 2; - saa->win.depth = 16; - saa->win.color_fmt = palette2fmt[VIDEO_PALETTE_RGB565]; - saa->win.bpl = 1024 * saa->win.bpp; - saa->win.swidth = 1024; - saa->win.sheight = 768; - saa->picture.brightness = 32768; - saa->picture.contrast = 38768; - saa->picture.colour = 32768; - saa->cap = 0; - saa->nr = num; - saa->playmode = VID_PLAY_NORMAL; - memset(saa->boardcfg, 0, 64); /* clear board config area */ - saa->saa7146_mem = NULL; - saa->dmavid1 = saa->dmavid2 = saa->dmavid3 = saa->dmaa1in = - saa->dmaa1out = saa->dmaa2in = saa->dmaa2out = - saa->pagevid1 = saa->pagevid2 = saa->pagevid3 = saa->pagea1in = - saa->pagea1out = saa->pagea2in = saa->pagea2out = - saa->pagedebi = saa->dmaRPS1 = saa->dmaRPS2 = saa->pageRPS1 = - saa->pageRPS2 = NULL; - saa->audbuf = saa->vidbuf = saa->osdbuf = saa->dmadebi = NULL; - saa->audhead = saa->vidtail = 0; - - init_waitqueue_head(&saa->i2cq); - init_waitqueue_head(&saa->audq); - init_waitqueue_head(&saa->debiq); - init_waitqueue_head(&saa->vidq); - spin_lock_init(&saa->lock); - - retval = pci_enable_device(pdev); - if (retval) { - dev_err(&pdev->dev, "%d: pci_enable_device failed!\n", num); - goto err; - } - - saa->id = pdev->device; - saa->irq = pdev->irq; - saa->saa7146_adr = pci_resource_start(pdev, 0); - pci_read_config_byte(pdev, PCI_CLASS_REVISION, &saa->revision); - - saa->saa7146_mem = ioremap(saa->saa7146_adr, 0x200); - if (saa->saa7146_mem == NULL) { - dev_err(&pdev->dev, "%d: ioremap failed!\n", num); - retval = -EIO; - goto err; - } - - memcpy(&saa->video_dev, &saa_template, sizeof(saa_template)); - saawrite(0, SAA7146_IER); /* turn off all interrupts */ - - retval = request_irq(saa->irq, saa7146_irq, IRQF_SHARED | IRQF_DISABLED, - "stradis", saa); - if (retval == -EINVAL) - dev_err(&pdev->dev, "%d: Bad irq number or handler\n", num); - else if (retval == -EBUSY) - dev_err(&pdev->dev, "%d: IRQ %ld busy, change your PnP config " - "in BIOS\n", num, saa->irq); - if (retval < 0) - goto errio; - - pci_set_master(pdev); - retval = video_register_device(&saa->video_dev, VFL_TYPE_GRABBER, - video_nr); - if (retval < 0) { - dev_err(&pdev->dev, "%d: error in registering video device!\n", - num); - goto errio; - } - - return 0; -errio: - iounmap(saa->saa7146_mem); -err: - return retval; -} - -static int __devinit init_saa7146(struct pci_dev *pdev) -{ - struct saa7146 *saa = pci_get_drvdata(pdev); - - saa->user = 0; - /* reset the saa7146 */ - saawrite(0xffff0000, SAA7146_MC1); - mdelay(5); - /* enable debi and i2c transfers and pins */ - saawrite(((SAA7146_MC1_EDP | SAA7146_MC1_EI2C | - SAA7146_MC1_TR_E_DEBI) << 16) | 0xffff, SAA7146_MC1); - /* ensure proper state of chip */ - saawrite(0x00000000, SAA7146_PAGE1); - saawrite(0x00f302c0, SAA7146_NUM_LINE_BYTE1); - saawrite(0x00000000, SAA7146_PAGE2); - saawrite(0x01400080, SAA7146_NUM_LINE_BYTE2); - saawrite(0x00000000, SAA7146_DD1_INIT); - saawrite(0x00000000, SAA7146_DD1_STREAM_B); - saawrite(0x00000000, SAA7146_DD1_STREAM_A); - saawrite(0x00000000, SAA7146_BRS_CTRL); - saawrite(0x80400040, SAA7146_BCS_CTRL); - saawrite(0x0000e000 /*| (1<<29) */ , SAA7146_HPS_CTRL); - saawrite(0x00000060, SAA7146_CLIP_FORMAT_CTRL); - saawrite(0x00000000, SAA7146_ACON1); - saawrite(0x00000000, SAA7146_ACON2); - saawrite(0x00000600, SAA7146_I2C_STATUS); - saawrite(((SAA7146_MC2_UPLD_D1_B | SAA7146_MC2_UPLD_D1_A | - SAA7146_MC2_UPLD_BRS | SAA7146_MC2_UPLD_HPS_H | - SAA7146_MC2_UPLD_HPS_V | SAA7146_MC2_UPLD_DMA2 | - SAA7146_MC2_UPLD_DMA1 | SAA7146_MC2_UPLD_I2C) << 16) | 0xffff, - SAA7146_MC2); - /* setup arbitration control registers */ - saawrite(0x1412121a, SAA7146_PCI_BT_V1); - - /* allocate 32k dma buffer + 4k for page table */ - if ((saa->dmadebi = kmalloc(32768 + 4096, GFP_KERNEL)) == NULL) { - dev_err(&pdev->dev, "%d: debi kmalloc failed\n", saa->nr); - goto err; - } -#if 0 - saa->pagedebi = saa->dmadebi + 32768; /* top 4k is for mmu */ - saawrite(virt_to_bus(saa->pagedebi) /*|0x800 */ , SAA7146_DEBI_PAGE); - for (i = 0; i < 12; i++) /* setup mmu page table */ - saa->pagedebi[i] = virt_to_bus((saa->dmadebi + i * 4096)); -#endif - saa->audhead = saa->vidhead = saa->osdhead = 0; - saa->audtail = saa->vidtail = saa->osdtail = 0; - if (saa->vidbuf == NULL && (saa->vidbuf = vmalloc(524288)) == NULL) { - dev_err(&pdev->dev, "%d: malloc failed\n", saa->nr); - goto err; - } - if (saa->audbuf == NULL && (saa->audbuf = vmalloc(65536)) == NULL) { - dev_err(&pdev->dev, "%d: malloc failed\n", saa->nr); - goto errfree; - } - if (saa->osdbuf == NULL && (saa->osdbuf = vmalloc(131072)) == NULL) { - dev_err(&pdev->dev, "%d: malloc failed\n", saa->nr); - goto errfree; - } - /* allocate 81920 byte buffer for clipping */ - if ((saa->dmavid2 = kzalloc(VIDEO_CLIPMAP_SIZE, GFP_KERNEL)) == NULL) { - dev_err(&pdev->dev, "%d: clip kmalloc failed\n", saa->nr); - goto errfree; - } - /* setup clipping registers */ - saawrite(virt_to_bus(saa->dmavid2), SAA7146_BASE_EVEN2); - saawrite(virt_to_bus(saa->dmavid2) + 128, SAA7146_BASE_ODD2); - saawrite(virt_to_bus(saa->dmavid2) + VIDEO_CLIPMAP_SIZE, - SAA7146_PROT_ADDR2); - saawrite(256, SAA7146_PITCH2); - saawrite(4, SAA7146_PAGE2); /* dma direction: read, no byteswap */ - saawrite(((SAA7146_MC2_UPLD_DMA2) << 16) | SAA7146_MC2_UPLD_DMA2, - SAA7146_MC2); - I2CBusScan(saa); - - return 0; -errfree: - vfree(saa->osdbuf); - vfree(saa->audbuf); - vfree(saa->vidbuf); - saa->audbuf = saa->osdbuf = saa->vidbuf = NULL; -err: - return -ENOMEM; -} - -static void stradis_release_saa(struct pci_dev *pdev) -{ - u8 command; - struct saa7146 *saa = pci_get_drvdata(pdev); - - /* turn off all capturing, DMA and IRQs */ - saawrite(0xffff0000, SAA7146_MC1); /* reset chip */ - saawrite(0, SAA7146_MC2); - saawrite(0, SAA7146_IER); - saawrite(0xffffffffUL, SAA7146_ISR); - - /* disable PCI bus-mastering */ - pci_read_config_byte(pdev, PCI_COMMAND, &command); - command &= ~PCI_COMMAND_MASTER; - pci_write_config_byte(pdev, PCI_COMMAND, command); - - /* unmap and free memory */ - saa->audhead = saa->audtail = saa->osdhead = 0; - saa->vidhead = saa->vidtail = saa->osdtail = 0; - vfree(saa->vidbuf); - vfree(saa->audbuf); - vfree(saa->osdbuf); - kfree(saa->dmavid2); - saa->audbuf = saa->vidbuf = saa->osdbuf = NULL; - saa->dmavid2 = NULL; - kfree(saa->dmadebi); - kfree(saa->dmavid1); - kfree(saa->dmavid3); - kfree(saa->dmaa1in); - kfree(saa->dmaa1out); - kfree(saa->dmaa2in); - kfree(saa->dmaa2out); - kfree(saa->dmaRPS1); - kfree(saa->dmaRPS2); - free_irq(saa->irq, saa); - if (saa->saa7146_mem) - iounmap(saa->saa7146_mem); - if (video_is_registered(&saa->video_dev)) - video_unregister_device(&saa->video_dev); -} - -static int __devinit stradis_probe(struct pci_dev *pdev, - const struct pci_device_id *ent) -{ - int retval = -EINVAL; - - if (saa_num >= SAA7146_MAX) - goto err; - - if (!pdev->subsystem_vendor) - dev_info(&pdev->dev, "%d: rev1 decoder\n", saa_num); - else - dev_info(&pdev->dev, "%d: SDM2xx found\n", saa_num); - - pci_set_drvdata(pdev, &saa7146s[saa_num]); - - retval = configure_saa7146(pdev, saa_num); - if (retval) { - dev_err(&pdev->dev, "%d: error in configuring\n", saa_num); - goto err; - } - - if (init_saa7146(pdev) < 0) { - dev_err(&pdev->dev, "%d: error in initialization\n", saa_num); - retval = -EIO; - goto errrel; - } - - saa_num++; - - return 0; -errrel: - stradis_release_saa(pdev); -err: - return retval; -} - -static void __devexit stradis_remove(struct pci_dev *pdev) -{ - stradis_release_saa(pdev); -} - -static struct pci_device_id stradis_pci_tbl[] = { - { PCI_DEVICE(PCI_VENDOR_ID_PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7146) }, - { 0 } -}; - - -static struct pci_driver stradis_driver = { - .name = "stradis", - .id_table = stradis_pci_tbl, - .probe = stradis_probe, - .remove = __devexit_p(stradis_remove) -}; - -static int __init stradis_init(void) -{ - int retval; - - saa_num = 0; - - retval = pci_register_driver(&stradis_driver); - if (retval) - printk(KERN_ERR "stradis: Unable to register pci driver.\n"); - - return retval; -} - -static void __exit stradis_exit(void) -{ - pci_unregister_driver(&stradis_driver); - printk(KERN_INFO "stradis: module cleanup complete\n"); -} - -module_init(stradis_init); -module_exit(stradis_exit); diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 303c52cfe915..078525ca19d0 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -53,6 +53,8 @@ source "drivers/staging/tm6000/Kconfig" source "drivers/staging/cpia/Kconfig" +source "drivers/staging/stradis/Kconfig" + source "drivers/staging/usbip/Kconfig" source "drivers/staging/winbond/Kconfig" diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index ddcac245c99a..057adca4d04d 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_VIDEO_GO7007) += go7007/ obj-$(CONFIG_VIDEO_CX25821) += cx25821/ obj-$(CONFIG_VIDEO_TM6000) += tm6000/ obj-$(CONFIG_VIDEO_CPIA) += cpia/ +obj-$(CONFIG_VIDEO_STRADIS) += stradis/ obj-$(CONFIG_LIRC_STAGING) += lirc/ obj-$(CONFIG_USB_IP_COMMON) += usbip/ obj-$(CONFIG_W35UND) += winbond/ diff --git a/drivers/staging/stradis/Kconfig b/drivers/staging/stradis/Kconfig new file mode 100644 index 000000000000..92e891141896 --- /dev/null +++ b/drivers/staging/stradis/Kconfig @@ -0,0 +1,7 @@ +config VIDEO_STRADIS + tristate "Stradis 4:2:2 MPEG-2 video driver (DEPRECATED)" + depends on EXPERIMENTAL && PCI && VIDEO_V4L1 && VIRT_TO_BUS + help + Say Y here to enable support for the Stradis 4:2:2 MPEG-2 video + driver for PCI. There is a product page at + . diff --git a/drivers/staging/stradis/Makefile b/drivers/staging/stradis/Makefile new file mode 100644 index 000000000000..0f1feab59e39 --- /dev/null +++ b/drivers/staging/stradis/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_VIDEO_STRADIS) += stradis.o + +EXTRA_CFLAGS += -Idrivers/media/video diff --git a/drivers/staging/stradis/TODO b/drivers/staging/stradis/TODO new file mode 100644 index 000000000000..f48150fe2fa9 --- /dev/null +++ b/drivers/staging/stradis/TODO @@ -0,0 +1,6 @@ +This is an obsolete driver for ancient stradis hardware. +We couldn't find anyone with this hardware in order to port it to use V4L2. + +If nobody take care on it, the driver will be removed for 2.6.38. + +Please send patches to linux-media@vger.kernel.org diff --git a/drivers/staging/stradis/stradis.c b/drivers/staging/stradis/stradis.c new file mode 100644 index 000000000000..a057824e7ebc --- /dev/null +++ b/drivers/staging/stradis/stradis.c @@ -0,0 +1,2213 @@ +/* + * stradis.c - stradis 4:2:2 mpeg decoder driver + * + * Stradis 4:2:2 MPEG-2 Decoder Driver + * Copyright (C) 1999 Nathan Laredo + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "saa7146.h" +#include "saa7146reg.h" +#include "ibmmpeg2.h" +#include "saa7121.h" +#include "cs8420.h" + +#define DEBUG(x) /* debug driver */ +#undef IDEBUG /* debug irq handler */ +#undef MDEBUG /* debug memory management */ + +#define SAA7146_MAX 6 + +static struct saa7146 saa7146s[SAA7146_MAX]; + +static int saa_num; /* number of SAA7146s in use */ + +static int video_nr = -1; +module_param(video_nr, int, 0); +MODULE_LICENSE("GPL"); + +#define nDebNormal 0x00480000 +#define nDebNoInc 0x00480000 +#define nDebVideo 0xd0480000 +#define nDebAudio 0xd0400000 +#define nDebDMA 0x02c80000 + +#define oDebNormal 0x13c80000 +#define oDebNoInc 0x13c80000 +#define oDebVideo 0xd1080000 +#define oDebAudio 0xd1080000 +#define oDebDMA 0x03080000 + +#define NewCard (saa->boardcfg[3]) +#define ChipControl (saa->boardcfg[1]) +#define NTSCFirstActive (saa->boardcfg[4]) +#define PALFirstActive (saa->boardcfg[5]) +#define NTSCLastActive (saa->boardcfg[54]) +#define PALLastActive (saa->boardcfg[55]) +#define Have2MB (saa->boardcfg[18] & 0x40) +#define HaveCS8420 (saa->boardcfg[18] & 0x04) +#define IBMMPEGCD20 (saa->boardcfg[18] & 0x20) +#define HaveCS3310 (saa->boardcfg[18] & 0x01) +#define CS3310MaxLvl ((saa->boardcfg[30] << 8) | saa->boardcfg[31]) +#define HaveCS4341 (saa->boardcfg[40] == 2) +#define SDIType (saa->boardcfg[27]) +#define CurrentMode (saa->boardcfg[2]) + +#define debNormal (NewCard ? nDebNormal : oDebNormal) +#define debNoInc (NewCard ? nDebNoInc : oDebNoInc) +#define debVideo (NewCard ? nDebVideo : oDebVideo) +#define debAudio (NewCard ? nDebAudio : oDebAudio) +#define debDMA (NewCard ? nDebDMA : oDebDMA) + +#ifdef USE_RESCUE_EEPROM_SDM275 +static unsigned char rescue_eeprom[64] = { + 0x00, 0x01, 0x04, 0x13, 0x26, 0x0f, 0x10, 0x00, 0x00, 0x00, 0x43, 0x63, + 0x22, 0x01, 0x29, 0x15, 0x73, 0x00, 0x1f, 'd', 'e', 'c', 'x', 'l', + 'd', 'v', 'a', 0x02, 0x00, 0x01, 0x00, 0xcc, 0xa4, 0x63, 0x09, 0xe2, + 0x10, 0x00, 0x0a, 0x00, 0x02, 0x02, 'd', 'e', 'c', 'x', 'l', 'a', + 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, +}; +#endif + +/* ----------------------------------------------------------------------- */ +/* Hardware I2C functions */ +static void I2CWipe(struct saa7146 *saa) +{ + int i; + /* set i2c to ~=100kHz, abort transfer, clear busy */ + saawrite(0x600 | SAA7146_I2C_ABORT, SAA7146_I2C_STATUS); + saawrite((SAA7146_MC2_UPLD_I2C << 16) | + SAA7146_MC2_UPLD_I2C, SAA7146_MC2); + /* wait for i2c registers to be programmed */ + for (i = 0; i < 1000 && + !(saaread(SAA7146_MC2) & SAA7146_MC2_UPLD_I2C); i++) + schedule(); + saawrite(0x600, SAA7146_I2C_STATUS); + saawrite((SAA7146_MC2_UPLD_I2C << 16) | + SAA7146_MC2_UPLD_I2C, SAA7146_MC2); + /* wait for i2c registers to be programmed */ + for (i = 0; i < 1000 && + !(saaread(SAA7146_MC2) & SAA7146_MC2_UPLD_I2C); i++) + schedule(); + saawrite(0x600, SAA7146_I2C_STATUS); + saawrite((SAA7146_MC2_UPLD_I2C << 16) | + SAA7146_MC2_UPLD_I2C, SAA7146_MC2); + /* wait for i2c registers to be programmed */ + for (i = 0; i < 1000 && + !(saaread(SAA7146_MC2) & SAA7146_MC2_UPLD_I2C); i++) + schedule(); +} + +/* read I2C */ +static int I2CRead(struct saa7146 *saa, unsigned char addr, + unsigned char subaddr, int dosub) +{ + int i; + + if (saaread(SAA7146_I2C_STATUS) & 0x3c) + I2CWipe(saa); + for (i = 0; + i < 1000 && (saaread(SAA7146_I2C_STATUS) & SAA7146_I2C_BUSY); + i++) + schedule(); + if (i == 1000) + I2CWipe(saa); + if (dosub) + saawrite(((addr & 0xfe) << 24) | (((addr | 1) & 0xff) << 8) | + ((subaddr & 0xff) << 16) | 0xed, SAA7146_I2C_TRANSFER); + else + saawrite(((addr & 0xfe) << 24) | (((addr | 1) & 0xff) << 16) | + 0xf1, SAA7146_I2C_TRANSFER); + saawrite((SAA7146_MC2_UPLD_I2C << 16) | + SAA7146_MC2_UPLD_I2C, SAA7146_MC2); + /* wait for i2c registers to be programmed */ + for (i = 0; i < 1000 && + !(saaread(SAA7146_MC2) & SAA7146_MC2_UPLD_I2C); i++) + schedule(); + /* wait for valid data */ + for (i = 0; i < 1000 && + (saaread(SAA7146_I2C_STATUS) & SAA7146_I2C_BUSY); i++) + schedule(); + if (saaread(SAA7146_I2C_STATUS) & SAA7146_I2C_ERR) + return -1; + if (i == 1000) + printk("i2c setup read timeout\n"); + saawrite(0x41, SAA7146_I2C_TRANSFER); + saawrite((SAA7146_MC2_UPLD_I2C << 16) | + SAA7146_MC2_UPLD_I2C, SAA7146_MC2); + /* wait for i2c registers to be programmed */ + for (i = 0; i < 1000 && + !(saaread(SAA7146_MC2) & SAA7146_MC2_UPLD_I2C); i++) + schedule(); + /* wait for valid data */ + for (i = 0; i < 1000 && + (saaread(SAA7146_I2C_TRANSFER) & SAA7146_I2C_BUSY); i++) + schedule(); + if (saaread(SAA7146_I2C_TRANSFER) & SAA7146_I2C_ERR) + return -1; + if (i == 1000) + printk("i2c read timeout\n"); + return ((saaread(SAA7146_I2C_TRANSFER) >> 24) & 0xff); +} + +/* set both to write both bytes, reset it to write only b1 */ + +static int I2CWrite(struct saa7146 *saa, unsigned char addr, unsigned char b1, + unsigned char b2, int both) +{ + int i; + u32 data; + + if (saaread(SAA7146_I2C_STATUS) & 0x3c) + I2CWipe(saa); + for (i = 0; i < 1000 && + (saaread(SAA7146_I2C_STATUS) & SAA7146_I2C_BUSY); i++) + schedule(); + if (i == 1000) + I2CWipe(saa); + data = ((addr & 0xfe) << 24) | ((b1 & 0xff) << 16); + if (both) + data |= ((b2 & 0xff) << 8) | 0xe5; + else + data |= 0xd1; + saawrite(data, SAA7146_I2C_TRANSFER); + saawrite((SAA7146_MC2_UPLD_I2C << 16) | SAA7146_MC2_UPLD_I2C, + SAA7146_MC2); + return 0; +} + +static void attach_inform(struct saa7146 *saa, int id) +{ + int i; + + DEBUG(printk(KERN_DEBUG "stradis%d: i2c: device found=%02x\n", saa->nr, + id)); + if (id == 0xa0) { /* we have rev2 or later board, fill in info */ + for (i = 0; i < 64; i++) + saa->boardcfg[i] = I2CRead(saa, 0xa0, i, 1); +#ifdef USE_RESCUE_EEPROM_SDM275 + if (saa->boardcfg[0] != 0) { + printk("stradis%d: WARNING: EEPROM STORED VALUES HAVE " + "BEEN IGNORED\n", saa->nr); + for (i = 0; i < 64; i++) + saa->boardcfg[i] = rescue_eeprom[i]; + } +#endif + printk("stradis%d: config =", saa->nr); + for (i = 0; i < 51; i++) { + printk(" %02x", saa->boardcfg[i]); + } + printk("\n"); + } +} + +static void I2CBusScan(struct saa7146 *saa) +{ + int i; + for (i = 0; i < 0xff; i += 2) + if ((I2CRead(saa, i, 0, 0)) >= 0) + attach_inform(saa, i); +} + +static int debiwait_maxwait; + +static int wait_for_debi_done(struct saa7146 *saa) +{ + int i; + + /* wait for registers to be programmed */ + for (i = 0; i < 100000 && + !(saaread(SAA7146_MC2) & SAA7146_MC2_UPLD_DEBI); i++) + saaread(SAA7146_MC2); + /* wait for transfer to complete */ + for (i = 0; i < 500000 && + (saaread(SAA7146_PSR) & SAA7146_PSR_DEBI_S); i++) + saaread(SAA7146_MC2); + + if (i > debiwait_maxwait) + printk("wait-for-debi-done maxwait: %d\n", + debiwait_maxwait = i); + + if (i == 500000) + return -1; + + return 0; +} + +static int debiwrite(struct saa7146 *saa, u32 config, int addr, + u32 val, int count) +{ + u32 cmd; + if (count <= 0 || count > 32764) + return -1; + if (wait_for_debi_done(saa) < 0) + return -1; + saawrite(config, SAA7146_DEBI_CONFIG); + if (count <= 4) /* immediate transfer */ + saawrite(val, SAA7146_DEBI_AD); + else /* block transfer */ + saawrite(virt_to_bus(saa->dmadebi), SAA7146_DEBI_AD); + saawrite((cmd = (count << 17) | (addr & 0xffff)), SAA7146_DEBI_COMMAND); + saawrite((SAA7146_MC2_UPLD_DEBI << 16) | SAA7146_MC2_UPLD_DEBI, + SAA7146_MC2); + return 0; +} + +static u32 debiread(struct saa7146 *saa, u32 config, int addr, int count) +{ + u32 result = 0; + + if (count > 32764 || count <= 0) + return 0; + if (wait_for_debi_done(saa) < 0) + return 0; + saawrite(virt_to_bus(saa->dmadebi), SAA7146_DEBI_AD); + saawrite((count << 17) | 0x10000 | (addr & 0xffff), + SAA7146_DEBI_COMMAND); + saawrite(config, SAA7146_DEBI_CONFIG); + saawrite((SAA7146_MC2_UPLD_DEBI << 16) | SAA7146_MC2_UPLD_DEBI, + SAA7146_MC2); + if (count > 4) /* not an immediate transfer */ + return count; + wait_for_debi_done(saa); + result = saaread(SAA7146_DEBI_AD); + if (count == 1) + result &= 0xff; + if (count == 2) + result &= 0xffff; + if (count == 3) + result &= 0xffffff; + return result; +} + +static void do_irq_send_data(struct saa7146 *saa) +{ + int split, audbytes, vidbytes; + + saawrite(SAA7146_PSR_PIN1, SAA7146_IER); + /* if special feature mode in effect, disable audio sending */ + if (saa->playmode != VID_PLAY_NORMAL) + saa->audtail = saa->audhead = 0; + if (saa->audhead <= saa->audtail) + audbytes = saa->audtail - saa->audhead; + else + audbytes = 65536 - (saa->audhead - saa->audtail); + if (saa->vidhead <= saa->vidtail) + vidbytes = saa->vidtail - saa->vidhead; + else + vidbytes = 524288 - (saa->vidhead - saa->vidtail); + if (audbytes == 0 && vidbytes == 0 && saa->osdtail == saa->osdhead) { + saawrite(0, SAA7146_IER); + return; + } + /* if at least 1 block audio waiting and audio fifo isn't full */ + if (audbytes >= 2048 && (debiread(saa, debNormal, IBM_MP2_AUD_FIFO, 2) + & 0xff) < 60) { + if (saa->audhead > saa->audtail) + split = 65536 - saa->audhead; + else + split = 0; + audbytes = 2048; + if (split > 0 && split < 2048) { + memcpy(saa->dmadebi, saa->audbuf + saa->audhead, split); + saa->audhead = 0; + audbytes -= split; + } else + split = 0; + memcpy(saa->dmadebi + split, saa->audbuf + saa->audhead, + audbytes); + saa->audhead += audbytes; + saa->audhead &= 0xffff; + debiwrite(saa, debAudio, (NewCard ? IBM_MP2_AUD_FIFO : + IBM_MP2_AUD_FIFOW), 0, 2048); + wake_up_interruptible(&saa->audq); + /* if at least 1 block video waiting and video fifo isn't full */ + } else if (vidbytes >= 30720 && (debiread(saa, debNormal, + IBM_MP2_FIFO, 2)) < 16384) { + if (saa->vidhead > saa->vidtail) + split = 524288 - saa->vidhead; + else + split = 0; + vidbytes = 30720; + if (split > 0 && split < 30720) { + memcpy(saa->dmadebi, saa->vidbuf + saa->vidhead, split); + saa->vidhead = 0; + vidbytes -= split; + } else + split = 0; + memcpy(saa->dmadebi + split, saa->vidbuf + saa->vidhead, + vidbytes); + saa->vidhead += vidbytes; + saa->vidhead &= 0x7ffff; + debiwrite(saa, debVideo, (NewCard ? IBM_MP2_FIFO : + IBM_MP2_FIFOW), 0, 30720); + wake_up_interruptible(&saa->vidq); + } + saawrite(SAA7146_PSR_DEBI_S | SAA7146_PSR_PIN1, SAA7146_IER); +} + +static void send_osd_data(struct saa7146 *saa) +{ + int size = saa->osdtail - saa->osdhead; + if (size > 30720) + size = 30720; + /* ensure some multiple of 8 bytes is transferred */ + size = 8 * ((size + 8) >> 3); + if (size) { + debiwrite(saa, debNormal, IBM_MP2_OSD_ADDR, + (saa->osdhead >> 3), 2); + memcpy(saa->dmadebi, &saa->osdbuf[saa->osdhead], size); + saa->osdhead += size; + /* block transfer of next 8 bytes to ~32k bytes */ + debiwrite(saa, debNormal, IBM_MP2_OSD_DATA, 0, size); + } + if (saa->osdhead >= saa->osdtail) { + saa->osdhead = saa->osdtail = 0; + debiwrite(saa, debNormal, IBM_MP2_MASK0, 0xc00c, 2); + } +} + +static irqreturn_t saa7146_irq(int irq, void *dev_id) +{ + struct saa7146 *saa = dev_id; + u32 stat, astat; + int count; + int handled = 0; + + count = 0; + while (1) { + /* get/clear interrupt status bits */ + stat = saaread(SAA7146_ISR); + astat = stat & saaread(SAA7146_IER); + if (!astat) + break; + handled = 1; + saawrite(astat, SAA7146_ISR); + if (astat & SAA7146_PSR_DEBI_S) { + do_irq_send_data(saa); + } + if (astat & SAA7146_PSR_PIN1) { + int istat; + /* the following read will trigger DEBI_S */ + istat = debiread(saa, debNormal, IBM_MP2_HOST_INT, 2); + if (istat & 1) { + saawrite(0, SAA7146_IER); + send_osd_data(saa); + saawrite(SAA7146_PSR_DEBI_S | + SAA7146_PSR_PIN1, SAA7146_IER); + } + if (istat & 0x20) { /* Video Start */ + saa->vidinfo.frame_count++; + } + if (istat & 0x400) { /* Picture Start */ + /* update temporal reference */ + } + if (istat & 0x200) { /* Picture Resolution Change */ + /* read new resolution */ + } + if (istat & 0x100) { /* New User Data found */ + /* read new user data */ + } + if (istat & 0x1000) { /* new GOP/SMPTE */ + /* read new SMPTE */ + } + if (istat & 0x8000) { /* Sequence Start Code */ + /* reset frame counter, load sizes */ + saa->vidinfo.frame_count = 0; + saa->vidinfo.h_size = 704; + saa->vidinfo.v_size = 480; +#if 0 + if (saa->endmarkhead != saa->endmarktail) { + saa->audhead = + saa->endmark[saa->endmarkhead]; + saa->endmarkhead++; + if (saa->endmarkhead >= MAX_MARKS) + saa->endmarkhead = 0; + } +#endif + } + if (istat & 0x4000) { /* Sequence Error Code */ + if (saa->endmarkhead != saa->endmarktail) { + saa->audhead = + saa->endmark[saa->endmarkhead]; + saa->endmarkhead++; + if (saa->endmarkhead >= MAX_MARKS) + saa->endmarkhead = 0; + } + } + } +#ifdef IDEBUG + if (astat & SAA7146_PSR_PPEF) { + IDEBUG(printk("stradis%d irq: PPEF\n", saa->nr)); + } + if (astat & SAA7146_PSR_PABO) { + IDEBUG(printk("stradis%d irq: PABO\n", saa->nr)); + } + if (astat & SAA7146_PSR_PPED) { + IDEBUG(printk("stradis%d irq: PPED\n", saa->nr)); + } + if (astat & SAA7146_PSR_RPS_I1) { + IDEBUG(printk("stradis%d irq: RPS_I1\n", saa->nr)); + } + if (astat & SAA7146_PSR_RPS_I0) { + IDEBUG(printk("stradis%d irq: RPS_I0\n", saa->nr)); + } + if (astat & SAA7146_PSR_RPS_LATE1) { + IDEBUG(printk("stradis%d irq: RPS_LATE1\n", saa->nr)); + } + if (astat & SAA7146_PSR_RPS_LATE0) { + IDEBUG(printk("stradis%d irq: RPS_LATE0\n", saa->nr)); + } + if (astat & SAA7146_PSR_RPS_E1) { + IDEBUG(printk("stradis%d irq: RPS_E1\n", saa->nr)); + } + if (astat & SAA7146_PSR_RPS_E0) { + IDEBUG(printk("stradis%d irq: RPS_E0\n", saa->nr)); + } + if (astat & SAA7146_PSR_RPS_TO1) { + IDEBUG(printk("stradis%d irq: RPS_TO1\n", saa->nr)); + } + if (astat & SAA7146_PSR_RPS_TO0) { + IDEBUG(printk("stradis%d irq: RPS_TO0\n", saa->nr)); + } + if (astat & SAA7146_PSR_UPLD) { + IDEBUG(printk("stradis%d irq: UPLD\n", saa->nr)); + } + if (astat & SAA7146_PSR_DEBI_E) { + IDEBUG(printk("stradis%d irq: DEBI_E\n", saa->nr)); + } + if (astat & SAA7146_PSR_I2C_S) { + IDEBUG(printk("stradis%d irq: I2C_S\n", saa->nr)); + } + if (astat & SAA7146_PSR_I2C_E) { + IDEBUG(printk("stradis%d irq: I2C_E\n", saa->nr)); + } + if (astat & SAA7146_PSR_A2_IN) { + IDEBUG(printk("stradis%d irq: A2_IN\n", saa->nr)); + } + if (astat & SAA7146_PSR_A2_OUT) { + IDEBUG(printk("stradis%d irq: A2_OUT\n", saa->nr)); + } + if (astat & SAA7146_PSR_A1_IN) { + IDEBUG(printk("stradis%d irq: A1_IN\n", saa->nr)); + } + if (astat & SAA7146_PSR_A1_OUT) { + IDEBUG(printk("stradis%d irq: A1_OUT\n", saa->nr)); + } + if (astat & SAA7146_PSR_AFOU) { + IDEBUG(printk("stradis%d irq: AFOU\n", saa->nr)); + } + if (astat & SAA7146_PSR_V_PE) { + IDEBUG(printk("stradis%d irq: V_PE\n", saa->nr)); + } + if (astat & SAA7146_PSR_VFOU) { + IDEBUG(printk("stradis%d irq: VFOU\n", saa->nr)); + } + if (astat & SAA7146_PSR_FIDA) { + IDEBUG(printk("stradis%d irq: FIDA\n", saa->nr)); + } + if (astat & SAA7146_PSR_FIDB) { + IDEBUG(printk("stradis%d irq: FIDB\n", saa->nr)); + } + if (astat & SAA7146_PSR_PIN3) { + IDEBUG(printk("stradis%d irq: PIN3\n", saa->nr)); + } + if (astat & SAA7146_PSR_PIN2) { + IDEBUG(printk("stradis%d irq: PIN2\n", saa->nr)); + } + if (astat & SAA7146_PSR_PIN0) { + IDEBUG(printk("stradis%d irq: PIN0\n", saa->nr)); + } + if (astat & SAA7146_PSR_ECS) { + IDEBUG(printk("stradis%d irq: ECS\n", saa->nr)); + } + if (astat & SAA7146_PSR_EC3S) { + IDEBUG(printk("stradis%d irq: EC3S\n", saa->nr)); + } + if (astat & SAA7146_PSR_EC0S) { + IDEBUG(printk("stradis%d irq: EC0S\n", saa->nr)); + } +#endif + count++; + if (count > 15) + printk(KERN_WARNING "stradis%d: irq loop %d\n", + saa->nr, count); + if (count > 20) { + saawrite(0, SAA7146_IER); + printk(KERN_ERR + "stradis%d: IRQ loop cleared\n", saa->nr); + } + } + return IRQ_RETVAL(handled); +} + +static int ibm_send_command(struct saa7146 *saa, + int command, int data, int chain) +{ + int i; + + if (chain) + debiwrite(saa, debNormal, IBM_MP2_COMMAND, (command << 1)| 1,2); + else + debiwrite(saa, debNormal, IBM_MP2_COMMAND, command << 1, 2); + debiwrite(saa, debNormal, IBM_MP2_CMD_DATA, data, 2); + debiwrite(saa, debNormal, IBM_MP2_CMD_STAT, 1, 2); + for (i = 0; i < 100 && + (debiread(saa, debNormal, IBM_MP2_CMD_STAT, 2) & 1); i++) + schedule(); + if (i == 100) + return -1; + return 0; +} + +static void cs4341_setlevel(struct saa7146 *saa, int left, int right) +{ + I2CWrite(saa, 0x22, 0x03, left > 94 ? 94 : left, 2); + I2CWrite(saa, 0x22, 0x04, right > 94 ? 94 : right, 2); +} + +static void initialize_cs4341(struct saa7146 *saa) +{ + int i; + for (i = 0; i < 200; i++) { + /* auto mute off, power on, no de-emphasis */ + /* I2S data up to 24-bit 64xFs internal SCLK */ + I2CWrite(saa, 0x22, 0x01, 0x11, 2); + /* ATAPI mixer settings */ + I2CWrite(saa, 0x22, 0x02, 0x49, 2); + /* attenuation left 3db */ + I2CWrite(saa, 0x22, 0x03, 0x00, 2); + /* attenuation right 3db */ + I2CWrite(saa, 0x22, 0x04, 0x00, 2); + I2CWrite(saa, 0x22, 0x01, 0x10, 2); + if (I2CRead(saa, 0x22, 0x02, 1) == 0x49) + break; + schedule(); + } + printk("stradis%d: CS4341 initialized (%d)\n", saa->nr, i); + return; +} + +static void initialize_cs8420(struct saa7146 *saa, int pro) +{ + int i; + u8 *sequence; + if (pro) + sequence = mode8420pro; + else + sequence = mode8420con; + for (i = 0; i < INIT8420LEN; i++) + I2CWrite(saa, 0x20, init8420[i * 2], init8420[i * 2 + 1], 2); + for (i = 0; i < MODE8420LEN; i++) + I2CWrite(saa, 0x20, sequence[i * 2], sequence[i * 2 + 1], 2); + printk("stradis%d: CS8420 initialized\n", saa->nr); +} + +static void initialize_saa7121(struct saa7146 *saa, int dopal) +{ + int i, mod; + u8 *sequence; + if (dopal) + sequence = init7121pal; + else + sequence = init7121ntsc; + mod = saaread(SAA7146_PSR) & 0x08; + /* initialize PAL/NTSC video encoder */ + for (i = 0; i < INIT7121LEN; i++) { + if (NewCard) { /* handle new card encoder differences */ + if (sequence[i * 2] == 0x3a) + I2CWrite(saa, 0x88, 0x3a, 0x13, 2); + else if (sequence[i * 2] == 0x6b) + I2CWrite(saa, 0x88, 0x6b, 0x20, 2); + else if (sequence[i * 2] == 0x6c) + I2CWrite(saa, 0x88, 0x6c, + dopal ? 0x09 : 0xf5, 2); + else if (sequence[i * 2] == 0x6d) + I2CWrite(saa, 0x88, 0x6d, + dopal ? 0x20 : 0x00, 2); + else if (sequence[i * 2] == 0x7a) + I2CWrite(saa, 0x88, 0x7a, + dopal ? (PALFirstActive - 1) : + (NTSCFirstActive - 4), 2); + else if (sequence[i * 2] == 0x7b) + I2CWrite(saa, 0x88, 0x7b, + dopal ? PALLastActive : + NTSCLastActive, 2); + else + I2CWrite(saa, 0x88, sequence[i * 2], + sequence[i * 2 + 1], 2); + } else { + if (sequence[i * 2] == 0x6b && mod) + I2CWrite(saa, 0x88, 0x6b, + (sequence[i * 2 + 1] ^ 0x09), 2); + else if (sequence[i * 2] == 0x7a) + I2CWrite(saa, 0x88, 0x7a, + dopal ? (PALFirstActive - 1) : + (NTSCFirstActive - 4), 2); + else if (sequence[i * 2] == 0x7b) + I2CWrite(saa, 0x88, 0x7b, + dopal ? PALLastActive : + NTSCLastActive, 2); + else + I2CWrite(saa, 0x88, sequence[i * 2], + sequence[i * 2 + 1], 2); + } + } +} + +static void set_genlock_offset(struct saa7146 *saa, int noffset) +{ + int nCode; + int PixelsPerLine = 858; + if (CurrentMode == VIDEO_MODE_PAL) + PixelsPerLine = 864; + if (noffset > 500) + noffset = 500; + else if (noffset < -500) + noffset = -500; + nCode = noffset + 0x100; + if (nCode == 1) + nCode = 0x401; + else if (nCode < 1) + nCode = 0x400 + PixelsPerLine + nCode; + debiwrite(saa, debNormal, XILINX_GLDELAY, nCode, 2); +} + +static void set_out_format(struct saa7146 *saa, int mode) +{ + initialize_saa7121(saa, (mode == VIDEO_MODE_NTSC ? 0 : 1)); + saa->boardcfg[2] = mode; + /* do not adjust analog video parameters here, use saa7121 init */ + /* you will affect the SDI output on the new card */ + if (mode == VIDEO_MODE_PAL) { /* PAL */ + debiwrite(saa, debNormal, XILINX_CTL0, 0x0808, 2); + mdelay(50); + saawrite(0x012002c0, SAA7146_NUM_LINE_BYTE1); + if (NewCard) { + debiwrite(saa, debNormal, IBM_MP2_DISP_MODE, 0xe100, 2); + mdelay(50); + } + debiwrite(saa, debNormal, IBM_MP2_DISP_MODE, + NewCard ? 0xe500 : 0x6500, 2); + debiwrite(saa, debNormal, IBM_MP2_DISP_DLY, + (1 << 8) | + (NewCard ? PALFirstActive : PALFirstActive - 6), 2); + } else { /* NTSC */ + debiwrite(saa, debNormal, XILINX_CTL0, 0x0800, 2); + mdelay(50); + saawrite(0x00f002c0, SAA7146_NUM_LINE_BYTE1); + debiwrite(saa, debNormal, IBM_MP2_DISP_MODE, + NewCard ? 0xe100 : 0x6100, 2); + debiwrite(saa, debNormal, IBM_MP2_DISP_DLY, + (1 << 8) | + (NewCard ? NTSCFirstActive : NTSCFirstActive - 6), 2); + } +} + +/* Intialize bitmangler to map from a byte value to the mangled word that + * must be output to program the Xilinx part through the DEBI port. + * Xilinx Data Bit->DEBI Bit: 0->15 1->7 2->6 3->12 4->11 5->2 6->1 7->0 + * transfer FPGA code, init IBM chip, transfer IBM microcode + * rev2 card mangles: 0->7 1->6 2->5 3->4 4->3 5->2 6->1 7->0 + */ +static u16 bitmangler[256]; + +static int initialize_fpga(struct video_code *bitdata) +{ + int i, num, startindex, failure = 0, loadtwo, loadfile = 0; + u16 *dmabuf; + u8 *newdma; + struct saa7146 *saa; + + /* verify fpga code */ + for (startindex = 0; startindex < bitdata->datasize; startindex++) + if (bitdata->data[startindex] == 255) + break; + if (startindex == bitdata->datasize) { + printk(KERN_INFO "stradis: bad fpga code\n"); + return -1; + } + /* initialize all detected cards */ + for (num = 0; num < saa_num; num++) { + saa = &saa7146s[num]; + if (saa->boardcfg[0] > 20) + continue; /* card was programmed */ + loadtwo = (saa->boardcfg[18] & 0x10); + if (!NewCard) /* we have an old board */ + for (i = 0; i < 256; i++) + bitmangler[i] = ((i & 0x01) << 15) | + ((i & 0x02) << 6) | ((i & 0x04) << 4) | + ((i & 0x08) << 9) | ((i & 0x10) << 7) | + ((i & 0x20) >> 3) | ((i & 0x40) >> 5) | + ((i & 0x80) >> 7); + else /* else we have a new board */ + for (i = 0; i < 256; i++) + bitmangler[i] = ((i & 0x01) << 7) | + ((i & 0x02) << 5) | ((i & 0x04) << 3) | + ((i & 0x08) << 1) | ((i & 0x10) >> 1) | + ((i & 0x20) >> 3) | ((i & 0x40) >> 5) | + ((i & 0x80) >> 7); + + dmabuf = (u16 *) saa->dmadebi; + newdma = (u8 *) saa->dmadebi; + if (NewCard) { /* SDM2xxx */ + if (!strncmp(bitdata->loadwhat, "decoder2", 8)) + continue; /* fpga not for this card */ + if (!strncmp(&saa->boardcfg[42], bitdata->loadwhat, 8)) + loadfile = 1; + else if (loadtwo && !strncmp(&saa->boardcfg[19], + bitdata->loadwhat, 8)) + loadfile = 2; + else if (!saa->boardcfg[42] && !strncmp("decxl", + bitdata->loadwhat, 8)) + loadfile = 1; /* special */ + else + continue; /* fpga not for this card */ + if (loadfile != 1 && loadfile != 2) + continue; /* skip to next card */ + if (saa->boardcfg[0] && loadfile == 1) + continue; /* skip to next card */ + if (saa->boardcfg[0] != 1 && loadfile == 2) + continue; /* skip to next card */ + saa->boardcfg[0]++; /* mark fpga handled */ + printk("stradis%d: loading %s\n", saa->nr, + bitdata->loadwhat); + if (loadtwo && loadfile == 2) + goto send_fpga_stuff; + /* turn on the Audio interface to set PROG low */ + saawrite(0x00400040, SAA7146_GPIO_CTRL); + saaread(SAA7146_PSR); /* ensure posted write */ + /* wait for everyone to reset */ + mdelay(10); + saawrite(0x00400000, SAA7146_GPIO_CTRL); + } else { /* original card */ + if (strncmp(bitdata->loadwhat, "decoder2", 8)) + continue; /* fpga not for this card */ + /* Pull the Xilinx PROG signal WS3 low */ + saawrite(0x02000200, SAA7146_MC1); + /* Turn on the Audio interface so can set PROG low */ + saawrite(0x000000c0, SAA7146_ACON1); + /* Pull the Xilinx INIT signal (GPIO2) low */ + saawrite(0x00400000, SAA7146_GPIO_CTRL); + /* Make sure everybody resets */ + saaread(SAA7146_PSR); /* ensure posted write */ + mdelay(10); + /* Release the Xilinx PROG signal */ + saawrite(0x00000000, SAA7146_ACON1); + /* Turn off the Audio interface */ + saawrite(0x02000000, SAA7146_MC1); + } + /* Release Xilinx INIT signal (WS2) */ + saawrite(0x00000000, SAA7146_GPIO_CTRL); + /* Wait for the INIT to go High */ + for (i = 0; + i < 10000 && !(saaread(SAA7146_PSR) & SAA7146_PSR_PIN2); + i++) + schedule(); + if (i == 1000) { + printk(KERN_INFO "stradis%d: no fpga INIT\n", saa->nr); + return -1; + } +send_fpga_stuff: + if (NewCard) { + for (i = startindex; i < bitdata->datasize; i++) + newdma[i - startindex] = + bitmangler[bitdata->data[i]]; + debiwrite(saa, 0x01420000, 0, 0, + ((bitdata->datasize - startindex) + 5)); + if (loadtwo && loadfile == 1) { + printk("stradis%d: awaiting 2nd FPGA bitfile\n", + saa->nr); + continue; /* skip to next card */ + } + } else { + for (i = startindex; i < bitdata->datasize; i++) + dmabuf[i - startindex] = + bitmangler[bitdata->data[i]]; + debiwrite(saa, 0x014a0000, 0, 0, + ((bitdata->datasize - startindex) + 5) * 2); + } + for (i = 0; + i < 1000 && !(saaread(SAA7146_PSR) & SAA7146_PSR_PIN2); + i++) + schedule(); + if (i == 1000) { + printk(KERN_INFO "stradis%d: FPGA load failed\n", + saa->nr); + failure++; + continue; + } + if (!NewCard) { + /* Pull the Xilinx INIT signal (GPIO2) low */ + saawrite(0x00400000, SAA7146_GPIO_CTRL); + saaread(SAA7146_PSR); /* ensure posted write */ + mdelay(2); + saawrite(0x00000000, SAA7146_GPIO_CTRL); + mdelay(2); + } + printk(KERN_INFO "stradis%d: FPGA Loaded\n", saa->nr); + saa->boardcfg[0] = 26; /* mark fpga programmed */ + /* set VXCO to its lowest frequency */ + debiwrite(saa, debNormal, XILINX_PWM, 0, 2); + if (NewCard) { + /* mute CS3310 */ + if (HaveCS3310) + debiwrite(saa, debNormal, XILINX_CS3310_CMPLT, + 0, 2); + /* set VXCO to PWM mode, release reset, blank on */ + debiwrite(saa, debNormal, XILINX_CTL0, 0xffc4, 2); + mdelay(10); + /* unmute CS3310 */ + if (HaveCS3310) + debiwrite(saa, debNormal, XILINX_CTL0, + 0x2020, 2); + } + /* set source Black */ + debiwrite(saa, debNormal, XILINX_CTL0, 0x1707, 2); + saa->boardcfg[4] = 22; /* set NTSC First Active Line */ + saa->boardcfg[5] = 23; /* set PAL First Active Line */ + saa->boardcfg[54] = 2; /* set NTSC Last Active Line - 256 */ + saa->boardcfg[55] = 54; /* set PAL Last Active Line - 256 */ + set_out_format(saa, VIDEO_MODE_NTSC); + mdelay(50); + /* begin IBM chip init */ + debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, 4, 2); + saaread(SAA7146_PSR); /* wait for reset */ + mdelay(5); + debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, 0, 2); + debiread(saa, debNormal, IBM_MP2_CHIP_CONTROL, 2); + debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, 0x10, 2); + debiwrite(saa, debNormal, IBM_MP2_CMD_ADDR, 0, 2); + debiwrite(saa, debNormal, IBM_MP2_CHIP_MODE, 0x2e, 2); + if (NewCard) { + mdelay(5); + /* set i2s rate converter to 48KHz */ + debiwrite(saa, debNormal, 0x80c0, 6, 2); + /* we must init CS8420 first since rev b pulls i2s */ + /* master clock low and CS4341 needs i2s master to */ + /* run the i2c port. */ + if (HaveCS8420) + /* 0=consumer, 1=pro */ + initialize_cs8420(saa, 0); + + mdelay(5); + if (HaveCS4341) + initialize_cs4341(saa); + } + debiwrite(saa, debNormal, IBM_MP2_INFC_CTL, 0x48, 2); + debiwrite(saa, debNormal, IBM_MP2_BEEP_CTL, 0xa000, 2); + debiwrite(saa, debNormal, IBM_MP2_DISP_LBOR, 0, 2); + debiwrite(saa, debNormal, IBM_MP2_DISP_TBOR, 0, 2); + if (NewCard) + set_genlock_offset(saa, 0); + debiwrite(saa, debNormal, IBM_MP2_FRNT_ATTEN, 0, 2); +#if 0 + /* enable genlock */ + debiwrite(saa, debNormal, XILINX_CTL0, 0x8000, 2); +#else + /* disable genlock */ + debiwrite(saa, debNormal, XILINX_CTL0, 0x8080, 2); +#endif + } + + return failure; +} + +static int do_ibm_reset(struct saa7146 *saa) +{ + /* failure if decoder not previously programmed */ + if (saa->boardcfg[0] < 37) + return -EIO; + /* mute CS3310 */ + if (HaveCS3310) + debiwrite(saa, debNormal, XILINX_CS3310_CMPLT, 0, 2); + /* disable interrupts */ + saawrite(0, SAA7146_IER); + saa->audhead = saa->audtail = 0; + saa->vidhead = saa->vidtail = 0; + /* tristate debi bus, disable debi transfers */ + saawrite(0x00880000, SAA7146_MC1); + /* ensure posted write */ + saaread(SAA7146_MC1); + mdelay(50); + /* re-enable debi transfers */ + saawrite(0x00880088, SAA7146_MC1); + /* set source Black */ + debiwrite(saa, debNormal, XILINX_CTL0, 0x1707, 2); + /* begin IBM chip init */ + set_out_format(saa, CurrentMode); + debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, 4, 2); + saaread(SAA7146_PSR); /* wait for reset */ + mdelay(5); + debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, 0, 2); + debiread(saa, debNormal, IBM_MP2_CHIP_CONTROL, 2); + debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, ChipControl, 2); + debiwrite(saa, debNormal, IBM_MP2_CHIP_MODE, 0x2e, 2); + if (NewCard) { + mdelay(5); + /* set i2s rate converter to 48KHz */ + debiwrite(saa, debNormal, 0x80c0, 6, 2); + /* we must init CS8420 first since rev b pulls i2s */ + /* master clock low and CS4341 needs i2s master to */ + /* run the i2c port. */ + if (HaveCS8420) + /* 0=consumer, 1=pro */ + initialize_cs8420(saa, 1); + + mdelay(5); + if (HaveCS4341) + initialize_cs4341(saa); + } + debiwrite(saa, debNormal, IBM_MP2_INFC_CTL, 0x48, 2); + debiwrite(saa, debNormal, IBM_MP2_BEEP_CTL, 0xa000, 2); + debiwrite(saa, debNormal, IBM_MP2_DISP_LBOR, 0, 2); + debiwrite(saa, debNormal, IBM_MP2_DISP_TBOR, 0, 2); + if (NewCard) + set_genlock_offset(saa, 0); + debiwrite(saa, debNormal, IBM_MP2_FRNT_ATTEN, 0, 2); + debiwrite(saa, debNormal, IBM_MP2_OSD_SIZE, 0x2000, 2); + debiwrite(saa, debNormal, IBM_MP2_AUD_CTL, 0x4552, 2); + if (ibm_send_command(saa, IBM_MP2_CONFIG_DECODER, + (ChipControl == 0x43 ? 0xe800 : 0xe000), 1)) { + printk(KERN_ERR "stradis%d: IBM config failed\n", saa->nr); + } + if (HaveCS3310) { + int i = CS3310MaxLvl; + debiwrite(saa, debNormal, XILINX_CS3310_CMPLT, ((i << 8)| i),2); + } + /* start video decoder */ + debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, ChipControl, 2); + /* 256k vid, 3520 bytes aud */ + debiwrite(saa, debNormal, IBM_MP2_RB_THRESHOLD, 0x4037, 2); + debiwrite(saa, debNormal, IBM_MP2_AUD_CTL, 0x4573, 2); + ibm_send_command(saa, IBM_MP2_PLAY, 0, 0); + /* enable buffer threshold irq */ + debiwrite(saa, debNormal, IBM_MP2_MASK0, 0xc00c, 2); + /* clear pending interrupts */ + debiread(saa, debNormal, IBM_MP2_HOST_INT, 2); + debiwrite(saa, debNormal, XILINX_CTL0, 0x1711, 2); + + return 0; +} + +/* load the decoder microcode */ +static int initialize_ibmmpeg2(struct video_code *microcode) +{ + int i, num; + struct saa7146 *saa; + + for (num = 0; num < saa_num; num++) { + saa = &saa7146s[num]; + /* check that FPGA is loaded */ + debiwrite(saa, debNormal, IBM_MP2_OSD_SIZE, 0xa55a, 2); + i = debiread(saa, debNormal, IBM_MP2_OSD_SIZE, 2); + if (i != 0xa55a) { + printk(KERN_INFO "stradis%d: %04x != 0xa55a\n", + saa->nr, i); +#if 0 + return -1; +#endif + } + if (!strncmp(microcode->loadwhat, "decoder.vid", 11)) { + if (saa->boardcfg[0] > 27) + continue; /* skip to next card */ + /* load video control store */ + saa->boardcfg[1] = 0x13; /* no-sync default */ + debiwrite(saa, debNormal, IBM_MP2_WR_PROT, 1, 2); + debiwrite(saa, debNormal, IBM_MP2_PROC_IADDR, 0, 2); + for (i = 0; i < microcode->datasize / 2; i++) + debiwrite(saa, debNormal, IBM_MP2_PROC_IDATA, + (microcode->data[i * 2] << 8) | + microcode->data[i * 2 + 1], 2); + debiwrite(saa, debNormal, IBM_MP2_PROC_IADDR, 0, 2); + debiwrite(saa, debNormal, IBM_MP2_WR_PROT, 0, 2); + debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, + ChipControl, 2); + saa->boardcfg[0] = 28; + } + if (!strncmp(microcode->loadwhat, "decoder.aud", 11)) { + if (saa->boardcfg[0] > 35) + continue; /* skip to next card */ + /* load audio control store */ + debiwrite(saa, debNormal, IBM_MP2_WR_PROT, 1, 2); + debiwrite(saa, debNormal, IBM_MP2_AUD_IADDR, 0, 2); + for (i = 0; i < microcode->datasize; i++) + debiwrite(saa, debNormal, IBM_MP2_AUD_IDATA, + microcode->data[i], 1); + debiwrite(saa, debNormal, IBM_MP2_AUD_IADDR, 0, 2); + debiwrite(saa, debNormal, IBM_MP2_WR_PROT, 0, 2); + debiwrite(saa, debNormal, IBM_MP2_OSD_SIZE, 0x2000, 2); + debiwrite(saa, debNormal, IBM_MP2_AUD_CTL, 0x4552, 2); + if (ibm_send_command(saa, IBM_MP2_CONFIG_DECODER, + 0xe000, 1)) { + printk(KERN_ERR "stradis%d: IBM config " + "failed\n", saa->nr); + return -1; + } + /* set PWM to center value */ + if (NewCard) { + debiwrite(saa, debNormal, XILINX_PWM, + saa->boardcfg[14] + + (saa->boardcfg[13] << 8), 2); + } else + debiwrite(saa, debNormal, XILINX_PWM, 0x46, 2); + + if (HaveCS3310) { + i = CS3310MaxLvl; + debiwrite(saa, debNormal, XILINX_CS3310_CMPLT, + (i << 8) | i, 2); + } + printk(KERN_INFO "stradis%d: IBM MPEGCD%d Inited\n", + saa->nr, 18 + (debiread(saa, debNormal, + IBM_MP2_CHIP_CONTROL, 2) >> 12)); + /* start video decoder */ + debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, + ChipControl, 2); + debiwrite(saa, debNormal, IBM_MP2_RB_THRESHOLD, 0x4037, + 2); /* 256k vid, 3520 bytes aud */ + debiwrite(saa, debNormal, IBM_MP2_AUD_CTL, 0x4573, 2); + ibm_send_command(saa, IBM_MP2_PLAY, 0, 0); + /* enable buffer threshold irq */ + debiwrite(saa, debNormal, IBM_MP2_MASK0, 0xc00c, 2); + debiread(saa, debNormal, IBM_MP2_HOST_INT, 2); + /* enable gpio irq */ + saawrite(0x00002000, SAA7146_GPIO_CTRL); + /* enable decoder output to HPS */ + debiwrite(saa, debNormal, XILINX_CTL0, 0x1711, 2); + saa->boardcfg[0] = 37; + } + } + + return 0; +} + +static u32 palette2fmt[] = { /* some of these YUV translations are wrong */ + 0xffffffff, 0x86000000, 0x87000000, 0x80000000, 0x8100000, 0x82000000, + 0x83000000, 0x00000000, 0x03000000, 0x03000000, 0x0a00000, 0x03000000, + 0x06000000, 0x00000000, 0x03000000, 0x0a000000, 0x0300000 +}; +static int bpp2fmt[4] = { + VIDEO_PALETTE_HI240, VIDEO_PALETTE_RGB565, VIDEO_PALETTE_RGB24, + VIDEO_PALETTE_RGB32 +}; + +/* I wish I could find a formula to calculate these... */ +static u32 h_prescale[64] = { + 0x10000000, 0x18040202, 0x18080000, 0x380c0606, 0x38100204, 0x38140808, + 0x38180000, 0x381c0000, 0x3820161c, 0x38242a3b, 0x38281230, 0x382c4460, + 0x38301040, 0x38340080, 0x38380000, 0x383c0000, 0x3840fefe, 0x3844ee9f, + 0x3848ee9f, 0x384cee9f, 0x3850ee9f, 0x38542a3b, 0x38581230, 0x385c0000, + 0x38600000, 0x38640000, 0x38680000, 0x386c0000, 0x38700000, 0x38740000, + 0x38780000, 0x387c0000, 0x30800000, 0x38840000, 0x38880000, 0x388c0000, + 0x38900000, 0x38940000, 0x38980000, 0x389c0000, 0x38a00000, 0x38a40000, + 0x38a80000, 0x38ac0000, 0x38b00000, 0x38b40000, 0x38b80000, 0x38bc0000, + 0x38c00000, 0x38c40000, 0x38c80000, 0x38cc0000, 0x38d00000, 0x38d40000, + 0x38d80000, 0x38dc0000, 0x38e00000, 0x38e40000, 0x38e80000, 0x38ec0000, + 0x38f00000, 0x38f40000, 0x38f80000, 0x38fc0000, +}; +static u32 v_gain[64] = { + 0x016000ff, 0x016100ff, 0x016100ff, 0x016200ff, 0x016200ff, 0x016200ff, + 0x016200ff, 0x016300ff, 0x016300ff, 0x016300ff, 0x016300ff, 0x016300ff, + 0x016300ff, 0x016300ff, 0x016300ff, 0x016400ff, 0x016400ff, 0x016400ff, + 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, + 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, + 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, + 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, + 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, + 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, + 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, + 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, +}; + +static void saa7146_set_winsize(struct saa7146 *saa) +{ + u32 format; + int offset, yacl, ysci; + saa->win.color_fmt = format = + (saa->win.depth == 15) ? palette2fmt[VIDEO_PALETTE_RGB555] : + palette2fmt[bpp2fmt[(saa->win.bpp - 1) & 3]]; + offset = saa->win.x * saa->win.bpp + saa->win.y * saa->win.bpl; + saawrite(saa->win.vidadr + offset, SAA7146_BASE_EVEN1); + saawrite(saa->win.vidadr + offset + saa->win.bpl, SAA7146_BASE_ODD1); + saawrite(saa->win.bpl * 2, SAA7146_PITCH1); + saawrite(saa->win.vidadr + saa->win.bpl * saa->win.sheight, + SAA7146_PROT_ADDR1); + saawrite(0, SAA7146_PAGE1); + saawrite(format | 0x60, SAA7146_CLIP_FORMAT_CTRL); + offset = (704 / (saa->win.width - 1)) & 0x3f; + saawrite(h_prescale[offset], SAA7146_HPS_H_PRESCALE); + offset = (720896 / saa->win.width) / (offset + 1); + saawrite((offset << 12) | 0x0c, SAA7146_HPS_H_SCALE); + if (CurrentMode == VIDEO_MODE_NTSC) { + yacl = /*(480 / saa->win.height - 1) & 0x3f */ 0; + ysci = 1024 - (saa->win.height * 1024 / 480); + } else { + yacl = /*(576 / saa->win.height - 1) & 0x3f */ 0; + ysci = 1024 - (saa->win.height * 1024 / 576); + } + saawrite((1 << 31) | (ysci << 21) | (yacl << 15), SAA7146_HPS_V_SCALE); + saawrite(v_gain[yacl], SAA7146_HPS_V_GAIN); + saawrite(((SAA7146_MC2_UPLD_DMA1 | SAA7146_MC2_UPLD_HPS_V | + SAA7146_MC2_UPLD_HPS_H) << 16) | (SAA7146_MC2_UPLD_DMA1 | + SAA7146_MC2_UPLD_HPS_V | SAA7146_MC2_UPLD_HPS_H), SAA7146_MC2); +} + +/* clip_draw_rectangle(cm,x,y,w,h) -- handle clipping an area + * bitmap is fixed width, 128 bytes (1024 pixels represented) + * arranged most-sigificant-bit-left in 32-bit words + * based on saa7146 clipping hardware, it swaps bytes if LE + * much of this makes up for egcs brain damage -- so if you + * are wondering "why did he do this?" it is because the C + * was adjusted to generate the optimal asm output without + * writing non-portable __asm__ directives. + */ + +static void clip_draw_rectangle(u32 *clipmap, int x, int y, int w, int h) +{ + register int startword, endword; + register u32 bitsleft, bitsright; + u32 *temp; + if (x < 0) { + w += x; + x = 0; + } + if (y < 0) { + h += y; + y = 0; + } + if (w <= 0 || h <= 0 || x > 1023 || y > 639) + return; /* throw away bad clips */ + if (x + w > 1024) + w = 1024 - x; + if (y + h > 640) + h = 640 - y; + startword = (x >> 5); + endword = ((x + w) >> 5); + bitsleft = (0xffffffff >> (x & 31)); + bitsright = (0xffffffff << (~((x + w) - (endword << 5)))); + temp = &clipmap[(y << 5) + startword]; + w = endword - startword; + if (!w) { + bitsleft |= bitsright; + for (y = 0; y < h; y++) { + *temp |= bitsleft; + temp += 32; + } + } else { + for (y = 0; y < h; y++) { + *temp++ |= bitsleft; + for (x = 1; x < w; x++) + *temp++ = 0xffffffff; + *temp |= bitsright; + temp += (32 - w); + } + } +} + +static void make_clip_tab(struct saa7146 *saa, struct video_clip *cr, int ncr) +{ + int i, width, height; + u32 *clipmap; + + clipmap = saa->dmavid2; + if ((width = saa->win.width) > 1023) + width = 1023; /* sanity check */ + if ((height = saa->win.height) > 640) + height = 639; /* sanity check */ + if (ncr > 0) { /* rectangles pased */ + /* convert rectangular clips to a bitmap */ + memset(clipmap, 0, VIDEO_CLIPMAP_SIZE); /* clear map */ + for (i = 0; i < ncr; i++) + clip_draw_rectangle(clipmap, cr[i].x, cr[i].y, + cr[i].width, cr[i].height); + } + /* clip against viewing window AND screen + so we do not have to rely on the user program + */ + clip_draw_rectangle(clipmap, (saa->win.x + width > saa->win.swidth) ? + (saa->win.swidth - saa->win.x) : width, 0, 1024, 768); + clip_draw_rectangle(clipmap, 0, + (saa->win.y + height > saa->win.sheight) ? + (saa->win.sheight - saa->win.y) : height, 1024, 768); + if (saa->win.x < 0) + clip_draw_rectangle(clipmap, 0, 0, -saa->win.x, 768); + if (saa->win.y < 0) + clip_draw_rectangle(clipmap, 0, 0, 1024, -saa->win.y); +} + +static long saa_ioctl(struct file *file, + unsigned int cmd, unsigned long argl) +{ + struct saa7146 *saa = file->private_data; + void __user *arg = (void __user *)argl; + + switch (cmd) { + case VIDIOCGCAP: + { + struct video_capability b; + strcpy(b.name, saa->video_dev.name); + b.type = VID_TYPE_CAPTURE | VID_TYPE_OVERLAY | + VID_TYPE_CLIPPING | VID_TYPE_FRAMERAM | + VID_TYPE_SCALES; + b.channels = 1; + b.audios = 1; + b.maxwidth = 768; + b.maxheight = 576; + b.minwidth = 32; + b.minheight = 32; + if (copy_to_user(arg, &b, sizeof(b))) + return -EFAULT; + return 0; + } + case VIDIOCGPICT: + { + struct video_picture p = saa->picture; + if (saa->win.depth == 8) + p.palette = VIDEO_PALETTE_HI240; + if (saa->win.depth == 15) + p.palette = VIDEO_PALETTE_RGB555; + if (saa->win.depth == 16) + p.palette = VIDEO_PALETTE_RGB565; + if (saa->win.depth == 24) + p.palette = VIDEO_PALETTE_RGB24; + if (saa->win.depth == 32) + p.palette = VIDEO_PALETTE_RGB32; + if (copy_to_user(arg, &p, sizeof(p))) + return -EFAULT; + return 0; + } + case VIDIOCSPICT: + { + struct video_picture p; + u32 format; + if (copy_from_user(&p, arg, sizeof(p))) + return -EFAULT; + if (p.palette < ARRAY_SIZE(palette2fmt)) { + format = palette2fmt[p.palette]; + saa->win.color_fmt = format; + saawrite(format | 0x60, + SAA7146_CLIP_FORMAT_CTRL); + } + saawrite(((p.brightness & 0xff00) << 16) | + ((p.contrast & 0xfe00) << 7) | + ((p.colour & 0xfe00) >> 9), SAA7146_BCS_CTRL); + saa->picture = p; + /* upload changed registers */ + saawrite(((SAA7146_MC2_UPLD_HPS_H | + SAA7146_MC2_UPLD_HPS_V) << 16) | + SAA7146_MC2_UPLD_HPS_H | + SAA7146_MC2_UPLD_HPS_V, SAA7146_MC2); + return 0; + } + case VIDIOCSWIN: + { + struct video_window vw; + struct video_clip *vcp = NULL; + + if (copy_from_user(&vw, arg, sizeof(vw))) + return -EFAULT; + + /* stop capture */ + if (vw.flags || vw.width < 16 || vw.height < 16) { + saawrite((SAA7146_MC1_TR_E_1 << 16), + SAA7146_MC1); + return -EINVAL; + } + /* 32-bit align start and adjust width */ + if (saa->win.bpp < 4) { + int i = vw.x; + vw.x = (vw.x + 3) & ~3; + i = vw.x - i; + vw.width -= i; + } + saa->win.x = vw.x; + saa->win.y = vw.y; + saa->win.width = vw.width; + if (saa->win.width > 768) + saa->win.width = 768; + saa->win.height = vw.height; + if (CurrentMode == VIDEO_MODE_NTSC) { + if (saa->win.height > 480) + saa->win.height = 480; + } else { + if (saa->win.height > 576) + saa->win.height = 576; + } + + /* stop capture */ + saawrite((SAA7146_MC1_TR_E_1 << 16), SAA7146_MC1); + saa7146_set_winsize(saa); + + /* + * Do any clips. + */ + if (vw.clipcount < 0) { + if (copy_from_user(saa->dmavid2, vw.clips, + VIDEO_CLIPMAP_SIZE)) + return -EFAULT; + } else if (vw.clipcount > 16384) { + return -EINVAL; + } else if (vw.clipcount > 0) { + vcp = vmalloc(sizeof(struct video_clip) * + vw.clipcount); + if (vcp == NULL) + return -ENOMEM; + if (copy_from_user(vcp, vw.clips, + sizeof(struct video_clip) * + vw.clipcount)) { + vfree(vcp); + return -EFAULT; + } + } else /* nothing clipped */ + memset(saa->dmavid2, 0, VIDEO_CLIPMAP_SIZE); + + make_clip_tab(saa, vcp, vw.clipcount); + if (vw.clipcount > 0) + vfree(vcp); + + /* start capture & clip dma if we have an address */ + if ((saa->cap & 3) && saa->win.vidadr != 0) + saawrite(((SAA7146_MC1_TR_E_1 | + SAA7146_MC1_TR_E_2) << 16) | 0xffff, + SAA7146_MC1); + return 0; + } + case VIDIOCGWIN: + { + struct video_window vw; + vw.x = saa->win.x; + vw.y = saa->win.y; + vw.width = saa->win.width; + vw.height = saa->win.height; + vw.chromakey = 0; + vw.flags = 0; + if (copy_to_user(arg, &vw, sizeof(vw))) + return -EFAULT; + return 0; + } + case VIDIOCCAPTURE: + { + int v; + if (copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + if (v == 0) { + saa->cap &= ~1; + saawrite((SAA7146_MC1_TR_E_1 << 16), + SAA7146_MC1); + } else { + if (saa->win.vidadr == 0 || saa->win.width == 0 + || saa->win.height == 0) + return -EINVAL; + saa->cap |= 1; + saawrite((SAA7146_MC1_TR_E_1 << 16) | 0xffff, + SAA7146_MC1); + } + return 0; + } + case VIDIOCGFBUF: + { + struct video_buffer v; + v.base = (void *)saa->win.vidadr; + v.height = saa->win.sheight; + v.width = saa->win.swidth; + v.depth = saa->win.depth; + v.bytesperline = saa->win.bpl; + if (copy_to_user(arg, &v, sizeof(v))) + return -EFAULT; + return 0; + + } + case VIDIOCSFBUF: + { + struct video_buffer v; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + if (v.depth != 8 && v.depth != 15 && v.depth != 16 && + v.depth != 24 && v.depth != 32 && v.width > 16 && + v.height > 16 && v.bytesperline > 16) + return -EINVAL; + if (v.base) + saa->win.vidadr = (unsigned long)v.base; + saa->win.sheight = v.height; + saa->win.swidth = v.width; + saa->win.bpp = ((v.depth + 7) & 0x38) / 8; + saa->win.depth = v.depth; + saa->win.bpl = v.bytesperline; + + DEBUG(printk("Display at %p is %d by %d, bytedepth %d, " + "bpl %d\n", v.base, v.width, v.height, + saa->win.bpp, saa->win.bpl)); + saa7146_set_winsize(saa); + return 0; + } + case VIDIOCKEY: + { + /* Will be handled higher up .. */ + return 0; + } + + case VIDIOCGAUDIO: + { + struct video_audio v; + v = saa->audio_dev; + v.flags &= ~(VIDEO_AUDIO_MUTE | VIDEO_AUDIO_MUTABLE); + v.flags |= VIDEO_AUDIO_MUTABLE | VIDEO_AUDIO_VOLUME; + strcpy(v.name, "MPEG"); + v.mode = VIDEO_SOUND_STEREO; + if (copy_to_user(arg, &v, sizeof(v))) + return -EFAULT; + return 0; + } + case VIDIOCSAUDIO: + { + struct video_audio v; + int i; + if (copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + i = (~(v.volume >> 8)) & 0xff; + if (!HaveCS4341) { + if (v.flags & VIDEO_AUDIO_MUTE) + debiwrite(saa, debNormal, + IBM_MP2_FRNT_ATTEN, 0xffff, 2); + if (!(v.flags & VIDEO_AUDIO_MUTE)) + debiwrite(saa, debNormal, + IBM_MP2_FRNT_ATTEN, 0x0000, 2); + if (v.flags & VIDEO_AUDIO_VOLUME) + debiwrite(saa, debNormal, + IBM_MP2_FRNT_ATTEN, + (i << 8) | i, 2); + } else { + if (v.flags & VIDEO_AUDIO_MUTE) + cs4341_setlevel(saa, 0xff, 0xff); + if (!(v.flags & VIDEO_AUDIO_MUTE)) + cs4341_setlevel(saa, 0, 0); + if (v.flags & VIDEO_AUDIO_VOLUME) + cs4341_setlevel(saa, i, i); + } + saa->audio_dev = v; + return 0; + } + + case VIDIOCGUNIT: + { + struct video_unit vu; + vu.video = saa->video_dev.minor; + vu.vbi = VIDEO_NO_UNIT; + vu.radio = VIDEO_NO_UNIT; + vu.audio = VIDEO_NO_UNIT; + vu.teletext = VIDEO_NO_UNIT; + if (copy_to_user(arg, &vu, sizeof(vu))) + return -EFAULT; + return 0; + } + case VIDIOCSPLAYMODE: + { + struct video_play_mode pmode; + if (copy_from_user((void *)&pmode, arg, + sizeof(struct video_play_mode))) + return -EFAULT; + switch (pmode.mode) { + case VID_PLAY_VID_OUT_MODE: + if (pmode.p1 != VIDEO_MODE_NTSC && + pmode.p1 != VIDEO_MODE_PAL) + return -EINVAL; + set_out_format(saa, pmode.p1); + return 0; + case VID_PLAY_GENLOCK: + debiwrite(saa, debNormal, XILINX_CTL0, + pmode.p1 ? 0x8000 : 0x8080, 2); + if (NewCard) + set_genlock_offset(saa, pmode.p2); + return 0; + case VID_PLAY_NORMAL: + debiwrite(saa, debNormal, + IBM_MP2_CHIP_CONTROL, ChipControl, 2); + ibm_send_command(saa, IBM_MP2_PLAY, 0, 0); + saa->playmode = pmode.mode; + return 0; + case VID_PLAY_PAUSE: + /* IBM removed the PAUSE command */ + /* they say use SINGLE_FRAME now */ + case VID_PLAY_SINGLE_FRAME: + ibm_send_command(saa, IBM_MP2_SINGLE_FRAME,0,0); + if (saa->playmode == pmode.mode) { + debiwrite(saa, debNormal, + IBM_MP2_CHIP_CONTROL, + ChipControl, 2); + } + saa->playmode = pmode.mode; + return 0; + case VID_PLAY_FAST_FORWARD: + ibm_send_command(saa, IBM_MP2_FAST_FORWARD,0,0); + saa->playmode = pmode.mode; + return 0; + case VID_PLAY_SLOW_MOTION: + ibm_send_command(saa, IBM_MP2_SLOW_MOTION, + pmode.p1, 0); + saa->playmode = pmode.mode; + return 0; + case VID_PLAY_IMMEDIATE_NORMAL: + /* ensure transfers resume */ + debiwrite(saa, debNormal, + IBM_MP2_CHIP_CONTROL, ChipControl, 2); + ibm_send_command(saa, IBM_MP2_IMED_NORM_PLAY, + 0, 0); + saa->playmode = VID_PLAY_NORMAL; + return 0; + case VID_PLAY_SWITCH_CHANNELS: + saa->audhead = saa->audtail = 0; + saa->vidhead = saa->vidtail = 0; + ibm_send_command(saa, IBM_MP2_FREEZE_FRAME,0,1); + ibm_send_command(saa, IBM_MP2_RESET_AUD_RATE, + 0, 1); + debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, + 0, 2); + ibm_send_command(saa, IBM_MP2_CHANNEL_SWITCH, + 0, 1); + debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, + ChipControl, 2); + ibm_send_command(saa, IBM_MP2_PLAY, 0, 0); + saa->playmode = VID_PLAY_NORMAL; + return 0; + case VID_PLAY_FREEZE_FRAME: + ibm_send_command(saa, IBM_MP2_FREEZE_FRAME,0,0); + saa->playmode = pmode.mode; + return 0; + case VID_PLAY_STILL_MODE: + ibm_send_command(saa, IBM_MP2_SET_STILL_MODE, + 0, 0); + saa->playmode = pmode.mode; + return 0; + case VID_PLAY_MASTER_MODE: + if (pmode.p1 == VID_PLAY_MASTER_NONE) + saa->boardcfg[1] = 0x13; + else if (pmode.p1 == VID_PLAY_MASTER_VIDEO) + saa->boardcfg[1] = 0x23; + else if (pmode.p1 == VID_PLAY_MASTER_AUDIO) + saa->boardcfg[1] = 0x43; + else + return -EINVAL; + debiwrite(saa, debNormal, + IBM_MP2_CHIP_CONTROL, ChipControl, 2); + return 0; + case VID_PLAY_ACTIVE_SCANLINES: + if (CurrentMode == VIDEO_MODE_PAL) { + if (pmode.p1 < 1 || pmode.p2 > 625) + return -EINVAL; + saa->boardcfg[5] = pmode.p1; + saa->boardcfg[55] = (pmode.p1 + + (pmode.p2 / 2) - 1) & 0xff; + } else { + if (pmode.p1 < 4 || pmode.p2 > 525) + return -EINVAL; + saa->boardcfg[4] = pmode.p1; + saa->boardcfg[54] = (pmode.p1 + + (pmode.p2 / 2) - 4) & 0xff; + } + set_out_format(saa, CurrentMode); + case VID_PLAY_RESET: + return do_ibm_reset(saa); + case VID_PLAY_END_MARK: + if (saa->endmarktail < saa->endmarkhead) { + if (saa->endmarkhead - + saa->endmarktail < 2) + return -ENOSPC; + } else if (saa->endmarkhead <=saa->endmarktail){ + if (saa->endmarktail - saa->endmarkhead + > (MAX_MARKS - 2)) + return -ENOSPC; + } else + return -ENOSPC; + saa->endmark[saa->endmarktail] = saa->audtail; + saa->endmarktail++; + if (saa->endmarktail >= MAX_MARKS) + saa->endmarktail = 0; + } + return -EINVAL; + } + case VIDIOCSWRITEMODE: + { + int mode; + if (copy_from_user((void *)&mode, arg, sizeof(int))) + return -EFAULT; + if (mode == VID_WRITE_MPEG_AUD || + mode == VID_WRITE_MPEG_VID || + mode == VID_WRITE_CC || + mode == VID_WRITE_TTX || + mode == VID_WRITE_OSD) { + saa->writemode = mode; + return 0; + } + return -EINVAL; + } + case VIDIOCSMICROCODE: + { + struct video_code ucode; + __u8 *udata; + int i; + if (copy_from_user(&ucode, arg, sizeof(ucode))) + return -EFAULT; + if (ucode.datasize > 65536 || ucode.datasize < 1024 || + strncmp(ucode.loadwhat, "dec", 3)) + return -EINVAL; + if ((udata = vmalloc(ucode.datasize)) == NULL) + return -ENOMEM; + if (copy_from_user(udata, ucode.data, ucode.datasize)) { + vfree(udata); + return -EFAULT; + } + ucode.data = udata; + if (!strncmp(ucode.loadwhat, "decoder.aud", 11) || + !strncmp(ucode.loadwhat, "decoder.vid", 11)) + i = initialize_ibmmpeg2(&ucode); + else + i = initialize_fpga(&ucode); + vfree(udata); + if (i) + return -EINVAL; + return 0; + + } + case VIDIOCGCHAN: /* this makes xawtv happy */ + { + struct video_channel v; + if (copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + v.flags = VIDEO_VC_AUDIO; + v.tuners = 0; + v.type = VID_TYPE_MPEG_DECODER; + v.norm = CurrentMode; + strcpy(v.name, "MPEG2"); + if (copy_to_user(arg, &v, sizeof(v))) + return -EFAULT; + return 0; + } + case VIDIOCSCHAN: /* this makes xawtv happy */ + { + struct video_channel v; + if (copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + /* do nothing */ + return 0; + } + default: + return -ENOIOCTLCMD; + } + return 0; +} + +static int saa_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct saa7146 *saa = file->private_data; + printk(KERN_DEBUG "stradis%d: saa_mmap called\n", saa->nr); + return -EINVAL; +} + +static ssize_t saa_read(struct file *file, char __user * buf, + size_t count, loff_t * ppos) +{ + return -EINVAL; +} + +static ssize_t saa_write(struct file *file, const char __user * buf, + size_t count, loff_t * ppos) +{ + struct saa7146 *saa = file->private_data; + unsigned long todo = count; + int blocksize, split; + unsigned long flags; + + while (todo > 0) { + if (saa->writemode == VID_WRITE_MPEG_AUD) { + spin_lock_irqsave(&saa->lock, flags); + if (saa->audhead <= saa->audtail) + blocksize = 65536 - + (saa->audtail - saa->audhead); + else + blocksize = saa->audhead - saa->audtail; + spin_unlock_irqrestore(&saa->lock, flags); + if (blocksize < 16384) { + saawrite(SAA7146_PSR_DEBI_S | + SAA7146_PSR_PIN1, SAA7146_IER); + saawrite(SAA7146_PSR_PIN1, SAA7146_PSR); + /* wait for buffer space to open */ + interruptible_sleep_on(&saa->audq); + } + spin_lock_irqsave(&saa->lock, flags); + if (saa->audhead <= saa->audtail) { + blocksize = 65536 - + (saa->audtail - saa->audhead); + split = 65536 - saa->audtail; + } else { + blocksize = saa->audhead - saa->audtail; + split = 65536; + } + spin_unlock_irqrestore(&saa->lock, flags); + blocksize--; + if (blocksize > todo) + blocksize = todo; + /* double check that we really have space */ + if (!blocksize) + return -ENOSPC; + if (split < blocksize) { + if (copy_from_user(saa->audbuf + + saa->audtail, buf, split)) + return -EFAULT; + buf += split; + todo -= split; + blocksize -= split; + saa->audtail = 0; + } + if (copy_from_user(saa->audbuf + saa->audtail, buf, + blocksize)) + return -EFAULT; + saa->audtail += blocksize; + todo -= blocksize; + buf += blocksize; + saa->audtail &= 0xffff; + } else if (saa->writemode == VID_WRITE_MPEG_VID) { + spin_lock_irqsave(&saa->lock, flags); + if (saa->vidhead <= saa->vidtail) + blocksize = 524288 - + (saa->vidtail - saa->vidhead); + else + blocksize = saa->vidhead - saa->vidtail; + spin_unlock_irqrestore(&saa->lock, flags); + if (blocksize < 65536) { + saawrite(SAA7146_PSR_DEBI_S | + SAA7146_PSR_PIN1, SAA7146_IER); + saawrite(SAA7146_PSR_PIN1, SAA7146_PSR); + /* wait for buffer space to open */ + interruptible_sleep_on(&saa->vidq); + } + spin_lock_irqsave(&saa->lock, flags); + if (saa->vidhead <= saa->vidtail) { + blocksize = 524288 - + (saa->vidtail - saa->vidhead); + split = 524288 - saa->vidtail; + } else { + blocksize = saa->vidhead - saa->vidtail; + split = 524288; + } + spin_unlock_irqrestore(&saa->lock, flags); + blocksize--; + if (blocksize > todo) + blocksize = todo; + /* double check that we really have space */ + if (!blocksize) + return -ENOSPC; + if (split < blocksize) { + if (copy_from_user(saa->vidbuf + + saa->vidtail, buf, split)) + return -EFAULT; + buf += split; + todo -= split; + blocksize -= split; + saa->vidtail = 0; + } + if (copy_from_user(saa->vidbuf + saa->vidtail, buf, + blocksize)) + return -EFAULT; + saa->vidtail += blocksize; + todo -= blocksize; + buf += blocksize; + saa->vidtail &= 0x7ffff; + } else if (saa->writemode == VID_WRITE_OSD) { + if (count > 131072) + return -ENOSPC; + if (copy_from_user(saa->osdbuf, buf, count)) + return -EFAULT; + buf += count; + saa->osdhead = 0; + saa->osdtail = count; + debiwrite(saa, debNormal, IBM_MP2_OSD_ADDR, 0, 2); + debiwrite(saa, debNormal, IBM_MP2_OSD_LINK_ADDR, 0, 2); + debiwrite(saa, debNormal, IBM_MP2_MASK0, 0xc00d, 2); + debiwrite(saa, debNormal, IBM_MP2_DISP_MODE, + debiread(saa, debNormal, + IBM_MP2_DISP_MODE, 2) | 1, 2); + /* trigger osd data transfer */ + saawrite(SAA7146_PSR_DEBI_S | + SAA7146_PSR_PIN1, SAA7146_IER); + saawrite(SAA7146_PSR_PIN1, SAA7146_PSR); + } + } + return count; +} + +static int saa_open(struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct saa7146 *saa = container_of(vdev, struct saa7146, video_dev); + + lock_kernel(); + file->private_data = saa; + + saa->user++; + if (saa->user > 1) { + unlock_kernel(); + return 0; /* device open already, don't reset */ + } + saa->writemode = VID_WRITE_MPEG_VID; /* default to video */ + unlock_kernel(); + return 0; +} + +static int saa_release(struct file *file) +{ + struct saa7146 *saa = file->private_data; + saa->user--; + + if (saa->user > 0) /* still someone using device */ + return 0; + saawrite(0x007f0000, SAA7146_MC1); /* stop all overlay dma */ + return 0; +} + +static const struct v4l2_file_operations saa_fops = { + .owner = THIS_MODULE, + .open = saa_open, + .release = saa_release, + .ioctl = saa_ioctl, + .read = saa_read, + .write = saa_write, + .mmap = saa_mmap, +}; + +/* template for video_device-structure */ +static struct video_device saa_template = { + .name = "SAA7146A", + .fops = &saa_fops, + .release = video_device_release_empty, +}; + +static int __devinit configure_saa7146(struct pci_dev *pdev, int num) +{ + int retval; + struct saa7146 *saa = pci_get_drvdata(pdev); + + saa->endmarkhead = saa->endmarktail = 0; + saa->win.x = saa->win.y = 0; + saa->win.width = saa->win.cropwidth = 720; + saa->win.height = saa->win.cropheight = 480; + saa->win.cropx = saa->win.cropy = 0; + saa->win.bpp = 2; + saa->win.depth = 16; + saa->win.color_fmt = palette2fmt[VIDEO_PALETTE_RGB565]; + saa->win.bpl = 1024 * saa->win.bpp; + saa->win.swidth = 1024; + saa->win.sheight = 768; + saa->picture.brightness = 32768; + saa->picture.contrast = 38768; + saa->picture.colour = 32768; + saa->cap = 0; + saa->nr = num; + saa->playmode = VID_PLAY_NORMAL; + memset(saa->boardcfg, 0, 64); /* clear board config area */ + saa->saa7146_mem = NULL; + saa->dmavid1 = saa->dmavid2 = saa->dmavid3 = saa->dmaa1in = + saa->dmaa1out = saa->dmaa2in = saa->dmaa2out = + saa->pagevid1 = saa->pagevid2 = saa->pagevid3 = saa->pagea1in = + saa->pagea1out = saa->pagea2in = saa->pagea2out = + saa->pagedebi = saa->dmaRPS1 = saa->dmaRPS2 = saa->pageRPS1 = + saa->pageRPS2 = NULL; + saa->audbuf = saa->vidbuf = saa->osdbuf = saa->dmadebi = NULL; + saa->audhead = saa->vidtail = 0; + + init_waitqueue_head(&saa->i2cq); + init_waitqueue_head(&saa->audq); + init_waitqueue_head(&saa->debiq); + init_waitqueue_head(&saa->vidq); + spin_lock_init(&saa->lock); + + retval = pci_enable_device(pdev); + if (retval) { + dev_err(&pdev->dev, "%d: pci_enable_device failed!\n", num); + goto err; + } + + saa->id = pdev->device; + saa->irq = pdev->irq; + saa->saa7146_adr = pci_resource_start(pdev, 0); + pci_read_config_byte(pdev, PCI_CLASS_REVISION, &saa->revision); + + saa->saa7146_mem = ioremap(saa->saa7146_adr, 0x200); + if (saa->saa7146_mem == NULL) { + dev_err(&pdev->dev, "%d: ioremap failed!\n", num); + retval = -EIO; + goto err; + } + + memcpy(&saa->video_dev, &saa_template, sizeof(saa_template)); + saawrite(0, SAA7146_IER); /* turn off all interrupts */ + + retval = request_irq(saa->irq, saa7146_irq, IRQF_SHARED | IRQF_DISABLED, + "stradis", saa); + if (retval == -EINVAL) + dev_err(&pdev->dev, "%d: Bad irq number or handler\n", num); + else if (retval == -EBUSY) + dev_err(&pdev->dev, "%d: IRQ %ld busy, change your PnP config " + "in BIOS\n", num, saa->irq); + if (retval < 0) + goto errio; + + pci_set_master(pdev); + retval = video_register_device(&saa->video_dev, VFL_TYPE_GRABBER, + video_nr); + if (retval < 0) { + dev_err(&pdev->dev, "%d: error in registering video device!\n", + num); + goto errio; + } + + return 0; +errio: + iounmap(saa->saa7146_mem); +err: + return retval; +} + +static int __devinit init_saa7146(struct pci_dev *pdev) +{ + struct saa7146 *saa = pci_get_drvdata(pdev); + + saa->user = 0; + /* reset the saa7146 */ + saawrite(0xffff0000, SAA7146_MC1); + mdelay(5); + /* enable debi and i2c transfers and pins */ + saawrite(((SAA7146_MC1_EDP | SAA7146_MC1_EI2C | + SAA7146_MC1_TR_E_DEBI) << 16) | 0xffff, SAA7146_MC1); + /* ensure proper state of chip */ + saawrite(0x00000000, SAA7146_PAGE1); + saawrite(0x00f302c0, SAA7146_NUM_LINE_BYTE1); + saawrite(0x00000000, SAA7146_PAGE2); + saawrite(0x01400080, SAA7146_NUM_LINE_BYTE2); + saawrite(0x00000000, SAA7146_DD1_INIT); + saawrite(0x00000000, SAA7146_DD1_STREAM_B); + saawrite(0x00000000, SAA7146_DD1_STREAM_A); + saawrite(0x00000000, SAA7146_BRS_CTRL); + saawrite(0x80400040, SAA7146_BCS_CTRL); + saawrite(0x0000e000 /*| (1<<29) */ , SAA7146_HPS_CTRL); + saawrite(0x00000060, SAA7146_CLIP_FORMAT_CTRL); + saawrite(0x00000000, SAA7146_ACON1); + saawrite(0x00000000, SAA7146_ACON2); + saawrite(0x00000600, SAA7146_I2C_STATUS); + saawrite(((SAA7146_MC2_UPLD_D1_B | SAA7146_MC2_UPLD_D1_A | + SAA7146_MC2_UPLD_BRS | SAA7146_MC2_UPLD_HPS_H | + SAA7146_MC2_UPLD_HPS_V | SAA7146_MC2_UPLD_DMA2 | + SAA7146_MC2_UPLD_DMA1 | SAA7146_MC2_UPLD_I2C) << 16) | 0xffff, + SAA7146_MC2); + /* setup arbitration control registers */ + saawrite(0x1412121a, SAA7146_PCI_BT_V1); + + /* allocate 32k dma buffer + 4k for page table */ + if ((saa->dmadebi = kmalloc(32768 + 4096, GFP_KERNEL)) == NULL) { + dev_err(&pdev->dev, "%d: debi kmalloc failed\n", saa->nr); + goto err; + } +#if 0 + saa->pagedebi = saa->dmadebi + 32768; /* top 4k is for mmu */ + saawrite(virt_to_bus(saa->pagedebi) /*|0x800 */ , SAA7146_DEBI_PAGE); + for (i = 0; i < 12; i++) /* setup mmu page table */ + saa->pagedebi[i] = virt_to_bus((saa->dmadebi + i * 4096)); +#endif + saa->audhead = saa->vidhead = saa->osdhead = 0; + saa->audtail = saa->vidtail = saa->osdtail = 0; + if (saa->vidbuf == NULL && (saa->vidbuf = vmalloc(524288)) == NULL) { + dev_err(&pdev->dev, "%d: malloc failed\n", saa->nr); + goto err; + } + if (saa->audbuf == NULL && (saa->audbuf = vmalloc(65536)) == NULL) { + dev_err(&pdev->dev, "%d: malloc failed\n", saa->nr); + goto errfree; + } + if (saa->osdbuf == NULL && (saa->osdbuf = vmalloc(131072)) == NULL) { + dev_err(&pdev->dev, "%d: malloc failed\n", saa->nr); + goto errfree; + } + /* allocate 81920 byte buffer for clipping */ + if ((saa->dmavid2 = kzalloc(VIDEO_CLIPMAP_SIZE, GFP_KERNEL)) == NULL) { + dev_err(&pdev->dev, "%d: clip kmalloc failed\n", saa->nr); + goto errfree; + } + /* setup clipping registers */ + saawrite(virt_to_bus(saa->dmavid2), SAA7146_BASE_EVEN2); + saawrite(virt_to_bus(saa->dmavid2) + 128, SAA7146_BASE_ODD2); + saawrite(virt_to_bus(saa->dmavid2) + VIDEO_CLIPMAP_SIZE, + SAA7146_PROT_ADDR2); + saawrite(256, SAA7146_PITCH2); + saawrite(4, SAA7146_PAGE2); /* dma direction: read, no byteswap */ + saawrite(((SAA7146_MC2_UPLD_DMA2) << 16) | SAA7146_MC2_UPLD_DMA2, + SAA7146_MC2); + I2CBusScan(saa); + + return 0; +errfree: + vfree(saa->osdbuf); + vfree(saa->audbuf); + vfree(saa->vidbuf); + saa->audbuf = saa->osdbuf = saa->vidbuf = NULL; +err: + return -ENOMEM; +} + +static void stradis_release_saa(struct pci_dev *pdev) +{ + u8 command; + struct saa7146 *saa = pci_get_drvdata(pdev); + + /* turn off all capturing, DMA and IRQs */ + saawrite(0xffff0000, SAA7146_MC1); /* reset chip */ + saawrite(0, SAA7146_MC2); + saawrite(0, SAA7146_IER); + saawrite(0xffffffffUL, SAA7146_ISR); + + /* disable PCI bus-mastering */ + pci_read_config_byte(pdev, PCI_COMMAND, &command); + command &= ~PCI_COMMAND_MASTER; + pci_write_config_byte(pdev, PCI_COMMAND, command); + + /* unmap and free memory */ + saa->audhead = saa->audtail = saa->osdhead = 0; + saa->vidhead = saa->vidtail = saa->osdtail = 0; + vfree(saa->vidbuf); + vfree(saa->audbuf); + vfree(saa->osdbuf); + kfree(saa->dmavid2); + saa->audbuf = saa->vidbuf = saa->osdbuf = NULL; + saa->dmavid2 = NULL; + kfree(saa->dmadebi); + kfree(saa->dmavid1); + kfree(saa->dmavid3); + kfree(saa->dmaa1in); + kfree(saa->dmaa1out); + kfree(saa->dmaa2in); + kfree(saa->dmaa2out); + kfree(saa->dmaRPS1); + kfree(saa->dmaRPS2); + free_irq(saa->irq, saa); + if (saa->saa7146_mem) + iounmap(saa->saa7146_mem); + if (video_is_registered(&saa->video_dev)) + video_unregister_device(&saa->video_dev); +} + +static int __devinit stradis_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + int retval = -EINVAL; + + if (saa_num >= SAA7146_MAX) + goto err; + + if (!pdev->subsystem_vendor) + dev_info(&pdev->dev, "%d: rev1 decoder\n", saa_num); + else + dev_info(&pdev->dev, "%d: SDM2xx found\n", saa_num); + + pci_set_drvdata(pdev, &saa7146s[saa_num]); + + retval = configure_saa7146(pdev, saa_num); + if (retval) { + dev_err(&pdev->dev, "%d: error in configuring\n", saa_num); + goto err; + } + + if (init_saa7146(pdev) < 0) { + dev_err(&pdev->dev, "%d: error in initialization\n", saa_num); + retval = -EIO; + goto errrel; + } + + saa_num++; + + return 0; +errrel: + stradis_release_saa(pdev); +err: + return retval; +} + +static void __devexit stradis_remove(struct pci_dev *pdev) +{ + stradis_release_saa(pdev); +} + +static struct pci_device_id stradis_pci_tbl[] = { + { PCI_DEVICE(PCI_VENDOR_ID_PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7146) }, + { 0 } +}; + + +static struct pci_driver stradis_driver = { + .name = "stradis", + .id_table = stradis_pci_tbl, + .probe = stradis_probe, + .remove = __devexit_p(stradis_remove) +}; + +static int __init stradis_init(void) +{ + int retval; + + saa_num = 0; + + retval = pci_register_driver(&stradis_driver); + if (retval) + printk(KERN_ERR "stradis: Unable to register pci driver.\n"); + + return retval; +} + +static void __exit stradis_exit(void) +{ + pci_unregister_driver(&stradis_driver); + printk(KERN_INFO "stradis: module cleanup complete\n"); +} + +module_init(stradis_init); +module_exit(stradis_exit); -- cgit v1.2.3 From d69f27186c16008540166c8017e9d4db2b477588 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 26 Sep 2010 08:16:56 -0300 Subject: V4L/DVB: v4l2-dev: after a disconnect any ioctl call will be blocked Until now all fops except release and (unlocked_)ioctl returned an error after the device node was unregistered. Extend this as well to the ioctl fops. There is nothing useful that an application can do here and it complicates the driver code unnecessarily. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/v4l2-framework.txt | 5 ++--- drivers/media/video/v4l2-dev.c | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt index 8fb9de4f4875..9b1d81c26b7d 100644 --- a/Documentation/video4linux/v4l2-framework.txt +++ b/Documentation/video4linux/v4l2-framework.txt @@ -551,9 +551,8 @@ from /dev). After video_unregister_device() returns no new opens can be done. However, in the case of USB devices some application might still have one of these -device nodes open. So after the unregister all file operations will return -an error as well, except for the ioctl and unlocked_ioctl file operations: -those will still be passed on since some buffer ioctls may still be needed. +device nodes open. So after the unregister all file operations (except +release, of course) will return an error as well. When the last user of the video device node exits, then the vdev->release() callback is called and you can do the final cleanup there. diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c index d4a353260c1e..f069c61cdf68 100644 --- a/drivers/media/video/v4l2-dev.c +++ b/drivers/media/video/v4l2-dev.c @@ -221,8 +221,8 @@ static long v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) struct video_device *vdev = video_devdata(filp); int ret; - /* Allow ioctl to continue even if the device was unregistered. - Things like dequeueing buffers might still be useful. */ + if (!vdev->fops->ioctl) + return -ENOTTY; if (vdev->fops->unlocked_ioctl) { ret = vdev->fops->unlocked_ioctl(filp, cmd, arg); } else if (vdev->fops->ioctl) { -- cgit v1.2.3 From ee6869afc922a9849979e49bb3bbcad794872fcb Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 26 Sep 2010 08:47:38 -0300 Subject: V4L/DVB: v4l2: add core serialization lock Drivers can optionally set a pointer to a mutex in struct video_device. The core will use that to lock before calling open, read, write, unlocked_ioctl, poll, mmap or release. Updated the documentation as well and ensure that v4l2-event knows about the lock: it will unlock it before doing a blocking wait on an event and relock it afterwards. Ensure that the 'video_is_registered' check is done when the lock is held: a typical disconnect will take the lock as well before unregistering the device nodes, so to prevent race conditions the video_is_registered check should also be done with the lock held. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/v4l2-framework.txt | 20 ++++++++ drivers/media/video/v4l2-dev.c | 76 +++++++++++++++++++++------- drivers/media/video/v4l2-event.c | 9 +++- include/media/v4l2-dev.h | 3 ++ 4 files changed, 89 insertions(+), 19 deletions(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt index 9b1d81c26b7d..a128e012a45c 100644 --- a/Documentation/video4linux/v4l2-framework.txt +++ b/Documentation/video4linux/v4l2-framework.txt @@ -453,6 +453,10 @@ You should also set these fields: - ioctl_ops: if you use the v4l2_ioctl_ops to simplify ioctl maintenance (highly recommended to use this and it might become compulsory in the future!), then set this to your v4l2_ioctl_ops struct. +- lock: leave to NULL if you want to do all the locking in the driver. + Otherwise you give it a pointer to a struct mutex_lock and before any + of the v4l2_file_operations is called this lock will be taken by the + core and released afterwards. - parent: you only set this if v4l2_device was registered with NULL as the parent device struct. This only happens in cases where one hardware device has multiple PCI devices that all share the same v4l2_device core. @@ -469,6 +473,22 @@ If you use v4l2_ioctl_ops, then you should set either .unlocked_ioctl or The v4l2_file_operations struct is a subset of file_operations. The main difference is that the inode argument is omitted since it is never used. +v4l2_file_operations and locking +-------------------------------- + +You can set a pointer to a mutex_lock in struct video_device. Usually this +will be either a top-level mutex or a mutex per device node. If you want +finer-grained locking then you have to set it to NULL and do you own locking. + +If a lock is specified then all file operations will be serialized on that +lock. If you use videobuf then you must pass the same lock to the videobuf +queue initialize function: if videobuf has to wait for a frame to arrive, then +it will temporarily unlock the lock and relock it afterwards. If your driver +also waits in the code, then you should do the same to allow other processes +to access the device node while the first process is waiting for something. + +The implementation of a hotplug disconnect should also take the lock before +calling v4l2_device_disconnect and video_unregister_device. video_device registration ------------------------- diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c index 5a54eabd4c42..a7702e3d149c 100644 --- a/drivers/media/video/v4l2-dev.c +++ b/drivers/media/video/v4l2-dev.c @@ -187,33 +187,50 @@ static ssize_t v4l2_read(struct file *filp, char __user *buf, size_t sz, loff_t *off) { struct video_device *vdev = video_devdata(filp); + int ret = -EIO; if (!vdev->fops->read) return -EINVAL; - if (!video_is_registered(vdev)) - return -EIO; - return vdev->fops->read(filp, buf, sz, off); + if (vdev->lock) + mutex_lock(vdev->lock); + if (video_is_registered(vdev)) + ret = vdev->fops->read(filp, buf, sz, off); + if (vdev->lock) + mutex_unlock(vdev->lock); + return ret; } static ssize_t v4l2_write(struct file *filp, const char __user *buf, size_t sz, loff_t *off) { struct video_device *vdev = video_devdata(filp); + int ret = -EIO; if (!vdev->fops->write) return -EINVAL; - if (!video_is_registered(vdev)) - return -EIO; - return vdev->fops->write(filp, buf, sz, off); + if (vdev->lock) + mutex_lock(vdev->lock); + if (video_is_registered(vdev)) + ret = vdev->fops->write(filp, buf, sz, off); + if (vdev->lock) + mutex_unlock(vdev->lock); + return ret; } static unsigned int v4l2_poll(struct file *filp, struct poll_table_struct *poll) { struct video_device *vdev = video_devdata(filp); + int ret = DEFAULT_POLLMASK; - if (!vdev->fops->poll || !video_is_registered(vdev)) - return DEFAULT_POLLMASK; - return vdev->fops->poll(filp, poll); + if (!vdev->fops->poll) + return ret; + if (vdev->lock) + mutex_lock(vdev->lock); + if (video_is_registered(vdev)) + ret = vdev->fops->poll(filp, poll); + if (vdev->lock) + mutex_unlock(vdev->lock); + return ret; } static long v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) @@ -224,7 +241,11 @@ static long v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) if (!vdev->fops->ioctl) return -ENOTTY; if (vdev->fops->unlocked_ioctl) { + if (vdev->lock) + mutex_lock(vdev->lock); ret = vdev->fops->unlocked_ioctl(filp, cmd, arg); + if (vdev->lock) + mutex_unlock(vdev->lock); } else if (vdev->fops->ioctl) { /* TODO: convert all drivers to unlocked_ioctl */ lock_kernel(); @@ -239,10 +260,17 @@ static long v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) static int v4l2_mmap(struct file *filp, struct vm_area_struct *vm) { struct video_device *vdev = video_devdata(filp); + int ret = -ENODEV; - if (!vdev->fops->mmap || !video_is_registered(vdev)) - return -ENODEV; - return vdev->fops->mmap(filp, vm); + if (!vdev->fops->mmap) + return ret; + if (vdev->lock) + mutex_lock(vdev->lock); + if (video_is_registered(vdev)) + ret = vdev->fops->mmap(filp, vm); + if (vdev->lock) + mutex_unlock(vdev->lock); + return ret; } /* Override for the open function */ @@ -254,17 +282,24 @@ static int v4l2_open(struct inode *inode, struct file *filp) /* Check if the video device is available */ mutex_lock(&videodev_lock); vdev = video_devdata(filp); - /* return ENODEV if the video device has been removed - already or if it is not registered anymore. */ - if (vdev == NULL || !video_is_registered(vdev)) { + /* return ENODEV if the video device has already been removed. */ + if (vdev == NULL) { mutex_unlock(&videodev_lock); return -ENODEV; } /* and increase the device refcount */ video_get(vdev); mutex_unlock(&videodev_lock); - if (vdev->fops->open) - ret = vdev->fops->open(filp); + if (vdev->fops->open) { + if (vdev->lock) + mutex_lock(vdev->lock); + if (video_is_registered(vdev)) + ret = vdev->fops->open(filp); + else + ret = -ENODEV; + if (vdev->lock) + mutex_unlock(vdev->lock); + } /* decrease the refcount in case of an error */ if (ret) @@ -278,8 +313,13 @@ static int v4l2_release(struct inode *inode, struct file *filp) struct video_device *vdev = video_devdata(filp); int ret = 0; - if (vdev->fops->release) + if (vdev->fops->release) { + if (vdev->lock) + mutex_lock(vdev->lock); vdev->fops->release(filp); + if (vdev->lock) + mutex_unlock(vdev->lock); + } /* decrease the refcount unconditionally since the release() return value is ignored. */ diff --git a/drivers/media/video/v4l2-event.c b/drivers/media/video/v4l2-event.c index de74ce07b5e2..69fd343d4774 100644 --- a/drivers/media/video/v4l2-event.c +++ b/drivers/media/video/v4l2-event.c @@ -134,15 +134,22 @@ int v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event, if (nonblocking) return __v4l2_event_dequeue(fh, event); + /* Release the vdev lock while waiting */ + if (fh->vdev->lock) + mutex_unlock(fh->vdev->lock); + do { ret = wait_event_interruptible(events->wait, events->navailable != 0); if (ret < 0) - return ret; + break; ret = __v4l2_event_dequeue(fh, event); } while (ret == -ENOENT); + if (fh->vdev->lock) + mutex_lock(fh->vdev->lock); + return ret; } EXPORT_SYMBOL_GPL(v4l2_event_dequeue); diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h index ba236ff35c8a..15802a067a12 100644 --- a/include/media/v4l2-dev.h +++ b/include/media/v4l2-dev.h @@ -94,6 +94,9 @@ struct video_device /* ioctl callbacks */ const struct v4l2_ioctl_ops *ioctl_ops; + + /* serialization lock */ + struct mutex *lock; }; /* dev to video-device */ -- cgit v1.2.3 From 9c84d89bf13b702ca0e0db5eb3880300c4bd58f4 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 11 Oct 2010 12:36:37 -0300 Subject: [media] radio-mr800: fix locking order Don't hold the lock before unregistering the device, since when the device is unregistered the datastruct containing the lock may be freed (if the refcount went to 0). Also fixed the framework documentation that erroneously suggested the wrong locking order as well. Reported-by: David Ellingsworth Signed-off-by: Hans Verkuil Acked-by: David Ellingsworth Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/v4l2-framework.txt | 2 +- drivers/media/radio/radio-mr800.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt index a128e012a45c..f22f35c271f3 100644 --- a/Documentation/video4linux/v4l2-framework.txt +++ b/Documentation/video4linux/v4l2-framework.txt @@ -488,7 +488,7 @@ also waits in the code, then you should do the same to allow other processes to access the device node while the first process is waiting for something. The implementation of a hotplug disconnect should also take the lock before -calling v4l2_device_disconnect and video_unregister_device. +calling v4l2_device_disconnect. video_device registration ------------------------- diff --git a/drivers/media/radio/radio-mr800.c b/drivers/media/radio/radio-mr800.c index 97967ad3cd12..2f56b26cece9 100644 --- a/drivers/media/radio/radio-mr800.c +++ b/drivers/media/radio/radio-mr800.c @@ -285,8 +285,8 @@ static void usb_amradio_disconnect(struct usb_interface *intf) mutex_lock(&radio->lock); v4l2_device_disconnect(&radio->v4l2_dev); - video_unregister_device(&radio->videodev); mutex_unlock(&radio->lock); + video_unregister_device(&radio->videodev); } /* vidioc_querycap - query device capabilities */ -- cgit v1.2.3 From 69194aba3d998af0a51efdf1bde9ce6642b8ab76 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 22 Oct 2010 20:50:50 -0200 Subject: [media] Documentation/video4linux/CARDLIST.[cx88|saa7134] Fix a few troubles at the card entries Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/CARDLIST.cx88 | 1 + Documentation/video4linux/CARDLIST.saa7134 | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/CARDLIST.cx88 b/Documentation/video4linux/CARDLIST.cx88 index f2510541373b..42517d9121de 100644 --- a/Documentation/video4linux/CARDLIST.cx88 +++ b/Documentation/video4linux/CARDLIST.cx88 @@ -83,3 +83,4 @@ 82 -> WinFast DTV2000 H rev. J [107d:6f2b] 83 -> Prof 7301 DVB-S/S2 [b034:3034] 84 -> Samsung SMT 7020 DVB-S [18ac:dc00,18ac:dccd] + 85 -> Twinhan VP-1027 DVB-S [1822:0023] diff --git a/Documentation/video4linux/CARDLIST.saa7134 b/Documentation/video4linux/CARDLIST.saa7134 index 4000c29fcfb6..8d9afc7d8014 100644 --- a/Documentation/video4linux/CARDLIST.saa7134 +++ b/Documentation/video4linux/CARDLIST.saa7134 @@ -126,7 +126,7 @@ 125 -> Beholder BeholdTV 409 [0000:4090] 126 -> Beholder BeholdTV 505 FM [5ace:5050] 127 -> Beholder BeholdTV 507 FM / BeholdTV 509 FM [5ace:5070,5ace:5090] -128 -> Beholder BeholdTV Columbus TVFM [0000:5201] +128 -> Beholder BeholdTV Columbus TV/FM [0000:5201] 129 -> Beholder BeholdTV 607 FM [5ace:6070] 130 -> Beholder BeholdTV M6 [5ace:6190] 131 -> Twinhan Hybrid DTV-DVB 3056 PCI [1822:0022] -- cgit v1.2.3 From 8298f2f810b988dccfa0ab51cd874e107514c036 Mon Sep 17 00:00:00 2001 From: Adrian Taylor Date: Wed, 20 Oct 2010 07:55:33 -0300 Subject: [media] Support for Elgato Video Capture This patch allows this device successfully to show video, at least from its composite input. I have no information about the true hardware contents of this device and so this patch is based solely on fiddling with things until it worked. The chip appears to be em2860, and the closest device with equivalent inputs is the Typhoon DVD Maker. Copying the settings for that device appears to do the trick. That's what this patch does. [mchehab@redhat.com: update CARDLIST.em28xx accordingly, via script] Signed-off-by: Adrian Taylor Reviewed-by: Jarod Wilson Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/CARDLIST.em28xx | 3 ++- drivers/media/video/em28xx/em28xx-cards.c | 18 ++++++++++++++++++ drivers/media/video/em28xx/em28xx.h | 1 + 3 files changed, 21 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/video4linux/CARDLIST.em28xx b/Documentation/video4linux/CARDLIST.em28xx index 5c568757c301..ac2616a62fc3 100644 --- a/Documentation/video4linux/CARDLIST.em28xx +++ b/Documentation/video4linux/CARDLIST.em28xx @@ -31,6 +31,7 @@ 30 -> Videology 20K14XUSB USB2.0 (em2820/em2840) 31 -> Usbgear VD204v9 (em2821) 32 -> Supercomp USB 2.0 TV (em2821) + 33 -> Elgato Video Capture (em2860) [0fd9:0033] 34 -> Terratec Cinergy A Hybrid XS (em2860) [0ccd:004f] 35 -> Typhoon DVD Maker (em2860) 36 -> NetGMBH Cam (em2860) @@ -45,7 +46,7 @@ 45 -> Pinnacle PCTV DVB-T (em2870) 46 -> Compro, VideoMate U3 (em2870) [185b:2870] 47 -> KWorld DVB-T 305U (em2880) [eb1a:e305] - 48 -> KWorld DVB-T 310U (em2880) [eb1a:e310] + 48 -> KWorld DVB-T 310U (em2880) 49 -> MSI DigiVox A/D (em2880) [eb1a:e310] 50 -> MSI DigiVox A/D II (em2880) [eb1a:e320] 51 -> Terratec Hybrid XS Secam (em2880) [0ccd:004c] diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index f6dbb2148060..a7cec2737d07 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -1660,6 +1660,22 @@ struct em28xx_board em28xx_boards[] = { .gpio = terratec_av350_unmute_gpio, } }, }, + + [EM2860_BOARD_ELGATO_VIDEO_CAPTURE] = { + .name = "Elgato Video Capture", + .decoder = EM28XX_SAA711X, + .tuner_type = TUNER_ABSENT, /* Capture only device */ + .input = { { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = SAA7115_COMPOSITE0, + .amux = EM28XX_AMUX_LINE_IN, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = SAA7115_SVIDEO3, + .amux = EM28XX_AMUX_LINE_IN, + } }, + }, + [EM2882_BOARD_EVGA_INDTUBE] = { .name = "Evga inDtube", .tuner_type = TUNER_XC2028, @@ -1784,6 +1800,8 @@ struct usb_device_id em28xx_id_table[] = { .driver_info = EM2860_BOARD_TERRATEC_AV350 }, { USB_DEVICE(0x0ccd, 0x0096), .driver_info = EM2860_BOARD_TERRATEC_GRABBY }, + { USB_DEVICE(0x0fd9, 0x0033), + .driver_info = EM2860_BOARD_ELGATO_VIDEO_CAPTURE}, { USB_DEVICE(0x185b, 0x2870), .driver_info = EM2870_BOARD_COMPRO_VIDEOMATE }, { USB_DEVICE(0x185b, 0x2041), diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h index adb20eb6d902..6a75e6a4fc21 100644 --- a/drivers/media/video/em28xx/em28xx.h +++ b/drivers/media/video/em28xx/em28xx.h @@ -74,6 +74,7 @@ #define EM2820_BOARD_VIDEOLOGY_20K14XUSB 30 #define EM2821_BOARD_USBGEAR_VD204 31 #define EM2821_BOARD_SUPERCOMP_USB_2 32 +#define EM2860_BOARD_ELGATO_VIDEO_CAPTURE 33 #define EM2860_BOARD_TERRATEC_HYBRID_XS 34 #define EM2860_BOARD_TYPHOON_DVD_MAKER 35 #define EM2860_BOARD_NETGMBH_CAM 36 -- cgit v1.2.3 From ab599a6d474e0dd4db3557a46dd15cff6e126d0b Mon Sep 17 00:00:00 2001 From: Malcolm Priestley Date: Sat, 16 Oct 2010 16:44:43 -0300 Subject: [media] lmedm04: driver for DM04/QQBOX updated to version 1.60 These include -later kill of usb_buffer to avoid kernel crash on hot unplugging. -DiSEqC functions. -LNB Power switch -Faster channel change. -support for LG tuner on LME2510C. -firmware switching for LG tuner. Signed-off-by: Malcolm Priestley Signed-off-by: Mauro Carvalho Chehab --- Documentation/dvb/lmedm04.txt | 9 +- drivers/media/dvb/dvb-usb/Kconfig | 1 - drivers/media/dvb/dvb-usb/lmedm04.c | 292 +++++++++++++++++++++++++++--------- drivers/media/dvb/dvb-usb/lmedm04.h | 44 ++---- 4 files changed, 243 insertions(+), 103 deletions(-) (limited to 'Documentation') diff --git a/Documentation/dvb/lmedm04.txt b/Documentation/dvb/lmedm04.txt index 4bde457fafc4..e175784b89bf 100644 --- a/Documentation/dvb/lmedm04.txt +++ b/Documentation/dvb/lmedm04.txt @@ -45,10 +45,13 @@ and run Other LG firmware can be extracted manually from US280D.sys only found in windows/system32/driver. -However, this firmware does not run very well under Windows -or with the Linux driver. -dd if=US280D.sys ibs=1 skip=36856 count=3976 of=dvb-usb-lme2510-lg.fw +dd if=US280D.sys ibs=1 skip=42616 count=3668 of=dvb-usb-lme2510-lg.fw + +for DM04 LME2510C (LG Tuner) +--------------------------- + +dd if=US280D.sys ibs=1 skip=35200 count=3850 of=dvb-usb-lme2510c-lg.fw --------------------------------------------------------------------- diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig index ce60c1e46e15..2525d3b3c88d 100644 --- a/drivers/media/dvb/dvb-usb/Kconfig +++ b/drivers/media/dvb/dvb-usb/Kconfig @@ -356,6 +356,5 @@ config DVB_USB_LME2510 select DVB_TDA826X if !DVB_FE_CUSTOMISE select DVB_STV0288 if !DVB_FE_CUSTOMISE select DVB_IX2505V if !DVB_FE_CUSTOMISE - depends on IR_CORE help Say Y here to support the LME DM04/QQBOX DVB-S USB2.0 . diff --git a/drivers/media/dvb/dvb-usb/lmedm04.c b/drivers/media/dvb/dvb-usb/lmedm04.c index d5374ac075d3..d939fbbf9fe6 100644 --- a/drivers/media/dvb/dvb-usb/lmedm04.c +++ b/drivers/media/dvb/dvb-usb/lmedm04.c @@ -1,14 +1,17 @@ /* DVB USB compliant linux driver for * * DM04/QQBOX DVB-S USB BOX LME2510C + SHARP:BS2F7HZ7395 - * LME2510 + LGTDQT-P001F + * LME2510C + LG TDQY-P001F + * LME2510 + LG TDQY-P001F * * MVB7395 (LME2510C+SHARP:BS2F7HZ7395) * SHARP:BS2F7HZ7395 = (STV0288+Sharp IX2505V) * - * MV001F (LME2510 +LGTDQY-P001F) + * MV001F (LME2510+LGTDQY-P001F) * LG TDQY - P001F =(TDA8263 + TDA10086H) * + * MVB0001F (LME2510C+LGTDQT-P001F) + * * For firmware see Documentation/dvb/lmedm04.txt * * I2C addresses: @@ -21,7 +24,6 @@ * ***Please Note*** * There are other variants of the DM04 * ***NOT SUPPORTED*** - * MVB0001F (LME2510C+LGTDQT-P001F) * MV0194 (LME2510+SHARP0194) * MVB0194 (LME2510C+SHARP0194) * @@ -51,10 +53,7 @@ * LME2510: Non Intel USB chipsets fail to maintain High Speed on * Boot or Hot Plug. * - * DiSEqC functions are not fully supported in this driver. The main - * reason is the frontend is cut off during streaming. Allowing frontend - * access will stall the driver. Applications that attempt to this, the - * commands are ignored. + * QQbox suffers from noise on LNB voltage. * * PID functions have been removed from this driver version due to * problems with different firmware and application versions. @@ -91,9 +90,14 @@ module_param_named(debug, dvb_usb_lme2510_debug, int, 0644); MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able))." DVB_USB_DEBUG_STATUS); +static int dvb_usb_lme2510_firmware; +module_param_named(firmware, dvb_usb_lme2510_firmware, int, 0644); +MODULE_PARM_DESC(firmware, "set default firmware 0=Sharp7395 1=LG"); + + DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); -#define TUNER_LG 0x1 -#define TUNER_S7395 0x2 +#define TUNER_LG 0x1 +#define TUNER_S7395 0x2 struct lme2510_state { u8 id; @@ -107,6 +111,8 @@ struct lme2510_state { u8 i2c_tuner_gate_w; u8 i2c_tuner_gate_r; u8 i2c_tuner_addr; + u8 stream_on; + u8 one_tune; void *buffer; struct urb *lme_urb; void *usb_buffer; @@ -117,6 +123,7 @@ static int lme2510_bulk_write(struct usb_device *dev, u8 *snd, int len, u8 pipe) { int ret, actual_l; + ret = usb_bulk_msg(dev, usb_sndbulkpipe(dev, pipe), snd, len , &actual_l, 500); return ret; @@ -151,22 +158,21 @@ static int lme2510_usb_talk(struct dvb_usb_device *d, /* the read/write capped at 512 */ memcpy(buff, wbuf, (wlen > 512) ? 512 : wlen); - ret = mutex_lock_interruptible(&d->usb_mutex); if (ret < 0) return -EAGAIN; ret |= usb_clear_halt(d->udev, usb_sndbulkpipe(d->udev, 0x01)); - msleep(5); - ret |= lme2510_bulk_write(d->udev, buff, wlen , 0x1); - msleep(5); - ret |= usb_clear_halt(d->udev, usb_rcvbulkpipe(d->udev, 0x1)); + ret |= lme2510_bulk_write(d->udev, buff, wlen , 0x01); + + msleep(12); + + ret |= usb_clear_halt(d->udev, usb_rcvbulkpipe(d->udev, 0x01)); - msleep(5); ret |= lme2510_bulk_read(d->udev, buff, (rlen > 512) ? - 512 : rlen , 0x1); + 512 : rlen , 0x01); if (rlen > 0) memcpy(rbuf, buff, rlen); @@ -176,6 +182,18 @@ static int lme2510_usb_talk(struct dvb_usb_device *d, return (ret < 0) ? -ENODEV : 0; } +static int lme2510_usb_talk_restart(struct dvb_usb_device *d, + u8 *wbuf, int wlen, u8 *rbuf, int rlen) { + static u8 stream_on[] = LME_ST_ON_W; + int ret; + u8 rbuff[10]; + /*Send Normal Command*/ + ret = lme2510_usb_talk(d, wbuf, wlen, rbuf, rlen); + /*Restart Stream Command*/ + ret |= lme2510_usb_talk(d, stream_on, sizeof(stream_on), + rbuff, sizeof(rbuff)); + return ret; +} static int lme2510_remote_keypress(struct dvb_usb_adapter *adap, u16 keypress) { struct dvb_usb_device *d = adap->dev; @@ -213,7 +231,6 @@ static void lme2510_int_response(struct urb *lme_urb) offset = ((lme_urb->actual_length/8) > 4) ? 4 : (lme_urb->actual_length/8) ; - for (i = 0; i < offset; ++i) { ibuf = (u8 *)&rbuf[i*8]; deb_info(5, "INT O/S C =%02x C/O=%02x Type =%02x%02x", @@ -228,7 +245,8 @@ static void lme2510_int_response(struct urb *lme_urb) case 0xbb: switch (st->tuner_config) { case TUNER_LG: - st->signal_lock = ibuf[2]; + if (ibuf[2] > 0) + st->signal_lock = ibuf[2]; st->signal_level = ibuf[4]; st->signal_sn = ibuf[3]; st->time_key = ibuf[7]; @@ -237,7 +255,7 @@ static void lme2510_int_response(struct urb *lme_urb) /* Tweak for earlier firmware*/ if (ibuf[1] == 0x03) { st->signal_level = ibuf[3]; - st->signal_sn = ibuf[2]; + st->signal_sn = ibuf[4]; } else { st->signal_level = ibuf[4]; st->signal_sn = ibuf[5]; @@ -295,6 +313,7 @@ static int lme2510_return_status(struct usb_device *dev) { int ret = 0; u8 data[10] = {0}; + ret |= usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), 0x06, 0x80, 0x0302, 0x00, data, 0x0006, 200); info("Firmware Status: %x (%x)", ret , data[2]); @@ -308,25 +327,48 @@ static int lme2510_msg(struct dvb_usb_device *d, int ret = 0; struct lme2510_state *st = d->priv; - if (st->i2c_talk_onoff == 1) { - if ((wbuf[2] == 0x1c) & (wbuf[3] == 0x0e)) - msleep(80); /*take your time when waiting for tune*/ - - if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) return -EAGAIN; + if (st->i2c_talk_onoff == 1) { + ret = lme2510_usb_talk(d, wbuf, wlen, rbuf, rlen); - mutex_unlock(&d->i2c_mutex); switch (st->tuner_config) { + case TUNER_LG: + if (wbuf[2] == 0x1c) { + if (wbuf[3] == 0x0e) { + st->signal_lock = rbuf[1]; + if ((st->stream_on & 1) && + (st->signal_lock & 0x10)) { + lme2510_usb_talk_restart(d, + wbuf, wlen, rbuf, rlen); + st->i2c_talk_onoff = 0; + } + msleep(80); + } + } + break; case TUNER_S7395: - if (wbuf[3] == 0x24) - st->signal_lock = rbuf[1]; + if (wbuf[2] == 0xd0) { + if (wbuf[3] == 0x24) { + st->signal_lock = rbuf[1]; + if ((st->stream_on & 1) && + (st->signal_lock & 0x8)) { + lme2510_usb_talk_restart(d, + wbuf, wlen, rbuf, rlen); + st->i2c_talk_onoff = 0; + } + } + if ((wbuf[3] != 0x6) & (wbuf[3] != 0x5)) + msleep(5); + + + } break; default: break; } - } else { switch (st->tuner_config) { case TUNER_LG: @@ -343,6 +385,17 @@ static int lme2510_msg(struct dvb_usb_device *d, rbuf[0] = 0x55; rbuf[1] = st->signal_sn; break; + /*DiSEqC functions as per TDA10086*/ + case 0x36: + case 0x48: + case 0x49: + case 0x4a: + case 0x4b: + case 0x4c: + case 0x4d: + if (wbuf[2] == 0x1c) + lme2510_usb_talk_restart(d, + wbuf, wlen, rbuf, rlen); default: break; } @@ -362,7 +415,31 @@ static int lme2510_msg(struct dvb_usb_device *d, rbuf[0] = 0x55; rbuf[1] = (st->signal_level & 0x80) ? 0 : st->signal_lock; + break; + case 0x6: + if (wbuf[2] == 0xd0) + lme2510_usb_talk(d, + wbuf, wlen, rbuf, rlen); + break; + case 0x1: + if (st->one_tune > 0) + break; + st->one_tune++; + st->i2c_talk_onoff = 1; + /*DiSEqC functions as per STV0288*/ + case 0x5: + case 0x7: + case 0x8: + case 0x9: + case 0xa: + case 0xb: + if (wbuf[2] == 0xd0) + lme2510_usb_talk_restart(d, + wbuf, wlen, rbuf, rlen); + break; default: + rbuf[0] = 0x55; + rbuf[1] = 0x00; break; } break; @@ -376,6 +453,8 @@ static int lme2510_msg(struct dvb_usb_device *d, } + mutex_unlock(&d->i2c_mutex); + return ret; } @@ -469,29 +548,34 @@ static int lme2510_identify_state(struct usb_device *udev, static int lme2510_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) { struct lme2510_state *st = adap->dev->priv; - static u8 reset[] = LME_RESET; static u8 stream_on[] = LME_ST_ON_W; static u8 clear_reg_3[] = LME_CLEAR_PID; static u8 rbuf[1]; + static u8 timeout; int ret = 0, len = 2, rlen = sizeof(rbuf); deb_info(1, "STM (%02x)", onoff); if (onoff == 1) { st->i2c_talk_onoff = 0; - msleep(400); /* give enough time for i2c to stop */ + timeout = 0; + /* wait for i2C to be free */ + while (mutex_lock_interruptible(&adap->dev->i2c_mutex) < 0) { + timeout++; + if (timeout > 5) + return -ENODEV; + } + msleep(100); ret |= lme2510_usb_talk(adap->dev, stream_on, len, rbuf, rlen); + st->stream_on = 1; + st->one_tune = 0; + mutex_unlock(&adap->dev->i2c_mutex); } else { deb_info(1, "STM Steam Off"); - if (st->tuner_config == TUNER_LG) - ret |= lme2510_usb_talk(adap->dev, clear_reg_3, + ret |= lme2510_usb_talk(adap->dev, clear_reg_3, sizeof(clear_reg_3), rbuf, rlen); - else - ret |= lme2510_usb_talk(adap->dev, - reset, sizeof(reset), rbuf, rlen); - - msleep(400); + st->stream_on = 0; st->i2c_talk_onoff = 1; } @@ -604,6 +688,63 @@ static int lme2510_download_firmware(struct usb_device *dev, return (ret < 0) ? -ENODEV : 0; } +/* Default firmware for LME2510C */ +const char lme_firmware[50] = "dvb-usb-lme2510c-s7395.fw"; + +static void lme_coldreset(struct usb_device *dev) +{ + int ret = 0, len_in; + u8 data[512] = {0}; + + data[0] = 0x0a; + len_in = 1; + info("FRM Firmware Cold Reset"); + ret |= lme2510_bulk_write(dev, data , len_in, 1); /*Cold Resetting*/ + ret |= lme2510_bulk_read(dev, data, len_in, 1); + return; +} + +static void lme_firmware_switch(struct usb_device *udev, int cold) +{ + const struct firmware *fw = NULL; + char lme2510c_s7395[] = "dvb-usb-lme2510c-s7395.fw"; + char lme2510c_lg[] = "dvb-usb-lme2510c-lg.fw"; + char *firm_msg[] = {"Loading", "Switching to"}; + int ret; + + if (udev->descriptor.idProduct == 0x1122) + return; + + switch (dvb_usb_lme2510_firmware) { + case 0: + default: + memcpy(&lme_firmware, lme2510c_s7395, sizeof(lme2510c_s7395)); + ret = request_firmware(&fw, lme_firmware, &udev->dev); + if (ret == 0) { + info("FRM %s S7395 Firmware", firm_msg[cold]); + break; + } + if (cold == 0) + dvb_usb_lme2510_firmware = 1; + else + cold = 0; + case 1: + memcpy(&lme_firmware, lme2510c_lg, sizeof(lme2510c_lg)); + ret = request_firmware(&fw, lme_firmware, &udev->dev); + if (ret == 0) { + info("FRM %s LG Firmware", firm_msg[cold]); + break; + } + info("FRM No Firmware Found - please install"); + dvb_usb_lme2510_firmware = 0; + cold = 0; + break; + } + release_firmware(fw); + if (cold) + lme_coldreset(udev); + return; +} static int lme2510_kill_urb(struct usb_data_stream *stream) { @@ -638,7 +779,6 @@ static struct ix2505v_config lme_tuner = { .tuner_chargepump = 0x3, }; - static int dm04_lme2510_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) { @@ -646,35 +786,28 @@ static int dm04_lme2510_set_voltage(struct dvb_frontend *fe, struct lme2510_state *st = adap->dev->priv; static u8 voltage_low[] = LME_VOLTAGE_L; static u8 voltage_high[] = LME_VOLTAGE_H; - static u8 reset[] = LME_RESET; - static u8 clear_reg_3[] = LME_CLEAR_PID; + static u8 lnb_on[] = LNB_ON; + static u8 lnb_off[] = LNB_OFF; static u8 rbuf[1]; int ret = 0, len = 3, rlen = 1; - msleep(100); - - if (st->tuner_config == TUNER_LG) - ret |= lme2510_usb_talk(adap->dev, clear_reg_3, - sizeof(clear_reg_3), rbuf, rlen); - else - ret |= lme2510_usb_talk(adap->dev, - reset, sizeof(reset), rbuf, rlen); - - /*always check & stop streaming*/ - lme2510_kill_urb(&adap->stream); - adap->feedcount = 0; + if (st->stream_on == 1) + return 0; - switch (voltage) { + ret |= lme2510_usb_talk(adap->dev, lnb_on, len, rbuf, rlen); - case SEC_VOLTAGE_18: - ret |= lme2510_usb_talk(adap->dev, - voltage_high, len, rbuf, rlen); + switch (voltage) { + case SEC_VOLTAGE_18: + ret |= lme2510_usb_talk(adap->dev, + voltage_high, len, rbuf, rlen); break; - case SEC_VOLTAGE_OFF: - case SEC_VOLTAGE_13: - default: - ret |= lme2510_usb_talk(adap->dev, + case SEC_VOLTAGE_OFF: + ret |= lme2510_usb_talk(adap->dev, + lnb_off, len, rbuf, rlen); + case SEC_VOLTAGE_13: + default: + ret |= lme2510_usb_talk(adap->dev, voltage_low, len, rbuf, rlen); break; @@ -714,6 +847,10 @@ static int dm04_lme2510_frontend_attach(struct dvb_usb_adapter *adap) &adap->dev->i2c_adap, 1)) { info("TUN TDA8263 Found"); st->tuner_config = TUNER_LG; + if (dvb_usb_lme2510_firmware != 1) { + dvb_usb_lme2510_firmware = 1; + lme_firmware_switch(adap->dev->udev, 1); + } return 0; } kfree(adap->fe); @@ -735,6 +872,10 @@ static int dm04_lme2510_frontend_attach(struct dvb_usb_adapter *adap) &adap->dev->i2c_adap)) { st->tuner_config = TUNER_S7395; info("TUN Sharp IX2505V silicon tuner"); + if (dvb_usb_lme2510_firmware != 0) { + dvb_usb_lme2510_firmware = 0; + lme_firmware_switch(adap->dev->udev, 1); + } return 0; } kfree(adap->fe); @@ -772,6 +913,8 @@ static int lme2510_probe(struct usb_interface *intf, return -ENODEV; } + lme_firmware_switch(udev, 0); + if (0 == dvb_usb_device_init(intf, &lme2510_properties, THIS_MODULE, NULL, adapter_nr)) { info("DEV registering device driver"); @@ -839,7 +982,7 @@ static struct dvb_usb_device_properties lme2510c_properties = { .caps = DVB_USB_IS_AN_I2C_ADAPTER, .usb_ctrl = DEVICE_SPECIFIC, .download_firmware = lme2510_download_firmware, - .firmware = "dvb-usb-lme2510c-s7395.fw", + .firmware = lme_firmware, .size_of_priv = sizeof(struct lme2510_state), .num_adapters = 1, .adapter = { @@ -849,7 +992,7 @@ static struct dvb_usb_device_properties lme2510c_properties = { /* parameter for the MPEG2-data transfer */ .stream = { .type = USB_BULK, - .count = 8, + .count = 10, .endpoint = 0x8, .u = { .bulk = { @@ -872,15 +1015,23 @@ static struct dvb_usb_device_properties lme2510c_properties = { } }; -void lme2510_exit_int(struct dvb_usb_device *d) +void *lme2510_exit_int(struct dvb_usb_device *d) { struct lme2510_state *st = d->priv; + struct dvb_usb_adapter *adap = &d->adapter[0]; + void *buffer = NULL; + + if (adap != NULL) { + lme2510_kill_urb(&adap->stream); + adap->feedcount = 0; + } + if (st->lme_urb != NULL) { - st->i2c_talk_onoff = 0; + st->i2c_talk_onoff = 1; st->signal_lock = 0; st->signal_level = 0; st->signal_sn = 0; - kfree(st->usb_buffer); + buffer = st->usb_buffer; usb_kill_urb(st->lme_urb); usb_free_coherent(d->udev, 5000, st->buffer, st->lme_urb->transfer_dma); @@ -888,18 +1039,19 @@ void lme2510_exit_int(struct dvb_usb_device *d) ir_input_unregister(d->rc_input_dev); info("Remote Stopped"); } - return; + return buffer; } void lme2510_exit(struct usb_interface *intf) { struct dvb_usb_device *d = usb_get_intfdata(intf); + void *usb_buffer; + if (d != NULL) { - d->adapter[0].feedcount = 0; - lme2510_exit_int(d); + usb_buffer = lme2510_exit_int(d); dvb_usb_device_exit(intf); + kfree(usb_buffer); } - } static struct usb_driver lme2510_driver = { @@ -932,5 +1084,5 @@ module_exit(lme2510_module_exit); MODULE_AUTHOR("Malcolm Priestley "); MODULE_DESCRIPTION("LM2510(C) DVB-S USB2.0"); -MODULE_VERSION("1.4"); +MODULE_VERSION("1.60"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/dvb-usb/lmedm04.h b/drivers/media/dvb/dvb-usb/lmedm04.h index 5a66c7ec4511..e6af16c1e3e5 100644 --- a/drivers/media/dvb/dvb-usb/lmedm04.h +++ b/drivers/media/dvb/dvb-usb/lmedm04.h @@ -1,14 +1,16 @@ /* DVB USB compliant linux driver for * * DM04/QQBOX DVB-S USB BOX LME2510C + SHARP:BS2F7HZ7395 - * LME2510C + LGTDQT-P001F + * LME2510C + LG TDQY-P001F + * LME2510 + LG TDQY-P001F * * MVB7395 (LME2510C+SHARP:BS2F7HZ7395) * SHARP:BS2F7HZ7395 = (STV0288+Sharp IX2505V) * - * MVB0001F (LME2510C+LGTDQT-P001F) + * MVB001F (LME2510+LGTDQT-P001F) * LG TDQY - P001F =(TDA8263 + TDA10086H) * + * MVB0001F (LME2510C+LGTDQT-P001F) * * 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 @@ -35,35 +37,33 @@ * end byte -1 = 20 * end byte = clear pid always a0, other wise 9c, 9a ?? * - * RESET (also clears PID filter) - * 3a 01 00 */ #define LME_ST_ON_W {0x06, 0x00} -#define LME_RESET {0x3a, 0x01, 0x00} #define LME_CLEAR_PID {0x03, 0x02, 0x20, 0xa0} - -/* LME Power Control +/* LNB Voltage * 07 XX XX - * offset 1 = 01 Power? my device cannot be powered down + * offset 1 = 01 * offset 2 = 00=Voltage low 01=Voltage high + * + * LNB Power + * 03 01 XX + * offset 2 = 00=ON 01=OFF */ #define LME_VOLTAGE_L {0x07, 0x01, 0x00} #define LME_VOLTAGE_H {0x07, 0x01, 0x01} - +#define LNB_ON {0x3a, 0x01, 0x00} +#define LNB_OFF {0x3a, 0x01, 0x01} /* Initial stv0288 settings for 7395 Frontend */ static u8 s7395_inittab[] = { - 0x00, 0x11, 0x01, 0x15, 0x02, 0x20, - 0x03, 0x8e, - 0x04, 0x8e, + 0x03, 0xa0, + 0x04, 0xa0, 0x05, 0x12, - 0x06, 0xff, - 0x07, 0x20, - 0x08, 0x00, + 0x06, 0x00, 0x09, 0x00, 0x0a, 0x04, 0x0b, 0x00, @@ -71,7 +71,6 @@ static u8 s7395_inittab[] = { 0x0d, 0x00, 0x0e, 0xc1, 0x0f, 0x54, - 0x10, 0x40, 0x11, 0x7a, 0x12, 0x03, 0x13, 0x48, @@ -84,23 +83,15 @@ static u8 s7395_inittab[] = { 0x1a, 0x88, 0x1b, 0x8f, 0x1c, 0xf0, - 0x1e, 0x80, 0x20, 0x0b, 0x21, 0x54, 0x22, 0xff, 0x23, 0x01, - 0x24, 0x9a, - 0x25, 0x7f, - 0x26, 0x00, - 0x27, 0x00, 0x28, 0x46, 0x29, 0x66, 0x2a, 0x90, 0x2b, 0xfa, 0x2c, 0xd9, - 0x2d, 0x02, - 0x2e, 0xb1, - 0x2f, 0x00, 0x30, 0x0, 0x31, 0x1e, 0x32, 0x14, @@ -115,8 +106,6 @@ static u8 s7395_inittab[] = { 0x3b, 0x13, 0x3c, 0x11, 0x3d, 0x30, - 0x3e, 0x00, - 0x3f, 0x00, 0x40, 0x63, 0x41, 0x04, 0x42, 0x60, @@ -126,8 +115,6 @@ static u8 s7395_inittab[] = { 0x46, 0x00, 0x47, 0x00, 0x4a, 0x00, - 0x4b, 0xd1, - 0x4c, 0x33, 0x50, 0x12, 0x51, 0x36, 0x52, 0x21, @@ -183,5 +170,4 @@ static u8 s7395_inittab[] = { 0xf2, 0xc0, 0xff, 0xff, }; - #endif -- cgit v1.2.3 From eb8942e9b6150d3cc89640f1fb243ca9626a1d14 Mon Sep 17 00:00:00 2001 From: Matti Aaltonen Date: Mon, 18 Oct 2010 10:52:37 -0300 Subject: [media] Documentation: v4l: Add hw_seek spacing and two TUNER_RDS_CAP flags Add a couple of words about the spacing field in the HW seek struct, also a few words about the new RDS tuner capability flags V4L2_TUNER_CAP_RDS_BLOCK-IO and V4L2_TUNER_CAP_RDS_CONTROLS. Signed-off-by: Matti J. Aaltonen Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/v4l/dev-rds.xml | 68 ++++++++++++++++------ .../DocBook/v4l/vidioc-s-hw-freq-seek.xml | 10 +++- 2 files changed, 59 insertions(+), 19 deletions(-) (limited to 'Documentation') diff --git a/Documentation/DocBook/v4l/dev-rds.xml b/Documentation/DocBook/v4l/dev-rds.xml index 0869d701b1e5..360d2737e649 100644 --- a/Documentation/DocBook/v4l/dev-rds.xml +++ b/Documentation/DocBook/v4l/dev-rds.xml @@ -3,15 +3,16 @@ The Radio Data System transmits supplementary information in binary format, for example the station name or travel information, on an inaudible audio subcarrier of a radio program. This -interface is aimed at devices capable of receiving and decoding RDS +interface is aimed at devices capable of receiving and/or transmitting RDS information. For more information see the core RDS standard and the RBDS standard . Note that the RBDS standard as is used in the USA is almost identical -to the RDS standard. Any RDS decoder can also handle RBDS. Only some of the fields -have slightly different meanings. See the RBDS standard for more information. +to the RDS standard. Any RDS decoder/encoder can also handle RBDS. Only some of the +fields have slightly different meanings. See the RBDS standard for more +information. The RBDS standard also specifies support for MMBS (Modified Mobile Search). This is a proprietary format which seems to be discontinued. The RDS interface does not @@ -21,16 +22,25 @@ be needed, then please contact the linux-media mailing list: &v4l-ml;.
Querying Capabilities - Devices supporting the RDS capturing API -set the V4L2_CAP_RDS_CAPTURE flag in + Devices supporting the RDS capturing API set +the V4L2_CAP_RDS_CAPTURE flag in the capabilities field of &v4l2-capability; -returned by the &VIDIOC-QUERYCAP; ioctl. -Any tuner that supports RDS will set the -V4L2_TUNER_CAP_RDS flag in the capability -field of &v4l2-tuner;. -Whether an RDS signal is present can be detected by looking at -the rxsubchans field of &v4l2-tuner;: the -V4L2_TUNER_SUB_RDS will be set if RDS data was detected. +returned by the &VIDIOC-QUERYCAP; ioctl. Any tuner that supports RDS +will set the V4L2_TUNER_CAP_RDS flag in +the capability field of &v4l2-tuner;. If +the driver only passes RDS blocks without interpreting the data +the V4L2_TUNER_SUB_RDS_BLOCK_IO flag has to be +set, see Reading RDS data. +For future use the +flag V4L2_TUNER_SUB_RDS_CONTROLS has also been +defined. However, a driver for a radio tuner with this capability does +not yet exist, so if you are planning to write such a driver you +should discuss this on the linux-media mailing list: &v4l-ml;. + + Whether an RDS signal is present can be detected by looking +at the rxsubchans field of &v4l2-tuner;: +the V4L2_TUNER_SUB_RDS will be set if RDS data +was detected. Devices supporting the RDS output API set the V4L2_CAP_RDS_OUTPUT flag in @@ -40,16 +50,31 @@ Any modulator that supports RDS will set the V4L2_TUNER_CAP_RDS flag in the capability field of &v4l2-modulator;. In order to enable the RDS transmission one must set the V4L2_TUNER_SUB_RDS -bit in the txsubchans field of &v4l2-modulator;. - +bit in the txsubchans field of &v4l2-modulator;. +If the driver only passes RDS blocks without interpreting the data +the V4L2_TUNER_SUB_RDS_BLOCK_IO flag has to be set. If the +tuner is capable of handling RDS entities like program identification codes and radio +text, the flag V4L2_TUNER_SUB_RDS_CONTROLS should be set, +see Writing RDS data and +FM Transmitter Control Reference.
-
+
Reading RDS data RDS data can be read from the radio device -with the &func-read; function. The data is packed in groups of three bytes, +with the &func-read; function. The data is packed in groups of three bytes. +
+ +
+ Writing RDS data + + RDS data can be written to the radio device +with the &func-write; function. The data is packed in groups of three bytes, as follows: +
+ +
struct <structname>v4l2_rds_data</structname> @@ -111,48 +136,57 @@ as follows: V4L2_RDS_BLOCK_MSK + 7 Mask for bits 0-2 to get the block ID. V4L2_RDS_BLOCK_A + 0 Block A. V4L2_RDS_BLOCK_B + 1 Block B. V4L2_RDS_BLOCK_C + 2 Block C. V4L2_RDS_BLOCK_D + 3 Block D. V4L2_RDS_BLOCK_C_ALT + 4 Block C'. V4L2_RDS_BLOCK_INVALID + read-only 7 An invalid block. V4L2_RDS_BLOCK_CORRECTED + read-only 0x40 A bit error was detected but corrected. V4L2_RDS_BLOCK_ERROR + read-only 0x80 - An incorrectable error occurred. + An uncorrectable error occurred. diff --git a/Documentation/DocBook/v4l/vidioc-s-hw-freq-seek.xml b/Documentation/DocBook/v4l/vidioc-s-hw-freq-seek.xml index 14b3ec7ed75b..c30dcc4232c0 100644 --- a/Documentation/DocBook/v4l/vidioc-s-hw-freq-seek.xml +++ b/Documentation/DocBook/v4l/vidioc-s-hw-freq-seek.xml @@ -51,7 +51,8 @@ Start a hardware frequency seek from the current frequency. To do this applications initialize the tuner, -type, seek_upward and +type, seek_upward, +spacing and wrap_around fields, and zero out the reserved array of a &v4l2-hw-freq-seek; and call the VIDIOC_S_HW_FREQ_SEEK ioctl with a pointer @@ -89,7 +90,12 @@ field and the &v4l2-tuner; index field. __u32 - reserved[8] + spacing + If non-zero, defines the hardware seek resolution in Hz. The driver selects the nearest value that is supported by the device. If spacing is zero a reasonable default value is used. + + + __u32 + reserved[7] Reserved for future extensions. Drivers and applications must set the array to zero. -- cgit v1.2.3 From a99e3c515e4e59eb66213baf57137b9ae78a1493 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 25 Feb 2010 14:33:27 -0300 Subject: [media] DocBook/v4l: Add missing formats used on gspca cpia1 and sn9c2028 Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/v4l/pixfmt.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'Documentation') diff --git a/Documentation/DocBook/v4l/pixfmt.xml b/Documentation/DocBook/v4l/pixfmt.xml index f4cc0a9e658e..861664dfd6f0 100644 --- a/Documentation/DocBook/v4l/pixfmt.xml +++ b/Documentation/DocBook/v4l/pixfmt.xml @@ -685,6 +685,11 @@ http://www.ivtvdriver.org/The format is documented in the kernel sources in the file Documentation/video4linux/cx2341x/README.hm12 + + V4L2_PIX_FMT_CPIA1 + 'CPIA' + YUV format used by the gspca cpia1 driver. + V4L2_PIX_FMT_SPCA501 'S501' @@ -770,6 +775,11 @@ kernel sources in the file Documentation/video4linux/cx2341x/README.hm 'S920' YUV 4:2:0 format of the gspca sn9c20x driver. + + V4L2_PIX_FMT_SN9C2028 + 'SONX' + Compressed GBRG bayer format of the gspca sn9c2028 driver. + V4L2_PIX_FMT_STV0680 'S680' -- cgit v1.2.3 From 039aa70218e28a07fdf78220b814cfc4c1c6c3d2 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 25 Feb 2010 14:33:27 -0300 Subject: [media] v4l: document new Bayer and monochrome pixel formats Document all four 10-bit Bayer formats, 10-bit monochrome and a missing 8-bit Bayer formats. create mode 100644 Documentation/DocBook/v4l/pixfmt-srggb10.xml create mode 100644 Documentation/DocBook/v4l/pixfmt-srggb8.xml create mode 100644 Documentation/DocBook/v4l/pixfmt-y10.xml [mchehab@redhat.com: remove duplicated symbol for V4L2-PIX-FMT-SGRBG10 and added the corresponding entries at media-entities.tmpl] Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media-entities.tmpl | 6 ++ Documentation/DocBook/v4l/pixfmt-srggb10.xml | 90 ++++++++++++++++++++++++++++ Documentation/DocBook/v4l/pixfmt-srggb8.xml | 67 +++++++++++++++++++++ Documentation/DocBook/v4l/pixfmt-y10.xml | 79 ++++++++++++++++++++++++ Documentation/DocBook/v4l/pixfmt.xml | 8 +-- 5 files changed, 245 insertions(+), 5 deletions(-) create mode 100644 Documentation/DocBook/v4l/pixfmt-srggb10.xml create mode 100644 Documentation/DocBook/v4l/pixfmt-srggb8.xml create mode 100644 Documentation/DocBook/v4l/pixfmt-y10.xml (limited to 'Documentation') diff --git a/Documentation/DocBook/media-entities.tmpl b/Documentation/DocBook/media-entities.tmpl index 6ae97157b1c7..be34dcbe0d90 100644 --- a/Documentation/DocBook/media-entities.tmpl +++ b/Documentation/DocBook/media-entities.tmpl @@ -250,6 +250,9 @@ + + + @@ -347,6 +350,9 @@ + + + diff --git a/Documentation/DocBook/v4l/pixfmt-srggb10.xml b/Documentation/DocBook/v4l/pixfmt-srggb10.xml new file mode 100644 index 000000000000..7b274092e60c --- /dev/null +++ b/Documentation/DocBook/v4l/pixfmt-srggb10.xml @@ -0,0 +1,90 @@ + + + V4L2_PIX_FMT_SRGGB10 ('RG10'), + V4L2_PIX_FMT_SGRBG10 ('BA10'), + V4L2_PIX_FMT_SGBRG10 ('GB10'), + V4L2_PIX_FMT_SBGGR10 ('BG10'), + + &manvol; + + + V4L2_PIX_FMT_SRGGB10 + V4L2_PIX_FMT_SGRBG10 + V4L2_PIX_FMT_SGBRG10 + V4L2_PIX_FMT_SBGGR10 + 10-bit Bayer formats expanded to 16 bits + + + Description + + The following four pixel formats are raw sRGB / Bayer formats with +10 bits per colour. Each colour component is stored in a 16-bit word, with 6 +unused high bits filled with zeros. Each n-pixel row contains n/2 green samples +and n/2 blue or red samples, with alternating red and blue rows. Bytes are +stored in memory in little endian order. They are conventionally described +as GRGR... BGBG..., RGRG... GBGB..., etc. Below is an example of one of these +formats + + + <constant>V4L2_PIX_FMT_SBGGR10</constant> 4 × 4 +pixel image + + + Byte Order. + Each cell is one byte, high 6 bits in high bytes are 0. + + + + + + start + 0: + B00low + B00high + G01low + G01high + B02low + B02high + G03low + G03high + + + start + 8: + G10low + G10high + R11low + R11high + G12low + G12high + R13low + R13high + + + start + 16: + B20low + B20high + G21low + G21high + B22low + B22high + G23low + G23high + + + start + 24: + G30low + G30high + R31low + R31high + G32low + G32high + R33low + R33high + + + + + + + + + diff --git a/Documentation/DocBook/v4l/pixfmt-srggb8.xml b/Documentation/DocBook/v4l/pixfmt-srggb8.xml new file mode 100644 index 000000000000..2570e3be3cf1 --- /dev/null +++ b/Documentation/DocBook/v4l/pixfmt-srggb8.xml @@ -0,0 +1,67 @@ + + + V4L2_PIX_FMT_SRGGB8 ('RGGB') + &manvol; + + + V4L2_PIX_FMT_SRGGB8 + Bayer RGB format + + + Description + + This is commonly the native format of digital cameras, +reflecting the arrangement of sensors on the CCD device. Only one red, +green or blue value is given for each pixel. Missing components must +be interpolated from neighbouring pixels. From left to right the first +row consists of a red and green value, the second row of a green and +blue value. This scheme repeats to the right and down for every two +columns and rows. + + + <constant>V4L2_PIX_FMT_SRGGB8</constant> 4 × 4 +pixel image + + + Byte Order. + Each cell is one byte. + + + + + + start + 0: + R00 + G01 + R02 + G03 + + + start + 4: + G10 + B11 + G12 + B13 + + + start + 8: + R20 + G21 + R22 + G23 + + + start + 12: + G30 + B31 + G32 + B33 + + + + + + + + + diff --git a/Documentation/DocBook/v4l/pixfmt-y10.xml b/Documentation/DocBook/v4l/pixfmt-y10.xml new file mode 100644 index 000000000000..d065043db8d8 --- /dev/null +++ b/Documentation/DocBook/v4l/pixfmt-y10.xml @@ -0,0 +1,79 @@ + + + V4L2_PIX_FMT_Y10 ('Y10 ') + &manvol; + + + V4L2_PIX_FMT_Y10 + Grey-scale image + + + Description + + This is a grey-scale image with a depth of 10 bits per pixel. Pixels +are stored in 16-bit words with unused high bits padded with 0. The least +significant byte is stored at lower memory addresses (little-endian). + + + <constant>V4L2_PIX_FMT_Y10</constant> 4 × 4 +pixel image + + + Byte Order. + Each cell is one byte. + + + + + + start + 0: + Y'00low + Y'00high + Y'01low + Y'01high + Y'02low + Y'02high + Y'03low + Y'03high + + + start + 8: + Y'10low + Y'10high + Y'11low + Y'11high + Y'12low + Y'12high + Y'13low + Y'13high + + + start + 16: + Y'20low + Y'20high + Y'21low + Y'21high + Y'22low + Y'22high + Y'23low + Y'23high + + + start + 24: + Y'30low + Y'30high + Y'31low + Y'31high + Y'32low + Y'32high + Y'33low + Y'33high + + + + + + + + + diff --git a/Documentation/DocBook/v4l/pixfmt.xml b/Documentation/DocBook/v4l/pixfmt.xml index 861664dfd6f0..d7c467187095 100644 --- a/Documentation/DocBook/v4l/pixfmt.xml +++ b/Documentation/DocBook/v4l/pixfmt.xml @@ -566,7 +566,9 @@ access the palette, this must be done with ioctls of the Linux framebuffer API.< &sub-sbggr8; &sub-sgbrg8; &sub-sgrbg8; + &sub-srggb8; &sub-sbggr16; + &sub-srggb10;
@@ -589,6 +591,7 @@ information. &sub-packed-yuv; &sub-grey; + &sub-y10; &sub-y16; &sub-yuyv; &sub-uyvy; @@ -710,11 +713,6 @@ kernel sources in the file Documentation/video4linux/cx2341x/README.hm 'S561' Compressed GBRG Bayer format used by the gspca driver. - - V4L2_PIX_FMT_SGRBG10 - 'DA10' - 10 bit raw Bayer, expanded to 16 bits. - V4L2_PIX_FMT_SGRBG10DPCM8 'DB10' -- cgit v1.2.3 From d8d627834b1f4dd21a63c2b524e9eb56173df57b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 23 Oct 2010 09:28:12 -0200 Subject: videodev2.h.xml: Update to reflect the latest changes at videodev2.h Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/v4l/videodev2.h.xml | 104 +++++++++++++++++++++++------- 1 file changed, 80 insertions(+), 24 deletions(-) (limited to 'Documentation') diff --git a/Documentation/DocBook/v4l/videodev2.h.xml b/Documentation/DocBook/v4l/videodev2.h.xml index eda385891ade..325b23b6964c 100644 --- a/Documentation/DocBook/v4l/videodev2.h.xml +++ b/Documentation/DocBook/v4l/videodev2.h.xml @@ -154,23 +154,13 @@ enum v4l2_buf_type { V4L2_BUF_TYPE_VBI_OUTPUT = 5, V4L2_BUF_TYPE_SLICED_VBI_CAPTURE = 6, V4L2_BUF_TYPE_SLICED_VBI_OUTPUT = 7, -#if 1 /*KEEP*/ +#if 1 /* Experimental */ V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY = 8, #endif V4L2_BUF_TYPE_PRIVATE = 0x80, }; -enum v4l2_ctrl_type { - V4L2_CTRL_TYPE_INTEGER = 1, - V4L2_CTRL_TYPE_BOOLEAN = 2, - V4L2_CTRL_TYPE_MENU = 3, - V4L2_CTRL_TYPE_BUTTON = 4, - V4L2_CTRL_TYPE_INTEGER64 = 5, - V4L2_CTRL_TYPE_CTRL_CLASS = 6, - V4L2_CTRL_TYPE_STRING = 7, -}; - enum v4l2_tuner_type { V4L2_TUNER_RADIO = 1, V4L2_TUNER_ANALOG_TV = 2, @@ -288,6 +278,7 @@ struct v4l2_pix_format { #define V4L2_PIX_FMT_RGB565 v4l2_fourcc('R', 'G', 'B', 'P') /* 16 RGB-5-6-5 */ #define V4L2_PIX_FMT_RGB555X v4l2_fourcc('R', 'G', 'B', 'Q') /* 16 RGB-5-5-5 BE */ #define V4L2_PIX_FMT_RGB565X v4l2_fourcc('R', 'G', 'B', 'R') /* 16 RGB-5-6-5 BE */ +#define V4L2_PIX_FMT_BGR666 v4l2_fourcc('B', 'G', 'R', 'H') /* 18 BGR-6-6-6 */ #define V4L2_PIX_FMT_BGR24 v4l2_fourcc('B', 'G', 'R', '3') /* 24 BGR-8-8-8 */ #define V4L2_PIX_FMT_RGB24 v4l2_fourcc('R', 'G', 'B', '3') /* 24 RGB-8-8-8 */ #define V4L2_PIX_FMT_BGR32 v4l2_fourcc('B', 'G', 'R', '4') /* 32 BGR-8-8-8-8 */ @@ -295,6 +286,9 @@ struct v4l2_pix_format { /* Grey formats */ #define V4L2_PIX_FMT_GREY v4l2_fourcc('G', 'R', 'E', 'Y') /* 8 Greyscale */ +#define V4L2_PIX_FMT_Y4 v4l2_fourcc('Y', '0', '4', ' ') /* 4 Greyscale */ +#define V4L2_PIX_FMT_Y6 v4l2_fourcc('Y', '0', '6', ' ') /* 6 Greyscale */ +#define V4L2_PIX_FMT_Y10 v4l2_fourcc('Y', '1', '0', ' ') /* 10 Greyscale */ #define V4L2_PIX_FMT_Y16 v4l2_fourcc('Y', '1', '6', ' ') /* 16 Greyscale */ /* Palette formats */ @@ -330,7 +324,11 @@ struct v4l2_pix_format { #define V4L2_PIX_FMT_SBGGR8 v4l2_fourcc('B', 'A', '8', '1') /* 8 BGBG.. GRGR.. */ #define V4L2_PIX_FMT_SGBRG8 v4l2_fourcc('G', 'B', 'R', 'G') /* 8 GBGB.. RGRG.. */ #define V4L2_PIX_FMT_SGRBG8 v4l2_fourcc('G', 'R', 'B', 'G') /* 8 GRGR.. BGBG.. */ -#define V4L2_PIX_FMT_SGRBG10 v4l2_fourcc('B', 'A', '1', '0') /* 10bit raw bayer */ +#define V4L2_PIX_FMT_SRGGB8 v4l2_fourcc('R', 'G', 'G', 'B') /* 8 RGRG.. GBGB.. */ +#define V4L2_PIX_FMT_SBGGR10 v4l2_fourcc('B', 'G', '1', '0') /* 10 BGBG.. GRGR.. */ +#define V4L2_PIX_FMT_SGBRG10 v4l2_fourcc('G', 'B', '1', '0') /* 10 GBGB.. RGRG.. */ +#define V4L2_PIX_FMT_SGRBG10 v4l2_fourcc('B', 'A', '1', '0') /* 10 GRGR.. BGBG.. */ +#define V4L2_PIX_FMT_SRGGB10 v4l2_fourcc('R', 'G', '1', '0') /* 10 RGRG.. GBGB.. */ /* 10bit raw bayer DPCM compressed to 8 bits */ #define V4L2_PIX_FMT_SGRBG10DPCM8 v4l2_fourcc('B', 'D', '1', '0') /* @@ -346,6 +344,7 @@ struct v4l2_pix_format { #define V4L2_PIX_FMT_MPEG v4l2_fourcc('M', 'P', 'E', 'G') /* MPEG-1/2/4 */ /* Vendor-specific formats */ +#define V4L2_PIX_FMT_CPIA1 v4l2_fourcc('C', 'P', 'I', 'A') /* cpia1 YUV */ #define V4L2_PIX_FMT_WNVA v4l2_fourcc('W', 'N', 'V', 'A') /* Winnov hw compress */ #define V4L2_PIX_FMT_SN9C10X v4l2_fourcc('S', '9', '1', '0') /* SN9C10x compression */ #define V4L2_PIX_FMT_SN9C20X_I420 v4l2_fourcc('S', '9', '2', '0') /* SN9C20x YUV 4:2:0 */ @@ -358,12 +357,13 @@ struct v4l2_pix_format { #define V4L2_PIX_FMT_SPCA561 v4l2_fourcc('S', '5', '6', '1') /* compressed GBRG bayer */ #define V4L2_PIX_FMT_PAC207 v4l2_fourcc('P', '2', '0', '7') /* compressed BGGR bayer */ #define V4L2_PIX_FMT_MR97310A v4l2_fourcc('M', '3', '1', '0') /* compressed BGGR bayer */ +#define V4L2_PIX_FMT_SN9C2028 v4l2_fourcc('S', 'O', 'N', 'X') /* compressed GBRG bayer */ #define V4L2_PIX_FMT_SQ905C v4l2_fourcc('9', '0', '5', 'C') /* compressed RGGB bayer */ #define V4L2_PIX_FMT_PJPG v4l2_fourcc('P', 'J', 'P', 'G') /* Pixart 73xx JPEG */ #define V4L2_PIX_FMT_OV511 v4l2_fourcc('O', '5', '1', '1') /* ov511 JPEG */ #define V4L2_PIX_FMT_OV518 v4l2_fourcc('O', '5', '1', '8') /* ov518 JPEG */ -#define V4L2_PIX_FMT_TM6000 v4l2_fourcc('T', 'M', '6', '0') /* tm5600/tm60x0 */ #define V4L2_PIX_FMT_STV0680 v4l2_fourcc('S', '6', '8', '0') /* stv0680 bayer */ +#define V4L2_PIX_FMT_TM6000 v4l2_fourcc('T', 'M', '6', '0') /* tm5600/tm60x0 */ #define V4L2_PIX_FMT_CIT_YYVYUY v4l2_fourcc('C', 'I', 'T', 'V') /* one line of Y then 1 line of VYUY */ #define V4L2_PIX_FMT_KONICA420 v4l2_fourcc('K', 'O', 'N', 'I') /* YUV420 planar in blocks of 256 pixels */ @@ -382,7 +382,7 @@ struct v4l2_fmtdesc { #define V4L2_FMT_FLAG_COMPRESSED 0x0001 #define V4L2_FMT_FLAG_EMULATED 0x0002 -#if 1 /*KEEP*/ +#if 1 /* Experimental Frame Size and frame rate enumeration */ /* * F R A M E S I Z E E N U M E R A T I O N @@ -546,6 +546,8 @@ struct v4l2_buffer { #define V4L2_BUF_FLAG_KEYFRAME 0x0008 /* Image is a keyframe (I-frame) */ #define V4L2_BUF_FLAG_PFRAME 0x0010 /* Image is a P-frame */ #define V4L2_BUF_FLAG_BFRAME 0x0020 /* Image is a B-frame */ +/* Buffer is ready, but the data contained within is corrupted. */ +#define V4L2_BUF_FLAG_ERROR 0x0040 #define V4L2_BUF_FLAG_TIMECODE 0x0100 /* timecode field is valid */ #define V4L2_BUF_FLAG_INPUT 0x0200 /* input field is valid */ @@ -936,6 +938,16 @@ struct v4l2_ext_controls { #define V4L2_CTRL_ID2CLASS(id) ((id) & 0x0fff0000UL) #define V4L2_CTRL_DRIVER_PRIV(id) (((id) & 0xffff) >= 0x1000) +enum v4l2_ctrl_type { + V4L2_CTRL_TYPE_INTEGER = 1, + V4L2_CTRL_TYPE_BOOLEAN = 2, + V4L2_CTRL_TYPE_MENU = 3, + V4L2_CTRL_TYPE_BUTTON = 4, + V4L2_CTRL_TYPE_INTEGER64 = 5, + V4L2_CTRL_TYPE_CTRL_CLASS = 6, + V4L2_CTRL_TYPE_STRING = 7, +}; + /* Used in the VIDIOC_QUERYCTRL ioctl for querying controls */ struct v4l2_queryctrl { __u32 id; @@ -1020,21 +1032,27 @@ enum v4l2_colorfx { V4L2_COLORFX_NONE = 0, V4L2_COLORFX_BW = 1, V4L2_COLORFX_SEPIA = 2, - V4L2_COLORFX_NEGATIVE = 3, - V4L2_COLORFX_EMBOSS = 4, - V4L2_COLORFX_SKETCH = 5, - V4L2_COLORFX_SKY_BLUE = 6, + V4L2_COLORFX_NEGATIVE = 3, + V4L2_COLORFX_EMBOSS = 4, + V4L2_COLORFX_SKETCH = 5, + V4L2_COLORFX_SKY_BLUE = 6, V4L2_COLORFX_GRASS_GREEN = 7, V4L2_COLORFX_SKIN_WHITEN = 8, - V4L2_COLORFX_VIVID = 9. + V4L2_COLORFX_VIVID = 9, }; #define V4L2_CID_AUTOBRIGHTNESS (V4L2_CID_BASE+32) #define V4L2_CID_BAND_STOP_FILTER (V4L2_CID_BASE+33) #define V4L2_CID_ROTATE (V4L2_CID_BASE+34) #define V4L2_CID_BG_COLOR (V4L2_CID_BASE+35) + +#define V4L2_CID_CHROMA_GAIN (V4L2_CID_BASE+36) + +#define V4L2_CID_ILLUMINATORS_1 (V4L2_CID_BASE+37) +#define V4L2_CID_ILLUMINATORS_2 (V4L2_CID_BASE+38) + /* last CID + 1 */ -#define V4L2_CID_LASTP1 (V4L2_CID_BASE+36) +#define V4L2_CID_LASTP1 (V4L2_CID_BASE+39) /* MPEG-class control IDs defined by V4L2 */ #define V4L2_CID_MPEG_BASE (V4L2_CTRL_CLASS_MPEG | 0x900) @@ -1351,6 +1369,8 @@ struct v4l2_modulator { #define V4L2_TUNER_CAP_SAP 0x0020 #define V4L2_TUNER_CAP_LANG1 0x0040 #define V4L2_TUNER_CAP_RDS 0x0080 +#define V4L2_TUNER_CAP_RDS_BLOCK_IO 0x0100 +#define V4L2_TUNER_CAP_RDS_CONTROLS 0x0200 /* Flags for the 'rxsubchans' field */ #define V4L2_TUNER_SUB_MONO 0x0001 @@ -1380,7 +1400,8 @@ struct v4l2_hw_freq_seek { enum v4l2_tuner_type type; __u32 seek_upward; __u32 wrap_around; - __u32 reserved[8]; + __u32 spacing; + __u32 reserved[7]; }; /* @@ -1435,7 +1456,7 @@ struct v4l2_audioout { * * NOTE: EXPERIMENTAL API */ -#if 1 /*KEEP*/ +#if 1 #define V4L2_ENC_IDX_FRAME_I (0) #define V4L2_ENC_IDX_FRAME_P (1) #define V4L2_ENC_IDX_FRAME_B (2) @@ -1627,6 +1648,38 @@ struct v4l2_streamparm { } parm; }; +/* + * E V E N T S + */ + +#define V4L2_EVENT_ALL 0 +#define V4L2_EVENT_VSYNC 1 +#define V4L2_EVENT_EOS 2 +#define V4L2_EVENT_PRIVATE_START 0x08000000 + +/* Payload for V4L2_EVENT_VSYNC */ +struct v4l2_event_vsync { + /* Can be V4L2_FIELD_ANY, _NONE, _TOP or _BOTTOM */ + __u8 field; +} __attribute__ ((packed)); + +struct v4l2_event { + __u32 type; + union { + struct v4l2_event_vsync vsync; + __u8 data[64]; + } u; + __u32 pending; + __u32 sequence; + struct timespec timestamp; + __u32 reserved[9]; +}; + +struct v4l2_event_subscription { + __u32 type; + __u32 reserved[7]; +}; + /* * A D V A N C E D D E B U G G I N G * @@ -1722,7 +1775,7 @@ struct v4l2_dbg_chip_ident { #define VIDIOC_G_EXT_CTRLS _IOWR('V', 71, struct v4l2_ext_controls) #define VIDIOC_S_EXT_CTRLS _IOWR('V', 72, struct v4l2_ext_controls) #define VIDIOC_TRY_EXT_CTRLS _IOWR('V', 73, struct v4l2_ext_controls) -#if 1 /*KEEP*/ +#if 1 #define VIDIOC_ENUM_FRAMESIZES _IOWR('V', 74, struct v4l2_frmsizeenum) #define VIDIOC_ENUM_FRAMEINTERVALS _IOWR('V', 75, struct v4l2_frmivalenum) #define VIDIOC_G_ENC_INDEX _IOR('V', 76, struct v4l2_enc_idx) @@ -1730,7 +1783,7 @@ struct v4l2_dbg_chip_ident { #define VIDIOC_TRY_ENCODER_CMD _IOWR('V', 78, struct v4l2_encoder_cmd) #endif -#if 1 /*KEEP*/ +#if 1 /* Experimental, meant for debugging, testing and internal use. Only implemented if CONFIG_VIDEO_ADV_DEBUG is defined. You must be root to use these ioctls. Never use these in applications! */ @@ -1749,6 +1802,9 @@ struct v4l2_dbg_chip_ident { #define VIDIOC_QUERY_DV_PRESET _IOR('V', 86, struct v4l2_dv_preset) #define VIDIOC_S_DV_TIMINGS _IOWR('V', 87, struct v4l2_dv_timings) #define VIDIOC_G_DV_TIMINGS _IOWR('V', 88, struct v4l2_dv_timings) +#define VIDIOC_DQEVENT _IOR('V', 89, struct v4l2_event) +#define VIDIOC_SUBSCRIBE_EVENT _IOW('V', 90, struct v4l2_event_subscription) +#define VIDIOC_UNSUBSCRIBE_EVENT _IOW('V', 91, struct v4l2_event_subscription) /* Reminder: when adding new ioctls please add support for them to drivers/media/video/v4l2-compat-ioctl32.c as well! */ -- cgit v1.2.3