summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorJeff Garzik <jgarzik@pobox.com>2005-11-16 15:12:58 -0500
committerJeff Garzik <jgarzik@pobox.com>2005-11-16 15:12:58 -0500
commit3be4bb06b53df2c0a760eeaa5f53448faddedfca (patch)
tree67dc02f39e48a8a37d8f3e4a38422d86f5164820 /drivers
parent85d6162d6cea9220e483989817eac0cebc03070e (diff)
parent7bdd720869ff75700b48b132ee71852615b55808 (diff)
downloadkernel-crypto-3be4bb06b53df2c0a760eeaa5f53448faddedfca.tar.gz
kernel-crypto-3be4bb06b53df2c0a760eeaa5f53448faddedfca.tar.xz
kernel-crypto-3be4bb06b53df2c0a760eeaa5f53448faddedfca.zip
Merge branch 'upstream-fixes'
Diffstat (limited to 'drivers')
-rw-r--r--drivers/acpi/bus.c3
-rw-r--r--drivers/base/firmware_class.c15
-rw-r--r--drivers/block/cciss_scsi.c2
-rw-r--r--drivers/block/pktcdvd.c2
-rw-r--r--drivers/char/Kconfig2
-rw-r--r--drivers/char/agp/amd64-agp.c17
-rw-r--r--drivers/char/agp/uninorth-agp.c4
-rw-r--r--drivers/char/ipmi/ipmi_msghandler.c2
-rw-r--r--drivers/char/pcmcia/Kconfig24
-rw-r--r--drivers/char/pcmcia/Makefile2
-rw-r--r--drivers/char/pcmcia/cm4000_cs.c2078
-rw-r--r--drivers/char/pcmcia/cm4040_cs.c841
-rw-r--r--drivers/char/pcmcia/cm4040_cs.h47
-rw-r--r--drivers/char/synclink.c31
-rw-r--r--drivers/char/tpm/tpm.c14
-rw-r--r--drivers/char/tpm/tpm.h7
-rw-r--r--drivers/char/tpm/tpm_atmel.c108
-rw-r--r--drivers/char/tpm/tpm_atmel.h129
-rw-r--r--drivers/char/watchdog/booke_wdt.c2
-rw-r--r--drivers/ide/pci/sl82c105.c83
-rw-r--r--drivers/ide/ppc/pmac.c11
-rw-r--r--drivers/isdn/hisax/hfc_usb.c32
-rw-r--r--drivers/md/md.c19
-rw-r--r--drivers/media/common/ir-common.c60
-rw-r--r--drivers/media/video/Kconfig14
-rw-r--r--drivers/media/video/Makefile5
-rw-r--r--drivers/media/video/bttv-cards.c24
-rw-r--r--drivers/media/video/bttv-driver.c4
-rw-r--r--drivers/media/video/bttv-gpio.c18
-rw-r--r--drivers/media/video/bttv.h3
-rw-r--r--drivers/media/video/bttvp.h2
-rw-r--r--drivers/media/video/cx25840/Makefile6
-rw-r--r--drivers/media/video/cx25840/cx25840-audio.c368
-rw-r--r--drivers/media/video/cx25840/cx25840-core.c1020
-rw-r--r--drivers/media/video/cx25840/cx25840-firmware.c167
-rw-r--r--drivers/media/video/cx25840/cx25840-vbi.c315
-rw-r--r--drivers/media/video/cx25840/cx25840.h85
-rw-r--r--drivers/media/video/cx88/cx88-dvb.c3
-rw-r--r--drivers/media/video/em28xx/em28xx-input.c3
-rw-r--r--drivers/media/video/ir-kbd-gpio.c292
-rw-r--r--drivers/media/video/ir-kbd-i2c.c52
-rw-r--r--drivers/media/video/saa7115.c1376
-rw-r--r--drivers/media/video/saa711x.c1
-rw-r--r--drivers/media/video/saa7127.c849
-rw-r--r--drivers/media/video/saa7134/Kconfig3
-rw-r--r--drivers/media/video/saa7134/Makefile7
-rw-r--r--drivers/media/video/saa7134/saa7134-alsa.c448
-rw-r--r--drivers/media/video/saa7134/saa7134-cards.c32
-rw-r--r--drivers/media/video/saa7134/saa7134-core.c121
-rw-r--r--drivers/media/video/saa7134/saa7134-input.c109
-rw-r--r--drivers/media/video/saa7134/saa7134-oss.c161
-rw-r--r--drivers/media/video/saa7134/saa7134.h2
-rw-r--r--drivers/media/video/tda8290.c4
-rw-r--r--drivers/media/video/tuner-core.c11
-rw-r--r--drivers/media/video/tuner-simple.c4
-rw-r--r--drivers/media/video/wm8775.c7
-rw-r--r--drivers/mmc/mmci.c1
-rw-r--r--drivers/net/3c509.c13
-rw-r--r--drivers/net/gianfar.c2
-rw-r--r--drivers/net/gianfar.h2
-rw-r--r--drivers/net/gianfar_ethtool.c2
-rw-r--r--drivers/net/gianfar_mii.c2
-rw-r--r--drivers/net/gianfar_mii.h2
-rw-r--r--drivers/net/irda/ali-ircc.c1
-rw-r--r--drivers/net/irda/nsc-ircc.c1
-rw-r--r--drivers/net/smc91x.h16
-rw-r--r--drivers/net/sungem.c2
-rw-r--r--drivers/net/wan/sdladrv.c2
-rw-r--r--drivers/pci/hotplug/pciehp_hpc.c2
-rw-r--r--drivers/pci/hotplug/rpaphp_pci.c2
-rw-r--r--drivers/pci/hotplug/shpchp_hpc.c2
-rw-r--r--drivers/pcmcia/cs.c6
-rw-r--r--drivers/pcmcia/ds.c3
-rw-r--r--drivers/pcmcia/i82365.c20
-rw-r--r--drivers/scsi/ahci.c39
-rw-r--r--drivers/scsi/ata_piix.c2
-rw-r--r--drivers/scsi/libata-core.c70
-rw-r--r--drivers/scsi/libata-scsi.c77
-rw-r--r--drivers/scsi/libata.h4
-rw-r--r--drivers/scsi/sata_promise.c2
-rw-r--r--drivers/scsi/sata_qstor.c2
-rw-r--r--drivers/scsi/sata_sil24.c21
-rw-r--r--drivers/scsi/sata_svw.c2
-rw-r--r--drivers/scsi/sata_sx4.c2
-rw-r--r--drivers/scsi/sata_vsc.c2
-rw-r--r--drivers/serial/68328serial.c7
-rw-r--r--drivers/serial/8250.c5
-rw-r--r--drivers/serial/8250_pnp.c2
-rw-r--r--drivers/serial/cpm_uart/cpm_uart_core.c2
-rw-r--r--drivers/serial/cpm_uart/cpm_uart_cpm1.c2
-rw-r--r--drivers/serial/cpm_uart/cpm_uart_cpm2.c2
-rw-r--r--drivers/serial/dz.c48
-rw-r--r--drivers/serial/mpc52xx_uart.c4
-rw-r--r--drivers/serial/sa1100.c4
-rw-r--r--drivers/serial/serial_core.c84
-rw-r--r--drivers/video/console/fbcon.c15
-rw-r--r--drivers/video/console/fbcon.h3
-rw-r--r--drivers/video/console/fbcon_ccw.c14
-rw-r--r--drivers/video/console/fbcon_cw.c14
-rw-r--r--drivers/video/console/fbcon_ud.c22
-rw-r--r--drivers/video/nvidia/nv_proto.h2
-rw-r--r--drivers/video/nvidia/nvidia.c2
-rw-r--r--drivers/video/vesafb.c2
-rw-r--r--drivers/video/w100fb.c2
104 files changed, 8706 insertions, 885 deletions
diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c
index 6a4da417c16..606f8733a77 100644
--- a/drivers/acpi/bus.c
+++ b/drivers/acpi/bus.c
@@ -28,6 +28,7 @@
#include <linux/list.h>
#include <linux/sched.h>
#include <linux/pm.h>
+#include <linux/pm_legacy.h>
#include <linux/device.h>
#include <linux/proc_fs.h>
#ifdef CONFIG_X86
@@ -754,7 +755,7 @@ static int __init acpi_init(void)
result = acpi_bus_init();
if (!result) {
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_LEGACY
if (!PM_IS_ACTIVE())
pm_active = 1;
else {
diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c
index 98f6c02d679..59dacb6552c 100644
--- a/drivers/base/firmware_class.c
+++ b/drivers/base/firmware_class.c
@@ -526,18 +526,23 @@ request_firmware_work_func(void *arg)
{
struct firmware_work *fw_work = arg;
const struct firmware *fw;
+ int ret;
if (!arg) {
WARN_ON(1);
return 0;
}
daemonize("%s/%s", "firmware", fw_work->name);
- _request_firmware(&fw, fw_work->name, fw_work->device,
+ ret = _request_firmware(&fw, fw_work->name, fw_work->device,
fw_work->hotplug);
- fw_work->cont(fw, fw_work->context);
- release_firmware(fw);
+ if (ret < 0)
+ fw_work->cont(NULL, fw_work->context);
+ else {
+ fw_work->cont(fw, fw_work->context);
+ release_firmware(fw);
+ }
module_put(fw_work->module);
kfree(fw_work);
- return 0;
+ return ret;
}
/**
@@ -586,6 +591,8 @@ request_firmware_nowait(
if (ret < 0) {
fw_work->cont(NULL, fw_work->context);
+ module_put(fw_work->module);
+ kfree(fw_work);
return ret;
}
return 0;
diff --git a/drivers/block/cciss_scsi.c b/drivers/block/cciss_scsi.c
index 3226aa11c6e..2942d32280a 100644
--- a/drivers/block/cciss_scsi.c
+++ b/drivers/block/cciss_scsi.c
@@ -255,7 +255,7 @@ scsi_cmd_stack_free(int ctlr)
#define DEVICETYPE(n) (n<0 || n>MAX_SCSI_DEVICE_CODE) ? \
"Unknown" : scsi_device_types[n]
-#if 1
+#if 0
static int xmargin=8;
static int amargin=60;
diff --git a/drivers/block/pktcdvd.c b/drivers/block/pktcdvd.c
index 59e5982a5db..c0233efabeb 100644
--- a/drivers/block/pktcdvd.c
+++ b/drivers/block/pktcdvd.c
@@ -1188,7 +1188,7 @@ static void pkt_count_states(struct pktcdvd_device *pd, int *states)
struct packet_data *pkt;
int i;
- for (i = 0; i <= PACKET_NUM_STATES; i++)
+ for (i = 0; i < PACKET_NUM_STATES; i++)
states[i] = 0;
spin_lock(&pd->cdrw.active_list_lock);
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index fdf4370db99..970f70d498f 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -735,7 +735,7 @@ config SGI_IP27_RTC
config GEN_RTC
tristate "Generic /dev/rtc emulation"
- depends on RTC!=y && !IA64 && !ARM && !PPC64 && !M32R && !SPARC32 && !SPARC64
+ depends on RTC!=y && !IA64 && !ARM && !M32R && !SPARC32 && !SPARC64
---help---
If you say Y here and create a character special file /dev/rtc with
major number 10 and minor number 135 using mknod ("man mknod"), you
diff --git a/drivers/char/agp/amd64-agp.c b/drivers/char/agp/amd64-agp.c
index 78ce98a69f3..76589782adc 100644
--- a/drivers/char/agp/amd64-agp.c
+++ b/drivers/char/agp/amd64-agp.c
@@ -57,9 +57,8 @@ static int nr_garts;
static struct pci_dev * hammers[MAX_HAMMER_GARTS];
static struct resource *aperture_resource;
-static int __initdata agp_try_unsupported;
+static int __initdata agp_try_unsupported = 1;
-static int gart_iterator;
#define for_each_nb() for(gart_iterator=0;gart_iterator<nr_garts;gart_iterator++)
static void flush_amd64_tlb(struct pci_dev *dev)
@@ -73,6 +72,7 @@ static void flush_amd64_tlb(struct pci_dev *dev)
static void amd64_tlbflush(struct agp_memory *temp)
{
+ int gart_iterator;
for_each_nb()
flush_amd64_tlb(hammers[gart_iterator]);
}
@@ -222,6 +222,7 @@ static struct aper_size_info_32 amd_8151_sizes[7] =
static int amd_8151_configure(void)
{
unsigned long gatt_bus = virt_to_gart(agp_bridge->gatt_table_real);
+ int gart_iterator;
/* Configure AGP regs in each x86-64 host bridge. */
for_each_nb() {
@@ -235,7 +236,7 @@ static int amd_8151_configure(void)
static void amd64_cleanup(void)
{
u32 tmp;
-
+ int gart_iterator;
for_each_nb() {
/* disable gart translation */
pci_read_config_dword (hammers[gart_iterator], AMD64_GARTAPERTURECTL, &tmp);
@@ -697,6 +698,16 @@ static struct pci_device_id agp_amd64_pci_table[] = {
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
},
+ /* ALI/ULI M1695 */
+ {
+ .class = (PCI_CLASS_BRIDGE_HOST << 8),
+ .class_mask = ~0,
+ .vendor = PCI_VENDOR_ID_AL,
+ .device = 0x1689,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ },
+
{ }
};
diff --git a/drivers/char/agp/uninorth-agp.c b/drivers/char/agp/uninorth-agp.c
index c8255312b8c..50947e38501 100644
--- a/drivers/char/agp/uninorth-agp.c
+++ b/drivers/char/agp/uninorth-agp.c
@@ -557,6 +557,10 @@ static struct agp_device_ids uninorth_agp_device_ids[] __devinitdata = {
.device_id = PCI_DEVICE_ID_APPLE_U3H_AGP,
.chipset_name = "U3H",
},
+ {
+ .device_id = PCI_DEVICE_ID_APPLE_IPID2_AGP,
+ .chipset_name = "UniNorth/Intrepid2",
+ },
};
static int __devinit agp_uninorth_probe(struct pci_dev *pdev,
diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c
index d16bd4b5c11..6b302a930e5 100644
--- a/drivers/char/ipmi/ipmi_msghandler.c
+++ b/drivers/char/ipmi/ipmi_msghandler.c
@@ -48,7 +48,7 @@
#define PFX "IPMI message handler: "
-#define IPMI_DRIVER_VERSION "36.0"
+#define IPMI_DRIVER_VERSION "38.0"
static struct ipmi_recv_msg *ipmi_alloc_recv_msg(void);
static int ipmi_init_msghandler(void);
diff --git a/drivers/char/pcmcia/Kconfig b/drivers/char/pcmcia/Kconfig
index d22bfdc1356..27c1179ee52 100644
--- a/drivers/char/pcmcia/Kconfig
+++ b/drivers/char/pcmcia/Kconfig
@@ -18,5 +18,29 @@ config SYNCLINK_CS
The module will be called synclinkmp. If you want to do that, say M
here.
+config CARDMAN_4000
+ tristate "Omnikey Cardman 4000 support"
+ depends on PCMCIA
+ help
+ Enable support for the Omnikey Cardman 4000 PCMCIA Smartcard
+ reader.
+
+ This kernel driver requires additional userspace support, either
+ by the vendor-provided PC/SC ifd_handler (http://www.omnikey.com/),
+ or via the cm4000 backend of OpenCT (http://www.opensc.com/).
+
+config CARDMAN_4040
+ tristate "Omnikey CardMan 4040 support"
+ depends on PCMCIA
+ help
+ Enable support for the Omnikey CardMan 4040 PCMCIA Smartcard
+ reader.
+
+ This card is basically a USB CCID device connected to a FIFO
+ in I/O space. To use the kernel driver, you will need either the
+ PC/SC ifdhandler provided from the Omnikey homepage
+ (http://www.omnikey.com/), or a current development version of OpenCT
+ (http://www.opensc.org/).
+
endmenu
diff --git a/drivers/char/pcmcia/Makefile b/drivers/char/pcmcia/Makefile
index 1fcd4c59195..0aae20985d5 100644
--- a/drivers/char/pcmcia/Makefile
+++ b/drivers/char/pcmcia/Makefile
@@ -5,3 +5,5 @@
#
obj-$(CONFIG_SYNCLINK_CS) += synclink_cs.o
+obj-$(CONFIG_CARDMAN_4000) += cm4000_cs.o
+obj-$(CONFIG_CARDMAN_4040) += cm4040_cs.o
diff --git a/drivers/char/pcmcia/cm4000_cs.c b/drivers/char/pcmcia/cm4000_cs.c
new file mode 100644
index 00000000000..ef011ef5dc4
--- /dev/null
+++ b/drivers/char/pcmcia/cm4000_cs.c
@@ -0,0 +1,2078 @@
+ /*
+ * A driver for the PCMCIA Smartcard Reader "Omnikey CardMan Mobile 4000"
+ *
+ * cm4000_cs.c support.linux@omnikey.com
+ *
+ * Tue Oct 23 11:32:43 GMT 2001 herp - cleaned up header files
+ * Sun Jan 20 10:11:15 MET 2002 herp - added modversion header files
+ * Thu Nov 14 16:34:11 GMT 2002 mh - added PPS functionality
+ * Tue Nov 19 16:36:27 GMT 2002 mh - added SUSPEND/RESUME functionailty
+ * Wed Jul 28 12:55:01 CEST 2004 mh - kernel 2.6 adjustments
+ *
+ * current version: 2.4.0gm4
+ *
+ * (C) 2000,2001,2002,2003,2004 Omnikey AG
+ *
+ * (C) 2005 Harald Welte <laforge@gnumonks.org>
+ * - Adhere to Kernel CodingStyle
+ * - Port to 2.6.13 "new" style PCMCIA
+ * - Check for copy_{from,to}_user return values
+ * - Use nonseekable_open()
+ *
+ * All rights reserved. Licensed under dual BSD/GPL license.
+ */
+
+/* #define PCMCIA_DEBUG 6 */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/ciscode.h>
+#include <pcmcia/ds.h>
+
+#include <linux/cm4000_cs.h>
+
+/* #define ATR_CSUM */
+
+#ifdef PCMCIA_DEBUG
+#define reader_to_dev(x) (&handle_to_dev(x->link.handle))
+static int pc_debug = PCMCIA_DEBUG;
+module_param(pc_debug, int, 0600);
+#define DEBUGP(n, rdr, x, args...) do { \
+ if (pc_debug >= (n)) \
+ dev_printk(KERN_DEBUG, reader_to_dev(rdr), "%s:" x, \
+ __FUNCTION__ , ## args); \
+ } while (0)
+#else
+#define DEBUGP(n, rdr, x, args...)
+#endif
+static char *version = "cm4000_cs.c v2.4.0gm5 - All bugs added by Harald Welte";
+
+#define T_1SEC (HZ)
+#define T_10MSEC msecs_to_jiffies(10)
+#define T_20MSEC msecs_to_jiffies(20)
+#define T_40MSEC msecs_to_jiffies(40)
+#define T_50MSEC msecs_to_jiffies(50)
+#define T_100MSEC msecs_to_jiffies(100)
+#define T_500MSEC msecs_to_jiffies(500)
+
+static void cm4000_detach(dev_link_t *link);
+static void cm4000_release(dev_link_t *link);
+
+static int major; /* major number we get from the kernel */
+
+/* note: the first state has to have number 0 always */
+
+#define M_FETCH_ATR 0
+#define M_TIMEOUT_WAIT 1
+#define M_READ_ATR_LEN 2
+#define M_READ_ATR 3
+#define M_ATR_PRESENT 4
+#define M_BAD_CARD 5
+#define M_CARDOFF 6
+
+#define LOCK_IO 0
+#define LOCK_MONITOR 1
+
+#define IS_AUTOPPS_ACT 6
+#define IS_PROCBYTE_PRESENT 7
+#define IS_INVREV 8
+#define IS_ANY_T0 9
+#define IS_ANY_T1 10
+#define IS_ATR_PRESENT 11
+#define IS_ATR_VALID 12
+#define IS_CMM_ABSENT 13
+#define IS_BAD_LENGTH 14
+#define IS_BAD_CSUM 15
+#define IS_BAD_CARD 16
+
+#define REG_FLAGS0(x) (x + 0)
+#define REG_FLAGS1(x) (x + 1)
+#define REG_NUM_BYTES(x) (x + 2)
+#define REG_BUF_ADDR(x) (x + 3)
+#define REG_BUF_DATA(x) (x + 4)
+#define REG_NUM_SEND(x) (x + 5)
+#define REG_BAUDRATE(x) (x + 6)
+#define REG_STOPBITS(x) (x + 7)
+
+struct cm4000_dev {
+ dev_link_t link; /* pcmcia link */
+ dev_node_t node; /* OS node (major,minor) */
+
+ unsigned char atr[MAX_ATR];
+ unsigned char rbuf[512];
+ unsigned char sbuf[512];
+
+ wait_queue_head_t devq; /* when removing cardman must not be
+ zeroed! */
+
+ wait_queue_head_t ioq; /* if IO is locked, wait on this Q */
+ wait_queue_head_t atrq; /* wait for ATR valid */
+ wait_queue_head_t readq; /* used by write to wake blk.read */
+
+ /* warning: do not move this fields.
+ * initialising to zero depends on it - see ZERO_DEV below. */
+ unsigned char atr_csum;
+ unsigned char atr_len_retry;
+ unsigned short atr_len;
+ unsigned short rlen; /* bytes avail. after write */
+ unsigned short rpos; /* latest read pos. write zeroes */
+ unsigned char procbyte; /* T=0 procedure byte */
+ unsigned char mstate; /* state of card monitor */
+ unsigned char cwarn; /* slow down warning */
+ unsigned char flags0; /* cardman IO-flags 0 */
+ unsigned char flags1; /* cardman IO-flags 1 */
+ unsigned int mdelay; /* variable monitor speeds, in jiffies */
+
+ unsigned int baudv; /* baud value for speed */
+ unsigned char ta1;
+ unsigned char proto; /* T=0, T=1, ... */
+ unsigned long flags; /* lock+flags (MONITOR,IO,ATR) * for concurrent
+ access */
+
+ unsigned char pts[4];
+
+ struct timer_list timer; /* used to keep monitor running */
+ int monitor_running;
+};
+
+#define ZERO_DEV(dev) \
+ memset(&dev->atr_csum,0, \
+ sizeof(struct cm4000_dev) - \
+ /*link*/ sizeof(dev_link_t) - \
+ /*node*/ sizeof(dev_node_t) - \
+ /*atr*/ MAX_ATR*sizeof(char) - \
+ /*rbuf*/ 512*sizeof(char) - \
+ /*sbuf*/ 512*sizeof(char) - \
+ /*queue*/ 4*sizeof(wait_queue_head_t))
+
+static dev_info_t dev_info = MODULE_NAME;
+static dev_link_t *dev_table[CM4000_MAX_DEV];
+
+/* This table doesn't use spaces after the comma between fields and thus
+ * violates CodingStyle. However, I don't really think wrapping it around will
+ * make it any clearer to read -HW */
+static unsigned char fi_di_table[10][14] = {
+/*FI 00 01 02 03 04 05 06 07 08 09 10 11 12 13 */
+/*DI */
+/* 0 */ {0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11},
+/* 1 */ {0x01,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x91,0x11,0x11,0x11,0x11},
+/* 2 */ {0x02,0x12,0x22,0x32,0x11,0x11,0x11,0x11,0x11,0x92,0xA2,0xB2,0x11,0x11},
+/* 3 */ {0x03,0x13,0x23,0x33,0x43,0x53,0x63,0x11,0x11,0x93,0xA3,0xB3,0xC3,0xD3},
+/* 4 */ {0x04,0x14,0x24,0x34,0x44,0x54,0x64,0x11,0x11,0x94,0xA4,0xB4,0xC4,0xD4},
+/* 5 */ {0x00,0x15,0x25,0x35,0x45,0x55,0x65,0x11,0x11,0x95,0xA5,0xB5,0xC5,0xD5},
+/* 6 */ {0x06,0x16,0x26,0x36,0x46,0x56,0x66,0x11,0x11,0x96,0xA6,0xB6,0xC6,0xD6},
+/* 7 */ {0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11},
+/* 8 */ {0x08,0x11,0x28,0x38,0x48,0x58,0x68,0x11,0x11,0x98,0xA8,0xB8,0xC8,0xD8},
+/* 9 */ {0x09,0x19,0x29,0x39,0x49,0x59,0x69,0x11,0x11,0x99,0xA9,0xB9,0xC9,0xD9}
+};
+
+#ifndef PCMCIA_DEBUG
+#define xoutb outb
+#define xinb inb
+#else
+static inline void xoutb(unsigned char val, unsigned short port)
+{
+ if (pc_debug >= 7)
+ printk(KERN_DEBUG "outb(val=%.2x,port=%.4x)\n", val, port);
+ outb(val, port);
+}
+static inline unsigned char xinb(unsigned short port)
+{
+ unsigned char val;
+
+ val = inb(port);
+ if (pc_debug >= 7)
+ printk(KERN_DEBUG "%.2x=inb(%.4x)\n", val, port);
+
+ return val;
+}
+#endif
+
+#define b_0000 15
+#define b_0001 14
+#define b_0010 13
+#define b_0011 12
+#define b_0100 11
+#define b_0101 10
+#define b_0110 9
+#define b_0111 8
+#define b_1000 7
+#define b_1001 6
+#define b_1010 5
+#define b_1011 4
+#define b_1100 3
+#define b_1101 2
+#define b_1110 1
+#define b_1111 0
+
+static unsigned char irtab[16] = {
+ b_0000, b_1000, b_0100, b_1100,
+ b_0010, b_1010, b_0110, b_1110,
+ b_0001, b_1001, b_0101, b_1101,
+ b_0011, b_1011, b_0111, b_1111
+};
+
+static void str_invert_revert(unsigned char *b, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ b[i] = (irtab[b[i] & 0x0f] << 4) | irtab[b[i] >> 4];
+}
+
+static unsigned char invert_revert(unsigned char ch)
+{
+ return (irtab[ch & 0x0f] << 4) | irtab[ch >> 4];
+}
+
+#define ATRLENCK(dev,pos) \
+ if (pos>=dev->atr_len || pos>=MAX_ATR) \
+ goto return_0;
+
+static unsigned int calc_baudv(unsigned char fidi)
+{
+ unsigned int wcrcf, wbrcf, fi_rfu, di_rfu;
+
+ fi_rfu = 372;
+ di_rfu = 1;
+
+ /* FI */
+ switch ((fidi >> 4) & 0x0F) {
+ case 0x00:
+ wcrcf = 372;
+ break;
+ case 0x01:
+ wcrcf = 372;
+ break;
+ case 0x02:
+ wcrcf = 558;
+ break;
+ case 0x03:
+ wcrcf = 744;
+ break;
+ case 0x04:
+ wcrcf = 1116;
+ break;
+ case 0x05:
+ wcrcf = 1488;
+ break;
+ case 0x06:
+ wcrcf = 1860;
+ break;
+ case 0x07:
+ wcrcf = fi_rfu;
+ break;
+ case 0x08:
+ wcrcf = fi_rfu;
+ break;
+ case 0x09:
+ wcrcf = 512;
+ break;
+ case 0x0A:
+ wcrcf = 768;
+ break;
+ case 0x0B:
+ wcrcf = 1024;
+ break;
+ case 0x0C:
+ wcrcf = 1536;
+ break;
+ case 0x0D:
+ wcrcf = 2048;
+ break;
+ default:
+ wcrcf = fi_rfu;
+ break;
+ }
+
+ /* DI */
+ switch (fidi & 0x0F) {
+ case 0x00:
+ wbrcf = di_rfu;
+ break;
+ case 0x01:
+ wbrcf = 1;
+ break;
+ case 0x02:
+ wbrcf = 2;
+ break;
+ case 0x03:
+ wbrcf = 4;
+ break;
+ case 0x04:
+ wbrcf = 8;
+ break;
+ case 0x05:
+ wbrcf = 16;
+ break;
+ case 0x06:
+ wbrcf = 32;
+ break;
+ case 0x07:
+ wbrcf = di_rfu;
+ break;
+ case 0x08:
+ wbrcf = 12;
+ break;
+ case 0x09:
+ wbrcf = 20;
+ break;
+ default:
+ wbrcf = di_rfu;
+ break;
+ }
+
+ return (wcrcf / wbrcf);
+}
+
+static unsigned short io_read_num_rec_bytes(ioaddr_t iobase, unsigned short *s)
+{
+ unsigned short tmp;
+
+ tmp = *s = 0;
+ do {
+ *s = tmp;
+ tmp = inb(REG_NUM_BYTES(iobase)) |
+ (inb(REG_FLAGS0(iobase)) & 4 ? 0x100 : 0);
+ } while (tmp != *s);
+
+ return *s;
+}
+
+static int parse_atr(struct cm4000_dev *dev)
+{
+ unsigned char any_t1, any_t0;
+ unsigned char ch, ifno;
+ int ix, done;
+
+ DEBUGP(3, dev, "-> parse_atr: dev->atr_len = %i\n", dev->atr_len);
+
+ if (dev->atr_len < 3) {
+ DEBUGP(5, dev, "parse_atr: atr_len < 3\n");
+ return 0;
+ }
+
+ if (dev->atr[0] == 0x3f)
+ set_bit(IS_INVREV, &dev->flags);
+ else
+ clear_bit(IS_INVREV, &dev->flags);
+ ix = 1;
+ ifno = 1;
+ ch = dev->atr[1];
+ dev->proto = 0; /* XXX PROTO */
+ any_t1 = any_t0 = done = 0;
+ dev->ta1 = 0x11; /* defaults to 9600 baud */
+ do {
+ if (ifno == 1 && (ch & 0x10)) {
+ /* read first interface byte and TA1 is present */
+ dev->ta1 = dev->atr[2];
+ DEBUGP(5, dev, "Card says FiDi is 0x%.2x\n", dev->ta1);
+ ifno++;
+ } else if ((ifno == 2) && (ch & 0x10)) { /* TA(2) */
+ dev->ta1 = 0x11;
+ ifno++;
+ }
+
+ DEBUGP(5, dev, "Yi=%.2x\n", ch & 0xf0);
+ ix += ((ch & 0x10) >> 4) /* no of int.face chars */
+ +((ch & 0x20) >> 5)
+ + ((ch & 0x40) >> 6)
+ + ((ch & 0x80) >> 7);
+ /* ATRLENCK(dev,ix); */
+ if (ch & 0x80) { /* TDi */
+ ch = dev->atr[ix];
+ if ((ch & 0x0f)) {
+ any_t1 = 1;
+ DEBUGP(5, dev, "card is capable of T=1\n");
+ } else {
+ any_t0 = 1;
+ DEBUGP(5, dev, "card is capable of T=0\n");
+ }
+ } else
+ done = 1;
+ } while (!done);
+
+ DEBUGP(5, dev, "ix=%d noHist=%d any_t1=%d\n",
+ ix, dev->atr[1] & 15, any_t1);
+ if (ix + 1 + (dev->atr[1] & 0x0f) + any_t1 != dev->atr_len) {
+ DEBUGP(5, dev, "length error\n");
+ return 0;
+ }
+ if (any_t0)
+ set_bit(IS_ANY_T0, &dev->flags);
+
+ if (any_t1) { /* compute csum */
+ dev->atr_csum = 0;
+#ifdef ATR_CSUM
+ for (i = 1; i < dev->atr_len; i++)
+ dev->atr_csum ^= dev->atr[i];
+ if (dev->atr_csum) {
+ set_bit(IS_BAD_CSUM, &dev->flags);
+ DEBUGP(5, dev, "bad checksum\n");
+ goto return_0;
+ }
+#endif
+ if (any_t0 == 0)
+ dev->proto = 1; /* XXX PROTO */
+ set_bit(IS_ANY_T1, &dev->flags);
+ }
+
+ return 1;
+}
+
+struct card_fixup {
+ char atr[12];
+ u_int8_t atr_len;
+ u_int8_t stopbits;
+};
+
+static struct card_fixup card_fixups[] = {
+ { /* ACOS */
+ .atr = { 0x3b, 0xb3, 0x11, 0x00, 0x00, 0x41, 0x01 },
+ .atr_len = 7,
+ .stopbits = 0x03,
+ },
+ { /* Motorola */
+ .atr = {0x3b, 0x76, 0x13, 0x00, 0x00, 0x80, 0x62, 0x07,
+ 0x41, 0x81, 0x81 },
+ .atr_len = 11,
+ .stopbits = 0x04,
+ },
+};
+
+static void set_cardparameter(struct cm4000_dev *dev)
+{
+ int i;
+ ioaddr_t iobase = dev->link.io.BasePort1;
+ u_int8_t stopbits = 0x02; /* ISO default */
+
+ DEBUGP(3, dev, "-> set_cardparameter\n");
+
+ dev->flags1 = dev->flags1 | (((dev->baudv - 1) & 0x0100) >> 8);
+ xoutb(dev->flags1, REG_FLAGS1(iobase));
+ DEBUGP(5, dev, "flags1 = 0x%02x\n", dev->flags1);
+
+ /* set baudrate */
+ xoutb((unsigned char)((dev->baudv - 1) & 0xFF), REG_BAUDRATE(iobase));
+
+ DEBUGP(5, dev, "baudv = %i -> write 0x%02x\n", dev->baudv,
+ ((dev->baudv - 1) & 0xFF));
+
+ /* set stopbits */
+ for (i = 0; i < ARRAY_SIZE(card_fixups); i++) {
+ if (!memcmp(dev->atr, card_fixups[i].atr,
+ card_fixups[i].atr_len))
+ stopbits = card_fixups[i].stopbits;
+ }
+ xoutb(stopbits, REG_STOPBITS(iobase));
+
+ DEBUGP(3, dev, "<- set_cardparameter\n");
+}
+
+static int set_protocol(struct cm4000_dev *dev, struct ptsreq *ptsreq)
+{
+
+ unsigned long tmp, i;
+ unsigned short num_bytes_read;
+ unsigned char pts_reply[4];
+ ssize_t rc;
+ ioaddr_t iobase = dev->link.io.BasePort1;
+
+ rc = 0;
+
+ DEBUGP(3, dev, "-> set_protocol\n");
+ DEBUGP(5, dev, "ptsreq->Protocol = 0x%.8x, ptsreq->Flags=0x%.8x, "
+ "ptsreq->pts1=0x%.2x, ptsreq->pts2=0x%.2x, "
+ "ptsreq->pts3=0x%.2x\n", (unsigned int)ptsreq->protocol,
+ (unsigned int)ptsreq->flags, ptsreq->pts1, ptsreq->pts2,
+ ptsreq->pts3);
+
+ /* Fill PTS structure */
+ dev->pts[0] = 0xff;
+ dev->pts[1] = 0x00;
+ tmp = ptsreq->protocol;
+ while ((tmp = (tmp >> 1)) > 0)
+ dev->pts[1]++;
+ dev->proto = dev->pts[1]; /* Set new protocol */
+ dev->pts[1] = (0x01 << 4) | (dev->pts[1]);
+
+ /* Correct Fi/Di according to CM4000 Fi/Di table */
+ DEBUGP(5, dev, "Ta(1) from ATR is 0x%.2x\n", dev->ta1);
+ /* set Fi/Di according to ATR TA(1) */
+ dev->pts[2] = fi_di_table[dev->ta1 & 0x0F][(dev->ta1 >> 4) & 0x0F];
+
+ /* Calculate PCK character */
+ dev->pts[3] = dev->pts[0] ^ dev->pts[1] ^ dev->pts[2];
+
+ DEBUGP(5, dev, "pts0=%.2x, pts1=%.2x, pts2=%.2x, pts3=%.2x\n",
+ dev->pts[0], dev->pts[1], dev->pts[2], dev->pts[3]);
+
+ /* check card convention */
+ if (test_bit(IS_INVREV, &dev->flags))
+ str_invert_revert(dev->pts, 4);
+
+ /* reset SM */
+ xoutb(0x80, REG_FLAGS0(iobase));
+
+ /* Enable access to the message buffer */
+ DEBUGP(5, dev, "Enable access to the messages buffer\n");
+ dev->flags1 = 0x20 /* T_Active */
+ | (test_bit(IS_INVREV, &dev->flags) ? 0x02 : 0x00) /* inv parity */
+ | ((dev->baudv >> 8) & 0x01); /* MSB-baud */
+ xoutb(dev->flags1, REG_FLAGS1(iobase));
+
+ DEBUGP(5, dev, "Enable message buffer -> flags1 = 0x%.2x\n",
+ dev->flags1);
+
+ /* write challenge to the buffer */
+ DEBUGP(5, dev, "Write challenge to buffer: ");
+ for (i = 0; i < 4; i++) {
+ xoutb(i, REG_BUF_ADDR(iobase));
+ xoutb(dev->pts[i], REG_BUF_DATA(iobase)); /* buf data */
+#ifdef PCMCIA_DEBUG
+ if (pc_debug >= 5)
+ printk("0x%.2x ", dev->pts[i]);
+ }
+ if (pc_debug >= 5)
+ printk("\n");
+#else
+ }
+#endif
+
+ /* set number of bytes to write */
+ DEBUGP(5, dev, "Set number of bytes to write\n");
+ xoutb(0x04, REG_NUM_SEND(iobase));
+
+ /* Trigger CARDMAN CONTROLLER */
+ xoutb(0x50, REG_FLAGS0(iobase));
+
+ /* Monitor progress */
+ /* wait for xmit done */
+ DEBUGP(5, dev, "Waiting for NumRecBytes getting valid\n");
+
+ for (i = 0; i < 100; i++) {
+ if (inb(REG_FLAGS0(iobase)) & 0x08) {
+ DEBUGP(5, dev, "NumRecBytes is valid\n");
+ break;
+ }
+ mdelay(10);
+ }
+ if (i == 100) {
+ DEBUGP(5, dev, "Timeout waiting for NumRecBytes getting "
+ "valid\n");
+ rc = -EIO;
+ goto exit_setprotocol;
+ }
+
+ DEBUGP(5, dev, "Reading NumRecBytes\n");
+ for (i = 0; i < 100; i++) {
+ io_read_num_rec_bytes(iobase, &num_bytes_read);
+ if (num_bytes_read >= 4) {
+ DEBUGP(2, dev, "NumRecBytes = %i\n", num_bytes_read);
+ break;
+ }
+ mdelay(10);
+ }
+
+ /* check whether it is a short PTS reply? */
+ if (num_bytes_read == 3)
+ i = 0;
+
+ if (i == 100) {
+ DEBUGP(5, dev, "Timeout reading num_bytes_read\n");
+ rc = -EIO;
+ goto exit_setprotocol;
+ }
+
+ DEBUGP(5, dev, "Reset the CARDMAN CONTROLLER\n");
+ xoutb(0x80, REG_FLAGS0(iobase));
+
+ /* Read PPS reply */
+ DEBUGP(5, dev, "Read PPS reply\n");
+ for (i = 0; i < num_bytes_read; i++) {
+ xoutb(i, REG_BUF_ADDR(iobase));
+ pts_reply[i] = inb(REG_BUF_DATA(iobase));
+ }
+
+#ifdef PCMCIA_DEBUG
+ DEBUGP(2, dev, "PTSreply: ");
+ for (i = 0; i < num_bytes_read; i++) {
+ if (pc_debug >= 5)
+ printk("0x%.2x ", pts_reply[i]);
+ }
+ printk("\n");
+#endif /* PCMCIA_DEBUG */
+
+ DEBUGP(5, dev, "Clear Tactive in Flags1\n");
+ xoutb(0x20, REG_FLAGS1(iobase));
+
+ /* Compare ptsreq and ptsreply */
+ if ((dev->pts[0] == pts_reply[0]) &&
+ (dev->pts[1] == pts_reply[1]) &&
+ (dev->pts[2] == pts_reply[2]) && (dev->pts[3] == pts_reply[3])) {
+ /* setcardparameter according to PPS */
+ dev->baudv = calc_baudv(dev->pts[2]);
+ set_cardparameter(dev);
+ } else if ((dev->pts[0] == pts_reply[0]) &&
+ ((dev->pts[1] & 0xef) == pts_reply[1]) &&
+ ((pts_reply[0] ^ pts_reply[1]) == pts_reply[2])) {
+ /* short PTS reply, set card parameter to default values */
+ dev->baudv = calc_baudv(0x11);
+ set_cardparameter(dev);
+ } else
+ rc = -EIO;
+
+exit_setprotocol:
+ DEBUGP(3, dev, "<- set_protocol\n");
+ return rc;
+}
+
+static int io_detect_cm4000(ioaddr_t iobase, struct cm4000_dev *dev)
+{
+
+ /* note: statemachine is assumed to be reset */
+ if (inb(REG_FLAGS0(iobase)) & 8) {
+ clear_bit(IS_ATR_VALID, &dev->flags);
+ set_bit(IS_CMM_ABSENT, &dev->flags);
+ return 0; /* detect CMM = 1 -> failure */
+ }
+ /* xoutb(0x40, REG_FLAGS1(iobase)); detectCMM */
+ xoutb(dev->flags1 | 0x40, REG_FLAGS1(iobase));
+ if ((inb(REG_FLAGS0(iobase)) & 8) == 0) {
+ clear_bit(IS_ATR_VALID, &dev->flags);
+ set_bit(IS_CMM_ABSENT, &dev->flags);
+ return 0; /* detect CMM=0 -> failure */
+ }
+ /* clear detectCMM again by restoring original flags1 */
+ xoutb(dev->flags1, REG_FLAGS1(iobase));
+ return 1;
+}
+
+static void terminate_monitor(struct cm4000_dev *dev)
+{
+
+ /* tell the monitor to stop and wait until
+ * it terminates.
+ */
+ DEBUGP(3, dev, "-> terminate_monitor\n");
+ wait_event_interruptible(dev->devq,
+ test_and_set_bit(LOCK_MONITOR,
+ (void *)&dev->flags));
+
+ /* now, LOCK_MONITOR has been set.
+ * allow a last cycle in the monitor.
+ * the monitor will indicate that it has
+ * finished by clearing this bit.
+ */
+ DEBUGP(5, dev, "Now allow last cycle of monitor!\n");
+ while (test_bit(LOCK_MONITOR, (void *)&dev->flags))
+ msleep(25);
+
+ DEBUGP(5, dev, "Delete timer\n");
+ del_timer_sync(&dev->timer);
+#ifdef PCMCIA_DEBUG
+ dev->monitor_running = 0;
+#endif
+
+ DEBUGP(3, dev, "<- terminate_monitor\n");
+}
+
+/*
+ * monitor the card every 50msec. as a side-effect, retrieve the
+ * atr once a card is inserted. another side-effect of retrieving the
+ * atr is that the card will be powered on, so there is no need to
+ * power on the card explictely from the application: the driver
+ * is already doing that for you.
+ */
+
+static void monitor_card(unsigned long p)
+{
+ struct cm4000_dev *dev = (struct cm4000_dev *) p;
+ ioaddr_t iobase = dev->link.io.BasePort1;
+ unsigned short s;
+ struct ptsreq ptsreq;
+ int i, atrc;
+
+ DEBUGP(7, dev, "-> monitor_card\n");
+
+ /* if someone has set the lock for us: we're done! */
+ if (test_and_set_bit(LOCK_MONITOR, &dev->flags)) {
+ DEBUGP(4, dev, "About to stop monitor\n");
+ /* no */
+ dev->rlen =
+ dev->rpos =
+ dev->atr_csum = dev->atr_len_retry = dev->cwarn = 0;
+ dev->mstate = M_FETCH_ATR;
+ clear_bit(LOCK_MONITOR, &dev->flags);
+ /* close et al. are sleeping on devq, so wake it */
+ wake_up_interruptible(&dev->devq);
+ DEBUGP(2, dev, "<- monitor_card (we are done now)\n");
+ return;
+ }
+
+ /* try to lock io: if it is already locked, just add another timer */
+ if (test_and_set_bit(LOCK_IO, (void *)&dev->flags)) {
+ DEBUGP(4, dev, "Couldn't get IO lock\n");
+ goto return_with_timer;
+ }
+
+ /* is a card/a reader inserted at all ? */
+ dev->flags0 = xinb(REG_FLAGS0(iobase));
+ DEBUGP(7, dev, "dev->flags0 = 0x%2x\n", dev->flags0);
+ DEBUGP(7, dev, "smartcard present: %s\n",
+ dev->flags0 & 1 ? "yes" : "no");
+ DEBUGP(7, dev, "cardman present: %s\n",
+ dev->flags0 == 0xff ? "no" : "yes");
+
+ if ((dev->flags0 & 1) == 0 /* no smartcard inserted */
+ || dev->flags0 == 0xff) { /* no cardman inserted */
+ /* no */
+ dev->rlen =
+ dev->rpos =
+ dev->atr_csum = dev->atr_len_retry = dev->cwarn = 0;
+ dev->mstate = M_FETCH_ATR;
+
+ dev->flags &= 0x000000ff; /* only keep IO and MONITOR locks */
+
+ if (dev->flags0 == 0xff) {
+ DEBUGP(4, dev, "set IS_CMM_ABSENT bit\n");
+ set_bit(IS_CMM_ABSENT, &dev->flags);
+ } else if (test_bit(IS_CMM_ABSENT, &dev->flags)) {
+ DEBUGP(4, dev, "clear IS_CMM_ABSENT bit "
+ "(card is removed)\n");
+ clear_bit(IS_CMM_ABSENT, &dev->flags);
+ }
+
+ goto release_io;
+ } else if ((dev->flags0 & 1) && test_bit(IS_CMM_ABSENT, &dev->flags)) {
+ /* cardman and card present but cardman was absent before
+ * (after suspend with inserted card) */
+ DEBUGP(4, dev, "clear IS_CMM_ABSENT bit (card is inserted)\n");
+ clear_bit(IS_CMM_ABSENT, &dev->flags);
+ }
+
+ if (test_bit(IS_ATR_VALID, &dev->flags) == 1) {
+ DEBUGP(7, dev, "believe ATR is already valid (do nothing)\n");
+ goto release_io;
+ }
+
+ switch (dev->mstate) {
+ unsigned char flags0;
+ case M_CARDOFF:
+ DEBUGP(4, dev, "M_CARDOFF\n");
+ flags0 = inb(REG_FLAGS0(iobase));
+ if (flags0 & 0x02) {
+ /* wait until Flags0 indicate power is off */
+ dev->mdelay = T_10MSEC;
+ } else {
+ /* Flags0 indicate power off and no card inserted now;
+ * Reset CARDMAN CONTROLLER */
+ xoutb(0x80, REG_FLAGS0(iobase));
+
+ /* prepare for fetching ATR again: after card off ATR
+ * is read again automatically */
+ dev->rlen =
+ dev->rpos =
+ dev->atr_csum =
+ dev->atr_len_retry = dev->cwarn = 0;
+ dev->mstate = M_FETCH_ATR;
+
+ /* minimal gap between CARDOFF and read ATR is 50msec */
+ dev->mdelay = T_50MSEC;
+ }
+ break;
+ case M_FETCH_ATR:
+ DEBUGP(4, dev, "M_FETCH_ATR\n");
+ xoutb(0x80, REG_FLAGS0(iobase));
+ DEBUGP(4, dev, "Reset BAUDV to 9600\n");
+ dev->baudv = 0x173; /* 9600 */
+ xoutb(0x02, REG_STOPBITS(iobase)); /* stopbits=2 */
+ xoutb(0x73, REG_BAUDRATE(iobase)); /* baud value */
+ xoutb(0x21, REG_FLAGS1(iobase)); /* T_Active=1, baud
+ value */
+ /* warm start vs. power on: */
+ xoutb(dev->flags0 & 2 ? 0x46 : 0x44, REG_FLAGS0(iobase));
+ dev->mdelay = T_40MSEC;
+ dev->mstate = M_TIMEOUT_WAIT;
+ break;
+ case M_TIMEOUT_WAIT:
+ DEBUGP(4, dev, "M_TIMEOUT_WAIT\n");
+ /* numRecBytes */
+ io_read_num_rec_bytes(iobase, &dev->atr_len);
+ dev->mdelay = T_10MSEC;
+ dev->mstate = M_READ_ATR_LEN;
+ break;
+ case M_READ_ATR_LEN:
+ DEBUGP(4, dev, "M_READ_ATR_LEN\n");
+ /* infinite loop possible, since there is no timeout */
+
+#define MAX_ATR_LEN_RETRY 100
+
+ if (dev->atr_len == io_read_num_rec_bytes(iobase, &s)) {
+ if (dev->atr_len_retry++ >= MAX_ATR_LEN_RETRY) { /* + XX msec */
+ dev->mdelay = T_10MSEC;
+ dev->mstate = M_READ_ATR;
+ }
+ } else {
+ dev->atr_len = s;
+ dev->atr_len_retry = 0; /* set new timeout */
+ }
+
+ DEBUGP(4, dev, "Current ATR_LEN = %i\n", dev->atr_len);
+ break;
+ case M_READ_ATR:
+ DEBUGP(4, dev, "M_READ_ATR\n");
+ xoutb(0x80, REG_FLAGS0(iobase)); /* reset SM */
+ for (i = 0; i < dev->atr_len; i++) {
+ xoutb(i, REG_BUF_ADDR(iobase));
+ dev->atr[i] = inb(REG_BUF_DATA(iobase));
+ }
+ /* Deactivate T_Active flags */
+ DEBUGP(4, dev, "Deactivate T_Active flags\n");
+ dev->flags1 = 0x01;
+ xoutb(dev->flags1, REG_FLAGS1(iobase));
+
+ /* atr is present (which doesnt mean it's valid) */
+ set_bit(IS_ATR_PRESENT, &dev->flags);
+ if (dev->atr[0] == 0x03)
+ str_invert_revert(dev->atr, dev->atr_len);
+ atrc = parse_atr(dev);
+ if (atrc == 0) { /* atr invalid */
+ dev->mdelay = 0;
+ dev->mstate = M_BAD_CARD;
+ } else {
+ dev->mdelay = T_50MSEC;
+ dev->mstate = M_ATR_PRESENT;
+ set_bit(IS_ATR_VALID, &dev->flags);
+ }
+
+ if (test_bit(IS_ATR_VALID, &dev->flags) == 1) {
+ DEBUGP(4, dev, "monitor_card: ATR valid\n");
+ /* if ta1 == 0x11, no PPS necessary (default values) */
+ /* do not do PPS with multi protocol cards */
+ if ((test_bit(IS_AUTOPPS_ACT, &dev->flags) == 0) &&
+ (dev->ta1 != 0x11) &&
+ !(test_bit(IS_ANY_T0, &dev->flags) &&
+ test_bit(IS_ANY_T1, &dev->flags))) {
+ DEBUGP(4, dev, "Perform AUTOPPS\n");
+ set_bit(IS_AUTOPPS_ACT, &dev->flags);
+ ptsreq.protocol = ptsreq.protocol =
+ (0x01 << dev->proto);
+ ptsreq.flags = 0x01;
+ ptsreq.pts1 = 0x00;
+ ptsreq.pts2 = 0x00;
+ ptsreq.pts3 = 0x00;
+ if (set_protocol(dev, &ptsreq) == 0) {
+ DEBUGP(4, dev, "AUTOPPS ret SUCC\n");
+ clear_bit(IS_AUTOPPS_ACT, &dev->flags);
+ wake_up_interruptible(&dev->atrq);
+ } else {
+ DEBUGP(4, dev, "AUTOPPS failed: "
+ "repower using defaults\n");
+ /* prepare for repowering */
+ clear_bit(IS_ATR_PRESENT, &dev->flags);
+ clear_bit(IS_ATR_VALID, &dev->flags);
+ dev->rlen =
+ dev->rpos =
+ dev->atr_csum =
+ dev->atr_len_retry = dev->cwarn = 0;
+ dev->mstate = M_FETCH_ATR;
+
+ dev->mdelay = T_50MSEC;
+ }
+ } else {
+ /* for cards which use slightly different
+ * params (extra guard time) */
+ set_cardparameter(dev);
+ if (test_bit(IS_AUTOPPS_ACT, &dev->flags) == 1)
+ DEBUGP(4, dev, "AUTOPPS already active "
+ "2nd try:use default values\n");
+ if (dev->ta1 == 0x11)
+ DEBUGP(4, dev, "No AUTOPPS necessary "
+ "TA(1)==0x11\n");
+ if (test_bit(IS_ANY_T0, &dev->flags)
+ && test_bit(IS_ANY_T1, &dev->flags))
+ DEBUGP(4, dev, "Do NOT perform AUTOPPS "
+ "with multiprotocol cards\n");
+ clear_bit(IS_AUTOPPS_ACT, &dev->flags);
+ wake_up_interruptible(&dev->atrq);
+ }
+ } else {
+ DEBUGP(4, dev, "ATR invalid\n");
+ wake_up_interruptible(&dev->atrq);
+ }
+ break;
+ case M_BAD_CARD:
+ DEBUGP(4, dev, "M_BAD_CARD\n");
+ /* slow down warning, but prompt immediately after insertion */
+ if (dev->cwarn == 0 || dev->cwarn == 10) {
+ set_bit(IS_BAD_CARD, &dev->flags);
+ printk(KERN_WARNING MODULE_NAME ": device %s: ",
+ dev->node.dev_name);
+ if (test_bit(IS_BAD_CSUM, &dev->flags)) {
+ DEBUGP(4, dev, "ATR checksum (0x%.2x, should "
+ "be zero) failed\n", dev->atr_csum);
+ }
+#ifdef PCMCIA_DEBUG
+ else if (test_bit(IS_BAD_LENGTH, &dev->flags)) {
+ DEBUGP(4, dev, "ATR length error\n");
+ } else {
+ DEBUGP(4, dev, "card damaged or wrong way "
+ "inserted\n");
+ }
+#endif
+ dev->cwarn = 0;
+ wake_up_interruptible(&dev->atrq); /* wake open */
+ }
+ dev->cwarn++;
+ dev->mdelay = T_100MSEC;
+ dev->mstate = M_FETCH_ATR;
+ break;
+ default:
+ DEBUGP(7, dev, "Unknown action\n");
+ break; /* nothing */
+ }
+
+release_io:
+ DEBUGP(7, dev, "release_io\n");
+ clear_bit(LOCK_IO, &dev->flags);
+ wake_up_interruptible(&dev->ioq); /* whoever needs IO */
+
+return_with_timer:
+ DEBUGP(7, dev, "<- monitor_card (returns with timer)\n");
+ dev->timer.expires = jiffies + dev->mdelay;
+ add_timer(&dev->timer);
+ clear_bit(LOCK_MONITOR, &dev->flags);
+}
+
+/* Interface to userland (file_operations) */
+
+static ssize_t cmm_read(struct file *filp, __user char *buf, size_t count,
+ loff_t *ppos)
+{
+ struct cm4000_dev *dev = filp->private_data;
+ ioaddr_t iobase = dev->link.io.BasePort1;
+ ssize_t rc;
+ int i, j, k;
+
+ DEBUGP(2, dev, "-> cmm_read(%s,%d)\n", current->comm, current->pid);
+
+ if (count == 0) /* according to manpage */
+ return 0;
+
+ if ((dev->link.state & DEV_PRESENT) == 0 || /* socket removed */
+ test_bit(IS_CMM_ABSENT, &dev->flags))
+ return -ENODEV;
+
+ if (test_bit(IS_BAD_CSUM, &dev->flags))
+ return -EIO;
+
+ /* also see the note about this in cmm_write */
+ if (wait_event_interruptible
+ (dev->atrq,
+ ((filp->f_flags & O_NONBLOCK)
+ || (test_bit(IS_ATR_PRESENT, (void *)&dev->flags) != 0)))) {
+ if (filp->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+ return -ERESTARTSYS;
+ }
+
+ if (test_bit(IS_ATR_VALID, &dev->flags) == 0)
+ return -EIO;
+
+ /* this one implements blocking IO */
+ if (wait_event_interruptible
+ (dev->readq,
+ ((filp->f_flags & O_NONBLOCK) || (dev->rpos < dev->rlen)))) {
+ if (filp->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+ return -ERESTARTSYS;
+ }
+
+ /* lock io */
+ if (wait_event_interruptible
+ (dev->ioq,
+ ((filp->f_flags & O_NONBLOCK)
+ || (test_and_set_bit(LOCK_IO, (void *)&dev->flags) == 0)))) {
+ if (filp->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+ return -ERESTARTSYS;
+ }
+
+ rc = 0;
+ dev->flags0 = inb(REG_FLAGS0(iobase));
+ if ((dev->flags0 & 1) == 0 /* no smartcard inserted */
+ || dev->flags0 == 0xff) { /* no cardman inserted */
+ clear_bit(IS_ATR_VALID, &dev->flags);
+ if (dev->flags0 & 1) {
+ set_bit(IS_CMM_ABSENT, &dev->flags);
+ rc = -ENODEV;
+ }
+ rc = -EIO;
+ goto release_io;
+ }
+
+ DEBUGP(4, dev, "begin read answer\n");
+ j = min(count, (size_t)(dev->rlen - dev->rpos));
+ k = dev->rpos;
+ if (k + j > 255)
+ j = 256 - k;
+ DEBUGP(4, dev, "read1 j=%d\n", j);
+ for (i = 0; i < j; i++) {
+ xoutb(k++, REG_BUF_ADDR(iobase));
+ dev->rbuf[i] = xinb(REG_BUF_DATA(iobase));
+ }
+ j = min(count, (size_t)(dev->rlen - dev->rpos));
+ if (k + j > 255) {
+ DEBUGP(4, dev, "read2 j=%d\n", j);
+ dev->flags1 |= 0x10; /* MSB buf addr set */
+ xoutb(dev->flags1, REG_FLAGS1(iobase));
+ for (; i < j; i++) {
+ xoutb(k++, REG_BUF_ADDR(iobase));
+ dev->rbuf[i] = xinb(REG_BUF_DATA(iobase));
+ }
+ }
+
+ if (dev->proto == 0 && count > dev->rlen - dev->rpos) {
+ DEBUGP(4, dev, "T=0 and count > buffer\n");
+ dev->rbuf[i] = dev->rbuf[i - 1];
+ dev->rbuf[i - 1] = dev->procbyte;
+ j++;
+ }
+ count = j;
+
+ dev->rpos = dev->rlen + 1;
+
+ /* Clear T1Active */
+ DEBUGP(4, dev, "Clear T1Active\n");
+ dev->flags1 &= 0xdf;
+ xoutb(dev->flags1, REG_FLAGS1(iobase));
+
+ xoutb(0, REG_FLAGS1(iobase)); /* clear detectCMM */
+ /* last check before exit */
+ if (!io_detect_cm4000(iobase, dev))
+ count = -ENODEV;
+
+ if (test_bit(IS_INVREV, &dev->flags) && count > 0)
+ str_invert_revert(dev->rbuf, count);
+
+ if (copy_to_user(buf, dev->rbuf, count))
+ return -EFAULT;
+
+release_io:
+ clear_bit(LOCK_IO, &dev->flags);
+ wake_up_interruptible(&dev->ioq);
+
+ DEBUGP(2, dev, "<- cmm_read returns: rc = %Zi\n",
+ (rc < 0 ? rc : count));
+ return rc < 0 ? rc : count;
+}
+
+static ssize_t cmm_write(struct file *filp, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct cm4000_dev *dev = (struct cm4000_dev *) filp->private_data;
+ ioaddr_t iobase = dev->link.io.BasePort1;
+ unsigned short s;
+ unsigned char tmp;
+ unsigned char infolen;
+ unsigned char sendT0;
+ unsigned short nsend;
+ unsigned short nr;
+ ssize_t rc;
+ int i;
+
+ DEBUGP(2, dev, "-> cmm_write(%s,%d)\n", current->comm, current->pid);
+
+ if (count == 0) /* according to manpage */
+ return 0;
+
+ if (dev->proto == 0 && count < 4) {
+ /* T0 must have at least 4 bytes */
+ DEBUGP(4, dev, "T0 short write\n");
+ return -EIO;
+ }
+
+ nr = count & 0x1ff; /* max bytes to write */
+
+ sendT0 = dev->proto ? 0 : nr > 5 ? 0x08 : 0;
+
+ if ((dev->link.state & DEV_PRESENT) == 0 || /* socket removed */
+ test_bit(IS_CMM_ABSENT, &dev->flags))
+ return -ENODEV;
+
+ if (test_bit(IS_BAD_CSUM, &dev->flags)) {
+ DEBUGP(4, dev, "bad csum\n");
+ return -EIO;
+ }
+
+ /*
+ * wait for atr to become valid.
+ * note: it is important to lock this code. if we dont, the monitor
+ * could be run between test_bit and the the call the sleep on the
+ * atr-queue. if *then* the monitor detects atr valid, it will wake up
+ * any process on the atr-queue, *but* since we have been interrupted,
+ * we do not yet sleep on this queue. this would result in a missed
+ * wake_up and the calling process would sleep forever (until
+ * interrupted). also, do *not* restore_flags before sleep_on, because
+ * this could result in the same situation!
+ */
+ if (wait_event_interruptible
+ (dev->atrq,
+ ((filp->f_flags & O_NONBLOCK)
+ || (test_bit(IS_ATR_PRESENT, (void *)&dev->flags) != 0)))) {
+ if (filp->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+ return -ERESTARTSYS;
+ }
+
+ if (test_bit(IS_ATR_VALID, &dev->flags) == 0) { /* invalid atr */
+ DEBUGP(4, dev, "invalid ATR\n");
+ return -EIO;
+ }
+
+ /* lock io */
+ if (wait_event_interruptible
+ (dev->ioq,
+ ((filp->f_flags & O_NONBLOCK)
+ || (test_and_set_bit(LOCK_IO, (void *)&dev->flags) == 0)))) {
+ if (filp->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+ return -ERESTARTSYS;
+ }
+
+ if (copy_from_user(dev->sbuf, buf, ((count > 512) ? 512 : count)))
+ return -EFAULT;
+
+ rc = 0;
+ dev->flags0 = inb(REG_FLAGS0(iobase));
+ if ((dev->flags0 & 1) == 0 /* no smartcard inserted */
+ || dev->flags0 == 0xff) { /* no cardman inserted */
+ clear_bit(IS_ATR_VALID, &dev->flags);
+ if (dev->flags0 & 1) {
+ set_bit(IS_CMM_ABSENT, &dev->flags);
+ rc = -ENODEV;
+ } else {
+ DEBUGP(4, dev, "IO error\n");
+ rc = -EIO;
+ }
+ goto release_io;
+ }
+
+ xoutb(0x80, REG_FLAGS0(iobase)); /* reset SM */
+
+ if (!io_detect_cm4000(iobase, dev)) {
+ rc = -ENODEV;
+ goto release_io;
+ }
+
+ /* reflect T=0 send/read mode in flags1 */
+ dev->flags1 |= (sendT0);
+
+ set_cardparameter(dev);
+
+ /* dummy read, reset flag procedure received */
+ tmp = inb(REG_FLAGS1(iobase));
+
+ dev->flags1 = 0x20 /* T_Active */
+ | (sendT0)
+ | (test_bit(IS_INVREV, &dev->flags) ? 2 : 0)/* inverse parity */
+ | (((dev->baudv - 1) & 0x0100) >> 8); /* MSB-Baud */
+ DEBUGP(1, dev, "set dev->flags1 = 0x%.2x\n", dev->flags1);
+ xoutb(dev->flags1, REG_FLAGS1(iobase));
+
+ /* xmit data */
+ DEBUGP(4, dev, "Xmit data\n");
+ for (i = 0; i < nr; i++) {
+ if (i >= 256) {
+ dev->flags1 = 0x20 /* T_Active */
+ | (sendT0) /* SendT0 */
+ /* inverse parity: */
+ | (test_bit(IS_INVREV, &dev->flags) ? 2 : 0)
+ | (((dev->baudv - 1) & 0x0100) >> 8) /* MSB-Baud */
+ | 0x10; /* set address high */
+ DEBUGP(4, dev, "dev->flags = 0x%.2x - set address "
+ "high\n", dev->flags1);
+ xoutb(dev->flags1, REG_FLAGS1(iobase));
+ }
+ if (test_bit(IS_INVREV, &dev->flags)) {
+ DEBUGP(4, dev, "Apply inverse convention for 0x%.2x "
+ "-> 0x%.2x\n", (unsigned char)dev->sbuf[i],
+ invert_revert(dev->sbuf[i]));
+ xoutb(i, REG_BUF_ADDR(iobase));
+ xoutb(invert_revert(dev->sbuf[i]),
+ REG_BUF_DATA(iobase));
+ } else {
+ xoutb(i, REG_BUF_ADDR(iobase));
+ xoutb(dev->sbuf[i], REG_BUF_DATA(iobase));
+ }
+ }
+ DEBUGP(4, dev, "Xmit done\n");
+
+ if (dev->proto == 0) {
+ /* T=0 proto: 0 byte reply */
+ if (nr == 4) {
+ DEBUGP(4, dev, "T=0 assumes 0 byte reply\n");
+ xoutb(i, REG_BUF_ADDR(iobase));
+ if (test_bit(IS_INVREV, &dev->flags))
+ xoutb(0xff, REG_BUF_DATA(iobase));
+ else
+ xoutb(0x00, REG_BUF_DATA(iobase));
+ }
+
+ /* numSendBytes */
+ if (sendT0)
+ nsend = nr;
+ else {
+ if (nr == 4)
+ nsend = 5;
+ else {
+ nsend = 5 + (unsigned char)dev->sbuf[4];
+ if (dev->sbuf[4] == 0)
+ nsend += 0x100;
+ }
+ }
+ } else
+ nsend = nr;
+
+ /* T0: output procedure byte */
+ if (test_bit(IS_INVREV, &dev->flags)) {
+ DEBUGP(4, dev, "T=0 set Procedure byte (inverse-reverse) "
+ "0x%.2x\n", invert_revert(dev->sbuf[1]));
+ xoutb(invert_revert(dev->sbuf[1]), REG_NUM_BYTES(iobase));
+ } else {
+ DEBUGP(4, dev, "T=0 set Procedure byte 0x%.2x\n", dev->sbuf[1]);
+ xoutb(dev->sbuf[1], REG_NUM_BYTES(iobase));
+ }
+
+ DEBUGP(1, dev, "set NumSendBytes = 0x%.2x\n",
+ (unsigned char)(nsend & 0xff));
+ xoutb((unsigned char)(nsend & 0xff), REG_NUM_SEND(iobase));
+
+ DEBUGP(1, dev, "Trigger CARDMAN CONTROLLER (0x%.2x)\n",
+ 0x40 /* SM_Active */
+ | (dev->flags0 & 2 ? 0 : 4) /* power on if needed */
+ |(dev->proto ? 0x10 : 0x08) /* T=1/T=0 */
+ |(nsend & 0x100) >> 8 /* MSB numSendBytes */ );
+ xoutb(0x40 /* SM_Active */
+ | (dev->flags0 & 2 ? 0 : 4) /* power on if needed */
+ |(dev->proto ? 0x10 : 0x08) /* T=1/T=0 */
+ |(nsend & 0x100) >> 8, /* MSB numSendBytes */
+ REG_FLAGS0(iobase));
+
+ /* wait for xmit done */
+ if (dev->proto == 1) {
+ DEBUGP(4, dev, "Wait for xmit done\n");
+ for (i = 0; i < 1000; i++) {
+ if (inb(REG_FLAGS0(iobase)) & 0x08)
+ break;
+ msleep_interruptible(10);
+ }
+ if (i == 1000) {
+ DEBUGP(4, dev, "timeout waiting for xmit done\n");
+ rc = -EIO;
+ goto release_io;
+ }
+ }
+
+ /* T=1: wait for infoLen */
+
+ infolen = 0;
+ if (dev->proto) {
+ /* wait until infoLen is valid */
+ for (i = 0; i < 6000; i++) { /* max waiting time of 1 min */
+ io_read_num_rec_bytes(iobase, &s);
+ if (s >= 3) {
+ infolen = inb(REG_FLAGS1(iobase));
+ DEBUGP(4, dev, "infolen=%d\n", infolen);
+ break;
+ }
+ msleep_interruptible(10);
+ }
+ if (i == 6000) {
+ DEBUGP(4, dev, "timeout waiting for infoLen\n");
+ rc = -EIO;
+ goto release_io;
+ }
+ } else
+ clear_bit(IS_PROCBYTE_PRESENT, &dev->flags);
+
+ /* numRecBytes | bit9 of numRecytes */
+ io_read_num_rec_bytes(iobase, &dev->rlen);
+ for (i = 0; i < 600; i++) { /* max waiting time of 2 sec */
+ if (dev->proto) {
+ if (dev->rlen >= infolen + 4)
+ break;
+ }
+ msleep_interruptible(10);
+ /* numRecBytes | bit9 of numRecytes */
+ io_read_num_rec_bytes(iobase, &s);
+ if (s > dev->rlen) {
+ DEBUGP(1, dev, "NumRecBytes inc (reset timeout)\n");
+ i = 0; /* reset timeout */
+ dev->rlen = s;
+ }
+ /* T=0: we are done when numRecBytes doesn't
+ * increment any more and NoProcedureByte
+ * is set and numRecBytes == bytes sent + 6
+ * (header bytes + data + 1 for sw2)
+ * except when the card replies an error
+ * which means, no data will be sent back.
+ */
+ else if (dev->proto == 0) {
+ if ((inb(REG_BUF_ADDR(iobase)) & 0x80)) {
+ /* no procedure byte received since last read */
+ DEBUGP(1, dev, "NoProcedure byte set\n");
+ /* i=0; */
+ } else {
+ /* procedure byte received since last read */
+ DEBUGP(1, dev, "NoProcedure byte unset "
+ "(reset timeout)\n");
+ dev->procbyte = inb(REG_FLAGS1(iobase));
+ DEBUGP(1, dev, "Read procedure byte 0x%.2x\n",
+ dev->procbyte);
+ i = 0; /* resettimeout */
+ }
+ if (inb(REG_FLAGS0(iobase)) & 0x08) {
+ DEBUGP(1, dev, "T0Done flag (read reply)\n");
+ break;
+ }
+ }
+ if (dev->proto)
+ infolen = inb(REG_FLAGS1(iobase));
+ }
+ if (i == 600) {
+ DEBUGP(1, dev, "timeout waiting for numRecBytes\n");
+ rc = -EIO;
+ goto release_io;
+ } else {
+ if (dev->proto == 0) {
+ DEBUGP(1, dev, "Wait for T0Done bit to be set\n");
+ for (i = 0; i < 1000; i++) {
+ if (inb(REG_FLAGS0(iobase)) & 0x08)
+ break;
+ msleep_interruptible(10);
+ }
+ if (i == 1000) {
+ DEBUGP(1, dev, "timeout waiting for T0Done\n");
+ rc = -EIO;
+ goto release_io;
+ }
+
+ dev->procbyte = inb(REG_FLAGS1(iobase));
+ DEBUGP(4, dev, "Read procedure byte 0x%.2x\n",
+ dev->procbyte);
+
+ io_read_num_rec_bytes(iobase, &dev->rlen);
+ DEBUGP(4, dev, "Read NumRecBytes = %i\n", dev->rlen);
+
+ }
+ }
+ /* T=1: read offset=zero, T=0: read offset=after challenge */
+ dev->rpos = dev->proto ? 0 : nr == 4 ? 5 : nr > dev->rlen ? 5 : nr;
+ DEBUGP(4, dev, "dev->rlen = %i, dev->rpos = %i, nr = %i\n",
+ dev->rlen, dev->rpos, nr);
+
+release_io:
+ DEBUGP(4, dev, "Reset SM\n");
+ xoutb(0x80, REG_FLAGS0(iobase)); /* reset SM */
+
+ if (rc < 0) {
+ DEBUGP(4, dev, "Write failed but clear T_Active\n");
+ dev->flags1 &= 0xdf;
+ xoutb(dev->flags1, REG_FLAGS1(iobase));
+ }
+
+ clear_bit(LOCK_IO, &dev->flags);
+ wake_up_interruptible(&dev->ioq);
+ wake_up_interruptible(&dev->readq); /* tell read we have data */
+
+ /* ITSEC E2: clear write buffer */
+ memset((char *)dev->sbuf, 0, 512);
+
+ /* return error or actually written bytes */
+ DEBUGP(2, dev, "<- cmm_write\n");
+ return rc < 0 ? rc : nr;
+}
+
+static void start_monitor(struct cm4000_dev *dev)
+{
+ DEBUGP(3, dev, "-> start_monitor\n");
+ if (!dev->monitor_running) {
+ DEBUGP(5, dev, "create, init and add timer\n");
+ init_timer(&dev->timer);
+ dev->monitor_running = 1;
+ dev->timer.expires = jiffies;
+ dev->timer.data = (unsigned long) dev;
+ dev->timer.function = monitor_card;
+ add_timer(&dev->timer);
+ } else
+ DEBUGP(5, dev, "monitor already running\n");
+ DEBUGP(3, dev, "<- start_monitor\n");
+}
+
+static void stop_monitor(struct cm4000_dev *dev)
+{
+ DEBUGP(3, dev, "-> stop_monitor\n");
+ if (dev->monitor_running) {
+ DEBUGP(5, dev, "stopping monitor\n");
+ terminate_monitor(dev);
+ /* reset monitor SM */
+ clear_bit(IS_ATR_VALID, &dev->flags);
+ clear_bit(IS_ATR_PRESENT, &dev->flags);
+ } else
+ DEBUGP(5, dev, "monitor already stopped\n");
+ DEBUGP(3, dev, "<- stop_monitor\n");
+}
+
+static int cmm_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ struct cm4000_dev *dev = filp->private_data;
+ ioaddr_t iobase = dev->link.io.BasePort1;
+ dev_link_t *link;
+ int size;
+ int rc;
+#ifdef PCMCIA_DEBUG
+ char *ioctl_names[CM_IOC_MAXNR + 1] = {
+ [_IOC_NR(CM_IOCGSTATUS)] "CM_IOCGSTATUS",
+ [_IOC_NR(CM_IOCGATR)] "CM_IOCGATR",
+ [_IOC_NR(CM_IOCARDOFF)] "CM_IOCARDOFF",
+ [_IOC_NR(CM_IOCSPTS)] "CM_IOCSPTS",
+ [_IOC_NR(CM_IOSDBGLVL)] "CM4000_DBGLVL",
+ };
+#endif
+ DEBUGP(3, dev, "cmm_ioctl(device=%d.%d) %s\n", imajor(inode),
+ iminor(inode), ioctl_names[_IOC_NR(cmd)]);
+
+ link = dev_table[iminor(inode)];
+ if (!(DEV_OK(link))) {
+ DEBUGP(4, dev, "DEV_OK false\n");
+ return -ENODEV;
+ }
+
+ if (test_bit(IS_CMM_ABSENT, &dev->flags)) {
+ DEBUGP(4, dev, "CMM_ABSENT flag set\n");
+ return -ENODEV;
+ }
+
+ if (_IOC_TYPE(cmd) != CM_IOC_MAGIC) {
+ DEBUGP(4, dev, "ioctype mismatch\n");
+ return -EINVAL;
+ }
+ if (_IOC_NR(cmd) > CM_IOC_MAXNR) {
+ DEBUGP(4, dev, "iocnr mismatch\n");
+ return -EINVAL;
+ }
+ size = _IOC_SIZE(cmd);
+ rc = 0;
+ DEBUGP(4, dev, "iocdir=%.4x iocr=%.4x iocw=%.4x iocsize=%d cmd=%.4x\n",
+ _IOC_DIR(cmd), _IOC_READ, _IOC_WRITE, size, cmd);
+
+ if (_IOC_DIR(cmd) & _IOC_READ) {
+ if (!access_ok(VERIFY_WRITE, (void *)arg, size))
+ return -EFAULT;
+ }
+ if (_IOC_DIR(cmd) & _IOC_WRITE) {
+ if (!access_ok(VERIFY_READ, (void *)arg, size))
+ return -EFAULT;
+ }
+
+ switch (cmd) {
+ case CM_IOCGSTATUS:
+ DEBUGP(4, dev, " ... in CM_IOCGSTATUS\n");
+ {
+ int status;
+
+ /* clear other bits, but leave inserted & powered as
+ * they are */
+ status = dev->flags0 & 3;
+ if (test_bit(IS_ATR_PRESENT, &dev->flags))
+ status |= CM_ATR_PRESENT;
+ if (test_bit(IS_ATR_VALID, &dev->flags))
+ status |= CM_ATR_VALID;
+ if (test_bit(IS_CMM_ABSENT, &dev->flags))
+ status |= CM_NO_READER;
+ if (test_bit(IS_BAD_CARD, &dev->flags))
+ status |= CM_BAD_CARD;
+ if (copy_to_user((int *)arg, &status, sizeof(int)))
+ return -EFAULT;
+ }
+ return 0;
+ case CM_IOCGATR:
+ DEBUGP(4, dev, "... in CM_IOCGATR\n");
+ {
+ struct atreq *atreq = (struct atreq *) arg;
+ int tmp;
+ /* allow nonblocking io and being interrupted */
+ if (wait_event_interruptible
+ (dev->atrq,
+ ((filp->f_flags & O_NONBLOCK)
+ || (test_bit(IS_ATR_PRESENT, (void *)&dev->flags)
+ != 0)))) {
+ if (filp->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+ return -ERESTARTSYS;
+ }
+
+ if (test_bit(IS_ATR_VALID, &dev->flags) == 0) {
+ tmp = -1;
+ if (copy_to_user(&(atreq->atr_len), &tmp,
+ sizeof(int)))
+ return -EFAULT;
+ } else {
+ if (copy_to_user(atreq->atr, dev->atr,
+ dev->atr_len))
+ return -EFAULT;
+
+ tmp = dev->atr_len;
+ if (copy_to_user(&(atreq->atr_len), &tmp, sizeof(int)))
+ return -EFAULT;
+ }
+ return 0;
+ }
+ case CM_IOCARDOFF:
+
+#ifdef PCMCIA_DEBUG
+ DEBUGP(4, dev, "... in CM_IOCARDOFF\n");
+ if (dev->flags0 & 0x01) {
+ DEBUGP(4, dev, " Card inserted\n");
+ } else {
+ DEBUGP(2, dev, " No card inserted\n");
+ }
+ if (dev->flags0 & 0x02) {
+ DEBUGP(4, dev, " Card powered\n");
+ } else {
+ DEBUGP(2, dev, " Card not powered\n");
+ }
+#endif
+
+ /* is a card inserted and powered? */
+ if ((dev->flags0 & 0x01) && (dev->flags0 & 0x02)) {
+
+ /* get IO lock */
+ if (wait_event_interruptible
+ (dev->ioq,
+ ((filp->f_flags & O_NONBLOCK)
+ || (test_and_set_bit(LOCK_IO, (void *)&dev->flags)
+ == 0)))) {
+ if (filp->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+ return -ERESTARTSYS;
+ }
+ /* Set Flags0 = 0x42 */
+ DEBUGP(4, dev, "Set Flags0=0x42 \n");
+ xoutb(0x42, REG_FLAGS0(iobase));
+ clear_bit(IS_ATR_PRESENT, &dev->flags);
+ clear_bit(IS_ATR_VALID, &dev->flags);
+ dev->mstate = M_CARDOFF;
+ clear_bit(LOCK_IO, &dev->flags);
+ if (wait_event_interruptible
+ (dev->atrq,
+ ((filp->f_flags & O_NONBLOCK)
+ || (test_bit(IS_ATR_VALID, (void *)&dev->flags) !=
+ 0)))) {
+ if (filp->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+ return -ERESTARTSYS;
+ }
+ }
+ /* release lock */
+ clear_bit(LOCK_IO, &dev->flags);
+ wake_up_interruptible(&dev->ioq);
+
+ return 0;
+ case CM_IOCSPTS:
+ {
+ struct ptsreq krnptsreq;
+
+ if (copy_from_user(&krnptsreq, (struct ptsreq *) arg,
+ sizeof(struct ptsreq)))
+ return -EFAULT;
+
+ rc = 0;
+ DEBUGP(4, dev, "... in CM_IOCSPTS\n");
+ /* wait for ATR to get valid */
+ if (wait_event_interruptible
+ (dev->atrq,
+ ((filp->f_flags & O_NONBLOCK)
+ || (test_bit(IS_ATR_PRESENT, (void *)&dev->flags)
+ != 0)))) {
+ if (filp->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+ return -ERESTARTSYS;
+ }
+ /* get IO lock */
+ if (wait_event_interruptible
+ (dev->ioq,
+ ((filp->f_flags & O_NONBLOCK)
+ || (test_and_set_bit(LOCK_IO, (void *)&dev->flags)
+ == 0)))) {
+ if (filp->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+ return -ERESTARTSYS;
+ }
+
+ if ((rc = set_protocol(dev, &krnptsreq)) != 0) {
+ /* auto power_on again */
+ dev->mstate = M_FETCH_ATR;
+ clear_bit(IS_ATR_VALID, &dev->flags);
+ }
+ /* release lock */
+ clear_bit(LOCK_IO, &dev->flags);
+ wake_up_interruptible(&dev->ioq);
+
+ }
+ return rc;
+#ifdef PCMCIA_DEBUG
+ case CM_IOSDBGLVL: /* set debug log level */
+ {
+ int old_pc_debug = 0;
+
+ old_pc_debug = pc_debug;
+ if (copy_from_user(&pc_debug, (int *)arg, sizeof(int)))
+ return -EFAULT;
+
+ if (old_pc_debug != pc_debug)
+ DEBUGP(0, dev, "Changed debug log level "
+ "to %i\n", pc_debug);
+ }
+ return rc;
+#endif
+ default:
+ DEBUGP(4, dev, "... in default (unknown IOCTL code)\n");
+ return -EINVAL;
+ }
+}
+
+static int cmm_open(struct inode *inode, struct file *filp)
+{
+ struct cm4000_dev *dev;
+ dev_link_t *link;
+ int rc, minor = iminor(inode);
+
+ if (minor >= CM4000_MAX_DEV)
+ return -ENODEV;
+
+ link = dev_table[minor];
+ if (link == NULL || !(DEV_OK(link)))
+ return -ENODEV;
+
+ if (link->open)
+ return -EBUSY;
+
+ dev = link->priv;
+ filp->private_data = dev;
+
+ DEBUGP(2, dev, "-> cmm_open(device=%d.%d process=%s,%d)\n",
+ imajor(inode), minor, current->comm, current->pid);
+
+ /* init device variables, they may be "polluted" after close
+ * or, the device may never have been closed (i.e. open failed)
+ */
+
+ ZERO_DEV(dev);
+
+ /* opening will always block since the
+ * monitor will be started by open, which
+ * means we have to wait for ATR becoming
+ * vaild = block until valid (or card
+ * inserted)
+ */
+ if (filp->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+
+ dev->mdelay = T_50MSEC;
+
+ /* start monitoring the cardstatus */
+ start_monitor(dev);
+
+ link->open = 1; /* only one open per device */
+ rc = 0;
+
+ DEBUGP(2, dev, "<- cmm_open\n");
+ return nonseekable_open(inode, filp);
+}
+
+static int cmm_close(struct inode *inode, struct file *filp)
+{
+ struct cm4000_dev *dev;
+ dev_link_t *link;
+ int minor = iminor(inode);
+
+ if (minor >= CM4000_MAX_DEV)
+ return -ENODEV;
+
+ link = dev_table[minor];
+ if (link == NULL)
+ return -ENODEV;
+
+ dev = link->priv;
+
+ DEBUGP(2, dev, "-> cmm_close(maj/min=%d.%d)\n",
+ imajor(inode), minor);
+
+ stop_monitor(dev);
+
+ ZERO_DEV(dev);
+
+ link->open = 0; /* only one open per device */
+ wake_up(&dev->devq); /* socket removed? */
+
+ DEBUGP(2, dev, "cmm_close\n");
+ return 0;
+}
+
+static void cmm_cm4000_release(dev_link_t * link)
+{
+ struct cm4000_dev *dev = link->priv;
+
+ /* dont terminate the monitor, rather rely on
+ * close doing that for us.
+ */
+ DEBUGP(3, dev, "-> cmm_cm4000_release\n");
+ while (link->open) {
+ printk(KERN_INFO MODULE_NAME ": delaying release until "
+ "process has terminated\n");
+ /* note: don't interrupt us:
+ * close the applications which own
+ * the devices _first_ !
+ */
+ wait_event(dev->devq, (link->open == 0));
+ }
+ /* dev->devq=NULL; this cannot be zeroed earlier */
+ DEBUGP(3, dev, "<- cmm_cm4000_release\n");
+ return;
+}
+
+/*==== Interface to PCMCIA Layer =======================================*/
+
+static void cm4000_config(dev_link_t * link, int devno)
+{
+ client_handle_t handle = link->handle;
+ struct cm4000_dev *dev;
+ tuple_t tuple;
+ cisparse_t parse;
+ config_info_t conf;
+ u_char buf[64];
+ int fail_fn, fail_rc;
+ int rc;
+
+ /* read the config-tuples */
+ tuple.DesiredTuple = CISTPL_CONFIG;
+ tuple.Attributes = 0;
+ tuple.TupleData = buf;
+ tuple.TupleDataMax = sizeof(buf);
+ tuple.TupleOffset = 0;
+
+ if ((fail_rc = pcmcia_get_first_tuple(handle, &tuple)) != CS_SUCCESS) {
+ fail_fn = GetFirstTuple;
+ goto cs_failed;
+ }
+ if ((fail_rc = pcmcia_get_tuple_data(handle, &tuple)) != CS_SUCCESS) {
+ fail_fn = GetTupleData;
+ goto cs_failed;
+ }
+ if ((fail_rc =
+ pcmcia_parse_tuple(handle, &tuple, &parse)) != CS_SUCCESS) {
+ fail_fn = ParseTuple;
+ goto cs_failed;
+ }
+ if ((fail_rc =
+ pcmcia_get_configuration_info(handle, &conf)) != CS_SUCCESS) {
+ fail_fn = GetConfigurationInfo;
+ goto cs_failed;
+ }
+
+ link->state |= DEV_CONFIG;
+ link->conf.ConfigBase = parse.config.base;
+ link->conf.Present = parse.config.rmask[0];
+ link->conf.Vcc = conf.Vcc;
+
+ link->io.BasePort2 = 0;
+ link->io.NumPorts2 = 0;
+ link->io.Attributes2 = 0;
+ tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+ for (rc = pcmcia_get_first_tuple(handle, &tuple);
+ rc == CS_SUCCESS; rc = pcmcia_get_next_tuple(handle, &tuple)) {
+
+ rc = pcmcia_get_tuple_data(handle, &tuple);
+ if (rc != CS_SUCCESS)
+ continue;
+ rc = pcmcia_parse_tuple(handle, &tuple, &parse);
+ if (rc != CS_SUCCESS)
+ continue;
+
+ link->conf.ConfigIndex = parse.cftable_entry.index;
+
+ if (!parse.cftable_entry.io.nwin)
+ continue;
+
+ /* Get the IOaddr */
+ link->io.BasePort1 = parse.cftable_entry.io.win[0].base;
+ link->io.NumPorts1 = parse.cftable_entry.io.win[0].len;
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+ if (!(parse.cftable_entry.io.flags & CISTPL_IO_8BIT))
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
+ if (!(parse.cftable_entry.io.flags & CISTPL_IO_16BIT))
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+ link->io.IOAddrLines = parse.cftable_entry.io.flags
+ & CISTPL_IO_LINES_MASK;
+
+ rc = pcmcia_request_io(handle, &link->io);
+ if (rc == CS_SUCCESS)
+ break; /* we are done */
+ }
+ if (rc != CS_SUCCESS)
+ goto cs_release;
+
+ link->conf.IntType = 00000002;
+
+ if ((fail_rc =
+ pcmcia_request_configuration(handle, &link->conf)) != CS_SUCCESS) {
+ fail_fn = RequestConfiguration;
+ goto cs_release;
+ }
+
+ dev = link->priv;
+ sprintf(dev->node.dev_name, DEVICE_NAME "%d", devno);
+ dev->node.major = major;
+ dev->node.minor = devno;
+ dev->node.next = NULL;
+ link->dev = &dev->node;
+ link->state &= ~DEV_CONFIG_PENDING;
+
+ return;
+
+cs_failed:
+ cs_error(handle, fail_fn, fail_rc);
+cs_release:
+ cm4000_release(link);
+
+ link->state &= ~DEV_CONFIG_PENDING;
+}
+
+static int cm4000_event(event_t event, int priority,
+ event_callback_args_t *args)
+{
+ dev_link_t *link;
+ struct cm4000_dev *dev;
+ int devno;
+
+ link = args->client_data;
+ dev = link->priv;
+
+ DEBUGP(3, dev, "-> cm4000_event\n");
+ for (devno = 0; devno < CM4000_MAX_DEV; devno++)
+ if (dev_table[devno] == link)
+ break;
+
+ if (devno == CM4000_MAX_DEV)
+ return CS_BAD_ADAPTER;
+
+ switch (event) {
+ case CS_EVENT_CARD_INSERTION:
+ DEBUGP(5, dev, "CS_EVENT_CARD_INSERTION\n");
+ link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+ cm4000_config(link, devno);
+ break;
+ case CS_EVENT_CARD_REMOVAL:
+ DEBUGP(5, dev, "CS_EVENT_CARD_REMOVAL\n");
+ link->state &= ~DEV_PRESENT;
+ stop_monitor(dev);
+ break;
+ case CS_EVENT_PM_SUSPEND:
+ DEBUGP(5, dev, "CS_EVENT_PM_SUSPEND "
+ "(fall-through to CS_EVENT_RESET_PHYSICAL)\n");
+ link->state |= DEV_SUSPEND;
+ /* fall-through */
+ case CS_EVENT_RESET_PHYSICAL:
+ DEBUGP(5, dev, "CS_EVENT_RESET_PHYSICAL\n");
+ if (link->state & DEV_CONFIG) {
+ DEBUGP(5, dev, "ReleaseConfiguration\n");
+ pcmcia_release_configuration(link->handle);
+ }
+ stop_monitor(dev);
+ break;
+ case CS_EVENT_PM_RESUME:
+ DEBUGP(5, dev, "CS_EVENT_PM_RESUME "
+ "(fall-through to CS_EVENT_CARD_RESET)\n");
+ link->state &= ~DEV_SUSPEND;
+ /* fall-through */
+ case CS_EVENT_CARD_RESET:
+ DEBUGP(5, dev, "CS_EVENT_CARD_RESET\n");
+ if ((link->state & DEV_CONFIG)) {
+ DEBUGP(5, dev, "RequestConfiguration\n");
+ pcmcia_request_configuration(link->handle, &link->conf);
+ }
+ if (link->open)
+ start_monitor(dev);
+ break;
+ default:
+ DEBUGP(5, dev, "unknown event %.2x\n", event);
+ break;
+ }
+ DEBUGP(3, dev, "<- cm4000_event\n");
+ return CS_SUCCESS;
+}
+
+static void cm4000_release(dev_link_t *link)
+{
+ cmm_cm4000_release(link->priv); /* delay release until device closed */
+ pcmcia_release_configuration(link->handle);
+ pcmcia_release_io(link->handle, &link->io);
+}
+
+static dev_link_t *cm4000_attach(void)
+{
+ struct cm4000_dev *dev;
+ dev_link_t *link;
+ client_reg_t client_reg;
+ int i;
+
+ for (i = 0; i < CM4000_MAX_DEV; i++)
+ if (dev_table[i] == NULL)
+ break;
+
+ if (i == CM4000_MAX_DEV) {
+ printk(KERN_NOTICE MODULE_NAME ": all devices in use\n");
+ return NULL;
+ }
+
+ /* create a new cm4000_cs device */
+ dev = kzalloc(sizeof(struct cm4000_dev), GFP_KERNEL);
+ if (dev == NULL)
+ return NULL;
+
+ link = &dev->link;
+ link->priv = dev;
+ link->conf.IntType = INT_MEMORY_AND_IO;
+ dev_table[i] = link;
+
+ /* register with card services */
+ client_reg.dev_info = &dev_info;
+ client_reg.EventMask =
+ CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+ CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
+ CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+ client_reg.Version = 0x0210;
+ client_reg.event_callback_args.client_data = link;
+
+ i = pcmcia_register_client(&link->handle, &client_reg);
+ if (i) {
+ cs_error(link->handle, RegisterClient, i);
+ cm4000_detach(link);
+ return NULL;
+ }
+
+ init_waitqueue_head(&dev->devq);
+ init_waitqueue_head(&dev->ioq);
+ init_waitqueue_head(&dev->atrq);
+ init_waitqueue_head(&dev->readq);
+
+ return link;
+}
+
+static void cm4000_detach_by_devno(int devno, dev_link_t * link)
+{
+ struct cm4000_dev *dev = link->priv;
+
+ DEBUGP(3, dev, "-> detach_by_devno(devno=%d)\n", devno);
+
+ if (link->state & DEV_CONFIG) {
+ DEBUGP(5, dev, "device still configured (try to release it)\n");
+ cm4000_release(link);
+ }
+
+ if (link->handle) {
+ pcmcia_deregister_client(link->handle);
+ }
+
+ dev_table[devno] = NULL;
+ kfree(dev);
+ return;
+}
+
+static void cm4000_detach(dev_link_t * link)
+{
+ int i;
+
+ /* find device */
+ for (i = 0; i < CM4000_MAX_DEV; i++)
+ if (dev_table[i] == link)
+ break;
+
+ if (i == CM4000_MAX_DEV)
+ return;
+
+ cm4000_detach_by_devno(i, link);
+ return;
+}
+
+static struct file_operations cm4000_fops = {
+ .owner = THIS_MODULE,
+ .read = cmm_read,
+ .write = cmm_write,
+ .ioctl = cmm_ioctl,
+ .open = cmm_open,
+ .release= cmm_close,
+};
+
+static struct pcmcia_device_id cm4000_ids[] = {
+ PCMCIA_DEVICE_MANF_CARD(0x0223, 0x0002),
+ PCMCIA_DEVICE_PROD_ID12("CardMan", "4000", 0x2FB368CA, 0xA2BD8C39),
+ PCMCIA_DEVICE_NULL,
+};
+MODULE_DEVICE_TABLE(pcmcia, cm4000_ids);
+
+static struct pcmcia_driver cm4000_driver = {
+ .owner = THIS_MODULE,
+ .drv = {
+ .name = "cm4000_cs",
+ },
+ .attach = cm4000_attach,
+ .detach = cm4000_detach,
+ .event = cm4000_event,
+ .id_table = cm4000_ids,
+};
+
+static int __init cmm_init(void)
+{
+ printk(KERN_INFO "%s\n", version);
+ pcmcia_register_driver(&cm4000_driver);
+ major = register_chrdev(0, DEVICE_NAME, &cm4000_fops);
+ if (major < 0) {
+ printk(KERN_WARNING MODULE_NAME
+ ": could not get major number\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static void __exit cmm_exit(void)
+{
+ int i;
+
+ printk(KERN_INFO MODULE_NAME ": unloading\n");
+ pcmcia_unregister_driver(&cm4000_driver);
+ for (i = 0; i < CM4000_MAX_DEV; i++)
+ if (dev_table[i])
+ cm4000_detach_by_devno(i, dev_table[i]);
+ unregister_chrdev(major, DEVICE_NAME);
+};
+
+module_init(cmm_init);
+module_exit(cmm_exit);
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/char/pcmcia/cm4040_cs.c b/drivers/char/pcmcia/cm4040_cs.c
new file mode 100644
index 00000000000..4c698d908ff
--- /dev/null
+++ b/drivers/char/pcmcia/cm4040_cs.c
@@ -0,0 +1,841 @@
+/*
+ * A driver for the Omnikey PCMCIA smartcard reader CardMan 4040
+ *
+ * (c) 2000-2004 Omnikey AG (http://www.omnikey.com/)
+ *
+ * (C) 2005 Harald Welte <laforge@gnumonks.org>
+ * - add support for poll()
+ * - driver cleanup
+ * - add waitqueues
+ * - adhere to linux kernel coding style and policies
+ * - support 2.6.13 "new style" pcmcia interface
+ *
+ * The device basically is a USB CCID compliant device that has been
+ * attached to an I/O-Mapped FIFO.
+ *
+ * All rights reserved, Dual BSD/GPL Licensed.
+ */
+
+/* #define PCMCIA_DEBUG 6 */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/poll.h>
+#include <linux/wait.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/ciscode.h>
+#include <pcmcia/ds.h>
+
+#include "cm4040_cs.h"
+
+
+#ifdef PCMCIA_DEBUG
+#define reader_to_dev(x) (&handle_to_dev(x->link.handle))
+static int pc_debug = PCMCIA_DEBUG;
+module_param(pc_debug, int, 0600);
+#define DEBUGP(n, rdr, x, args...) do { \
+ if (pc_debug >= (n)) \
+ dev_printk(KERN_DEBUG, reader_to_dev(rdr), "%s:" x, \
+ __FUNCTION__ , ##args); \
+ } while (0)
+#else
+#define DEBUGP(n, rdr, x, args...)
+#endif
+
+static char *version =
+"OMNIKEY CardMan 4040 v1.1.0gm4 - All bugs added by Harald Welte";
+
+#define CCID_DRIVER_BULK_DEFAULT_TIMEOUT (150*HZ)
+#define CCID_DRIVER_ASYNC_POWERUP_TIMEOUT (35*HZ)
+#define CCID_DRIVER_MINIMUM_TIMEOUT (3*HZ)
+#define READ_WRITE_BUFFER_SIZE 512
+#define POLL_LOOP_COUNT 1000
+
+/* how often to poll for fifo status change */
+#define POLL_PERIOD msecs_to_jiffies(10)
+
+static void reader_release(dev_link_t *link);
+static void reader_detach(dev_link_t *link);
+
+static int major;
+
+#define BS_READABLE 0x01
+#define BS_WRITABLE 0x02
+
+struct reader_dev {
+ dev_link_t link;
+ dev_node_t node;
+ wait_queue_head_t devq;
+ wait_queue_head_t poll_wait;
+ wait_queue_head_t read_wait;
+ wait_queue_head_t write_wait;
+ unsigned long buffer_status;
+ unsigned long timeout;
+ unsigned char s_buf[READ_WRITE_BUFFER_SIZE];
+ unsigned char r_buf[READ_WRITE_BUFFER_SIZE];
+ struct timer_list poll_timer;
+};
+
+static dev_info_t dev_info = MODULE_NAME;
+static dev_link_t *dev_table[CM_MAX_DEV];
+
+#ifndef PCMCIA_DEBUG
+#define xoutb outb
+#define xinb inb
+#else
+static inline void xoutb(unsigned char val, unsigned short port)
+{
+ if (pc_debug >= 7)
+ printk(KERN_DEBUG "outb(val=%.2x,port=%.4x)\n", val, port);
+ outb(val, port);
+}
+
+static inline unsigned char xinb(unsigned short port)
+{
+ unsigned char val;
+
+ val = inb(port);
+ if (pc_debug >= 7)
+ printk(KERN_DEBUG "%.2x=inb(%.4x)\n", val, port);
+ return val;
+}
+#endif
+
+/* poll the device fifo status register. not to be confused with
+ * the poll syscall. */
+static void cm4040_do_poll(unsigned long dummy)
+{
+ struct reader_dev *dev = (struct reader_dev *) dummy;
+ unsigned int obs = xinb(dev->link.io.BasePort1
+ + REG_OFFSET_BUFFER_STATUS);
+
+ if ((obs & BSR_BULK_IN_FULL)) {
+ set_bit(BS_READABLE, &dev->buffer_status);
+ DEBUGP(4, dev, "waking up read_wait\n");
+ wake_up_interruptible(&dev->read_wait);
+ } else
+ clear_bit(BS_READABLE, &dev->buffer_status);
+
+ if (!(obs & BSR_BULK_OUT_FULL)) {
+ set_bit(BS_WRITABLE, &dev->buffer_status);
+ DEBUGP(4, dev, "waking up write_wait\n");
+ wake_up_interruptible(&dev->write_wait);
+ } else
+ clear_bit(BS_WRITABLE, &dev->buffer_status);
+
+ if (dev->buffer_status)
+ wake_up_interruptible(&dev->poll_wait);
+
+ mod_timer(&dev->poll_timer, jiffies + POLL_PERIOD);
+}
+
+static void cm4040_stop_poll(struct reader_dev *dev)
+{
+ del_timer_sync(&dev->poll_timer);
+}
+
+static int wait_for_bulk_out_ready(struct reader_dev *dev)
+{
+ int i, rc;
+ int iobase = dev->link.io.BasePort1;
+
+ for (i = 0; i < POLL_LOOP_COUNT; i++) {
+ if ((xinb(iobase + REG_OFFSET_BUFFER_STATUS)
+ & BSR_BULK_OUT_FULL) == 0) {
+ DEBUGP(4, dev, "BulkOut empty (i=%d)\n", i);
+ return 1;
+ }
+ }
+
+ DEBUGP(4, dev, "wait_event_interruptible_timeout(timeout=%ld\n",
+ dev->timeout);
+ rc = wait_event_interruptible_timeout(dev->write_wait,
+ test_and_clear_bit(BS_WRITABLE,
+ &dev->buffer_status),
+ dev->timeout);
+
+ if (rc > 0)
+ DEBUGP(4, dev, "woke up: BulkOut empty\n");
+ else if (rc == 0)
+ DEBUGP(4, dev, "woke up: BulkOut full, returning 0 :(\n");
+ else if (rc < 0)
+ DEBUGP(4, dev, "woke up: signal arrived\n");
+
+ return rc;
+}
+
+/* Write to Sync Control Register */
+static int write_sync_reg(unsigned char val, struct reader_dev *dev)
+{
+ int iobase = dev->link.io.BasePort1;
+ int rc;
+
+ rc = wait_for_bulk_out_ready(dev);
+ if (rc <= 0)
+ return rc;
+
+ xoutb(val, iobase + REG_OFFSET_SYNC_CONTROL);
+ rc = wait_for_bulk_out_ready(dev);
+ if (rc <= 0)
+ return rc;
+
+ return 1;
+}
+
+static int wait_for_bulk_in_ready(struct reader_dev *dev)
+{
+ int i, rc;
+ int iobase = dev->link.io.BasePort1;
+
+ for (i = 0; i < POLL_LOOP_COUNT; i++) {
+ if ((xinb(iobase + REG_OFFSET_BUFFER_STATUS)
+ & BSR_BULK_IN_FULL) == BSR_BULK_IN_FULL) {
+ DEBUGP(3, dev, "BulkIn full (i=%d)\n", i);
+ return 1;
+ }
+ }
+
+ DEBUGP(4, dev, "wait_event_interruptible_timeout(timeout=%ld\n",
+ dev->timeout);
+ rc = wait_event_interruptible_timeout(dev->read_wait,
+ test_and_clear_bit(BS_READABLE,
+ &dev->buffer_status),
+ dev->timeout);
+ if (rc > 0)
+ DEBUGP(4, dev, "woke up: BulkIn full\n");
+ else if (rc == 0)
+ DEBUGP(4, dev, "woke up: BulkIn not full, returning 0 :(\n");
+ else if (rc < 0)
+ DEBUGP(4, dev, "woke up: signal arrived\n");
+
+ return rc;
+}
+
+static ssize_t cm4040_read(struct file *filp, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct reader_dev *dev = filp->private_data;
+ int iobase = dev->link.io.BasePort1;
+ size_t bytes_to_read;
+ unsigned long i;
+ size_t min_bytes_to_read;
+ int rc;
+ unsigned char uc;
+
+ DEBUGP(2, dev, "-> cm4040_read(%s,%d)\n", current->comm, current->pid);
+
+ if (count == 0)
+ return 0;
+
+ if (count < 10)
+ return -EFAULT;
+
+ if (filp->f_flags & O_NONBLOCK) {
+ DEBUGP(4, dev, "filep->f_flags O_NONBLOCK set\n");
+ DEBUGP(2, dev, "<- cm4040_read (failure)\n");
+ return -EAGAIN;
+ }
+
+ if ((dev->link.state & DEV_PRESENT)==0)
+ return -ENODEV;
+
+ for (i = 0; i < 5; i++) {
+ rc = wait_for_bulk_in_ready(dev);
+ if (rc <= 0) {
+ DEBUGP(5, dev, "wait_for_bulk_in_ready rc=%.2x\n", rc);
+ DEBUGP(2, dev, "<- cm4040_read (failed)\n");
+ if (rc == -ERESTARTSYS)
+ return rc;
+ return -EIO;
+ }
+ dev->r_buf[i] = xinb(iobase + REG_OFFSET_BULK_IN);
+#ifdef PCMCIA_DEBUG
+ if (pc_debug >= 6)
+ printk(KERN_DEBUG "%lu:%2x ", i, dev->r_buf[i]);
+ }
+ printk("\n");
+#else
+ }
+#endif
+
+ bytes_to_read = 5 + le32_to_cpu(*(__le32 *)&dev->r_buf[1]);
+
+ DEBUGP(6, dev, "BytesToRead=%lu\n", bytes_to_read);
+
+ min_bytes_to_read = min(count, bytes_to_read + 5);
+
+ DEBUGP(6, dev, "Min=%lu\n", min_bytes_to_read);
+
+ for (i = 0; i < (min_bytes_to_read-5); i++) {
+ rc = wait_for_bulk_in_ready(dev);
+ if (rc <= 0) {
+ DEBUGP(5, dev, "wait_for_bulk_in_ready rc=%.2x\n", rc);
+ DEBUGP(2, dev, "<- cm4040_read (failed)\n");
+ if (rc == -ERESTARTSYS)
+ return rc;
+ return -EIO;
+ }
+ dev->r_buf[i+5] = xinb(iobase + REG_OFFSET_BULK_IN);
+#ifdef PCMCIA_DEBUG
+ if (pc_debug >= 6)
+ printk(KERN_DEBUG "%lu:%2x ", i, dev->r_buf[i]);
+ }
+ printk("\n");
+#else
+ }
+#endif
+
+ *ppos = min_bytes_to_read;
+ if (copy_to_user(buf, dev->r_buf, min_bytes_to_read))
+ return -EFAULT;
+
+ rc = wait_for_bulk_in_ready(dev);
+ if (rc <= 0) {
+ DEBUGP(5, dev, "wait_for_bulk_in_ready rc=%.2x\n", rc);
+ DEBUGP(2, dev, "<- cm4040_read (failed)\n");
+ if (rc == -ERESTARTSYS)
+ return rc;
+ return -EIO;
+ }
+
+ rc = write_sync_reg(SCR_READER_TO_HOST_DONE, dev);
+ if (rc <= 0) {
+ DEBUGP(5, dev, "write_sync_reg c=%.2x\n", rc);
+ DEBUGP(2, dev, "<- cm4040_read (failed)\n");
+ if (rc == -ERESTARTSYS)
+ return rc;
+ else
+ return -EIO;
+ }
+
+ uc = xinb(iobase + REG_OFFSET_BULK_IN);
+
+ DEBUGP(2, dev, "<- cm4040_read (successfully)\n");
+ return min_bytes_to_read;
+}
+
+static ssize_t cm4040_write(struct file *filp, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct reader_dev *dev = filp->private_data;
+ int iobase = dev->link.io.BasePort1;
+ ssize_t rc;
+ int i;
+ unsigned int bytes_to_write;
+
+ DEBUGP(2, dev, "-> cm4040_write(%s,%d)\n", current->comm, current->pid);
+
+ if (count == 0) {
+ DEBUGP(2, dev, "<- cm4040_write empty read (successfully)\n");
+ return 0;
+ }
+
+ if (count < 5) {
+ DEBUGP(2, dev, "<- cm4040_write buffersize=%Zd < 5\n", count);
+ return -EIO;
+ }
+
+ if (filp->f_flags & O_NONBLOCK) {
+ DEBUGP(4, dev, "filep->f_flags O_NONBLOCK set\n");
+ DEBUGP(4, dev, "<- cm4040_write (failure)\n");
+ return -EAGAIN;
+ }
+
+ if ((dev->link.state & DEV_PRESENT) == 0)
+ return -ENODEV;
+
+ bytes_to_write = count;
+ if (copy_from_user(dev->s_buf, buf, bytes_to_write))
+ return -EFAULT;
+
+ switch (dev->s_buf[0]) {
+ case CMD_PC_TO_RDR_XFRBLOCK:
+ case CMD_PC_TO_RDR_SECURE:
+ case CMD_PC_TO_RDR_TEST_SECURE:
+ case CMD_PC_TO_RDR_OK_SECURE:
+ dev->timeout = CCID_DRIVER_BULK_DEFAULT_TIMEOUT;
+ break;
+
+ case CMD_PC_TO_RDR_ICCPOWERON:
+ dev->timeout = CCID_DRIVER_ASYNC_POWERUP_TIMEOUT;
+ break;
+
+ case CMD_PC_TO_RDR_GETSLOTSTATUS:
+ case CMD_PC_TO_RDR_ICCPOWEROFF:
+ case CMD_PC_TO_RDR_GETPARAMETERS:
+ case CMD_PC_TO_RDR_RESETPARAMETERS:
+ case CMD_PC_TO_RDR_SETPARAMETERS:
+ case CMD_PC_TO_RDR_ESCAPE:
+ case CMD_PC_TO_RDR_ICCCLOCK:
+ default:
+ dev->timeout = CCID_DRIVER_MINIMUM_TIMEOUT;
+ break;
+ }
+
+ rc = write_sync_reg(SCR_HOST_TO_READER_START, dev);
+ if (rc <= 0) {
+ DEBUGP(5, dev, "write_sync_reg c=%.2Zx\n", rc);
+ DEBUGP(2, dev, "<- cm4040_write (failed)\n");
+ if (rc == -ERESTARTSYS)
+ return rc;
+ else
+ return -EIO;
+ }
+
+ DEBUGP(4, dev, "start \n");
+
+ for (i = 0; i < bytes_to_write; i++) {
+ rc = wait_for_bulk_out_ready(dev);
+ if (rc <= 0) {
+ DEBUGP(5, dev, "wait_for_bulk_out_ready rc=%.2Zx\n",
+ rc);
+ DEBUGP(2, dev, "<- cm4040_write (failed)\n");
+ if (rc == -ERESTARTSYS)
+ return rc;
+ else
+ return -EIO;
+ }
+
+ xoutb(dev->s_buf[i],iobase + REG_OFFSET_BULK_OUT);
+ }
+ DEBUGP(4, dev, "end\n");
+
+ rc = write_sync_reg(SCR_HOST_TO_READER_DONE, dev);
+
+ if (rc <= 0) {
+ DEBUGP(5, dev, "write_sync_reg c=%.2Zx\n", rc);
+ DEBUGP(2, dev, "<- cm4040_write (failed)\n");
+ if (rc == -ERESTARTSYS)
+ return rc;
+ else
+ return -EIO;
+ }
+
+ DEBUGP(2, dev, "<- cm4040_write (successfully)\n");
+ return count;
+}
+
+static unsigned int cm4040_poll(struct file *filp, poll_table *wait)
+{
+ struct reader_dev *dev = filp->private_data;
+ unsigned int mask = 0;
+
+ poll_wait(filp, &dev->poll_wait, wait);
+
+ if (test_and_clear_bit(BS_READABLE, &dev->buffer_status))
+ mask |= POLLIN | POLLRDNORM;
+ if (test_and_clear_bit(BS_WRITABLE, &dev->buffer_status))
+ mask |= POLLOUT | POLLWRNORM;
+
+ DEBUGP(2, dev, "<- cm4040_poll(%u)\n", mask);
+
+ return mask;
+}
+
+static int cm4040_open(struct inode *inode, struct file *filp)
+{
+ struct reader_dev *dev;
+ dev_link_t *link;
+ int minor = iminor(inode);
+
+ if (minor >= CM_MAX_DEV)
+ return -ENODEV;
+
+ link = dev_table[minor];
+ if (link == NULL || !(DEV_OK(link)))
+ return -ENODEV;
+
+ if (link->open)
+ return -EBUSY;
+
+ dev = link->priv;
+ filp->private_data = dev;
+
+ if (filp->f_flags & O_NONBLOCK) {
+ DEBUGP(4, dev, "filep->f_flags O_NONBLOCK set\n");
+ return -EAGAIN;
+ }
+
+ link->open = 1;
+
+ dev->poll_timer.data = (unsigned long) dev;
+ mod_timer(&dev->poll_timer, jiffies + POLL_PERIOD);
+
+ DEBUGP(2, dev, "<- cm4040_open (successfully)\n");
+ return nonseekable_open(inode, filp);
+}
+
+static int cm4040_close(struct inode *inode, struct file *filp)
+{
+ struct reader_dev *dev = filp->private_data;
+ dev_link_t *link;
+ int minor = iminor(inode);
+
+ DEBUGP(2, dev, "-> cm4040_close(maj/min=%d.%d)\n", imajor(inode),
+ iminor(inode));
+
+ if (minor >= CM_MAX_DEV)
+ return -ENODEV;
+
+ link = dev_table[minor];
+ if (link == NULL)
+ return -ENODEV;
+
+ cm4040_stop_poll(dev);
+
+ link->open = 0;
+ wake_up(&dev->devq);
+
+ DEBUGP(2, dev, "<- cm4040_close\n");
+ return 0;
+}
+
+static void cm4040_reader_release(dev_link_t *link)
+{
+ struct reader_dev *dev = link->priv;
+
+ DEBUGP(3, dev, "-> cm4040_reader_release\n");
+ while (link->open) {
+ DEBUGP(3, dev, KERN_INFO MODULE_NAME ": delaying release "
+ "until process has terminated\n");
+ wait_event(dev->devq, (link->open == 0));
+ }
+ DEBUGP(3, dev, "<- cm4040_reader_release\n");
+ return;
+}
+
+static void reader_config(dev_link_t *link, int devno)
+{
+ client_handle_t handle;
+ struct reader_dev *dev;
+ tuple_t tuple;
+ cisparse_t parse;
+ config_info_t conf;
+ u_char buf[64];
+ int fail_fn, fail_rc;
+ int rc;
+
+ handle = link->handle;
+
+ tuple.DesiredTuple = CISTPL_CONFIG;
+ tuple.Attributes = 0;
+ tuple.TupleData = buf;
+ tuple.TupleDataMax = sizeof(buf);
+ tuple.TupleOffset = 0;
+
+ if ((fail_rc = pcmcia_get_first_tuple(handle, &tuple)) != CS_SUCCESS) {
+ fail_fn = GetFirstTuple;
+ goto cs_failed;
+ }
+ if ((fail_rc = pcmcia_get_tuple_data(handle, &tuple)) != CS_SUCCESS) {
+ fail_fn = GetTupleData;
+ goto cs_failed;
+ }
+ if ((fail_rc = pcmcia_parse_tuple(handle, &tuple, &parse))
+ != CS_SUCCESS) {
+ fail_fn = ParseTuple;
+ goto cs_failed;
+ }
+ if ((fail_rc = pcmcia_get_configuration_info(handle, &conf))
+ != CS_SUCCESS) {
+ fail_fn = GetConfigurationInfo;
+ goto cs_failed;
+ }
+
+ link->state |= DEV_CONFIG;
+ link->conf.ConfigBase = parse.config.base;
+ link->conf.Present = parse.config.rmask[0];
+ link->conf.Vcc = conf.Vcc;
+
+ link->io.BasePort2 = 0;
+ link->io.NumPorts2 = 0;
+ link->io.Attributes2 = 0;
+ tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+ for (rc = pcmcia_get_first_tuple(handle, &tuple);
+ rc == CS_SUCCESS;
+ rc = pcmcia_get_next_tuple(handle, &tuple)) {
+ rc = pcmcia_get_tuple_data(handle, &tuple);
+ if (rc != CS_SUCCESS)
+ continue;
+ rc = pcmcia_parse_tuple(handle, &tuple, &parse);
+ if (rc != CS_SUCCESS)
+ continue;
+
+ link->conf.ConfigIndex = parse.cftable_entry.index;
+
+ if (!parse.cftable_entry.io.nwin)
+ continue;
+
+ link->io.BasePort1 = parse.cftable_entry.io.win[0].base;
+ link->io.NumPorts1 = parse.cftable_entry.io.win[0].len;
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+ if (!(parse.cftable_entry.io.flags & CISTPL_IO_8BIT))
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
+ if (!(parse.cftable_entry.io.flags & CISTPL_IO_16BIT))
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+ link->io.IOAddrLines = parse.cftable_entry.io.flags
+ & CISTPL_IO_LINES_MASK;
+ rc = pcmcia_request_io(handle, &link->io);
+
+ dev_printk(KERN_INFO, &handle_to_dev(handle), "foo");
+ if (rc == CS_SUCCESS)
+ break;
+ else
+ dev_printk(KERN_INFO, &handle_to_dev(handle),
+ "pcmcia_request_io failed 0x%x\n", rc);
+ }
+ if (rc != CS_SUCCESS)
+ goto cs_release;
+
+ link->conf.IntType = 00000002;
+
+ if ((fail_rc = pcmcia_request_configuration(handle,&link->conf))
+ !=CS_SUCCESS) {
+ fail_fn = RequestConfiguration;
+ dev_printk(KERN_INFO, &handle_to_dev(handle),
+ "pcmcia_request_configuration failed 0x%x\n",
+ fail_rc);
+ goto cs_release;
+ }
+
+ dev = link->priv;
+ sprintf(dev->node.dev_name, DEVICE_NAME "%d", devno);
+ dev->node.major = major;
+ dev->node.minor = devno;
+ dev->node.next = NULL;
+ link->dev = &dev->node;
+ link->state &= ~DEV_CONFIG_PENDING;
+
+ DEBUGP(2, dev, "device " DEVICE_NAME "%d at 0x%.4x-0x%.4x\n", devno,
+ link->io.BasePort1, link->io.BasePort1+link->io.NumPorts1);
+ DEBUGP(2, dev, "<- reader_config (succ)\n");
+
+ return;
+
+cs_failed:
+ cs_error(handle, fail_fn, fail_rc);
+cs_release:
+ reader_release(link);
+ link->state &= ~DEV_CONFIG_PENDING;
+}
+
+static int reader_event(event_t event, int priority,
+ event_callback_args_t *args)
+{
+ dev_link_t *link;
+ struct reader_dev *dev;
+ int devno;
+
+ link = args->client_data;
+ dev = link->priv;
+ DEBUGP(3, dev, "-> reader_event\n");
+ for (devno = 0; devno < CM_MAX_DEV; devno++) {
+ if (dev_table[devno] == link)
+ break;
+ }
+ if (devno == CM_MAX_DEV)
+ return CS_BAD_ADAPTER;
+
+ switch (event) {
+ case CS_EVENT_CARD_INSERTION:
+ DEBUGP(5, dev, "CS_EVENT_CARD_INSERTION\n");
+ link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+ reader_config(link, devno);
+ break;
+ case CS_EVENT_CARD_REMOVAL:
+ DEBUGP(5, dev, "CS_EVENT_CARD_REMOVAL\n");
+ link->state &= ~DEV_PRESENT;
+ break;
+ case CS_EVENT_PM_SUSPEND:
+ DEBUGP(5, dev, "CS_EVENT_PM_SUSPEND "
+ "(fall-through to CS_EVENT_RESET_PHYSICAL)\n");
+ link->state |= DEV_SUSPEND;
+
+ case CS_EVENT_RESET_PHYSICAL:
+ DEBUGP(5, dev, "CS_EVENT_RESET_PHYSICAL\n");
+ if (link->state & DEV_CONFIG) {
+ DEBUGP(5, dev, "ReleaseConfiguration\n");
+ pcmcia_release_configuration(link->handle);
+ }
+ break;
+ case CS_EVENT_PM_RESUME:
+ DEBUGP(5, dev, "CS_EVENT_PM_RESUME "
+ "(fall-through to CS_EVENT_CARD_RESET)\n");
+ link->state &= ~DEV_SUSPEND;
+
+ case CS_EVENT_CARD_RESET:
+ DEBUGP(5, dev, "CS_EVENT_CARD_RESET\n");
+ if ((link->state & DEV_CONFIG)) {
+ DEBUGP(5, dev, "RequestConfiguration\n");
+ pcmcia_request_configuration(link->handle,
+ &link->conf);
+ }
+ break;
+ default:
+ DEBUGP(5, dev, "reader_event: unknown event %.2x\n",
+ event);
+ break;
+ }
+ DEBUGP(3, dev, "<- reader_event\n");
+ return CS_SUCCESS;
+}
+
+static void reader_release(dev_link_t *link)
+{
+ cm4040_reader_release(link->priv);
+ pcmcia_release_configuration(link->handle);
+ pcmcia_release_io(link->handle, &link->io);
+}
+
+static dev_link_t *reader_attach(void)
+{
+ struct reader_dev *dev;
+ dev_link_t *link;
+ client_reg_t client_reg;
+ int i;
+
+ for (i = 0; i < CM_MAX_DEV; i++) {
+ if (dev_table[i] == NULL)
+ break;
+ }
+
+ if (i == CM_MAX_DEV)
+ return NULL;
+
+ dev = kzalloc(sizeof(struct reader_dev), GFP_KERNEL);
+ if (dev == NULL)
+ return NULL;
+
+ dev->timeout = CCID_DRIVER_MINIMUM_TIMEOUT;
+ dev->buffer_status = 0;
+
+ link = &dev->link;
+ link->priv = dev;
+
+ link->conf.IntType = INT_MEMORY_AND_IO;
+ dev_table[i] = link;
+
+ client_reg.dev_info = &dev_info;
+ client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
+ client_reg.EventMask=
+ CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+ CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
+ CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+ client_reg.Version = 0x0210;
+ client_reg.event_callback_args.client_data = link;
+ i = pcmcia_register_client(&link->handle, &client_reg);
+ if (i) {
+ cs_error(link->handle, RegisterClient, i);
+ reader_detach(link);
+ return NULL;
+ }
+ init_waitqueue_head(&dev->devq);
+ init_waitqueue_head(&dev->poll_wait);
+ init_waitqueue_head(&dev->read_wait);
+ init_waitqueue_head(&dev->write_wait);
+ init_timer(&dev->poll_timer);
+ dev->poll_timer.function = &cm4040_do_poll;
+
+ return link;
+}
+
+static void reader_detach_by_devno(int devno, dev_link_t *link)
+{
+ struct reader_dev *dev = link->priv;
+
+ if (link->state & DEV_CONFIG) {
+ DEBUGP(5, dev, "device still configured (try to release it)\n");
+ reader_release(link);
+ }
+
+ pcmcia_deregister_client(link->handle);
+ dev_table[devno] = NULL;
+ DEBUGP(5, dev, "freeing dev=%p\n", dev);
+ cm4040_stop_poll(dev);
+ kfree(dev);
+ return;
+}
+
+static void reader_detach(dev_link_t *link)
+{
+ int i;
+
+ /* find device */
+ for (i = 0; i < CM_MAX_DEV; i++) {
+ if (dev_table[i] == link)
+ break;
+ }
+ if (i == CM_MAX_DEV)
+ return;
+
+ reader_detach_by_devno(i, link);
+ return;
+}
+
+static struct file_operations reader_fops = {
+ .owner = THIS_MODULE,
+ .read = cm4040_read,
+ .write = cm4040_write,
+ .open = cm4040_open,
+ .release = cm4040_close,
+ .poll = cm4040_poll,
+};
+
+static struct pcmcia_device_id cm4040_ids[] = {
+ PCMCIA_DEVICE_MANF_CARD(0x0223, 0x0200),
+ PCMCIA_DEVICE_PROD_ID12("OMNIKEY", "CardMan 4040",
+ 0xE32CDD8C, 0x8F23318B),
+ PCMCIA_DEVICE_NULL,
+};
+MODULE_DEVICE_TABLE(pcmcia, cm4040_ids);
+
+static struct pcmcia_driver reader_driver = {
+ .owner = THIS_MODULE,
+ .drv = {
+ .name = "cm4040_cs",
+ },
+ .attach = reader_attach,
+ .detach = reader_detach,
+ .event = reader_event,
+ .id_table = cm4040_ids,
+};
+
+static int __init cm4040_init(void)
+{
+ printk(KERN_INFO "%s\n", version);
+ pcmcia_register_driver(&reader_driver);
+ major = register_chrdev(0, DEVICE_NAME, &reader_fops);
+ if (major < 0) {
+ printk(KERN_WARNING MODULE_NAME
+ ": could not get major number\n");
+ return -1;
+ }
+ return 0;
+}
+
+static void __exit cm4040_exit(void)
+{
+ int i;
+
+ printk(KERN_INFO MODULE_NAME ": unloading\n");
+ pcmcia_unregister_driver(&reader_driver);
+ for (i = 0; i < CM_MAX_DEV; i++) {
+ if (dev_table[i])
+ reader_detach_by_devno(i, dev_table[i]);
+ }
+ unregister_chrdev(major, DEVICE_NAME);
+}
+
+module_init(cm4040_init);
+module_exit(cm4040_exit);
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/char/pcmcia/cm4040_cs.h b/drivers/char/pcmcia/cm4040_cs.h
new file mode 100644
index 00000000000..9a8b805c509
--- /dev/null
+++ b/drivers/char/pcmcia/cm4040_cs.h
@@ -0,0 +1,47 @@
+#ifndef _CM4040_H_
+#define _CM4040_H_
+
+#define CM_MAX_DEV 4
+
+#define DEVICE_NAME "cmx"
+#define MODULE_NAME "cm4040_cs"
+
+#define REG_OFFSET_BULK_OUT 0
+#define REG_OFFSET_BULK_IN 0
+#define REG_OFFSET_BUFFER_STATUS 1
+#define REG_OFFSET_SYNC_CONTROL 2
+
+#define BSR_BULK_IN_FULL 0x02
+#define BSR_BULK_OUT_FULL 0x01
+
+#define SCR_HOST_TO_READER_START 0x80
+#define SCR_ABORT 0x40
+#define SCR_EN_NOTIFY 0x20
+#define SCR_ACK_NOTIFY 0x10
+#define SCR_READER_TO_HOST_DONE 0x08
+#define SCR_HOST_TO_READER_DONE 0x04
+#define SCR_PULSE_INTERRUPT 0x02
+#define SCR_POWER_DOWN 0x01
+
+
+#define CMD_PC_TO_RDR_ICCPOWERON 0x62
+#define CMD_PC_TO_RDR_GETSLOTSTATUS 0x65
+#define CMD_PC_TO_RDR_ICCPOWEROFF 0x63
+#define CMD_PC_TO_RDR_SECURE 0x69
+#define CMD_PC_TO_RDR_GETPARAMETERS 0x6C
+#define CMD_PC_TO_RDR_RESETPARAMETERS 0x6D
+#define CMD_PC_TO_RDR_SETPARAMETERS 0x61
+#define CMD_PC_TO_RDR_XFRBLOCK 0x6F
+#define CMD_PC_TO_RDR_ESCAPE 0x6B
+#define CMD_PC_TO_RDR_ICCCLOCK 0x6E
+#define CMD_PC_TO_RDR_TEST_SECURE 0x74
+#define CMD_PC_TO_RDR_OK_SECURE 0x89
+
+
+#define CMD_RDR_TO_PC_SLOTSTATUS 0x81
+#define CMD_RDR_TO_PC_DATABLOCK 0x80
+#define CMD_RDR_TO_PC_PARAMETERS 0x82
+#define CMD_RDR_TO_PC_ESCAPE 0x83
+#define CMD_RDR_TO_PC_OK_SECURE 0x89
+
+#endif /* _CM4040_H_ */
diff --git a/drivers/char/synclink.c b/drivers/char/synclink.c
index 82c6abde68d..62aa0e534a6 100644
--- a/drivers/char/synclink.c
+++ b/drivers/char/synclink.c
@@ -1,7 +1,7 @@
/*
* linux/drivers/char/synclink.c
*
- * $Id: synclink.c,v 4.37 2005/09/07 13:13:19 paulkf Exp $
+ * $Id: synclink.c,v 4.38 2005/11/07 16:30:34 paulkf Exp $
*
* Device driver for Microgate SyncLink ISA and PCI
* high speed multiprotocol serial adapters.
@@ -101,6 +101,7 @@
#include <linux/termios.h>
#include <linux/workqueue.h>
#include <linux/hdlc.h>
+#include <linux/dma-mapping.h>
#ifdef CONFIG_HDLC_MODULE
#define CONFIG_HDLC 1
@@ -148,6 +149,7 @@ typedef struct _DMABUFFERENTRY
u32 link; /* 32-bit flat link to next buffer entry */
char *virt_addr; /* virtual address of data buffer */
u32 phys_entry; /* physical address of this buffer entry */
+ dma_addr_t dma_addr;
} DMABUFFERENTRY, *DMAPBUFFERENTRY;
/* The queue of BH actions to be performed */
@@ -233,7 +235,8 @@ struct mgsl_struct {
int ri_chkcount;
char *buffer_list; /* virtual address of Rx & Tx buffer lists */
- unsigned long buffer_list_phys;
+ u32 buffer_list_phys;
+ dma_addr_t buffer_list_dma_addr;
unsigned int rx_buffer_count; /* count of total allocated Rx buffers */
DMABUFFERENTRY *rx_buffer_list; /* list of receive buffer entries */
@@ -896,7 +899,7 @@ module_param_array(txdmabufs, int, NULL, 0);
module_param_array(txholdbufs, int, NULL, 0);
static char *driver_name = "SyncLink serial driver";
-static char *driver_version = "$Revision: 4.37 $";
+static char *driver_version = "$Revision: 4.38 $";
static int synclink_init_one (struct pci_dev *dev,
const struct pci_device_id *ent);
@@ -3811,11 +3814,10 @@ static int mgsl_alloc_buffer_list_memory( struct mgsl_struct *info )
/* inspect portions of the buffer while other portions are being */
/* updated by the adapter using Bus Master DMA. */
- info->buffer_list = kmalloc(BUFFERLISTSIZE, GFP_KERNEL | GFP_DMA);
- if ( info->buffer_list == NULL )
+ info->buffer_list = dma_alloc_coherent(NULL, BUFFERLISTSIZE, &info->buffer_list_dma_addr, GFP_KERNEL);
+ if (info->buffer_list == NULL)
return -ENOMEM;
-
- info->buffer_list_phys = isa_virt_to_bus(info->buffer_list);
+ info->buffer_list_phys = (u32)(info->buffer_list_dma_addr);
}
/* We got the memory for the buffer entry lists. */
@@ -3882,8 +3884,8 @@ static int mgsl_alloc_buffer_list_memory( struct mgsl_struct *info )
*/
static void mgsl_free_buffer_list_memory( struct mgsl_struct *info )
{
- if ( info->buffer_list && info->bus_type != MGSL_BUS_TYPE_PCI )
- kfree(info->buffer_list);
+ if (info->buffer_list && info->bus_type != MGSL_BUS_TYPE_PCI)
+ dma_free_coherent(NULL, BUFFERLISTSIZE, info->buffer_list, info->buffer_list_dma_addr);
info->buffer_list = NULL;
info->rx_buffer_list = NULL;
@@ -3910,7 +3912,7 @@ static void mgsl_free_buffer_list_memory( struct mgsl_struct *info )
static int mgsl_alloc_frame_memory(struct mgsl_struct *info,DMABUFFERENTRY *BufferList,int Buffercount)
{
int i;
- unsigned long phys_addr;
+ u32 phys_addr;
/* Allocate page sized buffers for the receive buffer list */
@@ -3922,11 +3924,10 @@ static int mgsl_alloc_frame_memory(struct mgsl_struct *info,DMABUFFERENTRY *Buff
info->last_mem_alloc += DMABUFFERSIZE;
} else {
/* ISA adapter uses system memory. */
- BufferList[i].virt_addr =
- kmalloc(DMABUFFERSIZE, GFP_KERNEL | GFP_DMA);
- if ( BufferList[i].virt_addr == NULL )
+ BufferList[i].virt_addr = dma_alloc_coherent(NULL, DMABUFFERSIZE, &BufferList[i].dma_addr, GFP_KERNEL);
+ if (BufferList[i].virt_addr == NULL)
return -ENOMEM;
- phys_addr = isa_virt_to_bus(BufferList[i].virt_addr);
+ phys_addr = (u32)(BufferList[i].dma_addr);
}
BufferList[i].phys_addr = phys_addr;
}
@@ -3957,7 +3958,7 @@ static void mgsl_free_frame_memory(struct mgsl_struct *info, DMABUFFERENTRY *Buf
for ( i = 0 ; i < Buffercount ; i++ ) {
if ( BufferList[i].virt_addr ) {
if ( info->bus_type != MGSL_BUS_TYPE_PCI )
- kfree(BufferList[i].virt_addr);
+ dma_free_coherent(NULL, DMABUFFERSIZE, BufferList[i].virt_addr, BufferList[i].dma_addr);
BufferList[i].virt_addr = NULL;
}
}
diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c
index 303f1588046..0b283d24673 100644
--- a/drivers/char/tpm/tpm.c
+++ b/drivers/char/tpm/tpm.c
@@ -43,6 +43,13 @@ static void user_reader_timeout(unsigned long ptr)
{
struct tpm_chip *chip = (struct tpm_chip *) ptr;
+ schedule_work(&chip->work);
+}
+
+static void timeout_work(void * ptr)
+{
+ struct tpm_chip *chip = ptr;
+
down(&chip->buffer_mutex);
atomic_set(&chip->data_pending, 0);
memset(chip->data_buffer, 0, TPM_BUFSIZE);
@@ -428,8 +435,7 @@ ssize_t tpm_read(struct file * file, char __user *buf,
ret_size = size;
down(&chip->buffer_mutex);
- if (copy_to_user
- ((void __user *) buf, chip->data_buffer, ret_size))
+ if (copy_to_user(buf, chip->data_buffer, ret_size))
ret_size = -EFAULT;
up(&chip->buffer_mutex);
}
@@ -460,7 +466,7 @@ void tpm_remove_hardware(struct device *dev)
sysfs_remove_group(&dev->kobj, chip->vendor->attr_group);
dev_mask[chip->dev_num / TPM_NUM_MASK_ENTRIES ] &=
- !(1 << (chip->dev_num % TPM_NUM_MASK_ENTRIES));
+ ~(1 << (chip->dev_num % TPM_NUM_MASK_ENTRIES));
kfree(chip);
@@ -528,6 +534,8 @@ int tpm_register_hardware(struct device *dev, struct tpm_vendor_specific *entry)
init_MUTEX(&chip->tpm_mutex);
INIT_LIST_HEAD(&chip->list);
+ INIT_WORK(&chip->work, timeout_work, chip);
+
init_timer(&chip->user_read_timer);
chip->user_read_timer.function = user_reader_timeout;
chip->user_read_timer.data = (unsigned long) chip;
diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
index 9293bcc4dc6..159882ca69d 100644
--- a/drivers/char/tpm/tpm.h
+++ b/drivers/char/tpm/tpm.h
@@ -50,7 +50,11 @@ struct tpm_vendor_specific {
u8 req_complete_mask;
u8 req_complete_val;
u8 req_canceled;
- u16 base; /* TPM base address */
+ void __iomem *iobase; /* ioremapped address */
+ unsigned long base; /* TPM base address */
+
+ int region_size;
+ int have_region;
int (*recv) (struct tpm_chip *, u8 *, size_t);
int (*send) (struct tpm_chip *, u8 *, size_t);
@@ -73,6 +77,7 @@ struct tpm_chip {
struct semaphore buffer_mutex;
struct timer_list user_read_timer; /* user needs to claim result */
+ struct work_struct work;
struct semaphore tpm_mutex; /* tpm is processing */
struct tpm_vendor_specific *vendor;
diff --git a/drivers/char/tpm/tpm_atmel.c b/drivers/char/tpm/tpm_atmel.c
index 32e01450c42..deb4b5c8091 100644
--- a/drivers/char/tpm/tpm_atmel.c
+++ b/drivers/char/tpm/tpm_atmel.c
@@ -19,14 +19,8 @@
*
*/
-#include <linux/platform_device.h>
#include "tpm.h"
-
-/* Atmel definitions */
-enum tpm_atmel_addr {
- TPM_ATMEL_BASE_ADDR_LO = 0x08,
- TPM_ATMEL_BASE_ADDR_HI = 0x09
-};
+#include "tpm_atmel.h"
/* write status bits */
enum tpm_atmel_write_status {
@@ -53,13 +47,13 @@ static int tpm_atml_recv(struct tpm_chip *chip, u8 *buf, size_t count)
return -EIO;
for (i = 0; i < 6; i++) {
- status = inb(chip->vendor->base + 1);
+ status = atmel_getb(chip, 1);
if ((status & ATML_STATUS_DATA_AVAIL) == 0) {
dev_err(chip->dev,
"error reading header\n");
return -EIO;
}
- *buf++ = inb(chip->vendor->base);
+ *buf++ = atmel_getb(chip, 0);
}
/* size of the data received */
@@ -70,7 +64,7 @@ static int tpm_atml_recv(struct tpm_chip *chip, u8 *buf, size_t count)
dev_err(chip->dev,
"Recv size(%d) less than available space\n", size);
for (; i < size; i++) { /* clear the waiting data anyway */
- status = inb(chip->vendor->base + 1);
+ status = atmel_getb(chip, 1);
if ((status & ATML_STATUS_DATA_AVAIL) == 0) {
dev_err(chip->dev,
"error reading data\n");
@@ -82,17 +76,17 @@ static int tpm_atml_recv(struct tpm_chip *chip, u8 *buf, size_t count)
/* read all the data available */
for (; i < size; i++) {
- status = inb(chip->vendor->base + 1);
+ status = atmel_getb(chip, 1);
if ((status & ATML_STATUS_DATA_AVAIL) == 0) {
dev_err(chip->dev,
"error reading data\n");
return -EIO;
}
- *buf++ = inb(chip->vendor->base);
+ *buf++ = atmel_getb(chip, 0);
}
/* make sure data available is gone */
- status = inb(chip->vendor->base + 1);
+ status = atmel_getb(chip, 1);
if (status & ATML_STATUS_DATA_AVAIL) {
dev_err(chip->dev, "data available is stuck\n");
return -EIO;
@@ -108,7 +102,7 @@ static int tpm_atml_send(struct tpm_chip *chip, u8 *buf, size_t count)
dev_dbg(chip->dev, "tpm_atml_send:\n");
for (i = 0; i < count; i++) {
dev_dbg(chip->dev, "%d 0x%x(%d)\n", i, buf[i], buf[i]);
- outb(buf[i], chip->vendor->base);
+ atmel_putb(buf[i], chip, 0);
}
return count;
@@ -116,12 +110,12 @@ static int tpm_atml_send(struct tpm_chip *chip, u8 *buf, size_t count)
static void tpm_atml_cancel(struct tpm_chip *chip)
{
- outb(ATML_STATUS_ABORT, chip->vendor->base + 1);
+ atmel_putb(ATML_STATUS_ABORT, chip, 1);
}
static u8 tpm_atml_status(struct tpm_chip *chip)
{
- return inb(chip->vendor->base + 1);
+ return atmel_getb(chip, 1);
}
static struct file_operations atmel_ops = {
@@ -162,12 +156,16 @@ static struct tpm_vendor_specific tpm_atmel = {
static struct platform_device *pdev;
-static void __devexit tpm_atml_remove(struct device *dev)
+static void atml_plat_remove(void)
{
- struct tpm_chip *chip = dev_get_drvdata(dev);
+ struct tpm_chip *chip = dev_get_drvdata(&pdev->dev);
+
if (chip) {
- release_region(chip->vendor->base, 2);
+ if (chip->vendor->have_region)
+ atmel_release_region(chip->vendor->base, chip->vendor->region_size);
+ atmel_put_base_addr(chip->vendor);
tpm_remove_hardware(chip->dev);
+ platform_device_unregister(pdev);
}
}
@@ -182,72 +180,40 @@ static struct device_driver atml_drv = {
static int __init init_atmel(void)
{
int rc = 0;
- int lo, hi;
driver_register(&atml_drv);
- lo = tpm_read_index(TPM_ADDR, TPM_ATMEL_BASE_ADDR_LO);
- hi = tpm_read_index(TPM_ADDR, TPM_ATMEL_BASE_ADDR_HI);
-
- tpm_atmel.base = (hi<<8)|lo;
-
- /* verify that it is an Atmel part */
- if (tpm_read_index(TPM_ADDR, 4) != 'A' || tpm_read_index(TPM_ADDR, 5) != 'T'
- || tpm_read_index(TPM_ADDR, 6) != 'M' || tpm_read_index(TPM_ADDR, 7) != 'L') {
- return -ENODEV;
- }
-
- /* verify chip version number is 1.1 */
- if ( (tpm_read_index(TPM_ADDR, 0x00) != 0x01) ||
- (tpm_read_index(TPM_ADDR, 0x01) != 0x01 ))
- return -ENODEV;
-
- pdev = kzalloc(sizeof(struct platform_device), GFP_KERNEL);
- if ( !pdev )
- return -ENOMEM;
-
- pdev->name = "tpm_atmel0";
- pdev->id = -1;
- pdev->num_resources = 0;
- pdev->dev.release = tpm_atml_remove;
- pdev->dev.driver = &atml_drv;
-
- if ((rc = platform_device_register(pdev)) < 0) {
- kfree(pdev);
- pdev = NULL;
- return rc;
+ if (atmel_get_base_addr(&tpm_atmel) != 0) {
+ rc = -ENODEV;
+ goto err_unreg_drv;
}
- if (request_region(tpm_atmel.base, 2, "tpm_atmel0") == NULL ) {
- platform_device_unregister(pdev);
- kfree(pdev);
- pdev = NULL;
- return -EBUSY;
- }
+ tpm_atmel.have_region = (atmel_request_region( tpm_atmel.base, tpm_atmel.region_size, "tpm_atmel0") == NULL) ? 0 : 1;
- if ((rc = tpm_register_hardware(&pdev->dev, &tpm_atmel)) < 0) {
- release_region(tpm_atmel.base, 2);
- platform_device_unregister(pdev);
- kfree(pdev);
- pdev = NULL;
- return rc;
+ if (IS_ERR(pdev = platform_device_register_simple("tpm_atmel", -1, NULL, 0 ))) {
+ rc = PTR_ERR(pdev);
+ goto err_rel_reg;
}
- dev_info(&pdev->dev, "Atmel TPM 1.1, Base Address: 0x%x\n",
- tpm_atmel.base);
+ if ((rc = tpm_register_hardware(&pdev->dev, &tpm_atmel)) < 0)
+ goto err_unreg_dev;
return 0;
+
+err_unreg_dev:
+ platform_device_unregister(pdev);
+err_rel_reg:
+ if (tpm_atmel.have_region)
+ atmel_release_region(tpm_atmel.base, tpm_atmel.region_size);
+ atmel_put_base_addr(&tpm_atmel);
+err_unreg_drv:
+ driver_unregister(&atml_drv);
+ return rc;
}
static void __exit cleanup_atmel(void)
{
- if (pdev) {
- tpm_atml_remove(&pdev->dev);
- platform_device_unregister(pdev);
- kfree(pdev);
- pdev = NULL;
- }
-
driver_unregister(&atml_drv);
+ atml_plat_remove();
}
module_init(init_atmel);
diff --git a/drivers/char/tpm/tpm_atmel.h b/drivers/char/tpm/tpm_atmel.h
new file mode 100644
index 00000000000..3c5b9a8d1c4
--- /dev/null
+++ b/drivers/char/tpm/tpm_atmel.h
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2005 IBM Corporation
+ *
+ * Authors:
+ * Kylene Hall <kjhall@us.ibm.com>
+ *
+ * Maintained by: <tpmdd_devel@lists.sourceforge.net>
+ *
+ * Device driver for TCG/TCPA TPM (trusted platform module).
+ * Specifications at www.trustedcomputinggroup.org
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ *
+ * These difference are required on power because the device must be
+ * discovered through the device tree and iomap must be used to get
+ * around the need for holes in the io_page_mask. This does not happen
+ * automatically because the tpm is not a normal pci device and lives
+ * under the root node.
+ *
+ */
+
+#ifdef CONFIG_PPC64
+#define atmel_getb(chip, offset) readb(chip->vendor->iobase + offset);
+#define atmel_putb(val, chip, offset) writeb(val, chip->vendor->iobase + offset)
+#define atmel_request_region request_mem_region
+#define atmel_release_region release_mem_region
+static inline void atmel_put_base_addr(struct tpm_vendor_specific *vendor)
+{
+ iounmap(vendor->iobase);
+}
+
+static int atmel_get_base_addr(struct tpm_vendor_specific *vendor)
+{
+ struct device_node *dn;
+ unsigned long address, size;
+ unsigned int *reg;
+ int reglen;
+ int naddrc;
+ int nsizec;
+
+ dn = of_find_node_by_name(NULL, "tpm");
+
+ if (!dn)
+ return 1;
+
+ if (!device_is_compatible(dn, "AT97SC3201")) {
+ of_node_put(dn);
+ return 1;
+ }
+
+ reg = (unsigned int *) get_property(dn, "reg", &reglen);
+ naddrc = prom_n_addr_cells(dn);
+ nsizec = prom_n_size_cells(dn);
+
+ of_node_put(dn);
+
+
+ if (naddrc == 2)
+ address = ((unsigned long) reg[0] << 32) | reg[1];
+ else
+ address = reg[0];
+
+ if (nsizec == 2)
+ size =
+ ((unsigned long) reg[naddrc] << 32) | reg[naddrc + 1];
+ else
+ size = reg[naddrc];
+
+ vendor->base = address;
+ vendor->region_size = size;
+ vendor->iobase = ioremap(address, size);
+ return 0;
+}
+#else
+#define atmel_getb(chip, offset) inb(chip->vendor->base + offset)
+#define atmel_putb(val, chip, offset) outb(val, chip->vendor->base + offset)
+#define atmel_request_region request_region
+#define atmel_release_region release_region
+/* Atmel definitions */
+enum tpm_atmel_addr {
+ TPM_ATMEL_BASE_ADDR_LO = 0x08,
+ TPM_ATMEL_BASE_ADDR_HI = 0x09
+};
+
+/* Verify this is a 1.1 Atmel TPM */
+static int atmel_verify_tpm11(void)
+{
+
+ /* verify that it is an Atmel part */
+ if (tpm_read_index(TPM_ADDR, 4) != 'A' ||
+ tpm_read_index(TPM_ADDR, 5) != 'T' ||
+ tpm_read_index(TPM_ADDR, 6) != 'M' ||
+ tpm_read_index(TPM_ADDR, 7) != 'L')
+ return 1;
+
+ /* query chip for its version number */
+ if (tpm_read_index(TPM_ADDR, 0x00) != 1 ||
+ tpm_read_index(TPM_ADDR, 0x01) != 1)
+ return 1;
+
+ /* This is an atmel supported part */
+ return 0;
+}
+
+static inline void atmel_put_base_addr(struct tpm_vendor_specific *vendor)
+{
+}
+
+/* Determine where to talk to device */
+static unsigned long atmel_get_base_addr(struct tpm_vendor_specific
+ *vendor)
+{
+ int lo, hi;
+
+ if (atmel_verify_tpm11() != 0)
+ return 1;
+
+ lo = tpm_read_index(TPM_ADDR, TPM_ATMEL_BASE_ADDR_LO);
+ hi = tpm_read_index(TPM_ADDR, TPM_ATMEL_BASE_ADDR_HI);
+
+ vendor->base = (hi << 8) | lo;
+ vendor->region_size = 2;
+
+ return 0;
+}
+#endif
diff --git a/drivers/char/watchdog/booke_wdt.c b/drivers/char/watchdog/booke_wdt.c
index abc30cca664..65830ec7104 100644
--- a/drivers/char/watchdog/booke_wdt.c
+++ b/drivers/char/watchdog/booke_wdt.c
@@ -4,7 +4,7 @@
* Watchdog timer for PowerPC Book-E systems
*
* Author: Matthew McClintock
- * Maintainer: Kumar Gala <kumar.gala@freescale.com>
+ * Maintainer: Kumar Gala <galak@kernel.crashing.org>
*
* Copyright 2005 Freescale Semiconductor Inc.
*
diff --git a/drivers/ide/pci/sl82c105.c b/drivers/ide/pci/sl82c105.c
index ea0806c82be..8a5c7b286b2 100644
--- a/drivers/ide/pci/sl82c105.c
+++ b/drivers/ide/pci/sl82c105.c
@@ -399,34 +399,6 @@ static unsigned int __devinit init_chipset_sl82c105(struct pci_dev *dev, const c
return dev->irq;
}
-static void __devinit init_dma_sl82c105(ide_hwif_t *hwif, unsigned long dma_base)
-{
- unsigned int rev;
- u8 dma_state;
-
- DBG(("init_dma_sl82c105(hwif: ide%d, dma_base: 0x%08x)\n", hwif->index, dma_base));
-
- hwif->autodma = 0;
-
- if (!dma_base)
- return;
-
- dma_state = hwif->INB(dma_base + 2);
- rev = sl82c105_bridge_revision(hwif->pci_dev);
- if (rev <= 5) {
- printk(" %s: Winbond 553 bridge revision %d, BM-DMA disabled\n",
- hwif->name, rev);
- dma_state &= ~0x60;
- } else {
- dma_state |= 0x60;
- if (!noautodma)
- hwif->autodma = 1;
- }
- hwif->OUTB(dma_state, dma_base + 2);
-
- ide_setup_dma(hwif, dma_base, 8);
-}
-
/*
* Initialise the chip
*/
@@ -434,6 +406,8 @@ static void __devinit init_dma_sl82c105(ide_hwif_t *hwif, unsigned long dma_base
static void __devinit init_hwif_sl82c105(ide_hwif_t *hwif)
{
struct pci_dev *dev = hwif->pci_dev;
+ unsigned int rev;
+ u8 dma_state;
u32 val;
DBG(("init_hwif_sl82c105(hwif: ide%d)\n", hwif->index));
@@ -455,33 +429,54 @@ static void __devinit init_hwif_sl82c105(ide_hwif_t *hwif)
pci_read_config_dword(dev, 0x40, &val);
*((u32 *)&hwif->hwif_data) = val;
+ hwif->atapi_dma = 0;
+ hwif->mwdma_mask = 0;
+ hwif->swdma_mask = 0;
+ hwif->autodma = 0;
+
if (!hwif->dma_base)
return;
- hwif->atapi_dma = 1;
- hwif->mwdma_mask = 0x07;
- hwif->swdma_mask = 0x07;
-
+ dma_state = hwif->INB(hwif->dma_base + 2) & ~0x60;
+ rev = sl82c105_bridge_revision(hwif->pci_dev);
+ if (rev <= 5) {
+ /*
+ * Never ever EVER under any circumstances enable
+ * DMA when the bridge is this old.
+ */
+ printk(" %s: Winbond 553 bridge revision %d, BM-DMA disabled\n",
+ hwif->name, rev);
+ } else {
#ifdef CONFIG_BLK_DEV_IDEDMA
- hwif->ide_dma_check = &sl82c105_check_drive;
- hwif->ide_dma_on = &sl82c105_ide_dma_on;
- hwif->ide_dma_off_quietly = &sl82c105_ide_dma_off_quietly;
- hwif->ide_dma_lostirq = &sl82c105_ide_dma_lost_irq;
- hwif->dma_start = &sl82c105_ide_dma_start;
- hwif->ide_dma_timeout = &sl82c105_ide_dma_timeout;
-
- if (!noautodma)
- hwif->autodma = 1;
- hwif->drives[0].autodma = hwif->autodma;
- hwif->drives[1].autodma = hwif->autodma;
+ dma_state |= 0x60;
+
+ hwif->atapi_dma = 1;
+ hwif->mwdma_mask = 0x07;
+ hwif->swdma_mask = 0x07;
+
+ hwif->ide_dma_check = &sl82c105_check_drive;
+ hwif->ide_dma_on = &sl82c105_ide_dma_on;
+ hwif->ide_dma_off_quietly = &sl82c105_ide_dma_off_quietly;
+ hwif->ide_dma_lostirq = &sl82c105_ide_dma_lost_irq;
+ hwif->dma_start = &sl82c105_ide_dma_start;
+ hwif->ide_dma_timeout = &sl82c105_ide_dma_timeout;
+
+ if (!noautodma)
+ hwif->autodma = 1;
+ hwif->drives[0].autodma = hwif->autodma;
+ hwif->drives[1].autodma = hwif->autodma;
+
+ if (hwif->mate)
+ hwif->serialized = hwif->mate->serialized = 1;
#endif /* CONFIG_BLK_DEV_IDEDMA */
+ }
+ hwif->OUTB(dma_state, hwif->dma_base + 2);
}
static ide_pci_device_t sl82c105_chipset __devinitdata = {
.name = "W82C105",
.init_chipset = init_chipset_sl82c105,
.init_hwif = init_hwif_sl82c105,
- .init_dma = init_dma_sl82c105,
.channels = 2,
.autodma = NOAUTODMA,
.enablebits = {{0x40,0x01,0x01}, {0x40,0x10,0x10}},
diff --git a/drivers/ide/ppc/pmac.c b/drivers/ide/ppc/pmac.c
index b3e65a65d20..136911a86e8 100644
--- a/drivers/ide/ppc/pmac.c
+++ b/drivers/ide/ppc/pmac.c
@@ -1667,11 +1667,16 @@ static struct macio_driver pmac_ide_macio_driver =
};
static struct pci_device_id pmac_ide_pci_match[] = {
- { PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_UNI_N_ATA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- { PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_IPID_ATA100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- { PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_K2_ATA100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ { PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_UNI_N_ATA,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ { PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_IPID_ATA100,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ { PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_K2_ATA100,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
{ PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_SH_ATA,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ { PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_IPID2_ATA,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
};
static struct pci_driver pmac_ide_pci_driver = {
diff --git a/drivers/isdn/hisax/hfc_usb.c b/drivers/isdn/hisax/hfc_usb.c
index 32bf0d5d0f9..f8457ef4882 100644
--- a/drivers/isdn/hisax/hfc_usb.c
+++ b/drivers/isdn/hisax/hfc_usb.c
@@ -71,78 +71,68 @@ typedef struct {
/****************************************/
static struct usb_device_id hfcusb_idtab[] = {
{
- .idVendor = 0x0959,
- .idProduct = 0x2bd0,
+ USB_DEVICE(0x0959, 0x2bd0),
.driver_info = (unsigned long) &((hfcsusb_vdata)
{LED_OFF, {4, 0, 2, 1},
"ISDN USB TA (Cologne Chip HFC-S USB based)"}),
},
{
- .idVendor = 0x0675,
- .idProduct = 0x1688,
+ USB_DEVICE(0x0675, 0x1688),
.driver_info = (unsigned long) &((hfcsusb_vdata)
{LED_SCHEME1, {1, 2, 0, 0},
"DrayTek miniVigor 128 USB ISDN TA"}),
},
{
- .idVendor = 0x07b0,
- .idProduct = 0x0007,
+ USB_DEVICE(0x07b0, 0x0007),
.driver_info = (unsigned long) &((hfcsusb_vdata)
{LED_SCHEME1, {0x80, -64, -32, -16},
"Billion tiny USB ISDN TA 128"}),
},
{
- .idVendor = 0x0742,
- .idProduct = 0x2008,
+ USB_DEVICE(0x0742, 0x2008),
.driver_info = (unsigned long) &((hfcsusb_vdata)
{LED_SCHEME1, {4, 0, 2, 1},
"Stollmann USB TA"}),
},
{
- .idVendor = 0x0742,
- .idProduct = 0x2009,
+ USB_DEVICE(0x0742, 0x2009),
.driver_info = (unsigned long) &((hfcsusb_vdata)
{LED_SCHEME1, {4, 0, 2, 1},
"Aceex USB ISDN TA"}),
},
{
- .idVendor = 0x0742,
- .idProduct = 0x200A,
+ USB_DEVICE(0x0742, 0x200A),
.driver_info = (unsigned long) &((hfcsusb_vdata)
{LED_SCHEME1, {4, 0, 2, 1},
"OEM USB ISDN TA"}),
},
{
- .idVendor = 0x08e3,
- .idProduct = 0x0301,
+ USB_DEVICE(0x08e3, 0x0301),
.driver_info = (unsigned long) &((hfcsusb_vdata)
{LED_SCHEME1, {2, 0, 1, 4},
"Olitec USB RNIS"}),
},
{
- .idVendor = 0x07fa,
- .idProduct = 0x0846,
+ USB_DEVICE(0x07fa, 0x0846),
.driver_info = (unsigned long) &((hfcsusb_vdata)
{LED_SCHEME1, {0x80, -64, -32, -16},
"Bewan Modem RNIS USB"}),
},
{
- .idVendor = 0x07fa,
- .idProduct = 0x0847,
+ USB_DEVICE(0x07fa, 0x0847),
.driver_info = (unsigned long) &((hfcsusb_vdata)
{LED_SCHEME1, {0x80, -64, -32, -16},
"Djinn Numeris USB"}),
},
{
- .idVendor = 0x07b0,
- .idProduct = 0x0006,
+ USB_DEVICE(0x07b0, 0x0006),
.driver_info = (unsigned long) &((hfcsusb_vdata)
{LED_SCHEME1, {0x80, -64, -32, -16},
"Twister ISDN TA"}),
},
+ { }
};
-
/***************************************************************/
/* structure defining input+output fifos (interrupt/bulk mode) */
/***************************************************************/
diff --git a/drivers/md/md.c b/drivers/md/md.c
index adf960d8a7c..f3fed662f32 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -3156,7 +3156,7 @@ static int md_ioctl(struct inode *inode, struct file *file,
if (cnt > 0 ) {
printk(KERN_WARNING
"md: %s(pid %d) used deprecated START_ARRAY ioctl. "
- "This will not be supported beyond 2.6\n",
+ "This will not be supported beyond July 2006\n",
current->comm, current->pid);
cnt--;
}
@@ -3437,10 +3437,19 @@ static int md_thread(void * arg)
allow_signal(SIGKILL);
while (!kthread_should_stop()) {
- wait_event_timeout(thread->wqueue,
- test_bit(THREAD_WAKEUP, &thread->flags)
- || kthread_should_stop(),
- thread->timeout);
+ /* We need to wait INTERRUPTIBLE so that
+ * we don't add to the load-average.
+ * That means we need to be sure no signals are
+ * pending
+ */
+ if (signal_pending(current))
+ flush_signals(current);
+
+ wait_event_interruptible_timeout
+ (thread->wqueue,
+ test_bit(THREAD_WAKEUP, &thread->flags)
+ || kthread_should_stop(),
+ thread->timeout);
try_to_freeze();
clear_bit(THREAD_WAKEUP, &thread->flags);
diff --git a/drivers/media/common/ir-common.c b/drivers/media/common/ir-common.c
index 4b71fd6f7ae..7972c73bc14 100644
--- a/drivers/media/common/ir-common.c
+++ b/drivers/media/common/ir-common.c
@@ -126,6 +126,66 @@ IR_KEYTAB_TYPE ir_codes_winfast[IR_KEYTAB_SIZE] = {
};
EXPORT_SYMBOL_GPL(ir_codes_winfast);
+IR_KEYTAB_TYPE ir_codes_pinnacle[IR_KEYTAB_SIZE] = {
+ [ 0x59 ] = KEY_MUTE,
+ [ 0x4a ] = KEY_POWER,
+
+ [ 0x18 ] = KEY_TEXT,
+ [ 0x26 ] = KEY_TV,
+ [ 0x3d ] = KEY_PRINT,
+
+ [ 0x48 ] = KEY_RED,
+ [ 0x04 ] = KEY_GREEN,
+ [ 0x11 ] = KEY_YELLOW,
+ [ 0x00 ] = KEY_BLUE,
+
+ [ 0x2d ] = KEY_VOLUMEUP,
+ [ 0x1e ] = KEY_VOLUMEDOWN,
+
+ [ 0x49 ] = KEY_MENU,
+
+ [ 0x16 ] = KEY_CHANNELUP,
+ [ 0x17 ] = KEY_CHANNELDOWN,
+
+ [ 0x20 ] = KEY_UP,
+ [ 0x21 ] = KEY_DOWN,
+ [ 0x22 ] = KEY_LEFT,
+ [ 0x23 ] = KEY_RIGHT,
+ [ 0x0d ] = KEY_SELECT,
+
+
+
+ [ 0x08 ] = KEY_BACK,
+ [ 0x07 ] = KEY_REFRESH,
+
+ [ 0x2f ] = KEY_ZOOM,
+ [ 0x29 ] = KEY_RECORD,
+
+ [ 0x4b ] = KEY_PAUSE,
+ [ 0x4d ] = KEY_REWIND,
+ [ 0x2e ] = KEY_PLAY,
+ [ 0x4e ] = KEY_FORWARD,
+ [ 0x53 ] = KEY_PREVIOUS,
+ [ 0x4c ] = KEY_STOP,
+ [ 0x54 ] = KEY_NEXT,
+
+ [ 0x69 ] = KEY_KP0,
+ [ 0x6a ] = KEY_KP1,
+ [ 0x6b ] = KEY_KP2,
+ [ 0x6c ] = KEY_KP3,
+ [ 0x6d ] = KEY_KP4,
+ [ 0x6e ] = KEY_KP5,
+ [ 0x6f ] = KEY_KP6,
+ [ 0x70 ] = KEY_KP7,
+ [ 0x71 ] = KEY_KP8,
+ [ 0x72 ] = KEY_KP9,
+
+ [ 0x74 ] = KEY_CHANNEL,
+ [ 0x0a ] = KEY_BACKSPACE,
+};
+
+EXPORT_SYMBOL_GPL(ir_codes_pinnacle);
+
/* empty keytable, can be used as placeholder for not-yet created keytables */
IR_KEYTAB_TYPE ir_codes_empty[IR_KEYTAB_SIZE] = {
[ 42 ] = KEY_COFFEE,
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index 199b0118885..1a3b3c7e5e9 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -333,4 +333,18 @@ config VIDEO_M32R_AR_M64278
Say Y here to use the Renesas M64278E-800 camera module,
which supports VGA(640x480 pixcels) size of images.
+config VIDEO_AUDIO_DECODER
+ tristate "Add support for additional audio chipsets"
+ depends on VIDEO_DEV && I2C && EXPERIMENTAL
+ ---help---
+ Say Y here to compile drivers for WM8775 and CS53L32A audio
+ decoders.
+
+config VIDEO_DECODER
+ tristate "Add support for additional video chipsets"
+ depends on VIDEO_DEV && I2C && EXPERIMENTAL
+ ---help---
+ Say Y here to compile drivers for SAA7115, SAA7127 and CX25840
+ video decoders.
+
endmenu
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index 3ac46599240..82060f9909d 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -36,10 +36,11 @@ 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_SAA7134) += ir-kbd-i2c.o saa7134/
obj-$(CONFIG_VIDEO_CX88) += cx88/
obj-$(CONFIG_VIDEO_EM28XX) += em28xx/
obj-$(CONFIG_VIDEO_EM28XX) += saa711x.o tvp5150.o
+obj-$(CONFIG_VIDEO_AUDIO_DECODER) += wm8775.o cs53l32a.o
obj-$(CONFIG_VIDEO_OVCAMCHIP) += ovcamchip/
obj-$(CONFIG_VIDEO_MXB) += saa7111.o tuner.o tda9840.o tea6415c.o tea6420.o mxb.o
obj-$(CONFIG_VIDEO_HEXIUM_ORION) += hexium_orion.o
@@ -55,4 +56,6 @@ obj-$(CONFIG_VIDEO_TVEEPROM) += tveeprom.o
obj-$(CONFIG_VIDEO_M32R_AR_M64278) += arv.o
+obj-$(CONFIG_VIDEO_DECODER) += saa7115.o cx25840/ saa7127.o
+
EXTRA_CFLAGS += -I$(srctree)/drivers/media/dvb/dvb-core
diff --git a/drivers/media/video/bttv-cards.c b/drivers/media/video/bttv-cards.c
index 3413bace443..e31ebb11c46 100644
--- a/drivers/media/video/bttv-cards.c
+++ b/drivers/media/video/bttv-cards.c
@@ -2133,7 +2133,10 @@ struct tvcard bttv_tvcards[] = {
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
.has_dvb = 1,
+ .has_remote = 1,
+ .gpiomask = 0x1b,
.no_gpioirq = 1,
+ .any_irq = 1,
},
[BTTV_BOARD_PV143] = {
/* Jorge Boncompte - DTI2 <jorge@dti2.net> */
@@ -2796,7 +2799,24 @@ struct tvcard bttv_tvcards[] = {
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
},
-
+ /* ---- card 0x8e ---------------------------------- */
+ [BTTV_BOARD_SABRENT_TVFM] = {
+ .name = "Sabrent TV-FM (bttv version)",
+ .video_inputs = 3,
+ .audio_inputs = 1,
+ .tuner = 0,
+ .svhs = 2,
+ .gpiomask = 0x108007,
+ .muxsel = { 2, 3, 1, 1},
+ .audiomux = { 100000, 100002, 100002, 100000},
+ .no_msp34xx = 1,
+ .no_tda9875 = 1,
+ .no_tda7432 = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_TNF_5335MF,
+ .tuner_addr = ADDR_UNSET,
+ .has_radio = 1,
+ },
};
static const unsigned int bttv_num_tvcards = ARRAY_SIZE(bttv_tvcards);
@@ -3367,6 +3387,8 @@ void __devinit bttv_init_card2(struct bttv *btv)
btv->has_remote=1;
if (!bttv_tvcards[btv->c.type].no_gpioirq)
btv->gpioirq=1;
+ if (bttv_tvcards[btv->c.type].any_irq)
+ btv->any_irq = 1;
if (bttv_tvcards[btv->c.type].audio_hook)
btv->audio_hook=bttv_tvcards[btv->c.type].audio_hook;
diff --git a/drivers/media/video/bttv-driver.c b/drivers/media/video/bttv-driver.c
index 0005741d551..709099f03bd 100644
--- a/drivers/media/video/bttv-driver.c
+++ b/drivers/media/video/bttv-driver.c
@@ -3667,6 +3667,10 @@ static irqreturn_t bttv_irq(int irq, void *dev_id, struct pt_regs * regs)
int handled = 0;
btv=(struct bttv *)dev_id;
+
+ if (btv->any_irq)
+ handled = bttv_any_irq(&btv->c);
+
count=0;
while (1) {
/* get/clear interrupt status bits */
diff --git a/drivers/media/video/bttv-gpio.c b/drivers/media/video/bttv-gpio.c
index 575ce8b8e71..616a5b7e510 100644
--- a/drivers/media/video/bttv-gpio.c
+++ b/drivers/media/video/bttv-gpio.c
@@ -113,6 +113,24 @@ void bttv_gpio_irq(struct bttv_core *core)
}
}
+int bttv_any_irq(struct bttv_core *core)
+{
+ struct bttv_sub_driver *drv;
+ struct bttv_sub_device *dev;
+ struct list_head *item;
+ int handled = 0;
+
+ list_for_each(item,&core->subs) {
+ dev = list_entry(item,struct bttv_sub_device,list);
+ drv = to_bttv_sub_drv(dev->dev.driver);
+ if (drv && drv->any_irq) {
+ if (drv->any_irq(dev))
+ handled = 1;
+ }
+ }
+ return handled;
+}
+
/* ----------------------------------------------------------------------- */
/* external: sub-driver register/unregister */
diff --git a/drivers/media/video/bttv.h b/drivers/media/video/bttv.h
index 124ea41dada..93298f06e01 100644
--- a/drivers/media/video/bttv.h
+++ b/drivers/media/video/bttv.h
@@ -162,6 +162,7 @@
#define BTTV_BOARD_PV_M4900 0x8b
#define BTTV_BOARD_OSPREY440 0x8c
#define BTTV_BOARD_ASOUND_SKYEYE 0x8d
+#define BTTV_BOARD_SABRENT_TVFM 0x8e
/* i2c address list */
#define I2C_TSA5522 0xc2
@@ -234,6 +235,7 @@ struct tvcard
unsigned int has_dvb:1;
unsigned int has_remote:1;
unsigned int no_gpioirq:1;
+ unsigned int any_irq:1;
/* other settings */
unsigned int pll;
@@ -333,6 +335,7 @@ struct bttv_sub_driver {
struct device_driver drv;
char wanted[BUS_ID_SIZE];
void (*gpio_irq)(struct bttv_sub_device *sub);
+ int (*any_irq)(struct bttv_sub_device *sub);
};
#define to_bttv_sub_drv(x) container_of((x), struct bttv_sub_driver, drv)
diff --git a/drivers/media/video/bttvp.h b/drivers/media/video/bttvp.h
index 386f546f7d1..3aa9c6e4fc3 100644
--- a/drivers/media/video/bttvp.h
+++ b/drivers/media/video/bttvp.h
@@ -208,6 +208,7 @@ extern struct bus_type bttv_sub_bus_type;
int bttv_sub_add_device(struct bttv_core *core, char *name);
int bttv_sub_del_devices(struct bttv_core *core);
void bttv_gpio_irq(struct bttv_core *core);
+int bttv_any_irq(struct bttv_core *core);
/* ---------------------------------------------------------- */
@@ -273,6 +274,7 @@ struct bttv {
struct bttv_pll_info pll;
int triton1;
int gpioirq;
+ int any_irq;
int use_i2c_hw;
/* old gpio interface */
diff --git a/drivers/media/video/cx25840/Makefile b/drivers/media/video/cx25840/Makefile
new file mode 100644
index 00000000000..543ebacdc9d
--- /dev/null
+++ b/drivers/media/video/cx25840/Makefile
@@ -0,0 +1,6 @@
+cx25840-objs := cx25840-core.o cx25840-audio.o cx25840-firmware.o \
+ cx25840-vbi.o
+
+obj-$(CONFIG_VIDEO_DECODER) += cx25840.o
+
+EXTRA_CFLAGS += -I$(src)/..
diff --git a/drivers/media/video/cx25840/cx25840-audio.c b/drivers/media/video/cx25840/cx25840-audio.c
new file mode 100644
index 00000000000..740908f8027
--- /dev/null
+++ b/drivers/media/video/cx25840/cx25840-audio.c
@@ -0,0 +1,368 @@
+/* cx25840 audio functions
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+
+#include <linux/videodev2.h>
+#include <linux/i2c.h>
+#include <media/audiochip.h>
+#include <media/v4l2-common.h>
+
+#include "cx25840.h"
+
+inline static int set_audclk_freq(struct i2c_client *client,
+ enum v4l2_audio_clock_freq freq)
+{
+ struct cx25840_state *state = i2c_get_clientdata(client);
+
+ /* assert soft reset */
+ cx25840_and_or(client, 0x810, ~0x1, 0x01);
+
+ /* common for all inputs and rates */
+ /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x10 */
+ cx25840_write(client, 0x127, 0x50);
+
+ switch (state->audio_input) {
+ case AUDIO_TUNER:
+ switch (freq) {
+ case V4L2_AUDCLK_32_KHZ:
+ /* VID_PLL and AUX_PLL */
+ cx25840_write4(client, 0x108, 0x0f040610);
+
+ /* AUX_PLL_FRAC */
+ cx25840_write4(client, 0x110, 0xee39bb01);
+
+ /* src3/4/6_ctl = 0x0801f77f */
+ cx25840_write4(client, 0x900, 0x7ff70108);
+ cx25840_write4(client, 0x904, 0x7ff70108);
+ cx25840_write4(client, 0x90c, 0x7ff70108);
+ break;
+
+ case V4L2_AUDCLK_441_KHZ:
+ /* VID_PLL and AUX_PLL */
+ cx25840_write4(client, 0x108, 0x0f040910);
+
+ /* AUX_PLL_FRAC */
+ cx25840_write4(client, 0x110, 0xd66bec00);
+
+ /* src3/4/6_ctl = 0x08016d59 */
+ cx25840_write4(client, 0x900, 0x596d0108);
+ cx25840_write4(client, 0x904, 0x596d0108);
+ cx25840_write4(client, 0x90c, 0x596d0108);
+ break;
+
+ case V4L2_AUDCLK_48_KHZ:
+ /* VID_PLL and AUX_PLL */
+ cx25840_write4(client, 0x108, 0x0f040a10);
+
+ /* AUX_PLL_FRAC */
+ cx25840_write4(client, 0x110, 0xe5d69800);
+
+ /* src3/4/6_ctl = 0x08014faa */
+ cx25840_write4(client, 0x900, 0xaa4f0108);
+ cx25840_write4(client, 0x904, 0xaa4f0108);
+ cx25840_write4(client, 0x90c, 0xaa4f0108);
+ break;
+ }
+ break;
+
+ case AUDIO_EXTERN_1:
+ case AUDIO_EXTERN_2:
+ case AUDIO_INTERN:
+ case AUDIO_RADIO:
+ switch (freq) {
+ case V4L2_AUDCLK_32_KHZ:
+ /* VID_PLL and AUX_PLL */
+ cx25840_write4(client, 0x108, 0x0f04081e);
+
+ /* AUX_PLL_FRAC */
+ cx25840_write4(client, 0x110, 0x69082a01);
+
+ /* src1_ctl = 0x08010000 */
+ cx25840_write4(client, 0x8f8, 0x00000108);
+
+ /* src3/4/6_ctl = 0x08020000 */
+ cx25840_write4(client, 0x900, 0x00000208);
+ cx25840_write4(client, 0x904, 0x00000208);
+ cx25840_write4(client, 0x90c, 0x00000208);
+
+ /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x14 */
+ cx25840_write(client, 0x127, 0x54);
+ break;
+
+ case V4L2_AUDCLK_441_KHZ:
+ /* VID_PLL and AUX_PLL */
+ cx25840_write4(client, 0x108, 0x0f040918);
+
+ /* AUX_PLL_FRAC */
+ cx25840_write4(client, 0x110, 0xd66bec00);
+
+ /* src1_ctl = 0x08010000 */
+ cx25840_write4(client, 0x8f8, 0xcd600108);
+
+ /* src3/4/6_ctl = 0x08020000 */
+ cx25840_write4(client, 0x900, 0x85730108);
+ cx25840_write4(client, 0x904, 0x85730108);
+ cx25840_write4(client, 0x90c, 0x85730108);
+ break;
+
+ case V4L2_AUDCLK_48_KHZ:
+ /* VID_PLL and AUX_PLL */
+ cx25840_write4(client, 0x108, 0x0f040a18);
+
+ /* AUX_PLL_FRAC */
+ cx25840_write4(client, 0x110, 0xe5d69800);
+
+ /* src1_ctl = 0x08010000 */
+ cx25840_write4(client, 0x8f8, 0x00800108);
+
+ /* src3/4/6_ctl = 0x08020000 */
+ cx25840_write4(client, 0x900, 0x55550108);
+ cx25840_write4(client, 0x904, 0x55550108);
+ cx25840_write4(client, 0x90c, 0x55550108);
+ break;
+ }
+ break;
+ }
+
+ /* deassert soft reset */
+ cx25840_and_or(client, 0x810, ~0x1, 0x00);
+
+ state->audclk_freq = freq;
+
+ return 0;
+}
+
+static int set_input(struct i2c_client *client, int audio_input)
+{
+ struct cx25840_state *state = i2c_get_clientdata(client);
+
+ cx25840_dbg("set audio input (%d)\n", audio_input);
+
+ /* stop microcontroller */
+ cx25840_and_or(client, 0x803, ~0x10, 0);
+
+ /* Mute everything to prevent the PFFT! */
+ cx25840_write(client, 0x8d3, 0x1f);
+
+ switch (audio_input) {
+ case AUDIO_TUNER:
+ /* Set Path1 to Analog Demod Main Channel */
+ cx25840_write4(client, 0x8d0, 0x7038061f);
+
+ /* When the microcontroller detects the
+ * audio format, it will unmute the lines */
+ cx25840_and_or(client, 0x803, ~0x10, 0x10);
+ break;
+
+ case AUDIO_EXTERN_1:
+ case AUDIO_EXTERN_2:
+ case AUDIO_INTERN:
+ case AUDIO_RADIO:
+ /* Set Path1 to Serial Audio Input */
+ cx25840_write4(client, 0x8d0, 0x12100101);
+
+ /* The microcontroller should not be started for the
+ * non-tuner inputs: autodetection is specific for
+ * TV audio. */
+ break;
+
+ default:
+ cx25840_dbg("Invalid audio input selection %d\n", audio_input);
+ return -EINVAL;
+ }
+
+ state->audio_input = audio_input;
+
+ return set_audclk_freq(client, state->audclk_freq);
+}
+
+inline static int get_volume(struct i2c_client *client)
+{
+ /* Volume runs +18dB to -96dB in 1/2dB steps
+ * change to fit the msp3400 -114dB to +12dB range */
+
+ /* check PATH1_VOLUME */
+ int vol = 228 - cx25840_read(client, 0x8d4);
+ vol = (vol / 2) + 23;
+ return vol << 9;
+}
+
+inline static void set_volume(struct i2c_client *client, int volume)
+{
+ /* First convert the volume to msp3400 values (0-127) */
+ int vol = volume >> 9;
+ /* now scale it up to cx25840 values
+ * -114dB to -96dB maps to 0
+ * this should be 19, but in my testing that was 4dB too loud */
+ if (vol <= 23) {
+ vol = 0;
+ } else {
+ vol -= 23;
+ }
+
+ /* PATH1_VOLUME */
+ cx25840_write(client, 0x8d4, 228 - (vol * 2));
+}
+
+inline static int get_bass(struct i2c_client *client)
+{
+ /* bass is 49 steps +12dB to -12dB */
+
+ /* check PATH1_EQ_BASS_VOL */
+ int bass = cx25840_read(client, 0x8d9) & 0x3f;
+ bass = (((48 - bass) * 0xffff) + 47) / 48;
+ return bass;
+}
+
+inline static void set_bass(struct i2c_client *client, int bass)
+{
+ /* PATH1_EQ_BASS_VOL */
+ cx25840_and_or(client, 0x8d9, ~0x3f, 48 - (bass * 48 / 0xffff));
+}
+
+inline static int get_treble(struct i2c_client *client)
+{
+ /* treble is 49 steps +12dB to -12dB */
+
+ /* check PATH1_EQ_TREBLE_VOL */
+ int treble = cx25840_read(client, 0x8db) & 0x3f;
+ treble = (((48 - treble) * 0xffff) + 47) / 48;
+ return treble;
+}
+
+inline static void set_treble(struct i2c_client *client, int treble)
+{
+ /* PATH1_EQ_TREBLE_VOL */
+ cx25840_and_or(client, 0x8db, ~0x3f, 48 - (treble * 48 / 0xffff));
+}
+
+inline static int get_balance(struct i2c_client *client)
+{
+ /* balance is 7 bit, 0 to -96dB */
+
+ /* check PATH1_BAL_LEVEL */
+ int balance = cx25840_read(client, 0x8d5) & 0x7f;
+ /* check PATH1_BAL_LEFT */
+ if ((cx25840_read(client, 0x8d5) & 0x80) == 0)
+ balance = 0x80 - balance;
+ else
+ balance = 0x80 + balance;
+ return balance << 8;
+}
+
+inline static void set_balance(struct i2c_client *client, int balance)
+{
+ int bal = balance >> 8;
+ if (bal > 0x80) {
+ /* PATH1_BAL_LEFT */
+ cx25840_and_or(client, 0x8d5, 0x7f, 0x80);
+ /* PATH1_BAL_LEVEL */
+ cx25840_and_or(client, 0x8d5, ~0x7f, bal & 0x7f);
+ } else {
+ /* PATH1_BAL_LEFT */
+ cx25840_and_or(client, 0x8d5, 0x7f, 0x00);
+ /* PATH1_BAL_LEVEL */
+ cx25840_and_or(client, 0x8d5, ~0x7f, 0x80 - bal);
+ }
+}
+
+inline static int get_mute(struct i2c_client *client)
+{
+ /* check SRC1_MUTE_EN */
+ return cx25840_read(client, 0x8d3) & 0x2 ? 1 : 0;
+}
+
+inline static void set_mute(struct i2c_client *client, int mute)
+{
+ struct cx25840_state *state = i2c_get_clientdata(client);
+
+ if (state->audio_input == AUDIO_TUNER) {
+ /* Must turn off microcontroller in order to mute sound.
+ * Not sure if this is the best method, but it does work.
+ * If the microcontroller is running, then it will undo any
+ * changes to the mute register. */
+ if (mute) {
+ /* disable microcontroller */
+ cx25840_and_or(client, 0x803, ~0x10, 0x00);
+ cx25840_write(client, 0x8d3, 0x1f);
+ } else {
+ /* enable microcontroller */
+ cx25840_and_or(client, 0x803, ~0x10, 0x10);
+ }
+ } else {
+ /* SRC1_MUTE_EN */
+ cx25840_and_or(client, 0x8d3, ~0x2, mute ? 0x02 : 0x00);
+ }
+}
+
+int cx25840_audio(struct i2c_client *client, unsigned int cmd, void *arg)
+{
+ struct v4l2_control *ctrl = arg;
+
+ switch (cmd) {
+ case AUDC_SET_INPUT:
+ return set_input(client, *(int *)arg);
+ case VIDIOC_INT_AUDIO_CLOCK_FREQ:
+ return set_audclk_freq(client, *(enum v4l2_audio_clock_freq *)arg);
+ case VIDIOC_G_CTRL:
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_VOLUME:
+ ctrl->value = get_volume(client);
+ break;
+ case V4L2_CID_AUDIO_BASS:
+ ctrl->value = get_bass(client);
+ break;
+ case V4L2_CID_AUDIO_TREBLE:
+ ctrl->value = get_treble(client);
+ break;
+ case V4L2_CID_AUDIO_BALANCE:
+ ctrl->value = get_balance(client);
+ break;
+ case V4L2_CID_AUDIO_MUTE:
+ ctrl->value = get_mute(client);
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case VIDIOC_S_CTRL:
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_VOLUME:
+ set_volume(client, ctrl->value);
+ break;
+ case V4L2_CID_AUDIO_BASS:
+ set_bass(client, ctrl->value);
+ break;
+ case V4L2_CID_AUDIO_TREBLE:
+ set_treble(client, ctrl->value);
+ break;
+ case V4L2_CID_AUDIO_BALANCE:
+ set_balance(client, ctrl->value);
+ break;
+ case V4L2_CID_AUDIO_MUTE:
+ set_mute(client, ctrl->value);
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c
new file mode 100644
index 00000000000..f6afeec499c
--- /dev/null
+++ b/drivers/media/video/cx25840/cx25840-core.c
@@ -0,0 +1,1020 @@
+/* cx25840 - Conexant CX25840 audio/video decoder driver
+ *
+ * Copyright (C) 2004 Ulf Eklund
+ *
+ * Based on the saa7115 driver and on the first verison of Chris Kennedy's
+ * cx25840 driver.
+ *
+ * Changes by Tyler Trafford <tatrafford@comcast.net>
+ * - cleanup/rewrite for V4L2 API (2005)
+ *
+ * VBI support by Hans Verkuil <hverkuil@xs4all.nl>.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+#include <linux/i2c.h>
+#include <media/audiochip.h>
+#include <media/v4l2-common.h>
+
+#include "cx25840.h"
+
+MODULE_DESCRIPTION("Conexant CX25840 audio/video decoder driver");
+MODULE_AUTHOR("Ulf Eklund, Chris Kennedy, Hans Verkuil, Tyler Trafford");
+MODULE_LICENSE("GPL");
+
+static unsigned short normal_i2c[] = { 0x88 >> 1, I2C_CLIENT_END };
+
+
+int cx25840_debug = 0;
+
+module_param(cx25840_debug, bool, 0644);
+
+MODULE_PARM_DESC(cx25840_debug, "Debugging messages [0=Off (default) 1=On]");
+
+I2C_CLIENT_INSMOD;
+
+/* ----------------------------------------------------------------------- */
+
+int cx25840_write(struct i2c_client *client, u16 addr, u8 value)
+{
+ u8 buffer[3];
+ buffer[0] = addr >> 8;
+ buffer[1] = addr & 0xff;
+ buffer[2] = value;
+ return i2c_master_send(client, buffer, 3);
+}
+
+int cx25840_write4(struct i2c_client *client, u16 addr, u32 value)
+{
+ u8 buffer[6];
+ buffer[0] = addr >> 8;
+ buffer[1] = addr & 0xff;
+ buffer[2] = value >> 24;
+ buffer[3] = (value >> 16) & 0xff;
+ buffer[4] = (value >> 8) & 0xff;
+ buffer[5] = value & 0xff;
+ return i2c_master_send(client, buffer, 6);
+}
+
+u8 cx25840_read(struct i2c_client * client, u16 addr)
+{
+ u8 buffer[2];
+ buffer[0] = addr >> 8;
+ buffer[1] = addr & 0xff;
+
+ if (i2c_master_send(client, buffer, 2) < 2)
+ return 0;
+
+ if (i2c_master_recv(client, buffer, 1) < 1)
+ return 0;
+
+ return buffer[0];
+}
+
+u32 cx25840_read4(struct i2c_client * client, u16 addr)
+{
+ u8 buffer[4];
+ buffer[0] = addr >> 8;
+ buffer[1] = addr & 0xff;
+
+ if (i2c_master_send(client, buffer, 2) < 2)
+ return 0;
+
+ if (i2c_master_recv(client, buffer, 4) < 4)
+ return 0;
+
+ return (buffer[0] << 24) | (buffer[1] << 16) |
+ (buffer[2] << 8) | buffer[3];
+}
+
+int cx25840_and_or(struct i2c_client *client, u16 addr, u8 and_mask,
+ u8 or_value)
+{
+ return cx25840_write(client, addr,
+ (cx25840_read(client, addr) & and_mask) |
+ or_value);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int set_input(struct i2c_client *, enum cx25840_input);
+static void input_change(struct i2c_client *);
+static void log_status(struct i2c_client *client);
+
+/* ----------------------------------------------------------------------- */
+
+static inline void init_dll1(struct i2c_client *client)
+{
+ /* This is the Hauppauge sequence used to
+ * initialize the Delay Lock Loop 1 (ADC DLL). */
+ cx25840_write(client, 0x159, 0x23);
+ cx25840_write(client, 0x15a, 0x87);
+ cx25840_write(client, 0x15b, 0x06);
+ cx25840_write(client, 0x159, 0xe1);
+ cx25840_write(client, 0x15a, 0x86);
+ cx25840_write(client, 0x159, 0xe0);
+ cx25840_write(client, 0x159, 0xe1);
+ cx25840_write(client, 0x15b, 0x10);
+}
+
+static inline void init_dll2(struct i2c_client *client)
+{
+ /* This is the Hauppauge sequence used to
+ * initialize the Delay Lock Loop 2 (ADC DLL). */
+ cx25840_write(client, 0x15d, 0xe3);
+ cx25840_write(client, 0x15e, 0x86);
+ cx25840_write(client, 0x15f, 0x06);
+ cx25840_write(client, 0x15d, 0xe1);
+ cx25840_write(client, 0x15d, 0xe0);
+ cx25840_write(client, 0x15d, 0xe1);
+}
+
+static void cx25840_initialize(struct i2c_client *client, int loadfw)
+{
+ struct cx25840_state *state = i2c_get_clientdata(client);
+
+ /* datasheet startup in numbered steps, refer to page 3-77 */
+ /* 2. */
+ cx25840_and_or(client, 0x803, ~0x10, 0x00);
+ /* The default of this register should be 4, but I get 0 instead.
+ * Set this register to 4 manually. */
+ cx25840_write(client, 0x000, 0x04);
+ /* 3. */
+ init_dll1(client);
+ init_dll2(client);
+ cx25840_write(client, 0x136, 0x0a);
+ /* 4. */
+ cx25840_write(client, 0x13c, 0x01);
+ cx25840_write(client, 0x13c, 0x00);
+ /* 5. */
+ if (loadfw)
+ cx25840_loadfw(client);
+ /* 6. */
+ cx25840_write(client, 0x115, 0x8c);
+ cx25840_write(client, 0x116, 0x07);
+ cx25840_write(client, 0x118, 0x02);
+ /* 7. */
+ cx25840_write(client, 0x4a5, 0x80);
+ cx25840_write(client, 0x4a5, 0x00);
+ cx25840_write(client, 0x402, 0x00);
+ /* 8. */
+ cx25840_write(client, 0x401, 0x18);
+ cx25840_write(client, 0x4a2, 0x10);
+ cx25840_write(client, 0x402, 0x04);
+ /* 10. */
+ cx25840_write(client, 0x8d3, 0x1f);
+ cx25840_write(client, 0x8e3, 0x03);
+
+ cx25840_vbi_setup(client);
+
+ /* trial and error says these are needed to get audio */
+ cx25840_write(client, 0x914, 0xa0);
+ cx25840_write(client, 0x918, 0xa0);
+ cx25840_write(client, 0x919, 0x01);
+
+ /* stereo prefered */
+ cx25840_write(client, 0x809, 0x04);
+ /* AC97 shift */
+ cx25840_write(client, 0x8cf, 0x0f);
+
+ /* (re)set video input */
+ set_input(client, state->input);
+ /* (re)set audio input */
+ cx25840_audio(client, AUDC_SET_INPUT, &state->audio_input);
+
+ /* start microcontroller */
+ cx25840_and_or(client, 0x803, ~0x10, 0x10);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void input_change(struct i2c_client *client)
+{
+ v4l2_std_id std = cx25840_get_v4lstd(client);
+
+ if (std & V4L2_STD_PAL) {
+ /* Follow tuner change procedure for PAL */
+ cx25840_write(client, 0x808, 0xff);
+ cx25840_write(client, 0x80b, 0x10);
+ } else if (std & V4L2_STD_SECAM) {
+ /* Select autodetect for SECAM */
+ cx25840_write(client, 0x808, 0xff);
+ cx25840_write(client, 0x80b, 0x10);
+ } else if (std & V4L2_STD_NTSC) {
+ /* NTSC */
+ cx25840_write(client, 0x808, 0xf6);
+ cx25840_write(client, 0x80b, 0x00);
+ }
+
+ if (cx25840_read(client, 0x803) & 0x10) {
+ /* restart audio decoder microcontroller */
+ cx25840_and_or(client, 0x803, ~0x10, 0x00);
+ cx25840_and_or(client, 0x803, ~0x10, 0x10);
+ }
+}
+
+static int set_input(struct i2c_client *client, enum cx25840_input input)
+{
+ struct cx25840_state *state = i2c_get_clientdata(client);
+
+ cx25840_dbg("decoder set input (%d)\n", input);
+
+ switch (input) {
+ case CX25840_TUNER:
+ cx25840_dbg("now setting Tuner input\n");
+
+ if (state->cardtype == CARDTYPE_PVR150) {
+ /* CH_SEL_ADC2=1 */
+ cx25840_and_or(client, 0x102, ~0x2, 0x02);
+ }
+
+ /* Video Input Control */
+ if (state->cardtype == CARDTYPE_PG600) {
+ cx25840_write(client, 0x103, 0x11);
+ } else {
+ cx25840_write(client, 0x103, 0x46);
+ }
+
+ /* INPUT_MODE=0 */
+ cx25840_and_or(client, 0x401, ~0x6, 0x00);
+ break;
+
+ case CX25840_COMPOSITE0:
+ case CX25840_COMPOSITE1:
+ cx25840_dbg("now setting Composite input\n");
+
+ /* Video Input Control */
+ if (state->cardtype == CARDTYPE_PG600) {
+ cx25840_write(client, 0x103, 0x00);
+ } else {
+ cx25840_write(client, 0x103, 0x02);
+ }
+
+ /* INPUT_MODE=0 */
+ cx25840_and_or(client, 0x401, ~0x6, 0x00);
+ break;
+
+ case CX25840_SVIDEO0:
+ case CX25840_SVIDEO1:
+ cx25840_dbg("now setting S-Video input\n");
+
+ /* CH_SEL_ADC2=0 */
+ cx25840_and_or(client, 0x102, ~0x2, 0x00);
+
+ /* Video Input Control */
+ if (state->cardtype == CARDTYPE_PG600) {
+ cx25840_write(client, 0x103, 0x02);
+ } else {
+ cx25840_write(client, 0x103, 0x10);
+ }
+
+ /* INPUT_MODE=1 */
+ cx25840_and_or(client, 0x401, ~0x6, 0x02);
+ break;
+
+ default:
+ cx25840_err("%d is not a valid input!\n", input);
+ return -EINVAL;
+ }
+
+ state->input = input;
+ input_change(client);
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int set_v4lstd(struct i2c_client *client, v4l2_std_id std)
+{
+ u8 fmt;
+
+ switch (std) {
+ /* zero is autodetect */
+ case 0: fmt = 0x0; break;
+ /* default ntsc to ntsc-m */
+ case V4L2_STD_NTSC:
+ case V4L2_STD_NTSC_M: fmt = 0x1; break;
+ case V4L2_STD_NTSC_M_JP: fmt = 0x2; break;
+ case V4L2_STD_NTSC_443: fmt = 0x3; break;
+ case V4L2_STD_PAL: fmt = 0x4; break;
+ case V4L2_STD_PAL_M: fmt = 0x5; break;
+ case V4L2_STD_PAL_N: fmt = 0x6; break;
+ case V4L2_STD_PAL_Nc: fmt = 0x7; break;
+ case V4L2_STD_PAL_60: fmt = 0x8; break;
+ case V4L2_STD_SECAM: fmt = 0xc; break;
+ default:
+ return -ERANGE;
+ }
+
+ cx25840_and_or(client, 0x400, ~0xf, fmt);
+ cx25840_vbi_setup(client);
+ return 0;
+}
+
+v4l2_std_id cx25840_get_v4lstd(struct i2c_client * client)
+{
+ /* check VID_FMT_SEL first */
+ u8 fmt = cx25840_read(client, 0x400) & 0xf;
+
+ if (!fmt) {
+ /* check AFD_FMT_STAT if set to autodetect */
+ fmt = cx25840_read(client, 0x40d) & 0xf;
+ }
+
+ switch (fmt) {
+ case 0x1: return V4L2_STD_NTSC_M;
+ case 0x2: return V4L2_STD_NTSC_M_JP;
+ case 0x3: return V4L2_STD_NTSC_443;
+ case 0x4: return V4L2_STD_PAL;
+ case 0x5: return V4L2_STD_PAL_M;
+ case 0x6: return V4L2_STD_PAL_N;
+ case 0x7: return V4L2_STD_PAL_Nc;
+ case 0x8: return V4L2_STD_PAL_60;
+ case 0xc: return V4L2_STD_SECAM;
+ default: return V4L2_STD_UNKNOWN;
+ }
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int set_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl)
+{
+ struct cx25840_state *state = i2c_get_clientdata(client);
+
+ switch (ctrl->id) {
+ case CX25840_CID_CARDTYPE:
+ switch (ctrl->value) {
+ case CARDTYPE_PVR150:
+ case CARDTYPE_PG600:
+ state->cardtype = ctrl->value;
+ break;
+ default:
+ return -ERANGE;
+ }
+
+ set_input(client, state->input);
+ break;
+
+ case V4L2_CID_BRIGHTNESS:
+ if (ctrl->value < 0 || ctrl->value > 255) {
+ cx25840_err("invalid brightness setting %d\n",
+ ctrl->value);
+ return -ERANGE;
+ }
+
+ cx25840_write(client, 0x414, ctrl->value - 128);
+ break;
+
+ case V4L2_CID_CONTRAST:
+ if (ctrl->value < 0 || ctrl->value > 127) {
+ cx25840_err("invalid contrast setting %d\n",
+ ctrl->value);
+ return -ERANGE;
+ }
+
+ cx25840_write(client, 0x415, ctrl->value << 1);
+ break;
+
+ case V4L2_CID_SATURATION:
+ if (ctrl->value < 0 || ctrl->value > 127) {
+ cx25840_err("invalid saturation setting %d\n",
+ ctrl->value);
+ return -ERANGE;
+ }
+
+ cx25840_write(client, 0x420, ctrl->value << 1);
+ cx25840_write(client, 0x421, ctrl->value << 1);
+ break;
+
+ case V4L2_CID_HUE:
+ if (ctrl->value < -127 || ctrl->value > 127) {
+ cx25840_err("invalid hue setting %d\n", ctrl->value);
+ return -ERANGE;
+ }
+
+ cx25840_write(client, 0x422, ctrl->value);
+ break;
+
+ case V4L2_CID_AUDIO_VOLUME:
+ case V4L2_CID_AUDIO_BASS:
+ case V4L2_CID_AUDIO_TREBLE:
+ case V4L2_CID_AUDIO_BALANCE:
+ case V4L2_CID_AUDIO_MUTE:
+ return cx25840_audio(client, VIDIOC_S_CTRL, ctrl);
+ }
+
+ return 0;
+}
+
+static int get_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl)
+{
+ struct cx25840_state *state = i2c_get_clientdata(client);
+
+ switch (ctrl->id) {
+ case CX25840_CID_CARDTYPE:
+ ctrl->value = state->cardtype;
+ break;
+ case V4L2_CID_BRIGHTNESS:
+ ctrl->value = cx25840_read(client, 0x414) + 128;
+ break;
+ case V4L2_CID_CONTRAST:
+ ctrl->value = cx25840_read(client, 0x415) >> 1;
+ break;
+ case V4L2_CID_SATURATION:
+ ctrl->value = cx25840_read(client, 0x420) >> 1;
+ break;
+ case V4L2_CID_HUE:
+ ctrl->value = cx25840_read(client, 0x422);
+ break;
+ case V4L2_CID_AUDIO_VOLUME:
+ case V4L2_CID_AUDIO_BASS:
+ case V4L2_CID_AUDIO_TREBLE:
+ case V4L2_CID_AUDIO_BALANCE:
+ case V4L2_CID_AUDIO_MUTE:
+ return cx25840_audio(client, VIDIOC_G_CTRL, ctrl);
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int get_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt)
+{
+ switch (fmt->type) {
+ case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+ return cx25840_vbi(client, VIDIOC_G_FMT, fmt);
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int set_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt)
+{
+ struct v4l2_pix_format *pix;
+ int HSC, VSC, Vsrc, Hsrc, filter, Vlines;
+ int is_pal = !(cx25840_get_v4lstd(client) & V4L2_STD_NTSC);
+
+ switch (fmt->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ pix = &(fmt->fmt.pix);
+
+ Vsrc = (cx25840_read(client, 0x476) & 0x3f) << 4;
+ Vsrc |= (cx25840_read(client, 0x475) & 0xf0) >> 4;
+
+ Hsrc = (cx25840_read(client, 0x472) & 0x3f) << 4;
+ Hsrc |= (cx25840_read(client, 0x471) & 0xf0) >> 4;
+
+ Vlines = pix->height + (is_pal ? 4 : 7);
+
+ if ((pix->width * 16 < Hsrc) || (Hsrc < pix->width) ||
+ (Vlines * 8 < Vsrc) || (Vsrc < Vlines)) {
+ cx25840_err("%dx%d is not a valid size!\n",
+ pix->width, pix->height);
+ return -ERANGE;
+ }
+
+ HSC = (Hsrc * (1 << 20)) / pix->width - (1 << 20);
+ VSC = (1 << 16) - (Vsrc * (1 << 9) / Vlines - (1 << 9));
+ VSC &= 0x1fff;
+
+ if (pix->width >= 385)
+ filter = 0;
+ else if (pix->width > 192)
+ filter = 1;
+ else if (pix->width > 96)
+ filter = 2;
+ else
+ filter = 3;
+
+ cx25840_dbg("decoder set size %dx%d -> scale %ux%u\n",
+ pix->width, pix->height, HSC, VSC);
+
+ /* HSCALE=HSC */
+ cx25840_write(client, 0x418, HSC & 0xff);
+ cx25840_write(client, 0x419, (HSC >> 8) & 0xff);
+ cx25840_write(client, 0x41a, HSC >> 16);
+ /* VSCALE=VSC */
+ cx25840_write(client, 0x41c, VSC & 0xff);
+ cx25840_write(client, 0x41d, VSC >> 8);
+ /* VS_INTRLACE=1 VFILT=filter */
+ cx25840_write(client, 0x41e, 0x8 | filter);
+ break;
+
+ case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+ return cx25840_vbi(client, VIDIOC_S_FMT, fmt);
+
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ return cx25840_vbi(client, VIDIOC_S_FMT, fmt);
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int cx25840_command(struct i2c_client *client, unsigned int cmd,
+ void *arg)
+{
+ struct cx25840_state *state = i2c_get_clientdata(client);
+ struct v4l2_tuner *vt = arg;
+ int result = 0;
+
+ switch (cmd) {
+ case 0:
+ break;
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ /* ioctls to allow direct access to the
+ * cx25840 registers for testing */
+ case VIDIOC_INT_G_REGISTER:
+ {
+ struct v4l2_register *reg = arg;
+
+ if (reg->i2c_id != I2C_DRIVERID_CX25840)
+ return -EINVAL;
+ reg->val = cx25840_read(client, reg->reg & 0x0fff);
+ break;
+ }
+
+ case VIDIOC_INT_S_REGISTER:
+ {
+ struct v4l2_register *reg = arg;
+
+ if (reg->i2c_id != I2C_DRIVERID_CX25840)
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ cx25840_write(client, reg->reg & 0x0fff, reg->val & 0xff);
+ break;
+ }
+#endif
+
+ case VIDIOC_INT_DECODE_VBI_LINE:
+ return cx25840_vbi(client, cmd, arg);
+
+ case VIDIOC_INT_AUDIO_CLOCK_FREQ:
+ case AUDC_SET_INPUT:
+ result = cx25840_audio(client, cmd, arg);
+ break;
+
+ case VIDIOC_STREAMON:
+ cx25840_dbg("enable output\n");
+ cx25840_write(client, 0x115, 0x8c);
+ cx25840_write(client, 0x116, 0x07);
+ break;
+
+ case VIDIOC_STREAMOFF:
+ cx25840_dbg("disable output\n");
+ cx25840_write(client, 0x115, 0x00);
+ cx25840_write(client, 0x116, 0x00);
+ break;
+
+ case VIDIOC_LOG_STATUS:
+ log_status(client);
+ break;
+
+ case VIDIOC_G_CTRL:
+ result = get_v4lctrl(client, (struct v4l2_control *)arg);
+ break;
+
+ case VIDIOC_S_CTRL:
+ result = set_v4lctrl(client, (struct v4l2_control *)arg);
+ break;
+
+ case VIDIOC_G_STD:
+ *(v4l2_std_id *)arg = cx25840_get_v4lstd(client);
+ break;
+
+ case VIDIOC_S_STD:
+ result = set_v4lstd(client, *(v4l2_std_id *)arg);
+ break;
+
+ case VIDIOC_G_INPUT:
+ *(int *)arg = state->input;
+ break;
+
+ case VIDIOC_S_INPUT:
+ result = set_input(client, *(int *)arg);
+ break;
+
+ case VIDIOC_S_FREQUENCY:
+ input_change(client);
+ break;
+
+ case VIDIOC_G_TUNER:
+ {
+ u8 mode = cx25840_read(client, 0x804);
+ u8 pref = cx25840_read(client, 0x809) & 0xf;
+ u8 vpres = cx25840_read(client, 0x80a) & 0x10;
+ int val = 0;
+
+ vt->capability |=
+ V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 |
+ V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP;
+
+ vt->signal = vpres ? 0xffff : 0x0;
+
+ /* get rxsubchans and audmode */
+ if ((mode & 0xf) == 1)
+ val |= V4L2_TUNER_SUB_STEREO;
+ else
+ val |= V4L2_TUNER_SUB_MONO;
+
+ if (mode == 2 || mode == 4)
+ val |= V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
+
+ if (mode & 0x10)
+ val |= V4L2_TUNER_SUB_SAP;
+
+ vt->rxsubchans = val;
+
+ switch (pref) {
+ case 0:
+ vt->audmode = V4L2_TUNER_MODE_MONO;
+ break;
+ case 1:
+ case 2:
+ vt->audmode = V4L2_TUNER_MODE_LANG2;
+ break;
+ case 4:
+ default:
+ vt->audmode = V4L2_TUNER_MODE_STEREO;
+ }
+ break;
+ }
+
+ case VIDIOC_S_TUNER:
+ switch (vt->audmode) {
+ case V4L2_TUNER_MODE_MONO:
+ case V4L2_TUNER_MODE_LANG1:
+ /* Force PREF_MODE to MONO */
+ cx25840_and_or(client, 0x809, ~0xf, 0x00);
+ break;
+ case V4L2_TUNER_MODE_STEREO:
+ /* Force PREF_MODE to STEREO */
+ cx25840_and_or(client, 0x809, ~0xf, 0x04);
+ break;
+ case V4L2_TUNER_MODE_LANG2:
+ /* Force PREF_MODE to LANG2 */
+ cx25840_and_or(client, 0x809, ~0xf, 0x01);
+ break;
+ }
+ break;
+
+ case VIDIOC_G_FMT:
+ result = get_v4lfmt(client, (struct v4l2_format *)arg);
+ break;
+
+ case VIDIOC_S_FMT:
+ result = set_v4lfmt(client, (struct v4l2_format *)arg);
+ break;
+
+ case VIDIOC_INT_RESET:
+ cx25840_initialize(client, 0);
+ break;
+
+ case VIDIOC_INT_G_CHIP_IDENT:
+ *(enum v4l2_chip_ident *)arg =
+ V4L2_IDENT_CX25840 + ((cx25840_read(client, 0x100) >> 4) & 0xf);
+ break;
+
+ default:
+ cx25840_err("invalid ioctl %x\n", cmd);
+ return -EINVAL;
+ }
+
+ return result;
+}
+
+/* ----------------------------------------------------------------------- */
+
+struct i2c_driver i2c_driver_cx25840;
+
+static int cx25840_detect_client(struct i2c_adapter *adapter, int address,
+ int kind)
+{
+ struct i2c_client *client;
+ struct cx25840_state *state;
+ u16 device_id;
+
+ /* Check if the adapter supports the needed features
+ * Not until kernel version 2.6.11 did the bit-algo
+ * correctly report that it would do an I2C-level xfer */
+ if (!i2c_check_functionality(adapter, I2C_FUNC_I2C))
+ return 0;
+
+ client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
+ if (client == 0)
+ return -ENOMEM;
+
+ memset(client, 0, sizeof(struct i2c_client));
+ client->addr = address;
+ client->adapter = adapter;
+ client->driver = &i2c_driver_cx25840;
+ client->flags = I2C_CLIENT_ALLOW_USE;
+ snprintf(client->name, sizeof(client->name) - 1, "cx25840");
+
+ cx25840_dbg("detecting cx25840 client on address 0x%x\n", address << 1);
+
+ device_id = cx25840_read(client, 0x101) << 8;
+ device_id |= cx25840_read(client, 0x100);
+
+ /* The high byte of the device ID should be
+ * 0x84 if chip is present */
+ if ((device_id & 0xff00) != 0x8400) {
+ cx25840_dbg("cx25840 not found\n");
+ kfree(client);
+ return 0;
+ }
+
+ cx25840_info("cx25%3x-2%x found @ 0x%x (%s)\n",
+ (device_id & 0xfff0) >> 4,
+ (device_id & 0x0f) < 3 ? (device_id & 0x0f) + 1 : 3,
+ address << 1, adapter->name);
+
+ state = kmalloc(sizeof(struct cx25840_state), GFP_KERNEL);
+ if (state == NULL) {
+ kfree(client);
+ return -ENOMEM;
+ }
+
+ i2c_set_clientdata(client, state);
+ memset(state, 0, sizeof(struct cx25840_state));
+ state->input = CX25840_TUNER;
+ state->audclk_freq = V4L2_AUDCLK_48_KHZ;
+ state->audio_input = AUDIO_TUNER;
+ state->cardtype = CARDTYPE_PVR150;
+
+ cx25840_initialize(client, 1);
+
+ i2c_attach_client(client);
+
+ return 0;
+}
+
+static int cx25840_attach_adapter(struct i2c_adapter *adapter)
+{
+#ifdef I2C_CLASS_TV_ANALOG
+ if (adapter->class & I2C_CLASS_TV_ANALOG)
+#else
+ if (adapter->id == I2C_HW_B_BT848)
+#endif
+ return i2c_probe(adapter, &addr_data, &cx25840_detect_client);
+ return 0;
+}
+
+static int cx25840_detach_client(struct i2c_client *client)
+{
+ struct cx25840_state *state = i2c_get_clientdata(client);
+ int err;
+
+ err = i2c_detach_client(client);
+ if (err) {
+ return err;
+ }
+
+ kfree(state);
+ kfree(client);
+
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+struct i2c_driver i2c_driver_cx25840 = {
+ .name = "cx25840",
+
+ .id = I2C_DRIVERID_CX25840,
+ .flags = I2C_DF_NOTIFY,
+
+ .attach_adapter = cx25840_attach_adapter,
+ .detach_client = cx25840_detach_client,
+ .command = cx25840_command,
+ .owner = THIS_MODULE,
+};
+
+
+static int __init m__init(void)
+{
+ return i2c_add_driver(&i2c_driver_cx25840);
+}
+
+static void __exit m__exit(void)
+{
+ i2c_del_driver(&i2c_driver_cx25840);
+}
+
+module_init(m__init);
+module_exit(m__exit);
+
+/* ----------------------------------------------------------------------- */
+
+static void log_status(struct i2c_client *client)
+{
+ static const char *const fmt_strs[] = {
+ "0x0",
+ "NTSC-M", "NTSC-J", "NTSC-4.43",
+ "PAL-BDGHI", "PAL-M", "PAL-N", "PAL-Nc", "PAL-60",
+ "0x9", "0xA", "0xB",
+ "SECAM",
+ "0xD", "0xE", "0xF"
+ };
+
+ struct cx25840_state *state = i2c_get_clientdata(client);
+ u8 microctrl_vidfmt = cx25840_read(client, 0x80a);
+ u8 vidfmt_sel = cx25840_read(client, 0x400) & 0xf;
+ u8 gen_stat1 = cx25840_read(client, 0x40d);
+ u8 download_ctl = cx25840_read(client, 0x803);
+ u8 mod_det_stat0 = cx25840_read(client, 0x804);
+ u8 mod_det_stat1 = cx25840_read(client, 0x805);
+ u8 audio_config = cx25840_read(client, 0x808);
+ u8 pref_mode = cx25840_read(client, 0x809);
+ u8 afc0 = cx25840_read(client, 0x80b);
+ u8 mute_ctl = cx25840_read(client, 0x8d3);
+ char *p;
+
+ cx25840_info("Video signal: %spresent\n",
+ (microctrl_vidfmt & 0x10) ? "" : "not ");
+ cx25840_info("Detected format: %s\n",
+ fmt_strs[gen_stat1 & 0xf]);
+
+ switch (mod_det_stat0) {
+ case 0x00: p = "mono"; break;
+ case 0x01: p = "stereo"; break;
+ case 0x02: p = "dual"; break;
+ case 0x04: p = "tri"; break;
+ case 0x10: p = "mono with SAP"; break;
+ case 0x11: p = "stereo with SAP"; break;
+ case 0x12: p = "dual with SAP"; break;
+ case 0x14: p = "tri with SAP"; break;
+ case 0xfe: p = "forced mode"; break;
+ default: p = "not defined";
+ }
+ cx25840_info("Detected audio mode: %s\n", p);
+
+ switch (mod_det_stat1) {
+ case 0x00: p = "not defined"; break;
+ case 0x01: p = "EIAJ"; break;
+ case 0x02: p = "A2-M"; break;
+ case 0x03: p = "A2-BG"; break;
+ case 0x04: p = "A2-DK1"; break;
+ case 0x05: p = "A2-DK2"; break;
+ case 0x06: p = "A2-DK3"; break;
+ case 0x07: p = "A1 (6.0 MHz FM Mono)"; break;
+ case 0x08: p = "AM-L"; break;
+ case 0x09: p = "NICAM-BG"; break;
+ case 0x0a: p = "NICAM-DK"; break;
+ case 0x0b: p = "NICAM-I"; break;
+ case 0x0c: p = "NICAM-L"; break;
+ case 0x0d: p = "BTSC/EIAJ/A2-M Mono (4.5 MHz FMMono)"; break;
+ case 0x0e: p = "IF FM Radio"; break;
+ case 0x0f: p = "BTSC"; break;
+ case 0x10: p = "high-deviation FM"; break;
+ case 0x11: p = "very high-deviation FM"; break;
+ case 0xfd: p = "unknown audio standard"; break;
+ case 0xfe: p = "forced audio standard"; break;
+ case 0xff: p = "no detected audio standard"; break;
+ default: p = "not defined";
+ }
+ cx25840_info("Detected audio standard: %s\n", p);
+ cx25840_info("Audio muted: %s\n",
+ (mute_ctl & 0x2) ? "yes" : "no");
+ cx25840_info("Audio microcontroller: %s\n",
+ (download_ctl & 0x10) ? "running" : "stopped");
+
+ switch (audio_config >> 4) {
+ case 0x00: p = "undefined"; break;
+ case 0x01: p = "BTSC"; break;
+ case 0x02: p = "EIAJ"; break;
+ case 0x03: p = "A2-M"; break;
+ case 0x04: p = "A2-BG"; break;
+ case 0x05: p = "A2-DK1"; break;
+ case 0x06: p = "A2-DK2"; break;
+ case 0x07: p = "A2-DK3"; break;
+ case 0x08: p = "A1 (6.0 MHz FM Mono)"; break;
+ case 0x09: p = "AM-L"; break;
+ case 0x0a: p = "NICAM-BG"; break;
+ case 0x0b: p = "NICAM-DK"; break;
+ case 0x0c: p = "NICAM-I"; break;
+ case 0x0d: p = "NICAM-L"; break;
+ case 0x0e: p = "FM radio"; break;
+ case 0x0f: p = "automatic detection"; break;
+ default: p = "undefined";
+ }
+ cx25840_info("Configured audio standard: %s\n", p);
+
+ if ((audio_config >> 4) < 0xF) {
+ switch (audio_config & 0xF) {
+ case 0x00: p = "MONO1 (LANGUAGE A/Mono L+R channel for BTSC, EIAJ, A2)"; break;
+ case 0x01: p = "MONO2 (LANGUAGE B)"; break;
+ case 0x02: p = "MONO3 (STEREO forced MONO)"; break;
+ case 0x03: p = "MONO4 (NICAM ANALOG-Language C/Analog Fallback)"; break;
+ case 0x04: p = "STEREO"; break;
+ case 0x05: p = "DUAL1 (AB)"; break;
+ case 0x06: p = "DUAL2 (AC) (FM)"; break;
+ case 0x07: p = "DUAL3 (BC) (FM)"; break;
+ case 0x08: p = "DUAL4 (AC) (AM)"; break;
+ case 0x09: p = "DUAL5 (BC) (AM)"; break;
+ case 0x0a: p = "SAP"; break;
+ default: p = "undefined";
+ }
+ cx25840_info("Configured audio mode: %s\n", p);
+ } else {
+ switch (audio_config & 0xF) {
+ case 0x00: p = "BG"; break;
+ case 0x01: p = "DK1"; break;
+ case 0x02: p = "DK2"; break;
+ case 0x03: p = "DK3"; break;
+ case 0x04: p = "I"; break;
+ case 0x05: p = "L"; break;
+ case 0x06: p = "BTSC"; break;
+ case 0x07: p = "EIAJ"; break;
+ case 0x08: p = "A2-M"; break;
+ case 0x09: p = "FM Radio"; break;
+ case 0x0f: p = "automatic standard and mode detection"; break;
+ default: p = "undefined";
+ }
+ cx25840_info("Configured audio system: %s\n", p);
+ }
+
+ cx25840_info("Specified standard: %s\n",
+ vidfmt_sel ? fmt_strs[vidfmt_sel] : "automatic detection");
+
+ switch (state->input) {
+ case CX25840_COMPOSITE0: p = "Composite 0"; break;
+ case CX25840_COMPOSITE1: p = "Composite 1"; break;
+ case CX25840_SVIDEO0: p = "S-Video 0"; break;
+ case CX25840_SVIDEO1: p = "S-Video 1"; break;
+ case CX25840_TUNER: p = "Tuner"; break;
+ }
+ cx25840_info("Specified input: %s\n", p);
+ cx25840_info("Specified audio input: %s\n",
+ state->audio_input == 0 ? "Tuner" : "External");
+
+ switch (state->audclk_freq) {
+ case V4L2_AUDCLK_441_KHZ: p = "44.1 kHz"; break;
+ case V4L2_AUDCLK_48_KHZ: p = "48 kHz"; break;
+ case V4L2_AUDCLK_32_KHZ: p = "32 kHz"; break;
+ default: p = "undefined";
+ }
+ cx25840_info("Specified audioclock freq: %s\n", p);
+
+ switch (pref_mode & 0xf) {
+ case 0: p = "mono/language A"; break;
+ case 1: p = "language B"; break;
+ case 2: p = "language C"; break;
+ case 3: p = "analog fallback"; break;
+ case 4: p = "stereo"; break;
+ case 5: p = "language AC"; break;
+ case 6: p = "language BC"; break;
+ case 7: p = "language AB"; break;
+ default: p = "undefined";
+ }
+ cx25840_info("Preferred audio mode: %s\n", p);
+
+ if ((audio_config & 0xf) == 0xf) {
+ switch ((afc0 >> 3) & 0x3) {
+ case 0: p = "system DK"; break;
+ case 1: p = "system L"; break;
+ case 2: p = "autodetect"; break;
+ default: p = "undefined";
+ }
+ cx25840_info("Selected 65 MHz format: %s\n", p);
+
+ switch (afc0 & 0x7) {
+ case 0: p = "chroma"; break;
+ case 1: p = "BTSC"; break;
+ case 2: p = "EIAJ"; break;
+ case 3: p = "A2-M"; break;
+ case 4: p = "autodetect"; break;
+ default: p = "undefined";
+ }
+ cx25840_info("Selected 45 MHz format: %s\n", p);
+ }
+}
diff --git a/drivers/media/video/cx25840/cx25840-firmware.c b/drivers/media/video/cx25840/cx25840-firmware.c
new file mode 100644
index 00000000000..df9d50a7554
--- /dev/null
+++ b/drivers/media/video/cx25840/cx25840-firmware.c
@@ -0,0 +1,167 @@
+/* cx25840 firmware functions
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <linux/firmware.h>
+#include <media/v4l2-common.h>
+
+#include "cx25840.h"
+
+#define FWFILE "v4l-cx25840.fw"
+#define FWSEND 1024
+
+#define FWDEV(x) &((x)->adapter->dev)
+
+static int fastfw = 1;
+static char *firmware = FWFILE;
+
+module_param(fastfw, bool, 0444);
+module_param(firmware, charp, 0444);
+
+MODULE_PARM_DESC(fastfw, "Load firmware fast [0=100MHz 1=333MHz (default)]");
+MODULE_PARM_DESC(firmware, "Firmware image [default: " FWFILE "]");
+
+static inline void set_i2c_delay(struct i2c_client *client, int delay)
+{
+ struct i2c_algo_bit_data *algod = client->adapter->algo_data;
+
+ /* We aren't guaranteed to be using algo_bit,
+ * so avoid the null pointer dereference
+ * and disable the 'fast firmware load' */
+ if (algod) {
+ algod->udelay = delay;
+ } else {
+ fastfw = 0;
+ }
+}
+
+static inline void start_fw_load(struct i2c_client *client)
+{
+ /* DL_ADDR_LB=0 DL_ADDR_HB=0 */
+ cx25840_write(client, 0x800, 0x00);
+ cx25840_write(client, 0x801, 0x00);
+ // DL_MAP=3 DL_AUTO_INC=0 DL_ENABLE=1
+ cx25840_write(client, 0x803, 0x0b);
+ /* AUTO_INC_DIS=1 */
+ cx25840_write(client, 0x000, 0x20);
+
+ if (fastfw)
+ set_i2c_delay(client, 3);
+}
+
+static inline void end_fw_load(struct i2c_client *client)
+{
+ if (fastfw)
+ set_i2c_delay(client, 10);
+
+ /* AUTO_INC_DIS=0 */
+ cx25840_write(client, 0x000, 0x00);
+ /* DL_ENABLE=0 */
+ cx25840_write(client, 0x803, 0x03);
+}
+
+static inline int check_fw_load(struct i2c_client *client, int size)
+{
+ /* DL_ADDR_HB DL_ADDR_LB */
+ int s = cx25840_read(client, 0x801) << 8;
+ s |= cx25840_read(client, 0x800);
+
+ if (size != s) {
+ cx25840_err("firmware %s load failed\n", firmware);
+ return -EINVAL;
+ }
+
+ cx25840_info("loaded %s firmware (%d bytes)\n", firmware, size);
+ return 0;
+}
+
+static inline int fw_write(struct i2c_client *client, u8 * data, int size)
+{
+ if (i2c_master_send(client, data, size) < size) {
+
+ if (fastfw) {
+ cx25840_err("333MHz i2c firmware load failed\n");
+ fastfw = 0;
+ set_i2c_delay(client, 10);
+
+ if (i2c_master_send(client, data, size) < size) {
+ cx25840_err
+ ("100MHz i2c firmware load failed\n");
+ return -ENOSYS;
+ }
+
+ } else {
+ cx25840_err("firmware load i2c failure\n");
+ return -ENOSYS;
+ }
+
+ }
+
+ return 0;
+}
+
+int cx25840_loadfw(struct i2c_client *client)
+{
+ const struct firmware *fw = NULL;
+ u8 buffer[4], *ptr;
+ int size, send, retval;
+
+ if (request_firmware(&fw, firmware, FWDEV(client)) != 0) {
+ cx25840_err("unable to open firmware %s\n", firmware);
+ return -EINVAL;
+ }
+
+ start_fw_load(client);
+
+ buffer[0] = 0x08;
+ buffer[1] = 0x02;
+ buffer[2] = fw->data[0];
+ buffer[3] = fw->data[1];
+ retval = fw_write(client, buffer, 4);
+
+ if (retval < 0) {
+ release_firmware(fw);
+ return retval;
+ }
+
+ size = fw->size - 2;
+ ptr = fw->data;
+ while (size > 0) {
+ ptr[0] = 0x08;
+ ptr[1] = 0x02;
+ send = size > (FWSEND - 2) ? FWSEND : size + 2;
+ retval = fw_write(client, ptr, send);
+
+ if (retval < 0) {
+ release_firmware(fw);
+ return retval;
+ }
+
+ size -= FWSEND - 2;
+ ptr += FWSEND - 2;
+ }
+
+ end_fw_load(client);
+
+ size = fw->size;
+ release_firmware(fw);
+
+ return check_fw_load(client, size);
+}
diff --git a/drivers/media/video/cx25840/cx25840-vbi.c b/drivers/media/video/cx25840/cx25840-vbi.c
new file mode 100644
index 00000000000..13ba4e15dde
--- /dev/null
+++ b/drivers/media/video/cx25840/cx25840-vbi.c
@@ -0,0 +1,315 @@
+/* cx25840 VBI functions
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+
+#include <linux/videodev2.h>
+#include <linux/i2c.h>
+#include <media/v4l2-common.h>
+
+#include "cx25840.h"
+
+static inline int odd_parity(u8 c)
+{
+ c ^= (c >> 4);
+ c ^= (c >> 2);
+ c ^= (c >> 1);
+
+ return c & 1;
+}
+
+static inline int decode_vps(u8 * dst, u8 * p)
+{
+ static const u8 biphase_tbl[] = {
+ 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
+ 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
+ 0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96,
+ 0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2,
+ 0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94,
+ 0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0,
+ 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
+ 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
+ 0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5,
+ 0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1,
+ 0xc3, 0x4b, 0x43, 0xc3, 0x87, 0x0f, 0x07, 0x87,
+ 0x83, 0x0b, 0x03, 0x83, 0xc3, 0x4b, 0x43, 0xc3,
+ 0xc1, 0x49, 0x41, 0xc1, 0x85, 0x0d, 0x05, 0x85,
+ 0x81, 0x09, 0x01, 0x81, 0xc1, 0x49, 0x41, 0xc1,
+ 0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5,
+ 0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1,
+ 0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4,
+ 0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0,
+ 0xc2, 0x4a, 0x42, 0xc2, 0x86, 0x0e, 0x06, 0x86,
+ 0x82, 0x0a, 0x02, 0x82, 0xc2, 0x4a, 0x42, 0xc2,
+ 0xc0, 0x48, 0x40, 0xc0, 0x84, 0x0c, 0x04, 0x84,
+ 0x80, 0x08, 0x00, 0x80, 0xc0, 0x48, 0x40, 0xc0,
+ 0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4,
+ 0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0,
+ 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
+ 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
+ 0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96,
+ 0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2,
+ 0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94,
+ 0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0,
+ 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
+ 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
+ };
+
+ u8 c, err = 0;
+ int i;
+
+ for (i = 0; i < 2 * 13; i += 2) {
+ err |= biphase_tbl[p[i]] | biphase_tbl[p[i + 1]];
+ c = (biphase_tbl[p[i + 1]] & 0xf) |
+ ((biphase_tbl[p[i]] & 0xf) << 4);
+ dst[i / 2] = c;
+ }
+
+ return err & 0xf0;
+}
+
+void cx25840_vbi_setup(struct i2c_client *client)
+{
+ v4l2_std_id std = cx25840_get_v4lstd(client);
+
+ if (std & ~V4L2_STD_NTSC) {
+ /* datasheet startup, step 8d */
+ cx25840_write(client, 0x49f, 0x11);
+
+ cx25840_write(client, 0x470, 0x84);
+ cx25840_write(client, 0x471, 0x00);
+ cx25840_write(client, 0x472, 0x2d);
+ cx25840_write(client, 0x473, 0x5d);
+
+ cx25840_write(client, 0x474, 0x24);
+ cx25840_write(client, 0x475, 0x40);
+ cx25840_write(client, 0x476, 0x24);
+ cx25840_write(client, 0x477, 0x28);
+
+ cx25840_write(client, 0x478, 0x1f);
+ cx25840_write(client, 0x479, 0x02);
+
+ if (std & V4L2_STD_SECAM) {
+ cx25840_write(client, 0x47a, 0x80);
+ cx25840_write(client, 0x47b, 0x00);
+ cx25840_write(client, 0x47c, 0x5f);
+ cx25840_write(client, 0x47d, 0x42);
+ } else {
+ cx25840_write(client, 0x47a, 0x90);
+ cx25840_write(client, 0x47b, 0x20);
+ cx25840_write(client, 0x47c, 0x63);
+ cx25840_write(client, 0x47d, 0x82);
+ }
+
+ cx25840_write(client, 0x47e, 0x0a);
+ cx25840_write(client, 0x47f, 0x01);
+ } else {
+ /* datasheet startup, step 8d */
+ cx25840_write(client, 0x49f, 0x14);
+
+ cx25840_write(client, 0x470, 0x7a);
+ cx25840_write(client, 0x471, 0x00);
+ cx25840_write(client, 0x472, 0x2d);
+ cx25840_write(client, 0x473, 0x5b);
+
+ cx25840_write(client, 0x474, 0x1a);
+ cx25840_write(client, 0x475, 0x70);
+ cx25840_write(client, 0x476, 0x1e);
+ cx25840_write(client, 0x477, 0x1e);
+
+ cx25840_write(client, 0x478, 0x1f);
+ cx25840_write(client, 0x479, 0x02);
+ cx25840_write(client, 0x47a, 0x50);
+ cx25840_write(client, 0x47b, 0x66);
+
+ cx25840_write(client, 0x47c, 0x1f);
+ cx25840_write(client, 0x47d, 0x7c);
+ cx25840_write(client, 0x47e, 0x08);
+ cx25840_write(client, 0x47f, 0x00);
+ }
+}
+
+int cx25840_vbi(struct i2c_client *client, unsigned int cmd, void *arg)
+{
+ struct v4l2_format *fmt;
+ struct v4l2_sliced_vbi_format *svbi;
+
+ switch (cmd) {
+ case VIDIOC_G_FMT:
+ {
+ static u16 lcr2vbi[] = {
+ 0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */
+ 0, V4L2_SLICED_WSS_625, 0, /* 4 */
+ V4L2_SLICED_CAPTION_525, /* 6 */
+ 0, 0, V4L2_SLICED_VPS, 0, 0, /* 9 */
+ 0, 0, 0, 0
+ };
+ int i;
+
+ fmt = arg;
+ if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
+ return -EINVAL;
+ svbi = &fmt->fmt.sliced;
+ memset(svbi, 0, sizeof(*svbi));
+ /* we're done if raw VBI is active */
+ if ((cx25840_read(client, 0x404) & 0x10) == 0)
+ break;
+
+ for (i = 7; i <= 23; i++) {
+ u8 v = cx25840_read(client, 0x424 + i - 7);
+
+ svbi->service_lines[0][i] = lcr2vbi[v >> 4];
+ svbi->service_lines[1][i] = lcr2vbi[v & 0xf];
+ svbi->service_set |=
+ svbi->service_lines[0][i] | svbi->service_lines[1][i];
+ }
+ break;
+ }
+
+ case VIDIOC_S_FMT:
+ {
+ int is_pal = !(cx25840_get_v4lstd(client) & V4L2_STD_NTSC);
+ int vbi_offset = is_pal ? 1 : 0;
+ int i, x;
+ u8 lcr[24];
+
+ fmt = arg;
+ if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
+ return -EINVAL;
+ svbi = &fmt->fmt.sliced;
+ if (svbi->service_set == 0) {
+ /* raw VBI */
+ memset(svbi, 0, sizeof(*svbi));
+
+ /* Setup VBI */
+ cx25840_vbi_setup(client);
+
+ /* VBI Offset */
+ cx25840_write(client, 0x47f, vbi_offset);
+ cx25840_write(client, 0x404, 0x2e);
+ break;
+ }
+
+ for (x = 0; x <= 23; x++)
+ lcr[x] = 0x00;
+
+ /* Setup VBI */
+ cx25840_vbi_setup(client);
+
+ /* Sliced VBI */
+ cx25840_write(client, 0x404, 0x36); /* Ancillery data */
+ cx25840_write(client, 0x406, 0x13);
+ cx25840_write(client, 0x47f, vbi_offset);
+
+ if (is_pal) {
+ for (i = 0; i <= 6; i++)
+ svbi->service_lines[0][i] =
+ svbi->service_lines[1][i] = 0;
+ } else {
+ for (i = 0; i <= 9; i++)
+ svbi->service_lines[0][i] =
+ svbi->service_lines[1][i] = 0;
+
+ for (i = 22; i <= 23; i++)
+ svbi->service_lines[0][i] =
+ svbi->service_lines[1][i] = 0;
+ }
+
+ for (i = 7; i <= 23; i++) {
+ for (x = 0; x <= 1; x++) {
+ switch (svbi->service_lines[1-x][i]) {
+ case V4L2_SLICED_TELETEXT_B:
+ lcr[i] |= 1 << (4 * x);
+ break;
+ case V4L2_SLICED_WSS_625:
+ lcr[i] |= 4 << (4 * x);
+ break;
+ case V4L2_SLICED_CAPTION_525:
+ lcr[i] |= 6 << (4 * x);
+ break;
+ case V4L2_SLICED_VPS:
+ lcr[i] |= 9 << (4 * x);
+ break;
+ }
+ }
+ }
+
+ for (x = 1, i = 0x424; i <= 0x434; i++, x++) {
+ cx25840_write(client, i, lcr[6 + x]);
+ }
+
+ cx25840_write(client, 0x43c, 0x16);
+
+ if (is_pal) {
+ cx25840_write(client, 0x474, 0x2a);
+ } else {
+ cx25840_write(client, 0x474, 0x1a + 6);
+ }
+ break;
+ }
+
+ case VIDIOC_INT_DECODE_VBI_LINE:
+ {
+ struct v4l2_decode_vbi_line *vbi = arg;
+ u8 *p = vbi->p;
+ int id1, id2, l, err = 0;
+
+ if (p[0] || p[1] != 0xff || p[2] != 0xff ||
+ (p[3] != 0x55 && p[3] != 0x91)) {
+ vbi->line = vbi->type = 0;
+ break;
+ }
+
+ p += 4;
+ id1 = p[-1];
+ id2 = p[0] & 0xf;
+ l = p[2] & 0x3f;
+ l += 5;
+ p += 4;
+
+ switch (id2) {
+ case 1:
+ id2 = V4L2_SLICED_TELETEXT_B;
+ break;
+ case 4:
+ id2 = V4L2_SLICED_WSS_625;
+ break;
+ case 6:
+ id2 = V4L2_SLICED_CAPTION_525;
+ err = !odd_parity(p[0]) || !odd_parity(p[1]);
+ break;
+ case 9:
+ id2 = V4L2_SLICED_VPS;
+ if (decode_vps(p, p) != 0) {
+ err = 1;
+ }
+ break;
+ default:
+ id2 = 0;
+ err = 1;
+ break;
+ }
+
+ vbi->type = err ? 0 : id2;
+ vbi->line = err ? 0 : l;
+ vbi->is_second_field = err ? 0 : (id1 == 0x55);
+ vbi->p = p;
+ break;
+ }
+ }
+
+ return 0;
+}
diff --git a/drivers/media/video/cx25840/cx25840.h b/drivers/media/video/cx25840/cx25840.h
new file mode 100644
index 00000000000..5c3f0639fb7
--- /dev/null
+++ b/drivers/media/video/cx25840/cx25840.h
@@ -0,0 +1,85 @@
+/* cx25840 API header
+ *
+ * Copyright (C) 2003-2004 Chris Kennedy
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _CX25840_H_
+#define _CX25840_H_
+
+
+#include <linux/videodev2.h>
+#include <linux/i2c.h>
+
+extern int cx25840_debug;
+
+#define cx25840_dbg(fmt, arg...) do { if (cx25840_debug) \
+ printk(KERN_INFO "%s debug %d-%04x: " fmt, client->driver->name, \
+ i2c_adapter_id(client->adapter), client->addr , ## arg); } while (0)
+
+#define cx25840_err(fmt, arg...) do { \
+ printk(KERN_ERR "%s %d-%04x: " fmt, client->driver->name, \
+ i2c_adapter_id(client->adapter), client->addr , ## arg); } while (0)
+
+#define cx25840_info(fmt, arg...) do { \
+ printk(KERN_INFO "%s %d-%04x: " fmt, client->driver->name, \
+ i2c_adapter_id(client->adapter), client->addr , ## arg); } while (0)
+
+#define CX25840_CID_CARDTYPE (V4L2_CID_PRIVATE_BASE+0)
+
+enum cx25840_cardtype {
+ CARDTYPE_PVR150,
+ CARDTYPE_PG600
+};
+
+enum cx25840_input {
+ CX25840_TUNER,
+ CX25840_COMPOSITE0,
+ CX25840_COMPOSITE1,
+ CX25840_SVIDEO0,
+ CX25840_SVIDEO1
+};
+
+struct cx25840_state {
+ enum cx25840_cardtype cardtype;
+ enum cx25840_input input;
+ int audio_input;
+ enum v4l2_audio_clock_freq audclk_freq;
+};
+
+/* ----------------------------------------------------------------------- */
+/* cx25850-core.c */
+int cx25840_write(struct i2c_client *client, u16 addr, u8 value);
+int cx25840_write4(struct i2c_client *client, u16 addr, u32 value);
+u8 cx25840_read(struct i2c_client *client, u16 addr);
+u32 cx25840_read4(struct i2c_client *client, u16 addr);
+int cx25840_and_or(struct i2c_client *client, u16 addr, u8 mask, u8 value);
+v4l2_std_id cx25840_get_v4lstd(struct i2c_client *client);
+
+/* ----------------------------------------------------------------------- */
+/* cx25850-firmware.c */
+int cx25840_loadfw(struct i2c_client *client);
+
+/* ----------------------------------------------------------------------- */
+/* cx25850-audio.c */
+int cx25840_audio(struct i2c_client *client, unsigned int cmd, void *arg);
+
+/* ----------------------------------------------------------------------- */
+/* cx25850-vbi.c */
+void cx25840_vbi_setup(struct i2c_client *client);
+int cx25840_vbi(struct i2c_client *client, unsigned int cmd, void *arg);
+
+#endif
diff --git a/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c
index 9cce91ec334..99ea955f598 100644
--- a/drivers/media/video/cx88/cx88-dvb.c
+++ b/drivers/media/video/cx88/cx88-dvb.c
@@ -439,9 +439,6 @@ static int dvb_register(struct cx8802_dev *dev)
/* Put the analog decoder in standby to keep it quiet */
cx88_call_i2c_clients (dev->core, TUNER_SET_STANDBY, NULL);
- /* Put the analog decoder in standby to keep it quiet */
- cx88_call_i2c_clients (dev->core, TUNER_SET_STANDBY, NULL);
-
/* register everything */
return videobuf_dvb_register(&dev->dvb, THIS_MODULE, dev);
}
diff --git a/drivers/media/video/em28xx/em28xx-input.c b/drivers/media/video/em28xx/em28xx-input.c
index 32c49df58ad..9b94f77d6fd 100644
--- a/drivers/media/video/em28xx/em28xx-input.c
+++ b/drivers/media/video/em28xx/em28xx-input.c
@@ -120,9 +120,6 @@ static int get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
if (buf[1]==0xff)
return 0;
- /* avoid fast reapeating */
- if (buf[1]==ir->old)
- return 0;
ir->old=buf[1];
/* Rearranges bits to the right order */
diff --git a/drivers/media/video/ir-kbd-gpio.c b/drivers/media/video/ir-kbd-gpio.c
index ed81934ef3c..5abfc0fbf6d 100644
--- a/drivers/media/video/ir-kbd-gpio.c
+++ b/drivers/media/video/ir-kbd-gpio.c
@@ -221,24 +221,99 @@ static IR_KEYTAB_TYPE ir_codes_conceptronic[IR_KEYTAB_SIZE] = {
[ 24 ] = KEY_MUTE // mute/unmute
};
+static IR_KEYTAB_TYPE ir_codes_nebula[IR_KEYTAB_SIZE] = {
+ [0x00] = KEY_KP0,
+ [0x01] = KEY_KP1,
+ [0x02] = KEY_KP2,
+ [0x03] = KEY_KP3,
+ [0x04] = KEY_KP4,
+ [0x05] = KEY_KP5,
+ [0x06] = KEY_KP6,
+ [0x07] = KEY_KP7,
+ [0x08] = KEY_KP8,
+ [0x09] = KEY_KP9,
+ [0x0a] = KEY_TV,
+ [0x0b] = KEY_AUX,
+ [0x0c] = KEY_DVD,
+ [0x0d] = KEY_POWER,
+ [0x0e] = KEY_MHP, /* labelled 'Picture' */
+ [0x0f] = KEY_AUDIO,
+ [0x10] = KEY_INFO,
+ [0x11] = KEY_F13, /* 16:9 */
+ [0x12] = KEY_F14, /* 14:9 */
+ [0x13] = KEY_EPG,
+ [0x14] = KEY_EXIT,
+ [0x15] = KEY_MENU,
+ [0x16] = KEY_UP,
+ [0x17] = KEY_DOWN,
+ [0x18] = KEY_LEFT,
+ [0x19] = KEY_RIGHT,
+ [0x1a] = KEY_ENTER,
+ [0x1b] = KEY_CHANNELUP,
+ [0x1c] = KEY_CHANNELDOWN,
+ [0x1d] = KEY_VOLUMEUP,
+ [0x1e] = KEY_VOLUMEDOWN,
+ [0x1f] = KEY_RED,
+ [0x20] = KEY_GREEN,
+ [0x21] = KEY_YELLOW,
+ [0x22] = KEY_BLUE,
+ [0x23] = KEY_SUBTITLE,
+ [0x24] = KEY_F15, /* AD */
+ [0x25] = KEY_TEXT,
+ [0x26] = KEY_MUTE,
+ [0x27] = KEY_REWIND,
+ [0x28] = KEY_STOP,
+ [0x29] = KEY_PLAY,
+ [0x2a] = KEY_FASTFORWARD,
+ [0x2b] = KEY_F16, /* chapter */
+ [0x2c] = KEY_PAUSE,
+ [0x2d] = KEY_PLAY,
+ [0x2e] = KEY_RECORD,
+ [0x2f] = KEY_F17, /* picture in picture */
+ [0x30] = KEY_KPPLUS, /* zoom in */
+ [0x31] = KEY_KPMINUS, /* zoom out */
+ [0x32] = KEY_F18, /* capture */
+ [0x33] = KEY_F19, /* web */
+ [0x34] = KEY_EMAIL,
+ [0x35] = KEY_PHONE,
+ [0x36] = KEY_PC
+};
+
struct IR {
struct bttv_sub_device *sub;
struct input_dev *input;
struct ir_input_state ir;
char name[32];
char phys[32];
+
+ /* Usual gpio signalling */
+
u32 mask_keycode;
u32 mask_keydown;
u32 mask_keyup;
-
- int polling;
+ u32 polling;
u32 last_gpio;
struct work_struct work;
struct timer_list timer;
+
+ /* RC5 gpio */
+
+ u32 rc5_gpio;
+ struct timer_list timer_end; /* timer_end for code completion */
+ struct timer_list timer_keyup; /* timer_end for key release */
+ u32 last_rc5; /* last good rc5 code */
+ u32 last_bit; /* last raw bit seen */
+ u32 code; /* raw code under construction */
+ struct timeval base_time; /* time of last seen code */
+ int active; /* building raw code */
};
static int debug;
module_param(debug, int, 0644); /* debug level (0,1,2) */
+static int repeat_delay = 500;
+module_param(repeat_delay, int, 0644);
+static int repeat_period = 33;
+module_param(repeat_period, int, 0644);
#define DEVNAME "ir-kbd-gpio"
#define dprintk(fmt, arg...) if (debug) \
@@ -254,7 +329,7 @@ static struct bttv_sub_driver driver = {
.probe = ir_probe,
.remove = ir_remove,
},
- .gpio_irq = ir_irq,
+ .gpio_irq = ir_irq,
};
/* ---------------------------------------------------------------------- */
@@ -327,6 +402,173 @@ static void ir_work(void *data)
mod_timer(&ir->timer, timeout);
}
+/* ---------------------------------------------------------------*/
+
+static int rc5_remote_gap = 885;
+module_param(rc5_remote_gap, int, 0644);
+static int rc5_key_timeout = 200;
+module_param(rc5_key_timeout, int, 0644);
+
+#define RC5_START(x) (((x)>>12)&3)
+#define RC5_TOGGLE(x) (((x)>>11)&1)
+#define RC5_ADDR(x) (((x)>>6)&31)
+#define RC5_INSTR(x) ((x)&63)
+
+/* decode raw bit pattern to RC5 code */
+static u32 rc5_decode(unsigned int code)
+{
+ unsigned int org_code = code;
+ unsigned int pair;
+ unsigned int rc5 = 0;
+ int i;
+
+ code = (code << 1) | 1;
+ for (i = 0; i < 14; ++i) {
+ pair = code & 0x3;
+ code >>= 2;
+
+ rc5 <<= 1;
+ switch (pair) {
+ case 0:
+ case 2:
+ break;
+ case 1:
+ rc5 |= 1;
+ break;
+ case 3:
+ dprintk("bad code: %x\n", org_code);
+ return 0;
+ }
+ }
+ dprintk("code=%x, rc5=%x, start=%x, toggle=%x, address=%x, "
+ "instr=%x\n", rc5, org_code, RC5_START(rc5),
+ RC5_TOGGLE(rc5), RC5_ADDR(rc5), RC5_INSTR(rc5));
+ return rc5;
+}
+
+static int ir_rc5_irq(struct bttv_sub_device *sub)
+{
+ struct IR *ir = dev_get_drvdata(&sub->dev);
+ struct timeval tv;
+ u32 gpio;
+ u32 gap;
+ unsigned long current_jiffies, timeout;
+
+ /* read gpio port */
+ gpio = bttv_gpio_read(ir->sub->core);
+
+ /* remote IRQ? */
+ if (!(gpio & 0x20))
+ return 0;
+
+ /* get time of bit */
+ current_jiffies = jiffies;
+ do_gettimeofday(&tv);
+
+ /* avoid overflow with gap >1s */
+ if (tv.tv_sec - ir->base_time.tv_sec > 1) {
+ gap = 200000;
+ } else {
+ gap = 1000000 * (tv.tv_sec - ir->base_time.tv_sec) +
+ tv.tv_usec - ir->base_time.tv_usec;
+ }
+
+ /* active code => add bit */
+ if (ir->active) {
+ /* only if in the code (otherwise spurious IRQ or timer
+ late) */
+ if (ir->last_bit < 28) {
+ ir->last_bit = (gap - rc5_remote_gap / 2) /
+ rc5_remote_gap;
+ ir->code |= 1 << ir->last_bit;
+ }
+ /* starting new code */
+ } else {
+ ir->active = 1;
+ ir->code = 0;
+ ir->base_time = tv;
+ ir->last_bit = 0;
+
+ timeout = current_jiffies + (500 + 30 * HZ) / 1000;
+ mod_timer(&ir->timer_end, timeout);
+ }
+
+ /* toggle GPIO pin 4 to reset the irq */
+ bttv_gpio_write(ir->sub->core, gpio & ~(1 << 4));
+ bttv_gpio_write(ir->sub->core, gpio | (1 << 4));
+ return 1;
+}
+
+static void ir_rc5_timer_end(unsigned long data)
+{
+ struct IR *ir = (struct IR *)data;
+ struct timeval tv;
+ unsigned long current_jiffies, timeout;
+ u32 gap;
+
+ /* get time */
+ current_jiffies = jiffies;
+ do_gettimeofday(&tv);
+
+ /* avoid overflow with gap >1s */
+ if (tv.tv_sec - ir->base_time.tv_sec > 1) {
+ gap = 200000;
+ } else {
+ gap = 1000000 * (tv.tv_sec - ir->base_time.tv_sec) +
+ tv.tv_usec - ir->base_time.tv_usec;
+ }
+
+ /* Allow some timmer jitter (RC5 is ~24ms anyway so this is ok) */
+ if (gap < 28000) {
+ dprintk("spurious timer_end\n");
+ return;
+ }
+
+ ir->active = 0;
+ if (ir->last_bit < 20) {
+ /* ignore spurious codes (caused by light/other remotes) */
+ dprintk("short code: %x\n", ir->code);
+ } else {
+ u32 rc5 = rc5_decode(ir->code);
+
+ /* two start bits? */
+ if (RC5_START(rc5) != 3) {
+ dprintk("rc5 start bits invalid: %u\n", RC5_START(rc5));
+
+ /* right address? */
+ } else if (RC5_ADDR(rc5) == 0x0) {
+ u32 toggle = RC5_TOGGLE(rc5);
+ u32 instr = RC5_INSTR(rc5);
+
+ /* Good code, decide if repeat/repress */
+ if (toggle != RC5_TOGGLE(ir->last_rc5) ||
+ instr != RC5_INSTR(ir->last_rc5)) {
+ dprintk("instruction %x, toggle %x\n", instr,
+ toggle);
+ ir_input_nokey(ir->input, &ir->ir);
+ ir_input_keydown(ir->input, &ir->ir, instr,
+ instr);
+ }
+
+ /* Set/reset key-up timer */
+ timeout = current_jiffies + (500 + rc5_key_timeout
+ * HZ) / 1000;
+ mod_timer(&ir->timer_keyup, timeout);
+
+ /* Save code for repeat test */
+ ir->last_rc5 = rc5;
+ }
+ }
+}
+
+static void ir_rc5_timer_keyup(unsigned long data)
+{
+ struct IR *ir = (struct IR *)data;
+
+ dprintk("key released\n");
+ ir_input_nokey(ir->input, &ir->ir);
+}
+
/* ---------------------------------------------------------------------- */
static int ir_probe(struct device *dev)
@@ -400,6 +642,12 @@ static int ir_probe(struct device *dev)
ir->mask_keyup = 0x006000;
ir->polling = 50; // ms
break;
+ case BTTV_BOARD_NEBULA_DIGITV:
+ ir_codes = ir_codes_nebula;
+ driver.any_irq = ir_rc5_irq;
+ driver.gpio_irq = NULL;
+ ir->rc5_gpio = 1;
+ break;
}
if (NULL == ir_codes) {
kfree(ir);
@@ -407,9 +655,17 @@ static int ir_probe(struct device *dev)
return -ENODEV;
}
- /* init hardware-specific stuff */
- bttv_gpio_inout(sub->core, ir->mask_keycode | ir->mask_keydown, 0);
- ir->sub = sub;
+ if (ir->rc5_gpio) {
+ u32 gpio;
+ /* enable remote irq */
+ bttv_gpio_inout(sub->core, (1 << 4), 1 << 4);
+ gpio = bttv_gpio_read(sub->core);
+ bttv_gpio_write(sub->core, gpio & ~(1 << 4));
+ bttv_gpio_write(sub->core, gpio | (1 << 4));
+ } else {
+ /* init hardware-specific stuff */
+ bttv_gpio_inout(sub->core, ir->mask_keycode | ir->mask_keydown, 0);
+ }
/* init input device */
snprintf(ir->name, sizeof(ir->name), "bttv IR (card=%d)",
@@ -417,6 +673,7 @@ static int ir_probe(struct device *dev)
snprintf(ir->phys, sizeof(ir->phys), "pci-%s/ir0",
pci_name(sub->core->pci));
+ ir->sub = sub;
ir_input_init(input_dev, &ir->ir, ir_type, ir_codes);
input_dev->name = ir->name;
input_dev->phys = ir->phys;
@@ -437,11 +694,25 @@ static int ir_probe(struct device *dev)
ir->timer.function = ir_timer;
ir->timer.data = (unsigned long)ir;
schedule_work(&ir->work);
+ } else if (ir->rc5_gpio) {
+ /* set timer_end for code completion */
+ init_timer(&ir->timer_end);
+ ir->timer_end.function = ir_rc5_timer_end;
+ ir->timer_end.data = (unsigned long)ir;
+
+ init_timer(&ir->timer_keyup);
+ ir->timer_keyup.function = ir_rc5_timer_keyup;
+ ir->timer_keyup.data = (unsigned long)ir;
}
/* all done */
dev_set_drvdata(dev, ir);
input_register_device(ir->input);
+ printk(DEVNAME ": %s detected at %s\n",ir->name,ir->phys);
+
+ /* the remote isn't as bouncy as a keyboard */
+ ir->input->rep[REP_DELAY] = repeat_delay;
+ ir->input->rep[REP_PERIOD] = repeat_period;
return 0;
}
@@ -454,6 +725,15 @@ static int ir_remove(struct device *dev)
del_timer(&ir->timer);
flush_scheduled_work();
}
+ if (ir->rc5_gpio) {
+ u32 gpio;
+
+ del_timer(&ir->timer_end);
+ flush_scheduled_work();
+
+ gpio = bttv_gpio_read(ir->sub->core);
+ bttv_gpio_write(ir->sub->core, gpio & ~(1 << 4));
+ }
input_unregister_device(ir->input);
kfree(ir);
diff --git a/drivers/media/video/ir-kbd-i2c.c b/drivers/media/video/ir-kbd-i2c.c
index 0085567a142..801c736e932 100644
--- a/drivers/media/video/ir-kbd-i2c.c
+++ b/drivers/media/video/ir-kbd-i2c.c
@@ -183,6 +183,58 @@ static int get_key_knc1(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
return 1;
}
+/* The new pinnacle PCTV remote (with the colored buttons)
+ *
+ * Ricardo Cerqueira <v4l@cerqueira.org>
+ */
+
+int get_key_pinnacle(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
+{
+ unsigned char b[4];
+ unsigned int start = 0,parity = 0,code = 0;
+
+ /* poll IR chip */
+ if (4 != i2c_master_recv(&ir->c,b,4)) {
+ dprintk(2,"read error\n");
+ return -EIO;
+ }
+
+ for (start = 0; start<4; start++) {
+ if (b[start] == 0x80) {
+ code=b[(start+3)%4];
+ parity=b[(start+2)%4];
+ }
+ }
+
+ /* Empty Request */
+ if (parity==0)
+ return 0;
+
+ /* Repeating... */
+ if (ir->old == parity)
+ return 0;
+
+
+ ir->old = parity;
+
+ /* Reduce code value to fit inside IR_KEYTAB_SIZE
+ *
+ * this is the only value that results in 42 unique
+ * codes < 128
+ */
+
+ code %= 0x88;
+
+ *ir_raw = code;
+ *ir_key = code;
+
+ dprintk(1,"Pinnacle PCTV key %02x\n", code);
+
+ return 1;
+}
+
+EXPORT_SYMBOL_GPL(get_key_pinnacle);
+
/* ----------------------------------------------------------------------- */
static void ir_key_poll(struct IR_i2c *ir)
diff --git a/drivers/media/video/saa7115.c b/drivers/media/video/saa7115.c
new file mode 100644
index 00000000000..0235cef07b3
--- /dev/null
+++ b/drivers/media/video/saa7115.c
@@ -0,0 +1,1376 @@
+/* saa7115 - Philips SAA7114/SAA7115 video decoder driver
+ *
+ * Based on saa7114 driver by Maxim Yevtyushkin, which is based on
+ * the saa7111 driver by Dave Perks.
+ *
+ * Copyright (C) 1998 Dave Perks <dperks@ibm.net>
+ * Copyright (C) 2002 Maxim Yevtyushkin <max@linuxmedialabs.com>
+ *
+ * Slight changes for video timing and attachment output by
+ * Wolfgang Scherr <scherr@net4you.net>
+ *
+ * Moved over to the linux >= 2.4.x i2c protocol (1/1/2003)
+ * by Ronald Bultje <rbultje@ronald.bitfreak.net>
+ *
+ * Added saa7115 support by Kevin Thayer <nufan_wfk at yahoo.com>
+ * (2/17/2003)
+ *
+ * VBI support (2004) and cleanups (2005) by Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+
+MODULE_DESCRIPTION("Philips SAA7114/SAA7115 video decoder driver");
+MODULE_AUTHOR("Maxim Yevtyushkin, Kevin Thayer, Chris Kennedy, Hans Verkuil");
+MODULE_LICENSE("GPL");
+
+static int debug = 0;
+module_param(debug, int, 0644);
+
+MODULE_PARM_DESC(debug, "Debug level (0-1)");
+
+#define saa7115_dbg(fmt,arg...) \
+ do { \
+ if (debug) \
+ printk(KERN_INFO "%s debug %d-%04x: " fmt, client->driver->name, \
+ i2c_adapter_id(client->adapter), client->addr , ## arg); \
+ } while (0)
+
+#define saa7115_err(fmt, arg...) do { \
+ printk(KERN_ERR "%s %d-%04x: " fmt, client->driver->name, \
+ i2c_adapter_id(client->adapter), client->addr , ## arg); } while (0)
+#define saa7115_info(fmt, arg...) do { \
+ printk(KERN_INFO "%s %d-%04x: " fmt, client->driver->name, \
+ i2c_adapter_id(client->adapter), client->addr , ## arg); } while (0)
+
+static unsigned short normal_i2c[] = { 0x42 >> 1, 0x40 >> 1, I2C_CLIENT_END };
+
+
+I2C_CLIENT_INSMOD;
+
+struct saa7115_state {
+ v4l2_std_id std;
+ int input;
+ int enable;
+ int bright;
+ int contrast;
+ int hue;
+ int sat;
+ enum v4l2_chip_ident ident;
+ enum v4l2_audio_clock_freq audclk_freq;
+};
+
+/* ----------------------------------------------------------------------- */
+
+static inline int saa7115_write(struct i2c_client *client, u8 reg, u8 value)
+{
+ return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static int saa7115_writeregs(struct i2c_client *client, const unsigned char *regs)
+{
+ unsigned char reg, data;
+
+ while (*regs != 0x00) {
+ reg = *(regs++);
+ data = *(regs++);
+ if (saa7115_write(client, reg, data) < 0)
+ return -1;
+ }
+ return 0;
+}
+
+static inline int saa7115_read(struct i2c_client *client, u8 reg)
+{
+ return i2c_smbus_read_byte_data(client, reg);
+}
+
+/* ----------------------------------------------------------------------- */
+
+/* If a value differs from the Hauppauge driver values, then the comment starts with
+ 'was 0xXX' to denote the Hauppauge value. Otherwise the value is identical to what the
+ Hauppauge driver sets. */
+
+static const unsigned char saa7115_init_auto_input[] = {
+ 0x01, 0x48, /* white peak control disabled */
+ 0x03, 0x20, /* was 0x30. 0x20: long vertical blanking */
+ 0x04, 0x90, /* analog gain set to 0 */
+ 0x05, 0x90, /* analog gain set to 0 */
+ 0x06, 0xeb, /* horiz sync begin = -21 */
+ 0x07, 0xe0, /* horiz sync stop = -17 */
+ 0x0a, 0x80, /* was 0x88. decoder brightness, 0x80 is itu standard */
+ 0x0b, 0x44, /* was 0x48. decoder contrast, 0x44 is itu standard */
+ 0x0c, 0x40, /* was 0x47. decoder saturation, 0x40 is itu standard */
+ 0x0d, 0x00, /* chrominance hue control */
+ 0x0f, 0x00, /* chrominance gain control: use automicatic mode */
+ 0x10, 0x06, /* chrominance/luminance control: active adaptive combfilter */
+ 0x11, 0x00, /* delay control */
+ 0x12, 0x9d, /* RTS0 output control: VGATE */
+ 0x13, 0x80, /* X-port output control: ITU656 standard mode, RTCO output enable RTCE */
+ 0x14, 0x00, /* analog/ADC/auto compatibility control */
+ 0x18, 0x40, /* raw data gain 0x00 = nominal */
+ 0x19, 0x80, /* raw data offset 0x80 = 0 LSB */
+ 0x1a, 0x77, /* color killer level control 0x77 = recommended */
+ 0x1b, 0x42, /* misc chroma control 0x42 = recommended */
+ 0x1c, 0xa9, /* combfilter control 0xA9 = recommended */
+ 0x1d, 0x01, /* combfilter control 0x01 = recommended */
+ 0x88, 0xd0, /* reset device */
+ 0x88, 0xf0, /* set device programmed, all in operational mode */
+ 0x00, 0x00
+};
+
+static const unsigned char saa7115_cfg_reset_scaler[] = {
+ 0x87, 0x00, /* disable I-port output */
+ 0x88, 0xd0, /* reset scaler */
+ 0x88, 0xf0, /* activate scaler */
+ 0x87, 0x01, /* enable I-port output */
+ 0x00, 0x00
+};
+
+/* ============== SAA7715 VIDEO templates ============= */
+
+static const unsigned char saa7115_cfg_60hz_fullres_x[] = {
+ 0xcc, 0xd0, /* hsize low (output), hor. output window size = 0x2d0 = 720 */
+ 0xcd, 0x02, /* hsize hi (output) */
+
+ /* Why not in 60hz-Land, too? */
+ 0xd0, 0x01, /* downscale = 1 */
+ 0xd8, 0x00, /* hor lum scaling 0x0400 = 1 */
+ 0xd9, 0x04,
+ 0xdc, 0x00, /* hor chrom scaling 0x0200. must be hor lum scaling / 2 */
+ 0xdd, 0x02, /* H-scaling incr chroma */
+
+ 0x00, 0x00
+};
+static const unsigned char saa7115_cfg_60hz_fullres_y[] = {
+ 0xce, 0xf8, /* vsize low (output), ver. output window size = 248 (but 60hz is 240?) */
+ 0xcf, 0x00, /* vsize hi (output) */
+
+ /* Why not in 60hz-Land, too? */
+ 0xd5, 0x40, /* Lum contrast, nominal value = 0x40 */
+ 0xd6, 0x40, /* Chroma satur. nominal value = 0x80 */
+
+ 0xe0, 0x00, /* V-scaling incr luma low */
+ 0xe1, 0x04, /* " hi */
+ 0xe2, 0x00, /* V-scaling incr chroma low */
+ 0xe3, 0x04, /* " hi */
+
+ 0x00, 0x00
+};
+
+static const unsigned char saa7115_cfg_60hz_video[] = {
+ 0x80, 0x00, /* reset tasks */
+ 0x88, 0xd0, /* reset scaler */
+
+ 0x15, 0x03, /* VGATE pulse start */
+ 0x16, 0x11, /* VGATE pulse stop */
+ 0x17, 0x9c, /* VGATE MSB and other values */
+
+ 0x08, 0x68, /* 0xBO: auto detection, 0x68 = NTSC */
+ 0x0e, 0x07, /* lots of different stuff... video autodetection is on */
+
+ 0x5a, 0x06, /* Vertical offset, standard 60hz value for ITU656 line counting */
+
+ /* Task A */
+ 0x90, 0x80, /* Task Handling Control */
+ 0x91, 0x48, /* X-port formats/config */
+ 0x92, 0x40, /* Input Ref. signal Def. */
+ 0x93, 0x84, /* I-port config */
+ 0x94, 0x01, /* hoffset low (input), 0x0002 is minimum */
+ 0x95, 0x00, /* hoffset hi (input) */
+ 0x96, 0xd0, /* hsize low (input), 0x02d0 = 720 */
+ 0x97, 0x02, /* hsize hi (input) */
+ 0x98, 0x05, /* voffset low (input) */
+ 0x99, 0x00, /* voffset hi (input) */
+ 0x9a, 0x0c, /* vsize low (input), 0x0c = 12 */
+ 0x9b, 0x00, /* vsize hi (input) */
+ 0x9c, 0xa0, /* hsize low (output), 0x05a0 = 1440 */
+ 0x9d, 0x05, /* hsize hi (output) */
+ 0x9e, 0x0c, /* vsize low (output), 0x0c = 12 */
+ 0x9f, 0x00, /* vsize hi (output) */
+
+ /* Task B */
+ 0xc0, 0x00, /* Task Handling Control */
+ 0xc1, 0x08, /* X-port formats/config */
+ 0xc2, 0x00, /* Input Ref. signal Def. */
+ 0xc3, 0x80, /* I-port config */
+ 0xc4, 0x02, /* hoffset low (input), 0x0002 is minimum */
+ 0xc5, 0x00, /* hoffset hi (input) */
+ 0xc6, 0xd0, /* hsize low (input), 0x02d0 = 720 */
+ 0xc7, 0x02, /* hsize hi (input) */
+ 0xc8, 0x12, /* voffset low (input), 0x12 = 18 */
+ 0xc9, 0x00, /* voffset hi (input) */
+ 0xca, 0xf8, /* vsize low (input), 0xf8 = 248 */
+ 0xcb, 0x00, /* vsize hi (input) */
+ 0xcc, 0xd0, /* hsize low (output), 0x02d0 = 720 */
+ 0xcd, 0x02, /* hsize hi (output) */
+
+ 0xf0, 0xad, /* Set PLL Register. 60hz 525 lines per frame, 27 MHz */
+ 0xf1, 0x05, /* low bit with 0xF0 */
+ 0xf5, 0xad, /* Set pulse generator register */
+ 0xf6, 0x01,
+
+ 0x87, 0x00, /* Disable I-port output */
+ 0x88, 0xd0, /* reset scaler */
+ 0x80, 0x20, /* Activate only task "B", continuous mode (was 0xA0) */
+ 0x88, 0xf0, /* activate scaler */
+ 0x87, 0x01, /* Enable I-port output */
+ 0x00, 0x00
+};
+
+static const unsigned char saa7115_cfg_50hz_fullres_x[] = {
+ 0xcc, 0xd0, /* hsize low (output), 720 same as 60hz */
+ 0xcd, 0x02, /* hsize hi (output) */
+
+ 0xd0, 0x01, /* down scale = 1 */
+ 0xd8, 0x00, /* hor lum scaling 0x0400 = 1 */
+ 0xd9, 0x04,
+ 0xdc, 0x00, /* hor chrom scaling 0x0200. must be hor lum scaling / 2 */
+ 0xdd, 0x02, /* H-scaling incr chroma */
+
+ 0x00, 0x00
+};
+static const unsigned char saa7115_cfg_50hz_fullres_y[] = {
+ 0xce, 0x20, /* vsize low (output), 0x0120 = 288 */
+ 0xcf, 0x01, /* vsize hi (output) */
+
+ 0xd5, 0x40, /* Lum contrast, nominal value = 0x40 */
+ 0xd6, 0x40, /* Chroma satur. nominal value = 0x80 */
+
+ 0xe0, 0x00, /* V-scaling incr luma low */
+ 0xe1, 0x04, /* " hi */
+ 0xe2, 0x00, /* V-scaling incr chroma low */
+ 0xe3, 0x04, /* " hi */
+
+ 0x00, 0x00
+};
+
+static const unsigned char saa7115_cfg_50hz_video[] = {
+ 0x80, 0x00, /* reset tasks */
+ 0x88, 0xd0, /* reset scaler */
+
+ 0x15, 0x37, /* VGATE start */
+ 0x16, 0x16, /* VGATE stop */
+ 0x17, 0x99, /* VGATE MSB and other values */
+
+ 0x08, 0x28, /* 0x28 = PAL */
+ 0x0e, 0x07, /* chrominance control 1 */
+
+ 0x5a, 0x03, /* Vertical offset, standard 50hz value */
+
+ /* Task A */
+ 0x90, 0x81, /* Task Handling Control */
+ 0x91, 0x48, /* X-port formats/config */
+ 0x92, 0x40, /* Input Ref. signal Def. */
+ 0x93, 0x84, /* I-port config */
+ /* This is weird: the datasheet says that you should use 2 as the minimum value, */
+ /* but Hauppauge uses 0, and changing that to 2 causes indeed problems (for 50hz) */
+ 0x94, 0x00, /* hoffset low (input), 0x0002 is minimum */
+ 0x95, 0x00, /* hoffset hi (input) */
+ 0x96, 0xd0, /* hsize low (input), 0x02d0 = 720 */
+ 0x97, 0x02, /* hsize hi (input) */
+ 0x98, 0x03, /* voffset low (input) */
+ 0x99, 0x00, /* voffset hi (input) */
+ 0x9a, 0x12, /* vsize low (input), 0x12 = 18 */
+ 0x9b, 0x00, /* vsize hi (input) */
+ 0x9c, 0xa0, /* hsize low (output), 0x05a0 = 1440 */
+ 0x9d, 0x05, /* hsize hi (output) */
+ 0x9e, 0x12, /* vsize low (output), 0x12 = 18 */
+ 0x9f, 0x00, /* vsize hi (output) */
+
+ /* Task B */
+ 0xc0, 0x00, /* Task Handling Control */
+ 0xc1, 0x08, /* X-port formats/config */
+ 0xc2, 0x00, /* Input Ref. signal Def. */
+ 0xc3, 0x80, /* I-port config */
+ 0xc4, 0x00, /* hoffset low (input), 0x0002 is minimum. See comment at 0x94 above. */
+ 0xc5, 0x00, /* hoffset hi (input) */
+ 0xc6, 0xd0, /* hsize low (input), 0x02d0 = 720 */
+ 0xc7, 0x02, /* hsize hi (input) */
+ 0xc8, 0x16, /* voffset low (input), 0x16 = 22 */
+ 0xc9, 0x00, /* voffset hi (input) */
+ 0xca, 0x20, /* vsize low (input), 0x0120 = 288 */
+ 0xcb, 0x01, /* vsize hi (input) */
+ 0xcc, 0xd0, /* hsize low (output), 0x02d0 = 720 */
+ 0xcd, 0x02, /* hsize hi (output) */
+ 0xce, 0x20, /* vsize low (output), 0x0120 = 288 */
+ 0xcf, 0x01, /* vsize hi (output) */
+
+ 0xf0, 0xb0, /* Set PLL Register. 50hz 625 lines per frame, 27 MHz */
+ 0xf1, 0x05, /* low bit with 0xF0, (was 0x05) */
+ 0xf5, 0xb0, /* Set pulse generator register */
+ 0xf6, 0x01,
+
+ 0x87, 0x00, /* Disable I-port output */
+ 0x88, 0xd0, /* reset scaler (was 0xD0) */
+ 0x80, 0x20, /* Activate only task "B" */
+ 0x88, 0xf0, /* activate scaler */
+ 0x87, 0x01, /* Enable I-port output */
+ 0x00, 0x00
+};
+
+/* ============== SAA7715 VIDEO templates (end) ======= */
+
+static const unsigned char saa7115_cfg_vbi_on[] = {
+ 0x80, 0x00, /* reset tasks */
+ 0x88, 0xd0, /* reset scaler */
+ 0x80, 0x30, /* Activate both tasks */
+ 0x88, 0xf0, /* activate scaler */
+ 0x87, 0x01, /* Enable I-port output */
+ 0x00, 0x00
+};
+
+static const unsigned char saa7115_cfg_vbi_off[] = {
+ 0x80, 0x00, /* reset tasks */
+ 0x88, 0xd0, /* reset scaler */
+ 0x80, 0x20, /* Activate only task "B" */
+ 0x88, 0xf0, /* activate scaler */
+ 0x87, 0x01, /* Enable I-port output */
+ 0x00, 0x00
+};
+
+static const unsigned char saa7115_init_misc[] = {
+ 0x38, 0x03, /* audio stuff */
+ 0x39, 0x10,
+ 0x3a, 0x08,
+
+ 0x81, 0x01, /* reg 0x15,0x16 define blanking window */
+ 0x82, 0x00,
+ 0x83, 0x01, /* I port settings */
+ 0x84, 0x20,
+ 0x85, 0x21,
+ 0x86, 0xc5,
+ 0x87, 0x01,
+
+ /* Task A */
+ 0xa0, 0x01, /* down scale = 1 */
+ 0xa1, 0x00, /* prescale accumulation length = 1 */
+ 0xa2, 0x00, /* dc gain and fir prefilter control */
+ 0xa4, 0x80, /* Lum Brightness, nominal value = 0x80 */
+ 0xa5, 0x40, /* Lum contrast, nominal value = 0x40 */
+ 0xa6, 0x40, /* Chroma satur. nominal value = 0x80 */
+ 0xa8, 0x00, /* hor lum scaling 0x0200 = 2 zoom */
+ 0xa9, 0x02, /* note: 2 x zoom ensures that VBI lines have same length as video lines. */
+ 0xaa, 0x00, /* H-phase offset Luma = 0 */
+ 0xac, 0x00, /* hor chrom scaling 0x0200. must be hor lum scaling / 2 */
+ 0xad, 0x01, /* H-scaling incr chroma */
+ 0xae, 0x00, /* H-phase offset chroma. must be offset luma / 2 */
+
+ 0xb0, 0x00, /* V-scaling incr luma low */
+ 0xb1, 0x04, /* " hi */
+ 0xb2, 0x00, /* V-scaling incr chroma low */
+ 0xb3, 0x04, /* " hi */
+ 0xb4, 0x01, /* V-scaling mode control */
+ 0xb8, 0x00, /* V-phase offset chroma 00 */
+ 0xb9, 0x00, /* V-phase offset chroma 01 */
+ 0xba, 0x00, /* V-phase offset chroma 10 */
+ 0xbb, 0x00, /* V-phase offset chroma 11 */
+ 0xbc, 0x00, /* V-phase offset luma 00 */
+ 0xbd, 0x00, /* V-phase offset luma 01 */
+ 0xbe, 0x00, /* V-phase offset luma 10 */
+ 0xbf, 0x00, /* V-phase offset luma 11 */
+
+ /* Task B */
+ 0xd0, 0x01, /* down scale = 1 */
+ 0xd1, 0x00, /* prescale accumulation length = 1 */
+ 0xd2, 0x00, /* dc gain and fir prefilter control */
+ 0xd4, 0x80, /* Lum Brightness, nominal value = 0x80 */
+ 0xd5, 0x40, /* Lum contrast, nominal value = 0x40 */
+ 0xd6, 0x40, /* Chroma satur. nominal value = 0x80 */
+ 0xd8, 0x00, /* hor lum scaling 0x0400 = 1 */
+ 0xd9, 0x04,
+ 0xda, 0x00, /* H-phase offset Luma = 0 */
+ 0xdc, 0x00, /* hor chrom scaling 0x0200. must be hor lum scaling / 2 */
+ 0xdd, 0x02, /* H-scaling incr chroma */
+ 0xde, 0x00, /* H-phase offset chroma. must be offset luma / 2 */
+
+ 0xe0, 0x00, /* V-scaling incr luma low */
+ 0xe1, 0x04, /* " hi */
+ 0xe2, 0x00, /* V-scaling incr chroma low */
+ 0xe3, 0x04, /* " hi */
+ 0xe4, 0x01, /* V-scaling mode control */
+ 0xe8, 0x00, /* V-phase offset chroma 00 */
+ 0xe9, 0x00, /* V-phase offset chroma 01 */
+ 0xea, 0x00, /* V-phase offset chroma 10 */
+ 0xeb, 0x00, /* V-phase offset chroma 11 */
+ 0xec, 0x00, /* V-phase offset luma 00 */
+ 0xed, 0x00, /* V-phase offset luma 01 */
+ 0xee, 0x00, /* V-phase offset luma 10 */
+ 0xef, 0x00, /* V-phase offset luma 11 */
+
+ 0xf2, 0x50, /* crystal clock = 24.576 MHz, target = 27MHz */
+ 0xf3, 0x46,
+ 0xf4, 0x00,
+ 0xf7, 0x4b, /* not the recommended settings! */
+ 0xf8, 0x00,
+ 0xf9, 0x4b,
+ 0xfa, 0x00,
+ 0xfb, 0x4b,
+ 0xff, 0x88, /* PLL2 lock detection settings: 71 lines 50% phase error */
+
+ /* Turn off VBI */
+ 0x40, 0x20, /* No framing code errors allowed. */
+ 0x41, 0xff,
+ 0x42, 0xff,
+ 0x43, 0xff,
+ 0x44, 0xff,
+ 0x45, 0xff,
+ 0x46, 0xff,
+ 0x47, 0xff,
+ 0x48, 0xff,
+ 0x49, 0xff,
+ 0x4a, 0xff,
+ 0x4b, 0xff,
+ 0x4c, 0xff,
+ 0x4d, 0xff,
+ 0x4e, 0xff,
+ 0x4f, 0xff,
+ 0x50, 0xff,
+ 0x51, 0xff,
+ 0x52, 0xff,
+ 0x53, 0xff,
+ 0x54, 0xff,
+ 0x55, 0xff,
+ 0x56, 0xff,
+ 0x57, 0xff,
+ 0x58, 0x40,
+ 0x59, 0x47,
+ 0x5b, 0x83,
+ 0x5d, 0xbd,
+ 0x5e, 0x35,
+
+ 0x02, 0x84, /* input tuner -> input 4, amplifier active */
+ 0x09, 0x53, /* 0x53, was 0x56 for 60hz. luminance control */
+
+ 0x80, 0x20, /* enable task B */
+ 0x88, 0xd0,
+ 0x88, 0xf0,
+ 0x00, 0x00
+};
+
+/* ============== SAA7715 AUDIO settings ============= */
+
+/* 48.0 kHz */
+static const unsigned char saa7115_cfg_48_audio[] = {
+ 0x34, 0xce,
+ 0x35, 0xfb,
+ 0x36, 0x30,
+ 0x00, 0x00
+};
+
+/* 44.1 kHz */
+static const unsigned char saa7115_cfg_441_audio[] = {
+ 0x34, 0xf2,
+ 0x35, 0x00,
+ 0x36, 0x2d,
+ 0x00, 0x00
+};
+
+/* 32.0 kHz */
+static const unsigned char saa7115_cfg_32_audio[] = {
+ 0x34, 0xdf,
+ 0x35, 0xa7,
+ 0x36, 0x20,
+ 0x00, 0x00
+};
+
+/* 48.0 kHz 60hz */
+static const unsigned char saa7115_cfg_60hz_48_audio[] = {
+ 0x30, 0xcd,
+ 0x31, 0x20,
+ 0x32, 0x03,
+ 0x00, 0x00
+};
+
+/* 48.0 kHz 50hz */
+static const unsigned char saa7115_cfg_50hz_48_audio[] = {
+ 0x30, 0x00,
+ 0x31, 0xc0,
+ 0x32, 0x03,
+ 0x00, 0x00
+};
+
+/* 44.1 kHz 60hz */
+static const unsigned char saa7115_cfg_60hz_441_audio[] = {
+ 0x30, 0xbc,
+ 0x31, 0xdf,
+ 0x32, 0x02,
+ 0x00, 0x00
+};
+
+/* 44.1 kHz 50hz */
+static const unsigned char saa7115_cfg_50hz_441_audio[] = {
+ 0x30, 0x00,
+ 0x31, 0x72,
+ 0x32, 0x03,
+ 0x00, 0x00
+};
+
+/* 32.0 kHz 60hz */
+static const unsigned char saa7115_cfg_60hz_32_audio[] = {
+ 0x30, 0xde,
+ 0x31, 0x15,
+ 0x32, 0x02,
+ 0x00, 0x00
+};
+
+/* 32.0 kHz 50hz */
+static const unsigned char saa7115_cfg_50hz_32_audio[] = {
+ 0x30, 0x00,
+ 0x31, 0x80,
+ 0x32, 0x02,
+ 0x00, 0x00
+};
+
+static int saa7115_odd_parity(u8 c)
+{
+ c ^= (c >> 4);
+ c ^= (c >> 2);
+ c ^= (c >> 1);
+
+ return c & 1;
+}
+
+static int saa7115_decode_vps(u8 * dst, u8 * p)
+{
+ static const u8 biphase_tbl[] = {
+ 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
+ 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
+ 0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96,
+ 0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2,
+ 0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94,
+ 0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0,
+ 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
+ 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
+ 0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5,
+ 0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1,
+ 0xc3, 0x4b, 0x43, 0xc3, 0x87, 0x0f, 0x07, 0x87,
+ 0x83, 0x0b, 0x03, 0x83, 0xc3, 0x4b, 0x43, 0xc3,
+ 0xc1, 0x49, 0x41, 0xc1, 0x85, 0x0d, 0x05, 0x85,
+ 0x81, 0x09, 0x01, 0x81, 0xc1, 0x49, 0x41, 0xc1,
+ 0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5,
+ 0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1,
+ 0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4,
+ 0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0,
+ 0xc2, 0x4a, 0x42, 0xc2, 0x86, 0x0e, 0x06, 0x86,
+ 0x82, 0x0a, 0x02, 0x82, 0xc2, 0x4a, 0x42, 0xc2,
+ 0xc0, 0x48, 0x40, 0xc0, 0x84, 0x0c, 0x04, 0x84,
+ 0x80, 0x08, 0x00, 0x80, 0xc0, 0x48, 0x40, 0xc0,
+ 0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4,
+ 0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0,
+ 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
+ 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
+ 0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96,
+ 0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2,
+ 0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94,
+ 0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0,
+ 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
+ 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
+ };
+ int i;
+ u8 c, err = 0;
+
+ for (i = 0; i < 2 * 13; i += 2) {
+ err |= biphase_tbl[p[i]] | biphase_tbl[p[i + 1]];
+ c = (biphase_tbl[p[i + 1]] & 0xf) | ((biphase_tbl[p[i]] & 0xf) << 4);
+ dst[i / 2] = c;
+ }
+ return err & 0xf0;
+}
+
+static int saa7115_decode_wss(u8 * p)
+{
+ static const int wss_bits[8] = {
+ 0, 0, 0, 1, 0, 1, 1, 1
+ };
+ unsigned char parity;
+ int wss = 0;
+ int i;
+
+ for (i = 0; i < 16; i++) {
+ int b1 = wss_bits[p[i] & 7];
+ int b2 = wss_bits[(p[i] >> 3) & 7];
+
+ if (b1 == b2)
+ return -1;
+ wss |= b2 << i;
+ }
+ parity = wss & 15;
+ parity ^= parity >> 2;
+ parity ^= parity >> 1;
+
+ if (!(parity & 1))
+ return -1;
+
+ return wss;
+}
+
+
+static int saa7115_set_audio_clock_freq(struct i2c_client *client, enum v4l2_audio_clock_freq freq)
+{
+ struct saa7115_state *state = i2c_get_clientdata(client);
+
+ saa7115_dbg("set audio clock freq: %d\n", freq);
+ switch (freq) {
+ case V4L2_AUDCLK_32_KHZ:
+ saa7115_writeregs(client, saa7115_cfg_32_audio);
+ if (state->std & V4L2_STD_525_60) {
+ saa7115_writeregs(client, saa7115_cfg_60hz_32_audio);
+ } else {
+ saa7115_writeregs(client, saa7115_cfg_50hz_32_audio);
+ }
+ break;
+ case V4L2_AUDCLK_441_KHZ:
+ saa7115_writeregs(client, saa7115_cfg_441_audio);
+ if (state->std & V4L2_STD_525_60) {
+ saa7115_writeregs(client, saa7115_cfg_60hz_441_audio);
+ } else {
+ saa7115_writeregs(client, saa7115_cfg_50hz_441_audio);
+ }
+ break;
+ case V4L2_AUDCLK_48_KHZ:
+ saa7115_writeregs(client, saa7115_cfg_48_audio);
+ if (state->std & V4L2_STD_525_60) {
+ saa7115_writeregs(client, saa7115_cfg_60hz_48_audio);
+ } else {
+ saa7115_writeregs(client, saa7115_cfg_50hz_48_audio);
+ }
+ break;
+ default:
+ saa7115_dbg("invalid audio setting %d\n", freq);
+ return -EINVAL;
+ }
+ state->audclk_freq = freq;
+ return 0;
+}
+
+static int saa7115_set_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl)
+{
+ struct saa7115_state *state = i2c_get_clientdata(client);
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ if (ctrl->value < 0 || ctrl->value > 255) {
+ saa7115_err("invalid brightness setting %d\n", ctrl->value);
+ return -ERANGE;
+ }
+
+ state->bright = ctrl->value;
+ saa7115_write(client, 0x0a, state->bright);
+ break;
+
+ case V4L2_CID_CONTRAST:
+ if (ctrl->value < 0 || ctrl->value > 127) {
+ saa7115_err("invalid contrast setting %d\n", ctrl->value);
+ return -ERANGE;
+ }
+
+ state->contrast = ctrl->value;
+ saa7115_write(client, 0x0b, state->contrast);
+ break;
+
+ case V4L2_CID_SATURATION:
+ if (ctrl->value < 0 || ctrl->value > 127) {
+ saa7115_err("invalid saturation setting %d\n", ctrl->value);
+ return -ERANGE;
+ }
+
+ state->sat = ctrl->value;
+ saa7115_write(client, 0x0c, state->sat);
+ break;
+
+ case V4L2_CID_HUE:
+ if (ctrl->value < -127 || ctrl->value > 127) {
+ saa7115_err("invalid hue setting %d\n", ctrl->value);
+ return -ERANGE;
+ }
+
+ state->hue = ctrl->value;
+ saa7115_write(client, 0x0d, state->hue);
+ break;
+ }
+
+ return 0;
+}
+
+static int saa7115_get_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl)
+{
+ struct saa7115_state *state = i2c_get_clientdata(client);
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ ctrl->value = state->bright;
+ break;
+ case V4L2_CID_CONTRAST:
+ ctrl->value = state->contrast;
+ break;
+ case V4L2_CID_SATURATION:
+ ctrl->value = state->sat;
+ break;
+ case V4L2_CID_HUE:
+ ctrl->value = state->hue;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void saa7115_set_v4lstd(struct i2c_client *client, v4l2_std_id std)
+{
+ struct saa7115_state *state = i2c_get_clientdata(client);
+ int taskb = saa7115_read(client, 0x80) & 0x10;
+
+ // This works for NTSC-M, SECAM-L and the 50Hz PAL variants.
+ if (std & V4L2_STD_525_60) {
+ saa7115_dbg("decoder set standard 60 Hz\n");
+ saa7115_writeregs(client, saa7115_cfg_60hz_video);
+ } else {
+ saa7115_dbg("decoder set standard 50 Hz\n");
+ saa7115_writeregs(client, saa7115_cfg_50hz_video);
+ }
+
+ state->std = std;
+
+ /* restart task B if needed */
+ if (taskb && state->ident == V4L2_IDENT_SAA7114) {
+ saa7115_writeregs(client, saa7115_cfg_vbi_on);
+ }
+
+ /* switch audio mode too! */
+ saa7115_set_audio_clock_freq(client, state->audclk_freq);
+}
+
+static v4l2_std_id saa7115_get_v4lstd(struct i2c_client *client)
+{
+ struct saa7115_state *state = i2c_get_clientdata(client);
+
+ return state->std;
+}
+
+static void saa7115_log_status(struct i2c_client *client)
+{
+ static const char * const audclk_freq_strs[] = {
+ "44.1 kHz",
+ "48 kHz",
+ "32 kHz"
+ };
+ struct saa7115_state *state = i2c_get_clientdata(client);
+ int reg1e, reg1f;
+ int signalOk;
+ int vcr;
+
+ saa7115_info("Audio frequency: %s\n", audclk_freq_strs[state->audclk_freq]);
+ if (client->name[6] == '4') {
+ /* status for the saa7114 */
+ reg1f = saa7115_read(client, 0x1f);
+ signalOk = (reg1f & 0xc1) == 0x81;
+ saa7115_info("Video signal: %s\n", signalOk ? "ok" : "bad");
+ saa7115_info("Frequency: %s\n", (reg1f & 0x20) ? "60Hz" : "50Hz");
+ return;
+ }
+
+ /* status for the saa7115 */
+ reg1e = saa7115_read(client, 0x1e);
+ reg1f = saa7115_read(client, 0x1f);
+
+ signalOk = (reg1f & 0xc1) == 0x81 && (reg1e & 0xc0) == 0x80;
+ vcr = !(reg1f & 0x10);
+
+ saa7115_info("Video signal: %s\n", signalOk ? (vcr ? "VCR" : "broadcast/DVD") : "bad");
+ saa7115_info("Frequency: %s\n", (reg1f & 0x20) ? "60Hz" : "50Hz");
+
+ switch (reg1e & 0x03) {
+ case 1:
+ saa7115_info("Detected format: NTSC\n");
+ break;
+ case 2:
+ saa7115_info("Detected format: PAL\n");
+ break;
+ case 3:
+ saa7115_info("Detected format: SECAM\n");
+ break;
+ default:
+ saa7115_info("Detected format: BW/No color\n");
+ break;
+ }
+}
+
+/* setup the sliced VBI lcr registers according to the sliced VBI format */
+static void saa7115_set_lcr(struct i2c_client *client, struct v4l2_sliced_vbi_format *fmt)
+{
+ struct saa7115_state *state = i2c_get_clientdata(client);
+ int is_50hz = (state->std & V4L2_STD_625_50);
+ u8 lcr[24];
+ int i, x;
+
+ /* saa7114 doesn't yet support VBI */
+ if (state->ident == V4L2_IDENT_SAA7114)
+ return;
+
+ for (i = 0; i <= 23; i++)
+ lcr[i] = 0xff;
+
+ if (fmt->service_set == 0) {
+ /* raw VBI */
+ if (is_50hz)
+ for (i = 6; i <= 23; i++)
+ lcr[i] = 0xdd;
+ else
+ for (i = 10; i <= 21; i++)
+ lcr[i] = 0xdd;
+ } else {
+ /* sliced VBI */
+ /* first clear lines that cannot be captured */
+ if (is_50hz) {
+ for (i = 0; i <= 5; i++)
+ fmt->service_lines[0][i] =
+ fmt->service_lines[1][i] = 0;
+ }
+ else {
+ for (i = 0; i <= 9; i++)
+ fmt->service_lines[0][i] =
+ fmt->service_lines[1][i] = 0;
+ for (i = 22; i <= 23; i++)
+ fmt->service_lines[0][i] =
+ fmt->service_lines[1][i] = 0;
+ }
+
+ /* Now set the lcr values according to the specified service */
+ for (i = 6; i <= 23; i++) {
+ lcr[i] = 0;
+ for (x = 0; x <= 1; x++) {
+ switch (fmt->service_lines[1-x][i]) {
+ case 0:
+ lcr[i] |= 0xf << (4 * x);
+ break;
+ case V4L2_SLICED_TELETEXT_B:
+ lcr[i] |= 1 << (4 * x);
+ break;
+ case V4L2_SLICED_CAPTION_525:
+ lcr[i] |= 4 << (4 * x);
+ break;
+ case V4L2_SLICED_WSS_625:
+ lcr[i] |= 5 << (4 * x);
+ break;
+ case V4L2_SLICED_VPS:
+ lcr[i] |= 7 << (4 * x);
+ break;
+ }
+ }
+ }
+ }
+
+ /* write the lcr registers */
+ for (i = 2; i <= 23; i++) {
+ saa7115_write(client, i - 2 + 0x41, lcr[i]);
+ }
+
+ /* enable/disable raw VBI capturing */
+ saa7115_writeregs(client, fmt->service_set == 0 ? saa7115_cfg_vbi_on : saa7115_cfg_vbi_off);
+}
+
+static int saa7115_get_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt)
+{
+ static u16 lcr2vbi[] = {
+ 0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */
+ 0, V4L2_SLICED_CAPTION_525, /* 4 */
+ V4L2_SLICED_WSS_625, 0, /* 5 */
+ V4L2_SLICED_VPS, 0, 0, 0, 0, /* 7 */
+ 0, 0, 0, 0
+ };
+ struct v4l2_sliced_vbi_format *sliced = &fmt->fmt.sliced;
+ int i;
+
+ if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
+ return -EINVAL;
+ memset(sliced, 0, sizeof(*sliced));
+ /* done if using raw VBI */
+ if (saa7115_read(client, 0x80) & 0x10)
+ return 0;
+ for (i = 2; i <= 23; i++) {
+ u8 v = saa7115_read(client, i - 2 + 0x41);
+
+ sliced->service_lines[0][i] = lcr2vbi[v >> 4];
+ sliced->service_lines[1][i] = lcr2vbi[v & 0xf];
+ sliced->service_set |=
+ sliced->service_lines[0][i] | sliced->service_lines[1][i];
+ }
+ return 0;
+}
+
+static int saa7115_set_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt)
+{
+ struct saa7115_state *state = i2c_get_clientdata(client);
+ struct v4l2_pix_format *pix;
+ int HPSC, HFSC;
+ int VSCY, Vsrc;
+ int is_50hz = state->std & V4L2_STD_625_50;
+
+ if (fmt->type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) {
+ saa7115_set_lcr(client, &fmt->fmt.sliced);
+ return 0;
+ }
+ if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ pix = &(fmt->fmt.pix);
+
+ saa7115_dbg("decoder set size\n");
+
+ /* FIXME need better bounds checking here */
+ if ((pix->width < 1) || (pix->width > 1440))
+ return -EINVAL;
+ if ((pix->height < 1) || (pix->height > 960))
+ return -EINVAL;
+
+ /* probably have a valid size, let's set it */
+ /* Set output width/height */
+ /* width */
+ saa7115_write(client, 0xcc, (u8) (pix->width & 0xff));
+ saa7115_write(client, 0xcd, (u8) ((pix->width >> 8) & 0xff));
+ /* height */
+ saa7115_write(client, 0xce, (u8) (pix->height & 0xff));
+ saa7115_write(client, 0xcf, (u8) ((pix->height >> 8) & 0xff));
+
+ /* Scaling settings */
+ /* Hprescaler is floor(inres/outres) */
+ /* FIXME hardcoding input res */
+ if (pix->width != 720) {
+ HPSC = (int)(720 / pix->width);
+ /* 0 is not allowed (div. by zero) */
+ HPSC = HPSC ? HPSC : 1;
+ HFSC = (int)((1024 * 720) / (HPSC * pix->width));
+
+ saa7115_dbg("Hpsc: 0x%05x, Hfsc: 0x%05x\n", HPSC, HFSC);
+ /* FIXME hardcodes to "Task B"
+ * write H prescaler integer */
+ saa7115_write(client, 0xd0, (u8) (HPSC & 0x3f));
+
+ /* write H fine-scaling (luminance) */
+ saa7115_write(client, 0xd8, (u8) (HFSC & 0xff));
+ saa7115_write(client, 0xd9, (u8) ((HFSC >> 8) & 0xff));
+ /* write H fine-scaling (chrominance)
+ * must be lum/2, so i'll just bitshift :) */
+ saa7115_write(client, 0xDC, (u8) ((HFSC >> 1) & 0xff));
+ saa7115_write(client, 0xDD, (u8) ((HFSC >> 9) & 0xff));
+ } else {
+ if (is_50hz) {
+ saa7115_dbg("Setting full 50hz width\n");
+ saa7115_writeregs(client, saa7115_cfg_50hz_fullres_x);
+ } else {
+ saa7115_dbg("Setting full 60hz width\n");
+ saa7115_writeregs(client, saa7115_cfg_60hz_fullres_x);
+ }
+ }
+
+ Vsrc = is_50hz ? 576 : 480;
+
+ if (pix->height != Vsrc) {
+ VSCY = (int)((1024 * Vsrc) / pix->height);
+ saa7115_dbg("Vsrc: %d, Vscy: 0x%05x\n", Vsrc, VSCY);
+
+ /* Correct Contrast and Luminance */
+ saa7115_write(client, 0xd5, (u8) (64 * 1024 / VSCY));
+ saa7115_write(client, 0xd6, (u8) (64 * 1024 / VSCY));
+
+ /* write V fine-scaling (luminance) */
+ saa7115_write(client, 0xe0, (u8) (VSCY & 0xff));
+ saa7115_write(client, 0xe1, (u8) ((VSCY >> 8) & 0xff));
+ /* write V fine-scaling (chrominance) */
+ saa7115_write(client, 0xe2, (u8) (VSCY & 0xff));
+ saa7115_write(client, 0xe3, (u8) ((VSCY >> 8) & 0xff));
+ } else {
+ if (is_50hz) {
+ saa7115_dbg("Setting full 50Hz height\n");
+ saa7115_writeregs(client, saa7115_cfg_50hz_fullres_y);
+ } else {
+ saa7115_dbg("Setting full 60hz height\n");
+ saa7115_writeregs(client, saa7115_cfg_60hz_fullres_y);
+ }
+ }
+
+ saa7115_writeregs(client, saa7115_cfg_reset_scaler);
+ return 0;
+}
+
+/* Decode the sliced VBI data stream as created by the saa7115.
+ The format is described in the saa7115 datasheet in Tables 25 and 26
+ and in Figure 33.
+ The current implementation uses SAV/EAV codes and not the ancillary data
+ headers. The vbi->p pointer points to the SDID byte right after the SAV
+ code. */
+static void saa7115_decode_vbi_line(struct i2c_client *client,
+ struct v4l2_decode_vbi_line *vbi)
+{
+ static const char vbi_no_data_pattern[] = {
+ 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0
+ };
+ struct saa7115_state *state = i2c_get_clientdata(client);
+ u8 *p = vbi->p;
+ u32 wss;
+ int id1, id2; /* the ID1 and ID2 bytes from the internal header */
+
+ vbi->type = 0; /* mark result as a failure */
+ id1 = p[2];
+ id2 = p[3];
+ /* Note: the field bit is inverted for 60 Hz video */
+ if (state->std & V4L2_STD_525_60)
+ id1 ^= 0x40;
+
+ /* Skip internal header, p now points to the start of the payload */
+ p += 4;
+ vbi->p = p;
+
+ /* calculate field and line number of the VBI packet (1-23) */
+ vbi->is_second_field = ((id1 & 0x40) != 0);
+ vbi->line = (id1 & 0x3f) << 3;
+ vbi->line |= (id2 & 0x70) >> 4;
+
+ /* Obtain data type */
+ id2 &= 0xf;
+
+ /* If the VBI slicer does not detect any signal it will fill up
+ the payload buffer with 0xa0 bytes. */
+ if (!memcmp(p, vbi_no_data_pattern, sizeof(vbi_no_data_pattern)))
+ return;
+
+ /* decode payloads */
+ switch (id2) {
+ case 1:
+ vbi->type = V4L2_SLICED_TELETEXT_B;
+ break;
+ case 4:
+ if (!saa7115_odd_parity(p[0]) || !saa7115_odd_parity(p[1]))
+ return;
+ vbi->type = V4L2_SLICED_CAPTION_525;
+ break;
+ case 5:
+ wss = saa7115_decode_wss(p);
+ if (wss == -1)
+ return;
+ p[0] = wss & 0xff;
+ p[1] = wss >> 8;
+ vbi->type = V4L2_SLICED_WSS_625;
+ break;
+ case 7:
+ if (saa7115_decode_vps(p, p) != 0)
+ return;
+ vbi->type = V4L2_SLICED_VPS;
+ break;
+ default:
+ return;
+ }
+}
+
+/* ============ SAA7115 AUDIO settings (end) ============= */
+
+static int saa7115_command(struct i2c_client *client, unsigned int cmd, void *arg)
+{
+ struct saa7115_state *state = i2c_get_clientdata(client);
+ int *iarg = arg;
+
+ /* ioctls to allow direct access to the saa7115 registers for testing */
+ switch (cmd) {
+ case VIDIOC_S_FMT:
+ return saa7115_set_v4lfmt(client, (struct v4l2_format *)arg);
+
+ case VIDIOC_G_FMT:
+ return saa7115_get_v4lfmt(client, (struct v4l2_format *)arg);
+
+ case VIDIOC_INT_AUDIO_CLOCK_FREQ:
+ return saa7115_set_audio_clock_freq(client, *(enum v4l2_audio_clock_freq *)arg);
+
+ case VIDIOC_G_TUNER:
+ {
+ struct v4l2_tuner *vt = arg;
+ int status;
+
+ status = saa7115_read(client, 0x1f);
+
+ saa7115_dbg("status: 0x%02x\n", status);
+ vt->signal = ((status & (1 << 6)) == 0) ? 0xffff : 0x0;
+ break;
+ }
+
+ case VIDIOC_LOG_STATUS:
+ saa7115_log_status(client);
+ break;
+
+ case VIDIOC_G_CTRL:
+ return saa7115_get_v4lctrl(client, (struct v4l2_control *)arg);
+
+ case VIDIOC_S_CTRL:
+ return saa7115_set_v4lctrl(client, (struct v4l2_control *)arg);
+
+ case VIDIOC_G_STD:
+ *(v4l2_std_id *)arg = saa7115_get_v4lstd(client);
+ break;
+
+ case VIDIOC_S_STD:
+ saa7115_set_v4lstd(client, *(v4l2_std_id *)arg);
+ break;
+
+ case VIDIOC_G_INPUT:
+ *(int *)arg = state->input;
+ break;
+
+ case VIDIOC_S_INPUT:
+ saa7115_dbg("decoder set input %d\n", *iarg);
+ /* inputs from 0-9 are available */
+ if (*iarg < 0 || *iarg > 9) {
+ return -EINVAL;
+ }
+
+ if (state->input == *iarg)
+ break;
+ saa7115_dbg("now setting %s input\n",
+ *iarg >= 6 ? "S-Video" : "Composite");
+ state->input = *iarg;
+
+ /* select mode */
+ saa7115_write(client, 0x02,
+ (saa7115_read(client, 0x02) & 0xf0) |
+ state->input);
+
+ /* bypass chrominance trap for modes 6..9 */
+ saa7115_write(client, 0x09,
+ (saa7115_read(client, 0x09) & 0x7f) |
+ (state->input < 6 ? 0x0 : 0x80));
+ break;
+
+ case VIDIOC_STREAMON:
+ case VIDIOC_STREAMOFF:
+ saa7115_dbg("%s output\n",
+ (cmd == VIDIOC_STREAMON) ? "enable" : "disable");
+
+ if (state->enable != (cmd == VIDIOC_STREAMON)) {
+ state->enable = (cmd == VIDIOC_STREAMON);
+ saa7115_write(client, 0x87, state->enable);
+ }
+ break;
+
+ case VIDIOC_INT_DECODE_VBI_LINE:
+ saa7115_decode_vbi_line(client, arg);
+ break;
+
+ case VIDIOC_INT_RESET:
+ saa7115_dbg("decoder RESET\n");
+ saa7115_writeregs(client, saa7115_cfg_reset_scaler);
+ break;
+
+ case VIDIOC_INT_G_VBI_DATA:
+ {
+ struct v4l2_sliced_vbi_data *data = arg;
+
+ switch (data->id) {
+ case V4L2_SLICED_WSS_625:
+ if (saa7115_read(client, 0x6b) & 0xc0)
+ return -EIO;
+ data->data[0] = saa7115_read(client, 0x6c);
+ data->data[1] = saa7115_read(client, 0x6d);
+ return 0;
+ case V4L2_SLICED_CAPTION_525:
+ if (data->field == 0) {
+ /* CC */
+ if (saa7115_read(client, 0x66) & 0xc0)
+ return -EIO;
+ data->data[0] = saa7115_read(client, 0x67);
+ data->data[1] = saa7115_read(client, 0x68);
+ return 0;
+ }
+ /* XDS */
+ if (saa7115_read(client, 0x66) & 0x30)
+ return -EIO;
+ data->data[0] = saa7115_read(client, 0x69);
+ data->data[1] = saa7115_read(client, 0x6a);
+ return 0;
+ default:
+ return -EINVAL;
+ }
+ break;
+ }
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ case VIDIOC_INT_G_REGISTER:
+ {
+ struct v4l2_register *reg = arg;
+
+ if (reg->i2c_id != I2C_DRIVERID_SAA711X)
+ return -EINVAL;
+ reg->val = saa7115_read(client, reg->reg & 0xff);
+ break;
+ }
+
+ case VIDIOC_INT_S_REGISTER:
+ {
+ struct v4l2_register *reg = arg;
+
+ if (reg->i2c_id != I2C_DRIVERID_SAA711X)
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ saa7115_write(client, reg->reg & 0xff, reg->val & 0xff);
+ break;
+ }
+#endif
+
+ case VIDIOC_INT_G_CHIP_IDENT:
+ *iarg = state->ident;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static struct i2c_driver i2c_driver_saa7115;
+
+static int saa7115_attach(struct i2c_adapter *adapter, int address, int kind)
+{
+ struct i2c_client *client;
+ struct saa7115_state *state;
+ u8 chip_id;
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return 0;
+
+ client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
+ if (client == 0)
+ return -ENOMEM;
+ memset(client, 0, sizeof(struct i2c_client));
+ client->addr = address;
+ client->adapter = adapter;
+ client->driver = &i2c_driver_saa7115;
+ client->flags = I2C_CLIENT_ALLOW_USE;
+ snprintf(client->name, sizeof(client->name) - 1, "saa7115");
+
+ saa7115_dbg("detecting saa7115 client on address 0x%x\n", address << 1);
+
+ saa7115_write(client, 0, 5);
+ chip_id = saa7115_read(client, 0) & 0x0f;
+ if (chip_id != 4 && chip_id != 5) {
+ saa7115_dbg("saa7115 not found\n");
+ kfree(client);
+ return 0;
+ }
+ if (chip_id == 4) {
+ snprintf(client->name, sizeof(client->name) - 1, "saa7114");
+ }
+ saa7115_info("saa711%d found @ 0x%x (%s)\n", chip_id, address << 1, adapter->name);
+
+ state = kmalloc(sizeof(struct saa7115_state), GFP_KERNEL);
+ i2c_set_clientdata(client, state);
+ if (state == NULL) {
+ kfree(client);
+ return -ENOMEM;
+ }
+ memset(state, 0, sizeof(struct saa7115_state));
+ state->std = V4L2_STD_NTSC;
+ state->input = -1;
+ state->enable = 1;
+ state->bright = 128;
+ state->contrast = 64;
+ state->hue = 0;
+ state->sat = 64;
+ state->ident = (chip_id == 4) ? V4L2_IDENT_SAA7114 : V4L2_IDENT_SAA7115;
+ state->audclk_freq = V4L2_AUDCLK_48_KHZ;
+
+ saa7115_dbg("writing init values\n");
+
+ /* init to 60hz/48khz */
+ saa7115_writeregs(client, saa7115_init_auto_input);
+ saa7115_writeregs(client, saa7115_init_misc);
+ saa7115_writeregs(client, saa7115_cfg_60hz_fullres_x);
+ saa7115_writeregs(client, saa7115_cfg_60hz_fullres_y);
+ saa7115_writeregs(client, saa7115_cfg_60hz_video);
+ saa7115_writeregs(client, saa7115_cfg_48_audio);
+ saa7115_writeregs(client, saa7115_cfg_60hz_48_audio);
+ saa7115_writeregs(client, saa7115_cfg_reset_scaler);
+
+ i2c_attach_client(client);
+
+ saa7115_dbg("status: (1E) 0x%02x, (1F) 0x%02x\n",
+ saa7115_read(client, 0x1e), saa7115_read(client, 0x1f));
+
+ return 0;
+}
+
+static int saa7115_probe(struct i2c_adapter *adapter)
+{
+#ifdef I2C_CLASS_TV_ANALOG
+ if (adapter->class & I2C_CLASS_TV_ANALOG)
+#else
+ if (adapter->id == I2C_HW_B_BT848)
+#endif
+ return i2c_probe(adapter, &addr_data, &saa7115_attach);
+ return 0;
+}
+
+static int saa7115_detach(struct i2c_client *client)
+{
+ struct saa7115_state *state = i2c_get_clientdata(client);
+ int err;
+
+ err = i2c_detach_client(client);
+ if (err) {
+ return err;
+ }
+
+ kfree(state);
+ kfree(client);
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+/* i2c implementation */
+static struct i2c_driver i2c_driver_saa7115 = {
+ .name = "saa7115",
+ .id = I2C_DRIVERID_SAA711X,
+ .flags = I2C_DF_NOTIFY,
+ .attach_adapter = saa7115_probe,
+ .detach_client = saa7115_detach,
+ .command = saa7115_command,
+ .owner = THIS_MODULE,
+};
+
+
+static int __init saa7115_init_module(void)
+{
+ return i2c_add_driver(&i2c_driver_saa7115);
+}
+
+static void __exit saa7115_cleanup_module(void)
+{
+ i2c_del_driver(&i2c_driver_saa7115);
+}
+
+module_init(saa7115_init_module);
+module_exit(saa7115_cleanup_module);
diff --git a/drivers/media/video/saa711x.c b/drivers/media/video/saa711x.c
index 9aa8827de2c..25b30f352d8 100644
--- a/drivers/media/video/saa711x.c
+++ b/drivers/media/video/saa711x.c
@@ -36,7 +36,6 @@
#include <asm/pgtable.h>
#include <asm/page.h>
#include <linux/sched.h>
-#include <asm/segment.h>
#include <linux/types.h>
#include <asm/uaccess.h>
#include <linux/videodev.h>
diff --git a/drivers/media/video/saa7127.c b/drivers/media/video/saa7127.c
new file mode 100644
index 00000000000..843431f10e3
--- /dev/null
+++ b/drivers/media/video/saa7127.c
@@ -0,0 +1,849 @@
+/*
+ * saa7127 - Philips SAA7127/SAA7129 video encoder driver
+ *
+ * Copyright (C) 2003 Roy Bulter <rbulter@hetnet.nl>
+ *
+ * Based on SAA7126 video encoder driver by Gillem & Andreas Oberritter
+ *
+ * Copyright (C) 2000-2001 Gillem <htoa@gmx.net>
+ * Copyright (C) 2002 Andreas Oberritter <obi@saftware.de>
+ *
+ * Based on Stadis 4:2:2 MPEG-2 Decoder Driver by Nathan Laredo
+ *
+ * Copyright (C) 1999 Nathan Laredo <laredo@gnu.org>
+ *
+ * This driver is designed for the Hauppauge 250/350 Linux driver
+ * from the ivtv Project
+ *
+ * Copyright (C) 2003 Kevin Thayer <nufan_wfk@yahoo.com>
+ *
+ * Dual output support:
+ * Copyright (C) 2004 Eric Varsanyi
+ *
+ * NTSC Tuning and 7.5 IRE Setup
+ * Copyright (C) 2004 Chris Kennedy <c@groovy.org>
+ *
+ * VBI additions & cleanup:
+ * Copyright (C) 2004, 2005 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * Note: the saa7126 is identical to the saa7127, and the saa7128 is
+ * identical to the saa7129, except that the saa7126 and saa7128 have
+ * macrovision anti-taping support. This driver will almost certainly
+ * work find for those chips, except of course for the missing anti-taping
+ * support.
+ *
+ * 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 <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+
+static int debug = 0;
+static int test_image = 0;
+
+MODULE_DESCRIPTION("Philips SAA7127/9 video encoder driver");
+MODULE_AUTHOR("Kevin Thayer, Chris Kennedy, Hans Verkuil");
+MODULE_LICENSE("GPL");
+module_param(debug, int, 0644);
+module_param(test_image, int, 0644);
+MODULE_PARM_DESC(debug, "debug level (0-2)");
+MODULE_PARM_DESC(test_image, "test_image (0-1)");
+
+#define saa7127_dbg(fmt, arg...) \
+ do { \
+ if (debug >= 1) \
+ printk(KERN_INFO "%s debug %d-%04x: " fmt, client->driver->name, \
+ i2c_adapter_id(client->adapter), client->addr , ## arg); \
+ } while (0)
+
+/* High volume debug. Use with care. */
+#define saa7127_dbg_highvol(fmt, arg...) \
+ do { \
+ if (debug == 2) \
+ printk(KERN_INFO "%s debug %d-%04x: " fmt, client->driver->name, \
+ i2c_adapter_id(client->adapter), client->addr , ## arg); \
+ } while (0)
+
+#define saa7127_err(fmt, arg...) do { \
+ printk(KERN_ERR "%s %d-%04x: " fmt, client->driver->name, \
+ i2c_adapter_id(client->adapter), client->addr , ## arg); } while (0)
+#define saa7127_info(fmt, arg...) do { \
+ printk(KERN_INFO "%s %d-%04x: " fmt, client->driver->name, \
+ i2c_adapter_id(client->adapter), client->addr , ## arg); } while (0)
+
+static unsigned short normal_i2c[] = { 0x88 >> 1, I2C_CLIENT_END };
+
+
+I2C_CLIENT_INSMOD;
+
+/*
+ * SAA7127 registers
+ */
+
+#define SAA7127_REG_STATUS 0x00
+#define SAA7127_REG_WIDESCREEN_CONFIG 0x26
+#define SAA7127_REG_WIDESCREEN_ENABLE 0x27
+#define SAA7127_REG_BURST_START 0x28
+#define SAA7127_REG_BURST_END 0x29
+#define SAA7127_REG_COPYGEN_0 0x2a
+#define SAA7127_REG_COPYGEN_1 0x2b
+#define SAA7127_REG_COPYGEN_2 0x2c
+#define SAA7127_REG_OUTPUT_PORT_CONTROL 0x2d
+#define SAA7127_REG_GAIN_LUMINANCE_RGB 0x38
+#define SAA7127_REG_GAIN_COLORDIFF_RGB 0x39
+#define SAA7127_REG_INPUT_PORT_CONTROL_1 0x3a
+#define SAA7129_REG_FADE_KEY_COL2 0x4f
+#define SAA7127_REG_CHROMA_PHASE 0x5a
+#define SAA7127_REG_GAINU 0x5b
+#define SAA7127_REG_GAINV 0x5c
+#define SAA7127_REG_BLACK_LEVEL 0x5d
+#define SAA7127_REG_BLANKING_LEVEL 0x5e
+#define SAA7127_REG_VBI_BLANKING 0x5f
+#define SAA7127_REG_DAC_CONTROL 0x61
+#define SAA7127_REG_BURST_AMP 0x62
+#define SAA7127_REG_SUBC3 0x63
+#define SAA7127_REG_SUBC2 0x64
+#define SAA7127_REG_SUBC1 0x65
+#define SAA7127_REG_SUBC0 0x66
+#define SAA7127_REG_LINE_21_ODD_0 0x67
+#define SAA7127_REG_LINE_21_ODD_1 0x68
+#define SAA7127_REG_LINE_21_EVEN_0 0x69
+#define SAA7127_REG_LINE_21_EVEN_1 0x6a
+#define SAA7127_REG_RCV_PORT_CONTROL 0x6b
+#define SAA7127_REG_VTRIG 0x6c
+#define SAA7127_REG_HTRIG_HI 0x6d
+#define SAA7127_REG_MULTI 0x6e
+#define SAA7127_REG_CLOSED_CAPTION 0x6f
+#define SAA7127_REG_RCV2_OUTPUT_START 0x70
+#define SAA7127_REG_RCV2_OUTPUT_END 0x71
+#define SAA7127_REG_RCV2_OUTPUT_MSBS 0x72
+#define SAA7127_REG_TTX_REQUEST_H_START 0x73
+#define SAA7127_REG_TTX_REQUEST_H_DELAY_LENGTH 0x74
+#define SAA7127_REG_CSYNC_ADVANCE_VSYNC_SHIFT 0x75
+#define SAA7127_REG_TTX_ODD_REQ_VERT_START 0x76
+#define SAA7127_REG_TTX_ODD_REQ_VERT_END 0x77
+#define SAA7127_REG_TTX_EVEN_REQ_VERT_START 0x78
+#define SAA7127_REG_TTX_EVEN_REQ_VERT_END 0x79
+#define SAA7127_REG_FIRST_ACTIVE 0x7a
+#define SAA7127_REG_LAST_ACTIVE 0x7b
+#define SAA7127_REG_MSB_VERTICAL 0x7c
+#define SAA7127_REG_DISABLE_TTX_LINE_LO_0 0x7e
+#define SAA7127_REG_DISABLE_TTX_LINE_LO_1 0x7f
+
+/*
+ **********************************************************************
+ *
+ * Arrays with configuration parameters for the SAA7127
+ *
+ **********************************************************************
+ */
+
+struct i2c_reg_value {
+ unsigned char reg;
+ unsigned char value;
+};
+
+static const struct i2c_reg_value saa7129_init_config_extra[] = {
+ { SAA7127_REG_OUTPUT_PORT_CONTROL, 0x38 },
+ { SAA7127_REG_VTRIG, 0xfa },
+};
+
+static const struct i2c_reg_value saa7127_init_config_common[] = {
+ { SAA7127_REG_WIDESCREEN_CONFIG, 0x0d },
+ { SAA7127_REG_WIDESCREEN_ENABLE, 0x00 },
+ { SAA7127_REG_COPYGEN_0, 0x77 },
+ { SAA7127_REG_COPYGEN_1, 0x41 },
+ { SAA7127_REG_COPYGEN_2, 0x00 }, /* Macrovision enable/disable */
+ { SAA7127_REG_OUTPUT_PORT_CONTROL, 0x9e },
+ { SAA7127_REG_GAIN_LUMINANCE_RGB, 0x00 },
+ { SAA7127_REG_GAIN_COLORDIFF_RGB, 0x00 },
+ { SAA7127_REG_INPUT_PORT_CONTROL_1, 0x80 }, /* for color bars */
+ { SAA7127_REG_LINE_21_ODD_0, 0x77 },
+ { SAA7127_REG_LINE_21_ODD_1, 0x41 },
+ { SAA7127_REG_LINE_21_EVEN_0, 0x88 },
+ { SAA7127_REG_LINE_21_EVEN_1, 0x41 },
+ { SAA7127_REG_RCV_PORT_CONTROL, 0x12 },
+ { SAA7127_REG_VTRIG, 0xf9 },
+ { SAA7127_REG_HTRIG_HI, 0x00 },
+ { SAA7127_REG_RCV2_OUTPUT_START, 0x41 },
+ { SAA7127_REG_RCV2_OUTPUT_END, 0xc3 },
+ { SAA7127_REG_RCV2_OUTPUT_MSBS, 0x00 },
+ { SAA7127_REG_TTX_REQUEST_H_START, 0x3e },
+ { SAA7127_REG_TTX_REQUEST_H_DELAY_LENGTH, 0xb8 },
+ { SAA7127_REG_CSYNC_ADVANCE_VSYNC_SHIFT, 0x03 },
+ { SAA7127_REG_TTX_ODD_REQ_VERT_START, 0x15 },
+ { SAA7127_REG_TTX_ODD_REQ_VERT_END, 0x16 },
+ { SAA7127_REG_TTX_EVEN_REQ_VERT_START, 0x15 },
+ { SAA7127_REG_TTX_EVEN_REQ_VERT_END, 0x16 },
+ { SAA7127_REG_FIRST_ACTIVE, 0x1a },
+ { SAA7127_REG_LAST_ACTIVE, 0x01 },
+ { SAA7127_REG_MSB_VERTICAL, 0xc0 },
+ { SAA7127_REG_DISABLE_TTX_LINE_LO_0, 0x00 },
+ { SAA7127_REG_DISABLE_TTX_LINE_LO_1, 0x00 },
+ { 0, 0 }
+};
+
+#define SAA7127_60HZ_DAC_CONTROL 0x15
+static const struct i2c_reg_value saa7127_init_config_60hz[] = {
+ { SAA7127_REG_BURST_START, 0x19 },
+ /* BURST_END is also used as a chip ID in saa7127_detect_client */
+ { SAA7127_REG_BURST_END, 0x1d },
+ { SAA7127_REG_CHROMA_PHASE, 0xa3 },
+ { SAA7127_REG_GAINU, 0x98 },
+ { SAA7127_REG_GAINV, 0xd3 },
+ { SAA7127_REG_BLACK_LEVEL, 0x39 },
+ { SAA7127_REG_BLANKING_LEVEL, 0x2e },
+ { SAA7127_REG_VBI_BLANKING, 0x2e },
+ { SAA7127_REG_DAC_CONTROL, 0x15 },
+ { SAA7127_REG_BURST_AMP, 0x4d },
+ { SAA7127_REG_SUBC3, 0x1f },
+ { SAA7127_REG_SUBC2, 0x7c },
+ { SAA7127_REG_SUBC1, 0xf0 },
+ { SAA7127_REG_SUBC0, 0x21 },
+ { SAA7127_REG_MULTI, 0x90 },
+ { SAA7127_REG_CLOSED_CAPTION, 0x11 },
+ { 0, 0 }
+};
+
+#define SAA7127_50HZ_DAC_CONTROL 0x02
+struct i2c_reg_value saa7127_init_config_50hz[] = {
+ { SAA7127_REG_BURST_START, 0x21 },
+ /* BURST_END is also used as a chip ID in saa7127_detect_client */
+ { SAA7127_REG_BURST_END, 0x1d },
+ { SAA7127_REG_CHROMA_PHASE, 0x3f },
+ { SAA7127_REG_GAINU, 0x7d },
+ { SAA7127_REG_GAINV, 0xaf },
+ { SAA7127_REG_BLACK_LEVEL, 0x33 },
+ { SAA7127_REG_BLANKING_LEVEL, 0x35 },
+ { SAA7127_REG_VBI_BLANKING, 0x35 },
+ { SAA7127_REG_DAC_CONTROL, 0x02 },
+ { SAA7127_REG_BURST_AMP, 0x2f },
+ { SAA7127_REG_SUBC3, 0xcb },
+ { SAA7127_REG_SUBC2, 0x8a },
+ { SAA7127_REG_SUBC1, 0x09 },
+ { SAA7127_REG_SUBC0, 0x2a },
+ { SAA7127_REG_MULTI, 0xa0 },
+ { SAA7127_REG_CLOSED_CAPTION, 0x00 },
+ { 0, 0 }
+};
+
+/* Enumeration for the Supported input types */
+enum saa7127_input_type {
+ SAA7127_INPUT_TYPE_NORMAL,
+ SAA7127_INPUT_TYPE_TEST_IMAGE
+};
+
+/* Enumeration for the Supported Output signal types */
+enum saa7127_output_type {
+ SAA7127_OUTPUT_TYPE_BOTH,
+ SAA7127_OUTPUT_TYPE_COMPOSITE,
+ SAA7127_OUTPUT_TYPE_SVIDEO,
+ SAA7127_OUTPUT_TYPE_RGB,
+ SAA7127_OUTPUT_TYPE_YUV_C,
+ SAA7127_OUTPUT_TYPE_YUV_V
+};
+
+/*
+ **********************************************************************
+ *
+ * Encoder Struct, holds the configuration state of the encoder
+ *
+ **********************************************************************
+ */
+
+struct saa7127_state {
+ v4l2_std_id std;
+ enum v4l2_chip_ident ident;
+ enum saa7127_input_type input_type;
+ enum saa7127_output_type output_type;
+ int video_enable;
+ int wss_enable;
+ u16 wss_mode;
+ int cc_enable;
+ u16 cc_data;
+ int xds_enable;
+ u16 xds_data;
+ int vps_enable;
+ u8 vps_data[5];
+ u8 reg_2d;
+ u8 reg_3a;
+ u8 reg_3a_cb; /* colorbar bit */
+ u8 reg_61;
+};
+
+static const char * const output_strs[] =
+{
+ "S-Video + Composite",
+ "Composite",
+ "S-Video",
+ "RGB",
+ "YUV C",
+ "YUV V"
+};
+
+static const char * const wss_strs[] = {
+ "invalid",
+ "letterbox 14:9 center",
+ "letterbox 14:9 top",
+ "invalid",
+ "letterbox 16:9 top",
+ "invalid",
+ "invalid",
+ "16:9 full format anamorphic"
+ "4:3 full format",
+ "invalid",
+ "invalid",
+ "letterbox 16:9 center",
+ "invalid",
+ "letterbox >16:9 center",
+ "14:9 full format center",
+ "invalid",
+};
+
+/* ----------------------------------------------------------------------- */
+
+static int saa7127_read(struct i2c_client *client, u8 reg)
+{
+ return i2c_smbus_read_byte_data(client, reg);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int saa7127_write(struct i2c_client *client, u8 reg, u8 val)
+{
+ int i;
+
+ for (i = 0; i < 3; i++) {
+ if (i2c_smbus_write_byte_data(client, reg, val) == 0)
+ return 0;
+ }
+ saa7127_err("I2C Write Problem\n");
+ return -1;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int saa7127_write_inittab(struct i2c_client *client,
+ const struct i2c_reg_value *regs)
+{
+ while (regs->reg != 0) {
+ saa7127_write(client, regs->reg, regs->value);
+ regs++;
+ }
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int saa7127_set_vps(struct i2c_client *client, struct v4l2_sliced_vbi_data *data)
+{
+ struct saa7127_state *state = i2c_get_clientdata(client);
+ int enable = (data->line != 0);
+
+ if (enable && (data->field != 0 || data->line != 16))
+ return -EINVAL;
+ if (state->vps_enable != enable) {
+ saa7127_dbg("Turn VPS Signal %s\n", enable ? "on" : "off");
+ saa7127_write(client, 0x54, enable << 7);
+ state->vps_enable = enable;
+ }
+ if (!enable)
+ return 0;
+
+ state->vps_data[0] = data->data[4];
+ state->vps_data[1] = data->data[10];
+ state->vps_data[2] = data->data[11];
+ state->vps_data[3] = data->data[12];
+ state->vps_data[4] = data->data[13];
+ saa7127_dbg("Set VPS data %02x %02x %02x %02x %02x\n",
+ state->vps_data[0], state->vps_data[1],
+ state->vps_data[2], state->vps_data[3],
+ state->vps_data[4]);
+ saa7127_write(client, 0x55, state->vps_data[0]);
+ saa7127_write(client, 0x56, state->vps_data[1]);
+ saa7127_write(client, 0x57, state->vps_data[2]);
+ saa7127_write(client, 0x58, state->vps_data[3]);
+ saa7127_write(client, 0x59, state->vps_data[4]);
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int saa7127_set_cc(struct i2c_client *client, struct v4l2_sliced_vbi_data *data)
+{
+ struct saa7127_state *state = i2c_get_clientdata(client);
+ u16 cc = data->data[0] << 8 | data->data[1];
+ int enable = (data->line != 0);
+
+ if (enable && (data->field != 0 || data->line != 21))
+ return -EINVAL;
+ if (state->cc_enable != enable) {
+ saa7127_dbg("Turn CC %s\n", enable ? "on" : "off");
+ saa7127_write(client, SAA7127_REG_CLOSED_CAPTION,
+ (enable << 6) | 0x11);
+ state->cc_enable = enable;
+ }
+ if (!enable)
+ return 0;
+
+ saa7127_dbg_highvol("CC data: %04x\n", cc);
+ saa7127_write(client, SAA7127_REG_LINE_21_ODD_0, cc & 0xff);
+ saa7127_write(client, SAA7127_REG_LINE_21_ODD_1, cc >> 8);
+ state->cc_data = cc;
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int saa7127_set_xds(struct i2c_client *client, struct v4l2_sliced_vbi_data *data)
+{
+ struct saa7127_state *state = i2c_get_clientdata(client);
+ u16 xds = data->data[1] << 8 | data->data[0];
+ int enable = (data->line != 0);
+
+ if (enable && (data->field != 1 || data->line != 21))
+ return -EINVAL;
+ if (state->xds_enable != enable) {
+ saa7127_dbg("Turn XDS %s\n", enable ? "on" : "off");
+ saa7127_write(client, SAA7127_REG_CLOSED_CAPTION,
+ (enable << 7) | 0x11);
+ state->xds_enable = enable;
+ }
+ if (!enable)
+ return 0;
+
+ saa7127_dbg_highvol("XDS data: %04x\n", xds);
+ saa7127_write(client, SAA7127_REG_LINE_21_EVEN_0, xds & 0xff);
+ saa7127_write(client, SAA7127_REG_LINE_21_EVEN_1, xds >> 8);
+ state->xds_data = xds;
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int saa7127_set_wss(struct i2c_client *client, struct v4l2_sliced_vbi_data *data)
+{
+ struct saa7127_state *state = i2c_get_clientdata(client);
+ int enable = (data->line != 0);
+
+ if (enable && (data->field != 0 || data->line != 23))
+ return -EINVAL;
+ if (state->wss_enable != enable) {
+ saa7127_dbg("Turn WSS %s\n", enable ? "on" : "off");
+ saa7127_write(client, 0x27, enable << 7);
+ state->wss_enable = enable;
+ }
+ if (!enable)
+ return 0;
+
+ saa7127_write(client, 0x26, data->data[0]);
+ saa7127_write(client, 0x27, 0x80 | (data->data[1] & 0x3f));
+ saa7127_dbg("WSS mode: %s\n", wss_strs[data->data[0] & 0xf]);
+ state->wss_mode = (data->data[1] & 0x3f) << 8 | data->data[0];
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int saa7127_set_video_enable(struct i2c_client *client, int enable)
+{
+ struct saa7127_state *state = i2c_get_clientdata(client);
+
+ if (enable) {
+ saa7127_dbg("Enable Video Output\n");
+ saa7127_write(client, 0x2d, state->reg_2d);
+ saa7127_write(client, 0x61, state->reg_61);
+ } else {
+ saa7127_dbg("Disable Video Output\n");
+ saa7127_write(client, 0x2d, (state->reg_2d & 0xf0));
+ saa7127_write(client, 0x61, (state->reg_61 | 0xc0));
+ }
+ state->video_enable = enable;
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int saa7127_set_std(struct i2c_client *client, v4l2_std_id std)
+{
+ struct saa7127_state *state = i2c_get_clientdata(client);
+ const struct i2c_reg_value *inittab;
+
+ if (std & V4L2_STD_525_60) {
+ saa7127_dbg("Selecting 60 Hz video Standard\n");
+ inittab = saa7127_init_config_60hz;
+ state->reg_61 = SAA7127_60HZ_DAC_CONTROL;
+ } else {
+ saa7127_dbg("Selecting 50 Hz video Standard\n");
+ inittab = saa7127_init_config_50hz;
+ state->reg_61 = SAA7127_50HZ_DAC_CONTROL;
+ }
+
+ /* Write Table */
+ saa7127_write_inittab(client, inittab);
+ state->std = std;
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int saa7127_set_output_type(struct i2c_client *client, int output)
+{
+ struct saa7127_state *state = i2c_get_clientdata(client);
+
+ switch (output) {
+ case SAA7127_OUTPUT_TYPE_RGB:
+ state->reg_2d = 0x0f; /* RGB + CVBS (for sync) */
+ state->reg_3a = 0x13; /* by default switch YUV to RGB-matrix on */
+ break;
+
+ case SAA7127_OUTPUT_TYPE_COMPOSITE:
+ state->reg_2d = 0x08; /* 00001000 CVBS only, RGB DAC's off (high impedance mode) */
+ state->reg_3a = 0x13; /* by default switch YUV to RGB-matrix on */
+ break;
+
+ case SAA7127_OUTPUT_TYPE_SVIDEO:
+ state->reg_2d = 0xff; /* 11111111 croma -> R, luma -> CVBS + G + B */
+ state->reg_3a = 0x13; /* by default switch YUV to RGB-matrix on */
+ break;
+
+ case SAA7127_OUTPUT_TYPE_YUV_V:
+ state->reg_2d = 0x4f; /* reg 2D = 01001111, all DAC's on, RGB + VBS */
+ state->reg_3a = 0x0b; /* reg 3A = 00001011, bypass RGB-matrix */
+ break;
+
+ case SAA7127_OUTPUT_TYPE_YUV_C:
+ state->reg_2d = 0x0f; /* reg 2D = 00001111, all DAC's on, RGB + CVBS */
+ state->reg_3a = 0x0b; /* reg 3A = 00001011, bypass RGB-matrix */
+ break;
+
+ case SAA7127_OUTPUT_TYPE_BOTH:
+ state->reg_2d = 0xbf;
+ state->reg_3a = 0x13; /* by default switch YUV to RGB-matrix on */
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ saa7127_dbg("Selecting %s output type\n", output_strs[output]);
+
+ /* Configure Encoder */
+ saa7127_write(client, 0x2d, state->reg_2d);
+ saa7127_write(client, 0x3a, state->reg_3a | state->reg_3a_cb);
+ state->output_type = output;
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int saa7127_set_input_type(struct i2c_client *client, int input)
+{
+ struct saa7127_state *state = i2c_get_clientdata(client);
+
+ switch (input) {
+ case SAA7127_INPUT_TYPE_NORMAL: /* avia */
+ saa7127_dbg("Selecting Normal Encoder Input\n");
+ state->reg_3a_cb = 0;
+ break;
+
+ case SAA7127_INPUT_TYPE_TEST_IMAGE: /* color bar */
+ saa7127_dbg("Selecting Color Bar generator\n");
+ state->reg_3a_cb = 0x80;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ saa7127_write(client, 0x3a, state->reg_3a | state->reg_3a_cb);
+ state->input_type = input;
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int saa7127_command(struct i2c_client *client,
+ unsigned int cmd, void *arg)
+{
+ struct saa7127_state *state = i2c_get_clientdata(client);
+ struct v4l2_format *fmt = arg;
+ int *iarg = arg;
+
+ switch (cmd) {
+ case VIDIOC_S_STD:
+ if (state->std == *(v4l2_std_id *)arg)
+ break;
+ return saa7127_set_std(client, *(v4l2_std_id *)arg);
+
+ case VIDIOC_G_STD:
+ *(v4l2_std_id *)arg = state->std;
+ break;
+
+ case VIDIOC_S_INPUT:
+ if (state->input_type == *iarg)
+ break;
+ return saa7127_set_input_type(client, *iarg);
+
+ case VIDIOC_S_OUTPUT:
+ if (state->output_type == *iarg)
+ break;
+ return saa7127_set_output_type(client, *iarg);
+
+ case VIDIOC_STREAMON:
+ case VIDIOC_STREAMOFF:
+ if (state->video_enable == (cmd == VIDIOC_STREAMON))
+ break;
+ return saa7127_set_video_enable(client, cmd == VIDIOC_STREAMON);
+
+ case VIDIOC_G_FMT:
+ if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
+ return -EINVAL;
+
+ memset(&fmt->fmt.sliced, 0, sizeof(fmt->fmt.sliced));
+ if (state->vps_enable)
+ fmt->fmt.sliced.service_lines[0][16] = V4L2_SLICED_VPS;
+ if (state->wss_enable)
+ fmt->fmt.sliced.service_lines[0][23] = V4L2_SLICED_WSS_625;
+ if (state->cc_enable) {
+ fmt->fmt.sliced.service_lines[0][21] = V4L2_SLICED_CAPTION_525;
+ fmt->fmt.sliced.service_lines[1][21] = V4L2_SLICED_CAPTION_525;
+ }
+ fmt->fmt.sliced.service_set =
+ (state->vps_enable ? V4L2_SLICED_VPS : 0) |
+ (state->wss_enable ? V4L2_SLICED_WSS_625 : 0) |
+ (state->cc_enable ? V4L2_SLICED_CAPTION_525 : 0);
+ break;
+
+ case VIDIOC_LOG_STATUS:
+ saa7127_info("Standard: %s\n", (state->std & V4L2_STD_525_60) ? "60 Hz" : "50 Hz");
+ saa7127_info("Input: %s\n", state->input_type ? "color bars" : "normal");
+ saa7127_info("Output: %s\n", state->video_enable ?
+ output_strs[state->output_type] : "disabled");
+ saa7127_info("WSS: %s\n", state->wss_enable ?
+ wss_strs[state->wss_mode] : "disabled");
+ saa7127_info("VPS: %s\n", state->vps_enable ? "enabled" : "disabled");
+ saa7127_info("CC: %s\n", state->cc_enable ? "enabled" : "disabled");
+ break;
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ case VIDIOC_INT_G_REGISTER:
+ {
+ struct v4l2_register *reg = arg;
+
+ if (reg->i2c_id != I2C_DRIVERID_SAA7127)
+ return -EINVAL;
+ reg->val = saa7127_read(client, reg->reg & 0xff);
+ break;
+ }
+
+ case VIDIOC_INT_S_REGISTER:
+ {
+ struct v4l2_register *reg = arg;
+
+ if (reg->i2c_id != I2C_DRIVERID_SAA7127)
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ saa7127_write(client, reg->reg & 0xff, reg->val & 0xff);
+ break;
+ }
+#endif
+
+ case VIDIOC_INT_S_VBI_DATA:
+ {
+ struct v4l2_sliced_vbi_data *data = arg;
+
+ switch (data->id) {
+ case V4L2_SLICED_WSS_625:
+ return saa7127_set_wss(client, data);
+ case V4L2_SLICED_VPS:
+ return saa7127_set_vps(client, data);
+ case V4L2_SLICED_CAPTION_525:
+ if (data->field == 0)
+ return saa7127_set_cc(client, data);
+ return saa7127_set_xds(client, data);
+ default:
+ return -EINVAL;
+ }
+ break;
+ }
+
+ case VIDIOC_INT_G_CHIP_IDENT:
+ *(enum v4l2_chip_ident *)arg = state->ident;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+struct i2c_driver i2c_driver_saa7127;
+
+/* ----------------------------------------------------------------------- */
+
+static int saa7127_attach(struct i2c_adapter *adapter, int address, int kind)
+{
+ struct i2c_client *client;
+ struct saa7127_state *state;
+ struct v4l2_sliced_vbi_data vbi = { 0, 0, 0, 0 }; /* set to disabled */
+ int read_result = 0;
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return 0;
+
+ client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
+ if (client == 0)
+ return -ENOMEM;
+
+ memset(client, 0, sizeof(struct i2c_client));
+ client->addr = address;
+ client->adapter = adapter;
+ client->driver = &i2c_driver_saa7127;
+ client->flags = I2C_CLIENT_ALLOW_USE;
+ snprintf(client->name, sizeof(client->name) - 1, "saa7127");
+
+ saa7127_dbg("detecting saa7127 client on address 0x%x\n", address << 1);
+
+ /* First test register 0: Bits 5-7 are a version ID (should be 0),
+ and bit 2 should also be 0.
+ This is rather general, so the second test is more specific and
+ looks at the 'ending point of burst in clock cycles' which is
+ 0x1d after a reset and not expected to ever change. */
+ if ((saa7127_read(client, 0) & 0xe4) != 0 ||
+ (saa7127_read(client, 0x29) & 0x3f) != 0x1d) {
+ saa7127_dbg("saa7127 not found\n");
+ kfree(client);
+ return 0;
+ }
+ state = kmalloc(sizeof(struct saa7127_state), GFP_KERNEL);
+
+ if (state == NULL) {
+ kfree(client);
+ return (-ENOMEM);
+ }
+
+ i2c_set_clientdata(client, state);
+ memset(state, 0, sizeof(struct saa7127_state));
+
+ /* Configure Encoder */
+
+ saa7127_dbg("Configuring encoder\n");
+ saa7127_write_inittab(client, saa7127_init_config_common);
+ saa7127_set_std(client, V4L2_STD_NTSC);
+ saa7127_set_output_type(client, SAA7127_OUTPUT_TYPE_BOTH);
+ saa7127_set_vps(client, &vbi);
+ saa7127_set_wss(client, &vbi);
+ saa7127_set_cc(client, &vbi);
+ saa7127_set_xds(client, &vbi);
+ if (test_image == 1) {
+ /* The Encoder has an internal Colorbar generator */
+ /* This can be used for debugging */
+ saa7127_set_input_type(client, SAA7127_INPUT_TYPE_TEST_IMAGE);
+ } else {
+ saa7127_set_input_type(client, SAA7127_INPUT_TYPE_NORMAL);
+ }
+ saa7127_set_video_enable(client, 1);
+
+ /* Detect if it's an saa7129 */
+ read_result = saa7127_read(client, SAA7129_REG_FADE_KEY_COL2);
+ saa7127_write(client, SAA7129_REG_FADE_KEY_COL2, 0xaa);
+ if (saa7127_read(client, SAA7129_REG_FADE_KEY_COL2) == 0xaa) {
+ saa7127_info("saa7129 found @ 0x%x (%s)\n", address << 1, adapter->name);
+ saa7127_write(client, SAA7129_REG_FADE_KEY_COL2, read_result);
+ saa7127_write_inittab(client, saa7129_init_config_extra);
+ state->ident = V4L2_IDENT_SAA7129;
+ } else {
+ saa7127_info("saa7127 found @ 0x%x (%s)\n", address << 1, adapter->name);
+ state->ident = V4L2_IDENT_SAA7127;
+ }
+
+ i2c_attach_client(client);
+
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int saa7127_probe(struct i2c_adapter *adapter)
+{
+#ifdef I2C_CLASS_TV_ANALOG
+ if (adapter->class & I2C_CLASS_TV_ANALOG)
+#else
+ if (adapter->id == I2C_HW_B_BT848)
+#endif
+ return i2c_probe(adapter, &addr_data, saa7127_attach);
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int saa7127_detach(struct i2c_client *client)
+{
+ struct saa7127_state *state = i2c_get_clientdata(client);
+ int err;
+
+ /* Turn off TV output */
+ saa7127_set_video_enable(client, 0);
+
+ err = i2c_detach_client(client);
+
+ if (err) {
+ return err;
+ }
+
+ kfree(state);
+ kfree(client);
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+struct i2c_driver i2c_driver_saa7127 = {
+ .name = "saa7127",
+ .id = I2C_DRIVERID_SAA7127,
+ .flags = I2C_DF_NOTIFY,
+ .attach_adapter = saa7127_probe,
+ .detach_client = saa7127_detach,
+ .command = saa7127_command,
+ .owner = THIS_MODULE,
+};
+
+
+/* ----------------------------------------------------------------------- */
+
+static int __init saa7127_init_module(void)
+{
+ return i2c_add_driver(&i2c_driver_saa7127);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void __exit saa7127_cleanup_module(void)
+{
+ i2c_del_driver(&i2c_driver_saa7127);
+}
+
+/* ----------------------------------------------------------------------- */
+
+module_init(saa7127_init_module);
+module_exit(saa7127_cleanup_module);
diff --git a/drivers/media/video/saa7134/Kconfig b/drivers/media/video/saa7134/Kconfig
index 624e8808a51..7bdeabe638c 100644
--- a/drivers/media/video/saa7134/Kconfig
+++ b/drivers/media/video/saa7134/Kconfig
@@ -1,10 +1,11 @@
config VIDEO_SAA7134
tristate "Philips SAA7134 support"
- depends on VIDEO_DEV && PCI && I2C && SOUND
+ depends on VIDEO_DEV && PCI && I2C && SOUND && SND
select VIDEO_BUF
select VIDEO_IR
select VIDEO_TUNER
select CRC32
+ select SND_PCM_OSS
---help---
This is a video4linux driver for Philips SAA713x based
TV cards.
diff --git a/drivers/media/video/saa7134/Makefile b/drivers/media/video/saa7134/Makefile
index e0b28f0533a..4226b61cc61 100644
--- a/drivers/media/video/saa7134/Makefile
+++ b/drivers/media/video/saa7134/Makefile
@@ -1,10 +1,11 @@
saa7134-objs := saa7134-cards.o saa7134-core.o saa7134-i2c.o \
- saa7134-oss.o saa7134-ts.o saa7134-tvaudio.o \
- saa7134-vbi.o saa7134-video.o saa7134-input.o
+ saa7134-ts.o saa7134-tvaudio.o saa7134-vbi.o \
+ saa7134-video.o saa7134-input.o
obj-$(CONFIG_VIDEO_SAA7134) += saa7134.o saa7134-empress.o \
- saa6752hs.o saa7134-alsa.o
+ saa6752hs.o saa7134-alsa.o \
+ saa7134-oss.o
obj-$(CONFIG_VIDEO_SAA7134_DVB) += saa7134-dvb.o
EXTRA_CFLAGS += -I$(src)/..
diff --git a/drivers/media/video/saa7134/saa7134-alsa.c b/drivers/media/video/saa7134/saa7134-alsa.c
index 4f3c4235432..5707c666660 100644
--- a/drivers/media/video/saa7134/saa7134-alsa.c
+++ b/drivers/media/video/saa7134/saa7134-alsa.c
@@ -30,7 +30,9 @@
#include <sound/core.h>
#include <sound/control.h>
#include <sound/pcm.h>
+#include <sound/pcm_params.h>
#include <sound/initval.h>
+#include <linux/interrupt.h>
#include "saa7134.h"
#include "saa7134-reg.h"
@@ -56,6 +58,8 @@ static int enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 0};
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for SAA7134 capture interface(s).");
+int position;
+
#define dprintk(fmt, arg...) if (debug) \
printk(KERN_DEBUG "%s/alsa: " fmt, dev->name , ## arg)
@@ -68,7 +72,7 @@ typedef struct snd_card_saa7134 {
int mixer_volume[MIXER_ADDR_LAST+1][2];
int capture_source[MIXER_ADDR_LAST+1][2];
struct pci_dev *pci;
- struct saa7134_dev *saadev;
+ struct saa7134_dev *dev;
unsigned long iobase;
int irq;
@@ -83,12 +87,10 @@ typedef struct snd_card_saa7134 {
*/
typedef struct snd_card_saa7134_pcm {
- struct saa7134_dev *saadev;
+ struct saa7134_dev *dev;
spinlock_t lock;
- unsigned int pcm_size; /* buffer size */
- unsigned int pcm_count; /* bytes per period */
- unsigned int pcm_bps; /* bytes per second */
+
snd_pcm_substream_t *substream;
} snd_card_saa7134_pcm_t;
@@ -100,13 +102,11 @@ static snd_card_t *snd_saa7134_cards[SNDRV_CARDS];
*
* Called when the capture device is released or the buffer overflows
*
- * - Copied verbatim from saa7134-oss's dsp_dma_stop. Can be dropped
- * if we just share dsp_dma_stop and use it here
+ * - Copied verbatim from saa7134-oss's dsp_dma_stop.
*
*/
static void saa7134_dma_stop(struct saa7134_dev *dev)
-
{
dev->dmasound.dma_blk = -1;
dev->dmasound.dma_running = 0;
@@ -118,8 +118,7 @@ static void saa7134_dma_stop(struct saa7134_dev *dev)
*
* Called when preparing the capture device for use
*
- * - Copied verbatim from saa7134-oss's dsp_dma_start. Can be dropped
- * if we just share dsp_dma_start and use it here
+ * - Copied verbatim from saa7134-oss's dsp_dma_start.
*
*/
@@ -170,9 +169,9 @@ void saa7134_irq_alsa_done(struct saa7134_dev *dev, unsigned long status)
if (dev->dmasound.read_count >= dev->dmasound.blksize * (dev->dmasound.blocks-2)) {
dprintk("irq: overrun [full=%d/%d] - Blocks in %d\n",dev->dmasound.read_count,
dev->dmasound.bufsize, dev->dmasound.blocks);
+ spin_unlock(&dev->slock);
snd_pcm_stop(dev->dmasound.substream,SNDRV_PCM_STATE_XRUN);
- saa7134_dma_stop(dev);
- goto done;
+ return;
}
/* next block addr */
@@ -194,6 +193,7 @@ void saa7134_irq_alsa_done(struct saa7134_dev *dev, unsigned long status)
snd_pcm_period_elapsed(dev->dmasound.substream);
spin_lock(&dev->slock);
}
+
done:
spin_unlock(&dev->slock);
@@ -209,7 +209,9 @@ void saa7134_irq_alsa_done(struct saa7134_dev *dev, unsigned long status)
static irqreturn_t saa7134_alsa_irq(int irq, void *dev_id, struct pt_regs *regs)
{
- struct saa7134_dev *dev = (struct saa7134_dev*) dev_id;
+ struct saa7134_dmasound *dmasound = dev_id;
+ struct saa7134_dev *dev = dmasound->priv_data;
+
unsigned long report, status;
int loop, handled = 0;
@@ -248,56 +250,23 @@ static int snd_card_saa7134_capture_trigger(snd_pcm_substream_t * substream,
int cmd)
{
snd_pcm_runtime_t *runtime = substream->runtime;
- snd_card_saa7134_pcm_t *saapcm = runtime->private_data;
- struct saa7134_dev *dev=saapcm->saadev;
+ snd_card_saa7134_pcm_t *pcm = runtime->private_data;
+ struct saa7134_dev *dev=pcm->dev;
int err = 0;
- spin_lock_irq(&dev->slock);
- if (cmd == SNDRV_PCM_TRIGGER_START) {
+ spin_lock(&dev->slock);
+ if (cmd == SNDRV_PCM_TRIGGER_START) {
/* start dma */
saa7134_dma_start(dev);
- } else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+ } else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
/* stop dma */
saa7134_dma_stop(dev);
- } else {
- err = -EINVAL;
- }
- spin_unlock_irq(&dev->slock);
-
- return err;
-}
-
-/*
- * DMA buffer config
- *
- * Sets the values that will later be used as the size of the buffer,
- * size of the fragments, and total number of fragments.
- * Must be called during the preparation stage, before memory is
- * allocated
- *
- * - Copied verbatim from saa7134-oss. Can be dropped
- * if we just share dsp_buffer_conf from OSS.
- */
+ } else {
+ err = -EINVAL;
+ }
+ spin_unlock(&dev->slock);
-static int dsp_buffer_conf(struct saa7134_dev *dev, int blksize, int blocks)
-{
- if (blksize < 0x100)
- blksize = 0x100;
- if (blksize > 0x10000)
- blksize = 0x10000;
-
- if (blocks < 2)
- blocks = 2;
- if ((blksize * blocks) > 1024*1024)
- blocks = 1024*1024 / blksize;
-
- dev->dmasound.blocks = blocks;
- dev->dmasound.blksize = blksize;
- dev->dmasound.bufsize = blksize * blocks;
-
- dprintk("buffer config: %d blocks / %d bytes, %d kB total\n",
- blocks,blksize,blksize * blocks / 1024);
- return 0;
+ return err;
}
/*
@@ -307,16 +276,16 @@ static int dsp_buffer_conf(struct saa7134_dev *dev, int blksize, int blocks)
* ALSA, but I was unable to use ALSA's own DMA, and had to force the
* usage of V4L's
*
- * - Copied verbatim from saa7134-oss. Can be dropped
- * if we just share dsp_buffer_init from OSS.
+ * - Copied verbatim from saa7134-oss.
+ *
*/
static int dsp_buffer_init(struct saa7134_dev *dev)
{
int err;
- if (!dev->dmasound.bufsize)
- BUG();
+ BUG_ON(!dev->dmasound.bufsize);
+
videobuf_dma_init(&dev->dmasound.dma);
err = videobuf_dma_init_kernel(&dev->dmasound.dma, PCI_DMA_FROMDEVICE,
(dev->dmasound.bufsize + PAGE_SIZE) >> PAGE_SHIFT);
@@ -326,6 +295,28 @@ static int dsp_buffer_init(struct saa7134_dev *dev)
}
/*
+ * DMA buffer release
+ *
+ * Called after closing the device, during snd_card_saa7134_capture_close
+ *
+ */
+
+static int dsp_buffer_free(struct saa7134_dev *dev)
+{
+ if (!dev->dmasound.blksize)
+ BUG();
+
+ videobuf_dma_free(&dev->dmasound.dma);
+
+ dev->dmasound.blocks = 0;
+ dev->dmasound.blksize = 0;
+ dev->dmasound.bufsize = 0;
+
+ return 0;
+}
+
+
+/*
* ALSA PCM preparation
*
* - One of the ALSA capture callbacks.
@@ -340,84 +331,30 @@ static int dsp_buffer_init(struct saa7134_dev *dev)
static int snd_card_saa7134_capture_prepare(snd_pcm_substream_t * substream)
{
snd_pcm_runtime_t *runtime = substream->runtime;
- int err, bswap, sign;
+ int bswap, sign;
u32 fmt, control;
snd_card_saa7134_t *saa7134 = snd_pcm_substream_chip(substream);
struct saa7134_dev *dev;
- snd_card_saa7134_pcm_t *saapcm = runtime->private_data;
- unsigned int bps;
- unsigned long size;
- unsigned count;
-
- size = snd_pcm_lib_buffer_bytes(substream);
- count = snd_pcm_lib_period_bytes(substream);
-
- saapcm->saadev->dmasound.substream = substream;
- bps = runtime->rate * runtime->channels;
- bps *= snd_pcm_format_width(runtime->format);
- bps /= 8;
- if (bps <= 0)
- return -EINVAL;
- saapcm->pcm_bps = bps;
- saapcm->pcm_size = snd_pcm_lib_buffer_bytes(substream);
- saapcm->pcm_count = snd_pcm_lib_period_bytes(substream);
-
+ snd_card_saa7134_pcm_t *pcm = runtime->private_data;
- dev=saa7134->saadev;
+ pcm->dev->dmasound.substream = substream;
- dsp_buffer_conf(dev,saapcm->pcm_count,(saapcm->pcm_size/saapcm->pcm_count));
+ dev = saa7134->dev;
- err = dsp_buffer_init(dev);
- if (0 != err)
- goto fail2;
-
- /* prepare buffer */
- if (0 != (err = videobuf_dma_pci_map(dev->pci,&dev->dmasound.dma)))
- return err;
- if (0 != (err = saa7134_pgtable_alloc(dev->pci,&dev->dmasound.pt)))
- goto fail1;
- if (0 != (err = saa7134_pgtable_build(dev->pci,&dev->dmasound.pt,
- dev->dmasound.dma.sglist,
- dev->dmasound.dma.sglen,
- 0)))
- goto fail2;
-
-
-
- switch (runtime->format) {
- case SNDRV_PCM_FORMAT_U8:
- case SNDRV_PCM_FORMAT_S8:
+ if (snd_pcm_format_width(runtime->format) == 8)
fmt = 0x00;
- break;
- case SNDRV_PCM_FORMAT_U16_LE:
- case SNDRV_PCM_FORMAT_U16_BE:
- case SNDRV_PCM_FORMAT_S16_LE:
- case SNDRV_PCM_FORMAT_S16_BE:
+ else
fmt = 0x01;
- break;
- default:
- err = -EINVAL;
- return 1;
- }
- switch (runtime->format) {
- case SNDRV_PCM_FORMAT_S8:
- case SNDRV_PCM_FORMAT_S16_LE:
- case SNDRV_PCM_FORMAT_S16_BE:
+ if (snd_pcm_format_signed(runtime->format))
sign = 1;
- break;
- default:
+ else
sign = 0;
- break;
- }
- switch (runtime->format) {
- case SNDRV_PCM_FORMAT_U16_BE:
- case SNDRV_PCM_FORMAT_S16_BE:
- bswap = 1; break;
- default:
- bswap = 0; break;
- }
+ if (snd_pcm_format_big_endian(runtime->format))
+ bswap = 1;
+ else
+ bswap = 0;
switch (dev->pci->device) {
case PCI_DEVICE_ID_PHILIPS_SAA7134:
@@ -445,7 +382,6 @@ static int snd_card_saa7134_capture_prepare(snd_pcm_substream_t * substream)
fmt |= 0x04;
saa_writel(SAA7133_NUM_SAMPLES, dev->dmasound.blksize -1);
saa_writel(SAA7133_AUDIO_CHANNEL, 0x543210 | (fmt << 24));
- //saa_writel(SAA7133_AUDIO_CHANNEL, 0x543210);
break;
}
@@ -459,12 +395,6 @@ static int snd_card_saa7134_capture_prepare(snd_pcm_substream_t * substream)
if (bswap)
control |= SAA7134_RS_CONTROL_BSWAP;
- /* I should be able to use runtime->dma_addr in the control
- byte, but it doesn't work. So I allocate the DMA using the
- V4L functions, and force ALSA to use that as the DMA area */
-
- runtime->dma_area = dev->dmasound.dma.vmalloc;
-
saa_writel(SAA7134_RS_BA1(6),0);
saa_writel(SAA7134_RS_BA2(6),dev->dmasound.blksize);
saa_writel(SAA7134_RS_PITCH(6),0);
@@ -473,12 +403,6 @@ static int snd_card_saa7134_capture_prepare(snd_pcm_substream_t * substream)
dev->dmasound.rate = runtime->rate;
return 0;
- fail2:
- saa7134_pgtable_free(dev->pci,&dev->dmasound.pt);
- fail1:
- videobuf_dma_pci_unmap(dev->pci,&dev->dmasound.dma);
- return err;
-
}
@@ -496,10 +420,8 @@ static int snd_card_saa7134_capture_prepare(snd_pcm_substream_t * substream)
static snd_pcm_uframes_t snd_card_saa7134_capture_pointer(snd_pcm_substream_t * substream)
{
snd_pcm_runtime_t *runtime = substream->runtime;
- snd_card_saa7134_pcm_t *saapcm = runtime->private_data;
- struct saa7134_dev *dev=saapcm->saadev;
-
-
+ snd_card_saa7134_pcm_t *pcm = runtime->private_data;
+ struct saa7134_dev *dev=pcm->dev;
if (dev->dmasound.read_count) {
dev->dmasound.read_count -= snd_pcm_lib_period_bytes(substream);
@@ -540,9 +462,9 @@ static snd_pcm_hardware_t snd_card_saa7134_capture =
static void snd_card_saa7134_runtime_free(snd_pcm_runtime_t *runtime)
{
- snd_card_saa7134_pcm_t *saapcm = runtime->private_data;
+ snd_card_saa7134_pcm_t *pcm = runtime->private_data;
- kfree(saapcm);
+ kfree(pcm);
}
@@ -552,17 +474,76 @@ static void snd_card_saa7134_runtime_free(snd_pcm_runtime_t *runtime)
* - One of the ALSA capture callbacks.
*
* Called on initialization, right before the PCM preparation
- * Usually used in ALSA to allocate the DMA, but since we don't use the
- * ALSA DMA it does nothing
*
*/
static int snd_card_saa7134_hw_params(snd_pcm_substream_t * substream,
snd_pcm_hw_params_t * hw_params)
{
+ snd_card_saa7134_t *saa7134 = snd_pcm_substream_chip(substream);
+ struct saa7134_dev *dev;
+ unsigned int period_size, periods;
+ int err;
- return 0;
+ period_size = params_period_bytes(hw_params);
+ periods = params_periods(hw_params);
+
+ snd_assert(period_size >= 0x100 && period_size <= 0x10000,
+ return -EINVAL);
+ snd_assert(periods >= 2, return -EINVAL);
+ snd_assert(period_size * periods <= 1024 * 1024, return -EINVAL);
+ dev = saa7134->dev;
+
+ if (dev->dmasound.blocks == periods &&
+ dev->dmasound.blksize == period_size)
+ return 0;
+
+ /* release the old buffer */
+ if (substream->runtime->dma_area) {
+ saa7134_pgtable_free(dev->pci, &dev->dmasound.pt);
+ videobuf_dma_pci_unmap(dev->pci, &dev->dmasound.dma);
+ dsp_buffer_free(dev);
+ substream->runtime->dma_area = NULL;
+ }
+ dev->dmasound.blocks = periods;
+ dev->dmasound.blksize = period_size;
+ dev->dmasound.bufsize = period_size * periods;
+
+ err = dsp_buffer_init(dev);
+ if (0 != err) {
+ dev->dmasound.blocks = 0;
+ dev->dmasound.blksize = 0;
+ dev->dmasound.bufsize = 0;
+ return err;
+ }
+
+ if (0 != (err = videobuf_dma_pci_map(dev->pci, &dev->dmasound.dma))) {
+ dsp_buffer_free(dev);
+ return err;
+ }
+ if (0 != (err = saa7134_pgtable_alloc(dev->pci,&dev->dmasound.pt))) {
+ videobuf_dma_pci_unmap(dev->pci, &dev->dmasound.dma);
+ dsp_buffer_free(dev);
+ return err;
+ }
+ if (0 != (err = saa7134_pgtable_build(dev->pci,&dev->dmasound.pt,
+ dev->dmasound.dma.sglist,
+ dev->dmasound.dma.sglen,
+ 0))) {
+ saa7134_pgtable_free(dev->pci, &dev->dmasound.pt);
+ videobuf_dma_pci_unmap(dev->pci, &dev->dmasound.dma);
+ dsp_buffer_free(dev);
+ return err;
+ }
+
+ /* I should be able to use runtime->dma_addr in the control
+ byte, but it doesn't work. So I allocate the DMA using the
+ V4L functions, and force ALSA to use that as the DMA area */
+
+ substream->runtime->dma_area = dev->dmasound.dma.vmalloc;
+
+ return 1;
}
@@ -572,33 +553,23 @@ static int snd_card_saa7134_hw_params(snd_pcm_substream_t * substream,
* - One of the ALSA capture callbacks.
*
* Called after closing the device, but before snd_card_saa7134_capture_close
- * Usually used in ALSA to free the DMA, but since we don't use the
- * ALSA DMA I'm almost sure this isn't necessary.
+ * It stops the DMA audio and releases the buffers.
*
*/
static int snd_card_saa7134_hw_free(snd_pcm_substream_t * substream)
{
- return 0;
-}
-
-/*
- * DMA buffer release
- *
- * Called after closing the device, during snd_card_saa7134_capture_close
- *
- */
-
-static int dsp_buffer_free(struct saa7134_dev *dev)
-{
- if (!dev->dmasound.blksize)
- BUG();
+ snd_card_saa7134_t *saa7134 = snd_pcm_substream_chip(substream);
+ struct saa7134_dev *dev;
- videobuf_dma_free(&dev->dmasound.dma);
+ dev = saa7134->dev;
- dev->dmasound.blocks = 0;
- dev->dmasound.blksize = 0;
- dev->dmasound.bufsize = 0;
+ if (substream->runtime->dma_area) {
+ saa7134_pgtable_free(dev->pci, &dev->dmasound.pt);
+ videobuf_dma_pci_unmap(dev->pci, &dev->dmasound.dma);
+ dsp_buffer_free(dev);
+ substream->runtime->dma_area = NULL;
+ }
return 0;
}
@@ -608,21 +579,12 @@ static int dsp_buffer_free(struct saa7134_dev *dev)
*
* - One of the ALSA capture callbacks.
*
- * Called after closing the device. It stops the DMA audio and releases
- * the buffers
+ * Called after closing the device.
*
*/
static int snd_card_saa7134_capture_close(snd_pcm_substream_t * substream)
{
- snd_card_saa7134_t *chip = snd_pcm_substream_chip(substream);
- struct saa7134_dev *dev = chip->saadev;
-
- /* unlock buffer */
- saa7134_pgtable_free(dev->pci,&dev->dmasound.pt);
- videobuf_dma_pci_unmap(dev->pci,&dev->dmasound.dma);
-
- dsp_buffer_free(dev);
return 0;
}
@@ -639,29 +601,28 @@ static int snd_card_saa7134_capture_close(snd_pcm_substream_t * substream)
static int snd_card_saa7134_capture_open(snd_pcm_substream_t * substream)
{
snd_pcm_runtime_t *runtime = substream->runtime;
- snd_card_saa7134_pcm_t *saapcm;
+ snd_card_saa7134_pcm_t *pcm;
snd_card_saa7134_t *saa7134 = snd_pcm_substream_chip(substream);
- struct saa7134_dev *dev = saa7134->saadev;
+ struct saa7134_dev *dev = saa7134->dev;
int err;
down(&dev->dmasound.lock);
- dev->dmasound.afmt = SNDRV_PCM_FORMAT_U8;
- dev->dmasound.channels = 2;
dev->dmasound.read_count = 0;
dev->dmasound.read_offset = 0;
up(&dev->dmasound.lock);
- saapcm = kzalloc(sizeof(*saapcm), GFP_KERNEL);
- if (saapcm == NULL)
+ pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);
+ if (pcm == NULL)
return -ENOMEM;
- saapcm->saadev=saa7134->saadev;
- spin_lock_init(&saapcm->lock);
+ pcm->dev=saa7134->dev;
- saapcm->substream = substream;
- runtime->private_data = saapcm;
+ spin_lock_init(&pcm->lock);
+
+ pcm->substream = substream;
+ runtime->private_data = pcm;
runtime->private_free = snd_card_saa7134_runtime_free;
runtime->hw = snd_card_saa7134_capture;
@@ -736,7 +697,6 @@ static int snd_saa7134_volume_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_
static int snd_saa7134_volume_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
{
snd_card_saa7134_t *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int change, addr = kcontrol->private_value;
int left, right;
@@ -750,12 +710,12 @@ static int snd_saa7134_volume_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_
right = 0;
if (right > 20)
right = 20;
- spin_lock_irqsave(&chip->mixer_lock, flags);
+ spin_lock_irq(&chip->mixer_lock);
change = chip->mixer_volume[addr][0] != left ||
chip->mixer_volume[addr][1] != right;
chip->mixer_volume[addr][0] = left;
chip->mixer_volume[addr][1] = right;
- spin_unlock_irqrestore(&chip->mixer_lock, flags);
+ spin_unlock_irq(&chip->mixer_lock);
return change;
}
@@ -777,38 +737,37 @@ static int snd_saa7134_capsrc_info(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_
static int snd_saa7134_capsrc_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
{
snd_card_saa7134_t *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int addr = kcontrol->private_value;
- spin_lock_irqsave(&chip->mixer_lock, flags);
+ spin_lock_irq(&chip->mixer_lock);
ucontrol->value.integer.value[0] = chip->capture_source[addr][0];
ucontrol->value.integer.value[1] = chip->capture_source[addr][1];
- spin_unlock_irqrestore(&chip->mixer_lock, flags);
+ spin_unlock_irq(&chip->mixer_lock);
+
return 0;
}
static int snd_saa7134_capsrc_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
{
snd_card_saa7134_t *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int change, addr = kcontrol->private_value;
int left, right;
u32 anabar, xbarin;
int analog_io, rate;
struct saa7134_dev *dev;
- dev = chip->saadev;
+ dev = chip->dev;
left = ucontrol->value.integer.value[0] & 1;
right = ucontrol->value.integer.value[1] & 1;
- spin_lock_irqsave(&chip->mixer_lock, flags);
+ spin_lock_irq(&chip->mixer_lock);
change = chip->capture_source[addr][0] != left ||
chip->capture_source[addr][1] != right;
chip->capture_source[addr][0] = left;
chip->capture_source[addr][1] = right;
dev->dmasound.input=addr;
- spin_unlock_irqrestore(&chip->mixer_lock, flags);
+ spin_unlock_irq(&chip->mixer_lock);
if (change) {
@@ -898,43 +857,44 @@ static int snd_card_saa7134_new_mixer(snd_card_saa7134_t * chip)
return 0;
}
-static int snd_saa7134_free(snd_card_saa7134_t *chip)
+static void snd_saa7134_free(snd_card_t * card)
{
- return 0;
-}
+ snd_card_saa7134_t *chip = card->private_data;
+
+ if (chip->dev->dmasound.priv_data == NULL)
+ return;
+
+ if (chip->irq >= 0) {
+ synchronize_irq(chip->irq);
+ free_irq(chip->irq, &chip->dev->dmasound);
+ }
+
+ chip->dev->dmasound.priv_data = NULL;
-static int snd_saa7134_dev_free(snd_device_t *device)
-{
- snd_card_saa7134_t *chip = device->device_data;
- return snd_saa7134_free(chip);
}
/*
* ALSA initialization
*
- * Called by saa7134-core, it creates the basic structures and registers
- * the ALSA devices
+ * Called by the init routine, once for each saa7134 device present,
+ * it creates the basic structures and registers the ALSA devices
*
*/
-int alsa_card_saa7134_create (struct saa7134_dev *saadev)
+int alsa_card_saa7134_create(struct saa7134_dev *dev, int devnum)
{
- static int dev;
snd_card_t *card;
snd_card_saa7134_t *chip;
int err;
- static snd_device_ops_t ops = {
- .dev_free = snd_saa7134_dev_free,
- };
- if (dev >= SNDRV_CARDS)
+ if (devnum >= SNDRV_CARDS)
return -ENODEV;
- if (!enable[dev])
+ if (!enable[devnum])
return -ENODEV;
- card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+ card = snd_card_new(index[devnum], id[devnum], THIS_MODULE, sizeof(snd_card_saa7134_t));
if (card == NULL)
return -ENOMEM;
@@ -943,34 +903,33 @@ int alsa_card_saa7134_create (struct saa7134_dev *saadev)
/* Card "creation" */
- chip = kcalloc(1, sizeof(*chip), GFP_KERNEL);
- if (chip == NULL) {
- return -ENOMEM;
- }
+ card->private_free = snd_saa7134_free;
+ chip = (snd_card_saa7134_t *) card->private_data;
spin_lock_init(&chip->lock);
spin_lock_init(&chip->mixer_lock);
- chip->saadev = saadev;
+ chip->dev = dev;
chip->card = card;
- chip->pci = saadev->pci;
- chip->irq = saadev->pci->irq;
- chip->iobase = pci_resource_start(saadev->pci, 0);
+ chip->pci = dev->pci;
+ chip->iobase = pci_resource_start(dev->pci, 0);
- err = request_irq(saadev->pci->irq, saa7134_alsa_irq,
- SA_SHIRQ | SA_INTERRUPT, saadev->name, saadev);
+
+ err = request_irq(dev->pci->irq, saa7134_alsa_irq,
+ SA_SHIRQ | SA_INTERRUPT, dev->name,
+ (void*) &dev->dmasound);
if (err < 0) {
printk(KERN_ERR "%s: can't get IRQ %d for ALSA\n",
- saadev->name, saadev->pci->irq);
+ dev->name, dev->pci->irq);
goto __nodev;
}
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
- goto __nodev;
- }
+ chip->irq = dev->pci->irq;
+
+ init_MUTEX(&dev->dmasound.lock);
if ((err = snd_card_saa7134_new_mixer(chip)) < 0)
goto __nodev;
@@ -984,16 +943,15 @@ int alsa_card_saa7134_create (struct saa7134_dev *saadev)
strcpy(card->shortname, "SAA7134");
sprintf(card->longname, "%s at 0x%lx irq %d",
- chip->saadev->name, chip->iobase, chip->irq);
+ chip->dev->name, chip->iobase, chip->irq);
if ((err = snd_card_register(card)) == 0) {
- snd_saa7134_cards[dev] = card;
+ snd_saa7134_cards[devnum] = card;
return 0;
}
__nodev:
snd_card_free(card);
- kfree(chip);
return err;
}
@@ -1007,21 +965,29 @@ __nodev:
static int saa7134_alsa_init(void)
{
- struct saa7134_dev *saadev = NULL;
- struct list_head *list;
+ struct saa7134_dev *dev = NULL;
+ struct list_head *list;
- printk(KERN_INFO "saa7134 ALSA driver for DMA sound loaded\n");
+ position = 0;
- list_for_each(list,&saa7134_devlist) {
- saadev = list_entry(list, struct saa7134_dev, devlist);
- alsa_card_saa7134_create(saadev);
- }
+ printk(KERN_INFO "saa7134 ALSA driver for DMA sound loaded\n");
- if (saadev == NULL)
+ list_for_each(list,&saa7134_devlist) {
+ dev = list_entry(list, struct saa7134_dev, devlist);
+ if (dev->dmasound.priv_data == NULL) {
+ dev->dmasound.priv_data = dev;
+ alsa_card_saa7134_create(dev,position);
+ position++;
+ } else {
+ printk(KERN_ERR "saa7134 ALSA: DMA sound is being handled by OSS. ignoring %s\n",dev->name);
+ return -EBUSY;
+ }
+ }
+
+ if (dev == NULL)
printk(KERN_INFO "saa7134 ALSA: no saa7134 cards found\n");
return 0;
-
}
/*
diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c
index 663d03e5bc6..75abc20b0cc 100644
--- a/drivers/media/video/saa7134/saa7134-cards.c
+++ b/drivers/media/video/saa7134/saa7134-cards.c
@@ -2529,6 +2529,32 @@ struct saa7134_board saa7134_boards[] = {
.amux = LINE1,
}},
},
+ [SAA7134_BOARD_MSI_TVATANYWHERE_PLUS] = {
+ .name = "MSI TV@Anywhere plus",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 3,
+ .amux = LINE1,
+ },{
+ .name = name_svideo,
+ .vmux = 0,
+ .amux = LINE1,
+ }},
+ .radio = {
+ .name = name_radio,
+ .amux = LINE1,
+ },
+ },
};
const unsigned int saa7134_bcount = ARRAY_SIZE(saa7134_boards);
@@ -2970,6 +2996,12 @@ struct pci_device_id saa7134_pci_tbl[] = {
.subdevice = 0x2018,
.driver_data = SAA7134_BOARD_PHILIPS_TIGER,
},{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x1462,
+ .subdevice = 0x6231,
+ .driver_data = SAA7134_BOARD_MSI_TVATANYWHERE_PLUS,
+ },{
/* --- boards without eeprom + subsystem ID --- */
.vendor = PCI_VENDOR_ID_PHILIPS,
.device = PCI_DEVICE_ID_PHILIPS_SAA7134,
diff --git a/drivers/media/video/saa7134/saa7134-core.c b/drivers/media/video/saa7134/saa7134-core.c
index 19b88744fb3..4275d2ddb86 100644
--- a/drivers/media/video/saa7134/saa7134-core.c
+++ b/drivers/media/video/saa7134/saa7134-core.c
@@ -53,13 +53,13 @@ static unsigned int gpio_tracking = 0;
module_param(gpio_tracking, int, 0644);
MODULE_PARM_DESC(gpio_tracking,"enable debug messages [gpio]");
-static unsigned int oss = 0;
-module_param(oss, int, 0444);
-MODULE_PARM_DESC(oss,"register oss devices (default: no)");
-
static unsigned int alsa = 0;
-module_param(alsa, int, 0444);
-MODULE_PARM_DESC(alsa,"register alsa devices (default: no)");
+module_param(alsa, int, 0644);
+MODULE_PARM_DESC(alsa,"enable ALSA DMA sound [dmasound]");
+
+static unsigned int oss = 0;
+module_param(oss, int, 0644);
+MODULE_PARM_DESC(oss,"enable OSS DMA sound [dmasound]");
static unsigned int latency = UNSET;
module_param(latency, int, 0444);
@@ -68,24 +68,18 @@ MODULE_PARM_DESC(latency,"pci latency timer");
static unsigned int video_nr[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET };
static unsigned int vbi_nr[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET };
static unsigned int radio_nr[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET };
-static unsigned int dsp_nr[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET };
-static unsigned int mixer_nr[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET };
static unsigned int tuner[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET };
static unsigned int card[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET };
module_param_array(video_nr, int, NULL, 0444);
module_param_array(vbi_nr, int, NULL, 0444);
module_param_array(radio_nr, int, NULL, 0444);
-module_param_array(dsp_nr, int, NULL, 0444);
-module_param_array(mixer_nr, int, NULL, 0444);
module_param_array(tuner, int, NULL, 0444);
module_param_array(card, int, NULL, 0444);
MODULE_PARM_DESC(video_nr, "video device number");
MODULE_PARM_DESC(vbi_nr, "vbi device number");
MODULE_PARM_DESC(radio_nr, "radio device number");
-MODULE_PARM_DESC(dsp_nr, "oss dsp device number");
-MODULE_PARM_DESC(mixer_nr, "oss mixer device number");
MODULE_PARM_DESC(tuner, "tuner type");
MODULE_PARM_DESC(card, "card type");
@@ -195,6 +189,7 @@ void saa7134_track_gpio(struct saa7134_dev *dev, char *msg)
static int need_empress;
static int need_dvb;
static int need_alsa;
+static int need_oss;
static int pending_call(struct notifier_block *self, unsigned long state,
void *module)
@@ -208,6 +203,8 @@ static int pending_call(struct notifier_block *self, unsigned long state,
request_module("saa7134-dvb");
if (need_alsa)
request_module("saa7134-alsa");
+ if (need_oss)
+ request_module("saa7134-oss");
return NOTIFY_DONE;
}
@@ -218,10 +215,11 @@ static struct notifier_block pending_notifier = {
static void request_module_depend(char *name, int *flag)
{
+ int err;
switch (THIS_MODULE->state) {
case MODULE_STATE_COMING:
if (!pending_registered) {
- register_module_notifier(&pending_notifier);
+ err = register_module_notifier(&pending_notifier);
pending_registered = 1;
}
*flag = 1;
@@ -578,12 +576,14 @@ static irqreturn_t saa7134_irq(int irq, void *dev_id, struct pt_regs *regs)
goto out;
}
- /* If alsa support is active and we get a sound report, exit
- and let the saa7134-alsa module deal with it */
+ /* If dmasound support is active and we get a sound report, exit
+ and let the saa7134-alsa/oss module deal with it */
- if ((report & SAA7134_IRQ_REPORT_DONE_RA3) && alsa) {
+ if ((report & SAA7134_IRQ_REPORT_DONE_RA3) &&
+ (dev->dmasound.priv_data != NULL) )
+ {
if (irq_debug > 1)
- printk(KERN_DEBUG "%s/irq: ignoring interrupt for ALSA\n",
+ printk(KERN_DEBUG "%s/irq: ignoring interrupt for DMA sound\n",
dev->name);
goto out;
}
@@ -609,12 +609,6 @@ static irqreturn_t saa7134_irq(int irq, void *dev_id, struct pt_regs *regs)
card_has_mpeg(dev))
saa7134_irq_ts_done(dev,status);
- if ((report & SAA7134_IRQ_REPORT_DONE_RA3)) {
- if (oss) {
- saa7134_irq_oss_done(dev,status);
- }
- }
-
if ((report & (SAA7134_IRQ_REPORT_GPIO16 |
SAA7134_IRQ_REPORT_GPIO18)) &&
dev->remote)
@@ -689,14 +683,6 @@ static int saa7134_hwinit1(struct saa7134_dev *dev)
* audio will not work.
*/
- switch (dev->pci->device) {
- case PCI_DEVICE_ID_PHILIPS_SAA7134:
- case PCI_DEVICE_ID_PHILIPS_SAA7133:
- case PCI_DEVICE_ID_PHILIPS_SAA7135:
- saa7134_oss_init1(dev);
- break;
- }
-
/* enable peripheral devices */
saa_writeb(SAA7134_SPECIAL_MODE, 0x01);
@@ -728,8 +714,6 @@ static int saa7134_hwinit2(struct saa7134_dev *dev)
irq2_mask |= (SAA7134_IRQ2_INTE_GPIO18 |
SAA7134_IRQ2_INTE_GPIO18A |
SAA7134_IRQ2_INTE_GPIO16 );
- else if (dev->has_remote == SAA7134_REMOTE_I2C)
- request_module("ir-kbd-i2c");
saa_writel(SAA7134_IRQ1, 0);
saa_writel(SAA7134_IRQ2, irq2_mask);
@@ -742,13 +726,6 @@ static int saa7134_hwfini(struct saa7134_dev *dev)
{
dprintk("hwfini\n");
- switch (dev->pci->device) {
- case PCI_DEVICE_ID_PHILIPS_SAA7134:
- case PCI_DEVICE_ID_PHILIPS_SAA7133:
- case PCI_DEVICE_ID_PHILIPS_SAA7135:
- saa7134_oss_fini(dev);
- break;
- }
if (card_has_mpeg(dev))
saa7134_ts_fini(dev);
saa7134_input_fini(dev);
@@ -986,11 +963,12 @@ static int __devinit saa7134_initdev(struct pci_dev *pci_dev,
if (card_is_dvb(dev))
request_module_depend("saa7134-dvb",&need_dvb);
- if (!oss && alsa) {
- dprintk("Requesting ALSA module\n");
+
+ if (alsa)
request_module_depend("saa7134-alsa",&need_alsa);
- }
+ if (oss)
+ request_module_depend("saa7134-oss",&need_oss);
v4l2_prio_init(&dev->prio);
@@ -1024,32 +1002,6 @@ static int __devinit saa7134_initdev(struct pci_dev *pci_dev,
dev->name,dev->radio_dev->minor & 0x1f);
}
- /* register oss devices */
- switch (dev->pci->device) {
- case PCI_DEVICE_ID_PHILIPS_SAA7134:
- case PCI_DEVICE_ID_PHILIPS_SAA7133:
- case PCI_DEVICE_ID_PHILIPS_SAA7135:
- if (oss) {
- err = dev->dmasound.minor_dsp =
- register_sound_dsp(&saa7134_dsp_fops,
- dsp_nr[dev->nr]);
- if (err < 0) {
- goto fail4;
- }
- printk(KERN_INFO "%s: registered device dsp%d\n",
- dev->name,dev->dmasound.minor_dsp >> 4);
-
- err = dev->dmasound.minor_mixer =
- register_sound_mixer(&saa7134_mixer_fops,
- mixer_nr[dev->nr]);
- if (err < 0)
- goto fail5;
- printk(KERN_INFO "%s: registered device mixer%d\n",
- dev->name,dev->dmasound.minor_mixer >> 4);
- }
- break;
- }
-
/* everything worked */
pci_set_drvdata(pci_dev,dev);
saa7134_devcount++;
@@ -1064,17 +1016,9 @@ static int __devinit saa7134_initdev(struct pci_dev *pci_dev,
/* check for signal */
saa7134_irq_video_intl(dev);
+
return 0;
- fail5:
- switch (dev->pci->device) {
- case PCI_DEVICE_ID_PHILIPS_SAA7134:
- case PCI_DEVICE_ID_PHILIPS_SAA7133:
- case PCI_DEVICE_ID_PHILIPS_SAA7135:
- if (oss)
- unregister_sound_dsp(dev->dmasound.minor_dsp);
- break;
- }
fail4:
saa7134_unregister_video(dev);
saa7134_i2c_unregister(dev);
@@ -1125,19 +1069,16 @@ static void __devexit saa7134_finidev(struct pci_dev *pci_dev)
saa7134_devcount--;
saa7134_i2c_unregister(dev);
- switch (dev->pci->device) {
- case PCI_DEVICE_ID_PHILIPS_SAA7134:
- case PCI_DEVICE_ID_PHILIPS_SAA7133:
- case PCI_DEVICE_ID_PHILIPS_SAA7135:
- if (oss) {
- unregister_sound_mixer(dev->dmasound.minor_mixer);
- unregister_sound_dsp(dev->dmasound.minor_dsp);
- }
- break;
- }
saa7134_unregister_video(dev);
- /* release ressources */
+ /* the DMA sound modules should be unloaded before reaching
+ this, but just in case they are still present... */
+ if (dev->dmasound.priv_data != NULL) {
+ free_irq(pci_dev->irq, &dev->dmasound);
+ dev->dmasound.priv_data = NULL;
+ }
+
+ /* release resources */
free_irq(pci_dev->irq, dev);
iounmap(dev->lmmio);
release_mem_region(pci_resource_start(pci_dev,0),
@@ -1225,7 +1166,7 @@ EXPORT_SYMBOL(saa7134_i2c_call_clients);
EXPORT_SYMBOL(saa7134_devlist);
EXPORT_SYMBOL(saa7134_boards);
-/* ----------------- For ALSA -------------------------------- */
+/* ----------------- for the DMA sound modules --------------- */
EXPORT_SYMBOL(saa7134_pgtable_free);
EXPORT_SYMBOL(saa7134_pgtable_build);
diff --git a/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c
index 329accda6d4..e648cc3bc96 100644
--- a/drivers/media/video/saa7134/saa7134-input.c
+++ b/drivers/media/video/saa7134/saa7134-input.c
@@ -485,64 +485,6 @@ static IR_KEYTAB_TYPE ir_codes_purpletv[IR_KEYTAB_SIZE] = {
};
-static IR_KEYTAB_TYPE ir_codes_pinnacle[IR_KEYTAB_SIZE] = {
- [ 0x59 ] = KEY_MUTE,
- [ 0x4a ] = KEY_POWER,
-
- [ 0x18 ] = KEY_TEXT,
- [ 0x26 ] = KEY_TV,
- [ 0x3d ] = KEY_PRINT,
-
- [ 0x48 ] = KEY_RED,
- [ 0x04 ] = KEY_GREEN,
- [ 0x11 ] = KEY_YELLOW,
- [ 0x00 ] = KEY_BLUE,
-
- [ 0x2d ] = KEY_VOLUMEUP,
- [ 0x1e ] = KEY_VOLUMEDOWN,
-
- [ 0x49 ] = KEY_MENU,
-
- [ 0x16 ] = KEY_CHANNELUP,
- [ 0x17 ] = KEY_CHANNELDOWN,
-
- [ 0x20 ] = KEY_UP,
- [ 0x21 ] = KEY_DOWN,
- [ 0x22 ] = KEY_LEFT,
- [ 0x23 ] = KEY_RIGHT,
- [ 0x0d ] = KEY_SELECT,
-
-
-
- [ 0x08 ] = KEY_BACK,
- [ 0x07 ] = KEY_REFRESH,
-
- [ 0x2f ] = KEY_ZOOM,
- [ 0x29 ] = KEY_RECORD,
-
- [ 0x4b ] = KEY_PAUSE,
- [ 0x4d ] = KEY_REWIND,
- [ 0x2e ] = KEY_PLAY,
- [ 0x4e ] = KEY_FORWARD,
- [ 0x53 ] = KEY_PREVIOUS,
- [ 0x4c ] = KEY_STOP,
- [ 0x54 ] = KEY_NEXT,
-
- [ 0x69 ] = KEY_KP0,
- [ 0x6a ] = KEY_KP1,
- [ 0x6b ] = KEY_KP2,
- [ 0x6c ] = KEY_KP3,
- [ 0x6d ] = KEY_KP4,
- [ 0x6e ] = KEY_KP5,
- [ 0x6f ] = KEY_KP6,
- [ 0x70 ] = KEY_KP7,
- [ 0x71 ] = KEY_KP8,
- [ 0x72 ] = KEY_KP9,
-
- [ 0x74 ] = KEY_CHANNEL,
- [ 0x0a ] = KEY_BACKSPACE,
-};
-
/* Mapping for the 28 key remote control as seen at
http://www.sednacomputer.com/photo/cardbus-tv.jpg
Pavel Mihaylov <bin@bash.info> */
@@ -635,57 +577,6 @@ static int get_key_purpletv(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
return 1;
}
-/* The new pinnacle PCTV remote (with the colored buttons)
- *
- * Ricardo Cerqueira <v4l@cerqueira.org>
- */
-
-static int get_key_pinnacle(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
-{
- unsigned char b[4];
- unsigned int start = 0,parity = 0,code = 0;
-
- /* poll IR chip */
- if (4 != i2c_master_recv(&ir->c,b,4)) {
- i2cdprintk("read error\n");
- return -EIO;
- }
-
- for (start = 0; start<4; start++) {
- if (b[start] == 0x80) {
- code=b[(start+3)%4];
- parity=b[(start+2)%4];
- }
- }
-
- /* Empty Request */
- if (parity==0)
- return 0;
-
- /* Repeating... */
- if (ir->old == parity)
- return 0;
-
-
- ir->old = parity;
-
- /* Reduce code value to fit inside IR_KEYTAB_SIZE
- *
- * this is the only value that results in 42 unique
- * codes < 128
- */
-
- code %= 0x88;
-
- *ir_raw = code;
- *ir_key = code;
-
- i2cdprintk("Pinnacle PCTV key %02x\n", code);
-
- return 1;
-}
-
-
void saa7134_input_irq(struct saa7134_dev *dev)
{
struct saa7134_ir *ir = dev->remote;
diff --git a/drivers/media/video/saa7134/saa7134-oss.c b/drivers/media/video/saa7134/saa7134-oss.c
index fd53dfcc164..fd9ed11ab1e 100644
--- a/drivers/media/video/saa7134/saa7134-oss.c
+++ b/drivers/media/video/saa7134/saa7134-oss.c
@@ -4,6 +4,8 @@
* oss dsp interface
*
* (c) 2001,02 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
+ * 2005 conversion to standalone module:
+ * Ricardo Cerqueira <v4l@cerqueira.org>
*
* 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
@@ -25,7 +27,9 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
+#include <linux/interrupt.h>
#include <linux/slab.h>
+#include <linux/sound.h>
#include <linux/soundcard.h>
#include "saa7134-reg.h"
@@ -33,15 +37,23 @@
/* ------------------------------------------------------------------ */
-static unsigned int oss_debug = 0;
-module_param(oss_debug, int, 0644);
-MODULE_PARM_DESC(oss_debug,"enable debug messages [oss]");
+static unsigned int debug = 0;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug,"enable debug messages [oss]");
-static unsigned int oss_rate = 0;
-module_param(oss_rate, int, 0444);
-MODULE_PARM_DESC(oss_rate,"sample rate (valid are: 32000,48000)");
+static unsigned int rate = 0;
+module_param(rate, int, 0444);
+MODULE_PARM_DESC(rate,"sample rate (valid are: 32000,48000)");
-#define dprintk(fmt, arg...) if (oss_debug) \
+static unsigned int dsp_nr[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET };
+MODULE_PARM_DESC(dsp_nr, "device numbers for SAA7134 capture interface(s).");
+module_param_array(dsp_nr, int, NULL, 0444);
+
+static unsigned int mixer_nr[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET };
+MODULE_PARM_DESC(mixer_nr, "mixer numbers for SAA7134 capture interface(s).");
+module_param_array(mixer_nr, int, NULL, 0444);
+
+#define dprintk(fmt, arg...) if (debug) \
printk(KERN_DEBUG "%s/oss: " fmt, dev->name , ## arg)
@@ -369,7 +381,7 @@ static int dsp_ioctl(struct inode *inode, struct file *file,
int __user *p = argp;
int val = 0;
- if (oss_debug > 1)
+ if (debug > 1)
saa7134_print_ioctl(dev->name,cmd);
switch (cmd) {
case OSS_GETVERSION:
@@ -665,7 +677,7 @@ static int mixer_ioctl(struct inode *inode, struct file *file,
void __user *argp = (void __user *) arg;
int __user *p = argp;
- if (oss_debug > 1)
+ if (debug > 1)
saa7134_print_ioctl(dev->name,cmd);
switch (cmd) {
case OSS_GETVERSION:
@@ -768,8 +780,41 @@ struct file_operations saa7134_mixer_fops = {
/* ------------------------------------------------------------------ */
+static irqreturn_t saa7134_oss_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct saa7134_dmasound *dmasound = dev_id;
+ struct saa7134_dev *dev = dmasound->priv_data;
+ unsigned long report, status;
+ int loop, handled = 0;
+
+ for (loop = 0; loop < 10; loop++) {
+ report = saa_readl(SAA7134_IRQ_REPORT);
+ status = saa_readl(SAA7134_IRQ_STATUS);
+
+ if (report & SAA7134_IRQ_REPORT_DONE_RA3) {
+ handled = 1;
+ saa_writel(SAA7134_IRQ_REPORT,report);
+ saa7134_irq_oss_done(dev, status);
+ } else {
+ goto out;
+ }
+ }
+
+ if (loop == 10) {
+ dprintk("error! looping IRQ!");
+ }
+out:
+ return IRQ_RETVAL(handled);
+}
+
int saa7134_oss_init1(struct saa7134_dev *dev)
{
+
+ if ((request_irq(dev->pci->irq, saa7134_oss_irq,
+ SA_SHIRQ | SA_INTERRUPT, dev->name,
+ (void*) &dev->dmasound)) < 0)
+ return -1;
+
/* general */
init_MUTEX(&dev->dmasound.lock);
init_waitqueue_head(&dev->dmasound.wq);
@@ -785,8 +830,8 @@ int saa7134_oss_init1(struct saa7134_dev *dev)
/* dsp */
dev->dmasound.rate = 32000;
- if (oss_rate)
- dev->dmasound.rate = oss_rate;
+ if (rate)
+ dev->dmasound.rate = rate;
dev->dmasound.rate = (dev->dmasound.rate > 40000) ? 48000 : 32000;
/* mixer */
@@ -840,7 +885,7 @@ void saa7134_irq_oss_done(struct saa7134_dev *dev, unsigned long status)
/* next block addr */
next_blk = (dev->dmasound.dma_blk + 2) % dev->dmasound.blocks;
saa_writel(reg,next_blk * dev->dmasound.blksize);
- if (oss_debug > 2)
+ if (debug > 2)
dprintk("irq: ok, %s, next_blk=%d, addr=%x\n",
(status & 0x10000000) ? "even" : "odd ", next_blk,
next_blk * dev->dmasound.blksize);
@@ -854,6 +899,98 @@ void saa7134_irq_oss_done(struct saa7134_dev *dev, unsigned long status)
spin_unlock(&dev->slock);
}
+int saa7134_dsp_create(struct saa7134_dev *dev)
+{
+ int err;
+
+ err = dev->dmasound.minor_dsp =
+ register_sound_dsp(&saa7134_dsp_fops,
+ dsp_nr[dev->nr]);
+ if (err < 0) {
+ goto fail;
+ }
+ printk(KERN_INFO "%s: registered device dsp%d\n",
+ dev->name,dev->dmasound.minor_dsp >> 4);
+
+ err = dev->dmasound.minor_mixer =
+ register_sound_mixer(&saa7134_mixer_fops,
+ mixer_nr[dev->nr]);
+ if (err < 0)
+ goto fail;
+ printk(KERN_INFO "%s: registered device mixer%d\n",
+ dev->name,dev->dmasound.minor_mixer >> 4);
+
+ return 0;
+
+fail:
+ unregister_sound_dsp(dev->dmasound.minor_dsp);
+ return 0;
+
+
+}
+
+static int saa7134_oss_init(void)
+{
+ struct saa7134_dev *dev = NULL;
+ struct list_head *list;
+
+ printk(KERN_INFO "saa7134 OSS driver for DMA sound loaded\n");
+
+ list_for_each(list,&saa7134_devlist) {
+ dev = list_entry(list, struct saa7134_dev, devlist);
+ if (dev->dmasound.priv_data == NULL) {
+ dev->dmasound.priv_data = dev;
+ saa7134_oss_init1(dev);
+ saa7134_dsp_create(dev);
+ } else {
+ printk(KERN_ERR "saa7134 OSS: DMA sound is being handled by ALSA, ignoring %s\n",dev->name);
+ return -EBUSY;
+ }
+ }
+
+ if (dev == NULL)
+ printk(KERN_INFO "saa7134 OSS: no saa7134 cards found\n");
+
+ return 0;
+
+}
+
+void saa7134_oss_exit(void)
+{
+ struct saa7134_dev *dev = NULL;
+ struct list_head *list;
+
+ list_for_each(list,&saa7134_devlist) {
+ dev = list_entry(list, struct saa7134_dev, devlist);
+
+ /* Device isn't registered by OSS, probably ALSA's */
+ if (!dev->dmasound.minor_dsp)
+ continue;
+
+ unregister_sound_mixer(dev->dmasound.minor_mixer);
+ unregister_sound_dsp(dev->dmasound.minor_dsp);
+
+ saa7134_oss_fini(dev);
+
+ if (dev->pci->irq > 0) {
+ synchronize_irq(dev->pci->irq);
+ free_irq(dev->pci->irq,&dev->dmasound);
+ }
+
+ dev->dmasound.priv_data = NULL;
+
+ }
+
+ printk(KERN_INFO "saa7134 OSS driver for DMA sound unloaded\n");
+
+ return;
+}
+
+module_init(saa7134_oss_init);
+module_exit(saa7134_oss_exit);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
+
/* ----------------------------------------------------------- */
/*
* Local variables:
diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h
index fb972747166..244e1973081 100644
--- a/drivers/media/video/saa7134/saa7134.h
+++ b/drivers/media/video/saa7134/saa7134.h
@@ -208,6 +208,7 @@ struct saa7134_format {
#define SAA7134_BOARD_SEDNA_PC_TV_CARDBUS 79
#define SAA7134_BOARD_ASUSTEK_DIGIMATRIX_TV 80
#define SAA7134_BOARD_PHILIPS_TIGER 81
+#define SAA7134_BOARD_MSI_TVATANYWHERE_PLUS 82
#define SAA7134_MAXBOARDS 8
#define SAA7134_INPUT_MAX 8
@@ -383,6 +384,7 @@ struct saa7134_dmasound {
unsigned int dma_blk;
unsigned int read_offset;
unsigned int read_count;
+ void * priv_data;
snd_pcm_substream_t *substream;
};
diff --git a/drivers/media/video/tda8290.c b/drivers/media/video/tda8290.c
index b2dfe07e9f9..61d94ddaff4 100644
--- a/drivers/media/video/tda8290.c
+++ b/drivers/media/video/tda8290.c
@@ -437,6 +437,10 @@ static void set_audio(struct tuner *t)
t->sgIF = 124;
t->tda8290_easy_mode = 0x20;
mode = "L";
+ } else if (t->std & V4L2_STD_SECAM_LC) {
+ t->sgIF = 20;
+ t->tda8290_easy_mode = 0x40;
+ mode = "LC";
}
tuner_dbg("setting tda8290 to system %s\n", mode);
}
diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c
index 73c4041c35d..e58abdfcaab 100644
--- a/drivers/media/video/tuner-core.c
+++ b/drivers/media/video/tuner-core.c
@@ -251,7 +251,7 @@ static inline int check_mode(struct tuner *t, char *cmd)
static char pal[] = "-";
module_param_string(pal, pal, sizeof(pal), 0644);
-static char secam[] = "-";
+static char secam[] = "--";
module_param_string(secam, secam, sizeof(secam), 0644);
/* get more precise norm info from insmod option */
@@ -307,8 +307,13 @@ static int tuner_fixup_std(struct tuner *t)
break;
case 'l':
case 'L':
- tuner_dbg ("insmod fixup: SECAM => SECAM-L\n");
- t->std = V4L2_STD_SECAM_L;
+ if ((secam[1]=='C')||(secam[1]=='c')) {
+ tuner_dbg ("insmod fixup: SECAM => SECAM-L'\n");
+ t->std = V4L2_STD_SECAM_LC;
+ } else {
+ tuner_dbg ("insmod fixup: SECAM => SECAM-L\n");
+ t->std = V4L2_STD_SECAM_L;
+ }
break;
case '-':
/* default parameter, do nothing */
diff --git a/drivers/media/video/tuner-simple.c b/drivers/media/video/tuner-simple.c
index d832205818f..e0c9fdb9914 100644
--- a/drivers/media/video/tuner-simple.c
+++ b/drivers/media/video/tuner-simple.c
@@ -233,7 +233,7 @@ static struct tunertype tuners[] = {
{ "Ymec TVision TVF-5533MF", Philips, NTSC,
16*160.00,16*454.00,0x01,0x02,0x04,0x8e,732},
- /* 60-68 */
+ /* 60-69 */
{ "Thomson DDT 7611 (ATSC/NTSC)", THOMSON, ATSC,
16*157.25,16*454.00,0x39,0x3a,0x3c,0x8e,732},
{ "Tena TNF9533-D/IF/TNF9533-B/DF", Philips, PAL,
@@ -252,6 +252,8 @@ static struct tunertype tuners[] = {
16*160.00,16*442.00,0xa1,0xa2,0xa4,0xc8,623 },
{ "Philips TUV1236D ATSC/NTSC dual in", Philips, ATSC,
16*157.25,16*454.00,0x01,0x02,0x04,0xce,732 },
+ { "Tena TNF 5335 MF", Philips, NTSC,
+ 16*157.25,16*454.00,0x01,0x02,0x04,0x8e,732 },
};
unsigned const int tuner_count = ARRAY_SIZE(tuners);
diff --git a/drivers/media/video/wm8775.c b/drivers/media/video/wm8775.c
index 22f28622200..a6936ad74fc 100644
--- a/drivers/media/video/wm8775.c
+++ b/drivers/media/video/wm8775.c
@@ -5,6 +5,11 @@
*
* Based on saa7115 driver
*
+ * Copyright (C) 2005 Hans Verkuil <hverkuil@xs4all.nl>
+ * - Cleanup
+ * - V4L2 API update
+ * - sound fixes
+ *
* 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
@@ -31,7 +36,7 @@
#include <media/audiochip.h>
MODULE_DESCRIPTION("wm8775 driver");
-MODULE_AUTHOR("Ulf Eklund");
+MODULE_AUTHOR("Ulf Eklund, Hans Verkuil");
MODULE_LICENSE("GPL");
#define wm8775_err(fmt, arg...) do { \
diff --git a/drivers/mmc/mmci.c b/drivers/mmc/mmci.c
index 1e6bdba2675..166c9b0ad04 100644
--- a/drivers/mmc/mmci.c
+++ b/drivers/mmc/mmci.c
@@ -22,7 +22,6 @@
#include <asm/div64.h>
#include <asm/io.h>
-#include <asm/irq.h>
#include <asm/scatterlist.h>
#include <asm/sizes.h>
#include <asm/hardware/amba.h>
diff --git a/drivers/net/3c509.c b/drivers/net/3c509.c
index 977935a3d89..824e430486c 100644
--- a/drivers/net/3c509.c
+++ b/drivers/net/3c509.c
@@ -84,6 +84,7 @@ static int max_interrupt_work = 10;
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/pm.h>
+#include <linux/pm_legacy.h>
#include <linux/skbuff.h>
#include <linux/delay.h> /* for udelay() */
#include <linux/spinlock.h>
@@ -173,7 +174,7 @@ struct el3_private {
/* skb send-queue */
int head, size;
struct sk_buff *queue[SKB_QUEUE_SIZE];
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_LEGACY
struct pm_dev *pmdev;
#endif
enum {
@@ -200,7 +201,7 @@ static void el3_tx_timeout (struct net_device *dev);
static void el3_down(struct net_device *dev);
static void el3_up(struct net_device *dev);
static struct ethtool_ops ethtool_ops;
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_LEGACY
static int el3_suspend(struct pm_dev *pdev);
static int el3_resume(struct pm_dev *pdev);
static int el3_pm_callback(struct pm_dev *pdev, pm_request_t rqst, void *data);
@@ -361,7 +362,7 @@ static void el3_common_remove (struct net_device *dev)
struct el3_private *lp = netdev_priv(dev);
(void) lp; /* Keep gcc quiet... */
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_LEGACY
if (lp->pmdev)
pm_unregister(lp->pmdev);
#endif
@@ -571,7 +572,7 @@ no_pnp:
if (err)
goto out1;
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_LEGACY
/* register power management */
lp->pmdev = pm_register(PM_ISA_DEV, card_idx, el3_pm_callback);
if (lp->pmdev) {
@@ -1479,7 +1480,7 @@ el3_up(struct net_device *dev)
}
/* Power Management support functions */
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_LEGACY
static int
el3_suspend(struct pm_dev *pdev)
@@ -1548,7 +1549,7 @@ el3_pm_callback(struct pm_dev *pdev, pm_request_t rqst, void *data)
return 0;
}
-#endif /* CONFIG_PM */
+#endif /* CONFIG_PM_LEGACY */
/* Parameters that may be passed into the module. */
static int debug = -1;
diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c
index e3a329539f1..0f030b73cbb 100644
--- a/drivers/net/gianfar.c
+++ b/drivers/net/gianfar.c
@@ -6,7 +6,7 @@
* Based on 8260_io/fcc_enet.c
*
* Author: Andy Fleming
- * Maintainer: Kumar Gala (kumar.gala@freescale.com)
+ * Maintainer: Kumar Gala
*
* Copyright (c) 2002-2004 Freescale Semiconductor, Inc.
*
diff --git a/drivers/net/gianfar.h b/drivers/net/gianfar.h
index 220084e5334..5065ba82cb7 100644
--- a/drivers/net/gianfar.h
+++ b/drivers/net/gianfar.h
@@ -6,7 +6,7 @@
* Based on 8260_io/fcc_enet.c
*
* Author: Andy Fleming
- * Maintainer: Kumar Gala (kumar.gala@freescale.com)
+ * Maintainer: Kumar Gala
*
* Copyright (c) 2002-2004 Freescale Semiconductor, Inc.
*
diff --git a/drivers/net/gianfar_ethtool.c b/drivers/net/gianfar_ethtool.c
index 5a2d810ce57..cfa3cd7c91a 100644
--- a/drivers/net/gianfar_ethtool.c
+++ b/drivers/net/gianfar_ethtool.c
@@ -6,7 +6,7 @@
* Based on e1000 ethtool support
*
* Author: Andy Fleming
- * Maintainer: Kumar Gala (kumar.gala@freescale.com)
+ * Maintainer: Kumar Gala
*
* Copyright (c) 2003,2004 Freescale Semiconductor, Inc.
*
diff --git a/drivers/net/gianfar_mii.c b/drivers/net/gianfar_mii.c
index 9544279e8bc..04a462c2a5b 100644
--- a/drivers/net/gianfar_mii.c
+++ b/drivers/net/gianfar_mii.c
@@ -5,7 +5,7 @@
* Provides Bus interface for MIIM regs
*
* Author: Andy Fleming
- * Maintainer: Kumar Gala (kumar.gala@freescale.com)
+ * Maintainer: Kumar Gala
*
* Copyright (c) 2002-2004 Freescale Semiconductor, Inc.
*
diff --git a/drivers/net/gianfar_mii.h b/drivers/net/gianfar_mii.h
index 56e5665d5c9..e85eb216fb5 100644
--- a/drivers/net/gianfar_mii.h
+++ b/drivers/net/gianfar_mii.h
@@ -5,7 +5,7 @@
* Driver for the MDIO bus controller in the Gianfar register space
*
* Author: Andy Fleming
- * Maintainer: Kumar Gala (kumar.gala@freescale.com)
+ * Maintainer: Kumar Gala
*
* Copyright (c) 2002-2004 Freescale Semiconductor, Inc.
*
diff --git a/drivers/net/irda/ali-ircc.c b/drivers/net/irda/ali-ircc.c
index 9bf34681d3d..2e7882eb7d6 100644
--- a/drivers/net/irda/ali-ircc.c
+++ b/drivers/net/irda/ali-ircc.c
@@ -40,6 +40,7 @@
#include <asm/byteorder.h>
#include <linux/pm.h>
+#include <linux/pm_legacy.h>
#include <net/irda/wrapper.h>
#include <net/irda/irda.h>
diff --git a/drivers/net/irda/nsc-ircc.c b/drivers/net/irda/nsc-ircc.c
index 805714ec9a8..ee717d0e939 100644
--- a/drivers/net/irda/nsc-ircc.c
+++ b/drivers/net/irda/nsc-ircc.c
@@ -59,6 +59,7 @@
#include <asm/byteorder.h>
#include <linux/pm.h>
+#include <linux/pm_legacy.h>
#include <net/irda/wrapper.h>
#include <net/irda/irda.h>
diff --git a/drivers/net/smc91x.h b/drivers/net/smc91x.h
index a10cd184d59..5c2824be4ee 100644
--- a/drivers/net/smc91x.h
+++ b/drivers/net/smc91x.h
@@ -100,14 +100,14 @@
#define SMC_IO_SHIFT 0
#define SMC_NOWAIT 1
-#define SMC_inb(a, r) inb((a) + (r))
-#define SMC_insb(a, r, p, l) insb((a) + (r), p, (l))
-#define SMC_inw(a, r) inw((a) + (r))
-#define SMC_insw(a, r, p, l) insw((a) + (r), p, l)
-#define SMC_outb(v, a, r) outb(v, (a) + (r))
-#define SMC_outsb(a, r, p, l) outsb((a) + (r), p, (l))
-#define SMC_outw(v, a, r) outw(v, (a) + (r))
-#define SMC_outsw(a, r, p, l) outsw((a) + (r), p, l)
+#define SMC_inb(a, r) readb((a) + (r))
+#define SMC_insb(a, r, p, l) readsb((a) + (r), p, (l))
+#define SMC_inw(a, r) readw((a) + (r))
+#define SMC_insw(a, r, p, l) readsw((a) + (r), p, l)
+#define SMC_outb(v, a, r) writeb(v, (a) + (r))
+#define SMC_outsb(a, r, p, l) writesb((a) + (r), p, (l))
+#define SMC_outw(v, a, r) writew(v, (a) + (r))
+#define SMC_outsw(a, r, p, l) writesw((a) + (r), p, l)
#define set_irq_type(irq, type) do {} while (0)
diff --git a/drivers/net/sungem.c b/drivers/net/sungem.c
index de399563a9d..081717d0137 100644
--- a/drivers/net/sungem.c
+++ b/drivers/net/sungem.c
@@ -128,6 +128,8 @@ static struct pci_device_id gem_pci_tbl[] = {
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
{ PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_SH_SUNGEM,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
+ { PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_IPID2_GMAC,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
{0, }
};
diff --git a/drivers/net/wan/sdladrv.c b/drivers/net/wan/sdladrv.c
index 7c2cf2e7630..032c0f81928 100644
--- a/drivers/net/wan/sdladrv.c
+++ b/drivers/net/wan/sdladrv.c
@@ -1994,7 +1994,7 @@ static int detect_s514 (sdlahw_t* hw)
modname, hw->irq);
/* map the physical PCI memory to virtual memory */
- (void *)hw->dpmbase = ioremap((unsigned long)S514_mem_base_addr,
+ hw->dpmbase = ioremap((unsigned long)S514_mem_base_addr,
(unsigned long)MAX_SIZEOF_S514_MEMORY);
/* map the physical control register memory to virtual memory */
hw->vector = (unsigned long)ioremap(
diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c
index 4a3cecca012..2387e75da0f 100644
--- a/drivers/pci/hotplug/pciehp_hpc.c
+++ b/drivers/pci/hotplug/pciehp_hpc.c
@@ -31,6 +31,8 @@
#include <linux/module.h>
#include <linux/types.h>
#include <linux/pci.h>
+#include <linux/interrupt.h>
+
#include "../pci.h"
#include "pciehp.h"
diff --git a/drivers/pci/hotplug/rpaphp_pci.c b/drivers/pci/hotplug/rpaphp_pci.c
index a7859a84d1a..4b35097b3d9 100644
--- a/drivers/pci/hotplug/rpaphp_pci.c
+++ b/drivers/pci/hotplug/rpaphp_pci.c
@@ -253,7 +253,7 @@ rpaphp_pci_config_slot(struct pci_bus *bus)
if (!dn || !dn->child)
return NULL;
- if (systemcfg->platform == PLATFORM_PSERIES_LPAR) {
+ if (_machine == PLATFORM_PSERIES_LPAR) {
of_scan_bus(dn, bus);
if (list_empty(&bus->devices)) {
err("%s: No new device found\n", __FUNCTION__);
diff --git a/drivers/pci/hotplug/shpchp_hpc.c b/drivers/pci/hotplug/shpchp_hpc.c
index 40905a6c809..9987a6fd65b 100644
--- a/drivers/pci/hotplug/shpchp_hpc.c
+++ b/drivers/pci/hotplug/shpchp_hpc.c
@@ -31,6 +31,8 @@
#include <linux/module.h>
#include <linux/types.h>
#include <linux/pci.h>
+#include <linux/interrupt.h>
+
#include "shpchp.h"
#ifdef DEBUG
diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c
index 234cdca6fe1..a30aa74304a 100644
--- a/drivers/pcmcia/cs.c
+++ b/drivers/pcmcia/cs.c
@@ -513,6 +513,11 @@ static int socket_insert(struct pcmcia_socket *skt)
ret = socket_setup(skt, setup_delay);
if (ret == CS_SUCCESS) {
skt->state |= SOCKET_PRESENT;
+
+ printk(KERN_NOTICE "pccard: %s card inserted into slot %d\n",
+ (skt->state & SOCKET_CARDBUS) ? "CardBus" : "PCMCIA",
+ skt->sock);
+
#ifdef CONFIG_CARDBUS
if (skt->state & SOCKET_CARDBUS) {
cb_alloc(skt);
@@ -598,6 +603,7 @@ static int socket_resume(struct pcmcia_socket *skt)
static void socket_remove(struct pcmcia_socket *skt)
{
+ printk(KERN_NOTICE "pccard: card ejected from slot %d\n", skt->sock);
socket_shutdown(skt);
cs_socket_put(skt);
}
diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c
index 39d096b5292..7f8219f3fd9 100644
--- a/drivers/pcmcia/ds.c
+++ b/drivers/pcmcia/ds.c
@@ -544,6 +544,9 @@ struct pcmcia_device * pcmcia_device_add(struct pcmcia_socket *s, unsigned int f
list_add_tail(&p_dev->socket_device_list, &s->devices_list);
spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
+ printk(KERN_NOTICE "pcmcia: registering new device %s\n",
+ p_dev->devname);
+
pcmcia_device_query(p_dev);
if (device_register(&p_dev->dev)) {
diff --git a/drivers/pcmcia/i82365.c b/drivers/pcmcia/i82365.c
index 4ddd76239b3..4d56bc9926d 100644
--- a/drivers/pcmcia/i82365.c
+++ b/drivers/pcmcia/i82365.c
@@ -1339,10 +1339,7 @@ static struct device_driver i82365_driver = {
.resume = pcmcia_socket_dev_resume,
};
-static struct platform_device i82365_device = {
- .name = "i82365",
- .id = 0,
-};
+static struct platform_device *i82365_device;
static int __init init_i82365(void)
{
@@ -1352,7 +1349,14 @@ static int __init init_i82365(void)
if (ret)
return ret;
- ret = platform_device_register(&i82365_device);
+ i82365_device = platform_device_alloc("i82365", 0);
+ if (i82365_device) {
+ ret = platform_device_add(i82365_device);
+ if (ret)
+ platform_device_put(i82365_device);
+ } else
+ ret = -ENOMEM;
+
if (ret) {
driver_unregister(&i82365_driver);
return ret;
@@ -1365,7 +1369,7 @@ static int __init init_i82365(void)
if (sockets == 0) {
printk("not found.\n");
- platform_device_unregister(&i82365_device);
+ platform_device_unregister(i82365_device);
release_region(i365_base, 2);
driver_unregister(&i82365_driver);
return -ENODEV;
@@ -1377,7 +1381,7 @@ static int __init init_i82365(void)
/* register sockets with the pcmcia core */
for (i = 0; i < sockets; i++) {
- socket[i].socket.dev.dev = &i82365_device.dev;
+ socket[i].socket.dev.dev = &i82365_device->dev;
socket[i].socket.ops = &pcic_operations;
socket[i].socket.resource_ops = &pccard_nonstatic_ops;
socket[i].socket.owner = THIS_MODULE;
@@ -1415,7 +1419,7 @@ static void __exit exit_i82365(void)
if (socket[i].flags & IS_REGISTERED)
pcmcia_unregister_socket(&socket[i].socket);
}
- platform_device_unregister(&i82365_device);
+ platform_device_unregister(i82365_device);
if (poll_interval != 0)
del_timer_sync(&poll_timer);
if (grab_irq != 0)
diff --git a/drivers/scsi/ahci.c b/drivers/scsi/ahci.c
index 4e96ec5f2ff..83467a05dc8 100644
--- a/drivers/scsi/ahci.c
+++ b/drivers/scsi/ahci.c
@@ -48,7 +48,7 @@
#include <asm/io.h>
#define DRV_NAME "ahci"
-#define DRV_VERSION "1.01"
+#define DRV_VERSION "1.2"
enum {
@@ -558,13 +558,26 @@ static void ahci_qc_prep(struct ata_queued_cmd *qc)
pp->cmd_slot[0].opts |= cpu_to_le32(n_elem << 16);
}
-static void ahci_intr_error(struct ata_port *ap, u32 irq_stat)
+static void ahci_restart_port(struct ata_port *ap, u32 irq_stat)
{
void __iomem *mmio = ap->host_set->mmio_base;
void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
u32 tmp;
int work;
+ if ((ap->device[0].class != ATA_DEV_ATAPI) ||
+ ((irq_stat & PORT_IRQ_TF_ERR) == 0))
+ printk(KERN_WARNING "ata%u: port reset, "
+ "p_is %x is %x pis %x cmd %x tf %x ss %x se %x\n",
+ ap->id,
+ irq_stat,
+ readl(mmio + HOST_IRQ_STAT),
+ readl(port_mmio + PORT_IRQ_STAT),
+ readl(port_mmio + PORT_CMD),
+ readl(port_mmio + PORT_TFDATA),
+ readl(port_mmio + PORT_SCR_STAT),
+ readl(port_mmio + PORT_SCR_ERR));
+
/* stop DMA */
tmp = readl(port_mmio + PORT_CMD);
tmp &= ~PORT_CMD_START;
@@ -602,8 +615,6 @@ static void ahci_intr_error(struct ata_port *ap, u32 irq_stat)
tmp |= PORT_CMD_START;
writel(tmp, port_mmio + PORT_CMD);
readl(port_mmio + PORT_CMD); /* flush */
-
- printk(KERN_WARNING "ata%u: error occurred, port reset\n", ap->id);
}
static void ahci_eng_timeout(struct ata_port *ap)
@@ -614,17 +625,17 @@ static void ahci_eng_timeout(struct ata_port *ap)
struct ata_queued_cmd *qc;
unsigned long flags;
- DPRINTK("ENTER\n");
+ printk(KERN_WARNING "ata%u: handling error/timeout\n", ap->id);
spin_lock_irqsave(&host_set->lock, flags);
- ahci_intr_error(ap, readl(port_mmio + PORT_IRQ_STAT));
-
qc = ata_qc_from_tag(ap, ap->active_tag);
if (!qc) {
printk(KERN_ERR "ata%u: BUG: timeout without command\n",
ap->id);
} else {
+ ahci_restart_port(ap, readl(port_mmio + PORT_IRQ_STAT));
+
/* hack alert! We cannot use the supplied completion
* function from inside the ->eh_strategy_handler() thread.
* libata is the only user of ->eh_strategy_handler() in
@@ -659,9 +670,19 @@ static inline int ahci_host_intr(struct ata_port *ap, struct ata_queued_cmd *qc)
}
if (status & PORT_IRQ_FATAL) {
- ahci_intr_error(ap, status);
+ unsigned int err_mask;
+ if (status & PORT_IRQ_TF_ERR)
+ err_mask = AC_ERR_DEV;
+ else if (status & PORT_IRQ_IF_ERR)
+ err_mask = AC_ERR_ATA_BUS;
+ else
+ err_mask = AC_ERR_HOST_BUS;
+
+ /* command processing has stopped due to error; restart */
+ ahci_restart_port(ap, status);
+
if (qc)
- ata_qc_complete(qc, AC_ERR_OTHER);
+ ata_qc_complete(qc, err_mask);
}
return 1;
diff --git a/drivers/scsi/ata_piix.c b/drivers/scsi/ata_piix.c
index 855428ff37e..333d69dd84e 100644
--- a/drivers/scsi/ata_piix.c
+++ b/drivers/scsi/ata_piix.c
@@ -50,7 +50,7 @@
#include <linux/libata.h>
#define DRV_NAME "ata_piix"
-#define DRV_VERSION "1.04"
+#define DRV_VERSION "1.05"
enum {
PIIX_IOCFG = 0x54, /* IDE I/O configuration register */
diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c
index d81db3a3d4b..bb604dfbdef 100644
--- a/drivers/scsi/libata-core.c
+++ b/drivers/scsi/libata-core.c
@@ -1263,7 +1263,7 @@ retry:
}
/* ATAPI-specific feature tests */
- else {
+ else if (dev->class == ATA_DEV_ATAPI) {
if (ata_id_is_ata(dev->id)) /* sanity check */
goto err_out_nosup;
@@ -1570,11 +1570,13 @@ int ata_timing_compute(struct ata_device *adev, unsigned short speed,
/*
* Find the mode.
- */
+ */
if (!(s = ata_timing_find_mode(speed)))
return -EINVAL;
+ memcpy(t, s, sizeof(*s));
+
/*
* If the drive is an EIDE drive, it can tell us it needs extended
* PIO/MW_DMA cycle timing.
@@ -1595,7 +1597,7 @@ int ata_timing_compute(struct ata_device *adev, unsigned short speed,
* Convert the timing to bus clock counts.
*/
- ata_timing_quantize(s, t, T, UT);
+ ata_timing_quantize(t, t, T, UT);
/*
* Even in DMA/UDMA modes we still use PIO access for IDENTIFY, S.M.A.R.T
@@ -2399,7 +2401,7 @@ static void ata_sg_clean(struct ata_queued_cmd *qc)
if (qc->flags & ATA_QCFLAG_SINGLE)
assert(qc->n_elem == 1);
- DPRINTK("unmapping %u sg elements\n", qc->n_elem);
+ VPRINTK("unmapping %u sg elements\n", qc->n_elem);
/* if we padded the buffer out to 32-bit bound, and data
* xfer direction is from-device, we must copy from the
@@ -2409,7 +2411,8 @@ static void ata_sg_clean(struct ata_queued_cmd *qc)
pad_buf = ap->pad + (qc->tag * ATA_DMA_PAD_SZ);
if (qc->flags & ATA_QCFLAG_SG) {
- dma_unmap_sg(ap->host_set->dev, sg, qc->n_elem, dir);
+ if (qc->n_elem)
+ dma_unmap_sg(ap->host_set->dev, sg, qc->n_elem, dir);
/* restore last sg */
sg[qc->orig_n_elem - 1].length += qc->pad_len;
if (pad_buf) {
@@ -2419,8 +2422,10 @@ static void ata_sg_clean(struct ata_queued_cmd *qc)
kunmap_atomic(psg->page, KM_IRQ0);
}
} else {
- dma_unmap_single(ap->host_set->dev, sg_dma_address(&sg[0]),
- sg_dma_len(&sg[0]), dir);
+ if (sg_dma_len(&sg[0]) > 0)
+ dma_unmap_single(ap->host_set->dev,
+ sg_dma_address(&sg[0]), sg_dma_len(&sg[0]),
+ dir);
/* restore sg */
sg->length += qc->pad_len;
if (pad_buf)
@@ -2619,6 +2624,11 @@ static int ata_sg_setup_one(struct ata_queued_cmd *qc)
sg->length, qc->pad_len);
}
+ if (!sg->length) {
+ sg_dma_address(sg) = 0;
+ goto skip_map;
+ }
+
dma_address = dma_map_single(ap->host_set->dev, qc->buf_virt,
sg->length, dir);
if (dma_mapping_error(dma_address)) {
@@ -2628,6 +2638,7 @@ static int ata_sg_setup_one(struct ata_queued_cmd *qc)
}
sg_dma_address(sg) = dma_address;
+skip_map:
sg_dma_len(sg) = sg->length;
DPRINTK("mapped buffer of %d bytes for %s\n", sg_dma_len(sg),
@@ -2655,7 +2666,7 @@ static int ata_sg_setup(struct ata_queued_cmd *qc)
struct ata_port *ap = qc->ap;
struct scatterlist *sg = qc->__sg;
struct scatterlist *lsg = &sg[qc->n_elem - 1];
- int n_elem, dir;
+ int n_elem, pre_n_elem, dir, trim_sg = 0;
VPRINTK("ENTER, ata%u\n", ap->id);
assert(qc->flags & ATA_QCFLAG_SG);
@@ -2689,13 +2700,24 @@ static int ata_sg_setup(struct ata_queued_cmd *qc)
sg_dma_len(psg) = ATA_DMA_PAD_SZ;
/* trim last sg */
lsg->length -= qc->pad_len;
+ if (lsg->length == 0)
+ trim_sg = 1;
DPRINTK("padding done, sg[%d].length=%u pad_len=%u\n",
qc->n_elem - 1, lsg->length, qc->pad_len);
}
+ pre_n_elem = qc->n_elem;
+ if (trim_sg && pre_n_elem)
+ pre_n_elem--;
+
+ if (!pre_n_elem) {
+ n_elem = 0;
+ goto skip_map;
+ }
+
dir = qc->dma_dir;
- n_elem = dma_map_sg(ap->host_set->dev, sg, qc->n_elem, dir);
+ n_elem = dma_map_sg(ap->host_set->dev, sg, pre_n_elem, dir);
if (n_elem < 1) {
/* restore last sg */
lsg->length += qc->pad_len;
@@ -2704,6 +2726,7 @@ static int ata_sg_setup(struct ata_queued_cmd *qc)
DPRINTK("%d sg elements mapped\n", n_elem);
+skip_map:
qc->n_elem = n_elem;
return 0;
@@ -3263,32 +3286,11 @@ static void ata_qc_timeout(struct ata_queued_cmd *qc)
{
struct ata_port *ap = qc->ap;
struct ata_host_set *host_set = ap->host_set;
- struct ata_device *dev = qc->dev;
u8 host_stat = 0, drv_stat;
unsigned long flags;
DPRINTK("ENTER\n");
- /* FIXME: doesn't this conflict with timeout handling? */
- if (qc->dev->class == ATA_DEV_ATAPI && qc->scsicmd) {
- struct scsi_cmnd *cmd = qc->scsicmd;
-
- if (!(cmd->eh_eflags & SCSI_EH_CANCEL_CMD)) {
-
- /* finish completing original command */
- spin_lock_irqsave(&host_set->lock, flags);
- __ata_qc_complete(qc);
- spin_unlock_irqrestore(&host_set->lock, flags);
-
- atapi_request_sense(ap, dev, cmd);
-
- cmd->result = (CHECK_CONDITION << 1) | (DID_OK << 16);
- scsi_finish_command(cmd);
-
- goto out;
- }
- }
-
spin_lock_irqsave(&host_set->lock, flags);
/* hack alert! We cannot use the supplied completion
@@ -3327,7 +3329,6 @@ static void ata_qc_timeout(struct ata_queued_cmd *qc)
spin_unlock_irqrestore(&host_set->lock, flags);
-out:
DPRINTK("EXIT\n");
}
@@ -3411,16 +3412,11 @@ struct ata_queued_cmd *ata_qc_new_init(struct ata_port *ap,
qc = ata_qc_new(ap);
if (qc) {
- qc->__sg = NULL;
- qc->flags = 0;
qc->scsicmd = NULL;
qc->ap = ap;
qc->dev = dev;
- qc->cursect = qc->cursg = qc->cursg_ofs = 0;
- qc->nsect = 0;
- qc->nbytes = qc->curbytes = 0;
- ata_tf_init(ap, &qc->tf, dev->devno);
+ ata_qc_reinit(qc);
}
return qc;
diff --git a/drivers/scsi/libata-scsi.c b/drivers/scsi/libata-scsi.c
index 0df4b682965..3b4ca55a333 100644
--- a/drivers/scsi/libata-scsi.c
+++ b/drivers/scsi/libata-scsi.c
@@ -1955,22 +1955,44 @@ void ata_scsi_badcmd(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *), u8
done(cmd);
}
-void atapi_request_sense(struct ata_port *ap, struct ata_device *dev,
- struct scsi_cmnd *cmd)
+static int atapi_sense_complete(struct ata_queued_cmd *qc,unsigned int err_mask)
{
- DECLARE_COMPLETION(wait);
- struct ata_queued_cmd *qc;
- unsigned long flags;
- int rc;
+ if (err_mask && ((err_mask & AC_ERR_DEV) == 0))
+ /* FIXME: not quite right; we don't want the
+ * translation of taskfile registers into
+ * a sense descriptors, since that's only
+ * correct for ATA, not ATAPI
+ */
+ ata_gen_ata_desc_sense(qc);
- DPRINTK("ATAPI request sense\n");
+ qc->scsidone(qc->scsicmd);
+ return 0;
+}
- qc = ata_qc_new_init(ap, dev);
- BUG_ON(qc == NULL);
+/* is it pointless to prefer PIO for "safety reasons"? */
+static inline int ata_pio_use_silly(struct ata_port *ap)
+{
+ return (ap->flags & ATA_FLAG_PIO_DMA);
+}
+
+static void atapi_request_sense(struct ata_queued_cmd *qc)
+{
+ struct ata_port *ap = qc->ap;
+ struct scsi_cmnd *cmd = qc->scsicmd;
+
+ DPRINTK("ATAPI request sense\n");
/* FIXME: is this needed? */
memset(cmd->sense_buffer, 0, sizeof(cmd->sense_buffer));
+ ap->ops->tf_read(ap, &qc->tf);
+
+ /* fill these in, for the case where they are -not- overwritten */
+ cmd->sense_buffer[0] = 0x70;
+ cmd->sense_buffer[2] = qc->tf.feature >> 4;
+
+ ata_qc_reinit(qc);
+
ata_sg_init_one(qc, cmd->sense_buffer, sizeof(cmd->sense_buffer));
qc->dma_dir = DMA_FROM_DEVICE;
@@ -1981,22 +2003,20 @@ void atapi_request_sense(struct ata_port *ap, struct ata_device *dev,
qc->tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
qc->tf.command = ATA_CMD_PACKET;
- qc->tf.protocol = ATA_PROT_ATAPI;
- qc->tf.lbam = (8 * 1024) & 0xff;
- qc->tf.lbah = (8 * 1024) >> 8;
+ if (ata_pio_use_silly(ap)) {
+ qc->tf.protocol = ATA_PROT_ATAPI_DMA;
+ qc->tf.feature |= ATAPI_PKT_DMA;
+ } else {
+ qc->tf.protocol = ATA_PROT_ATAPI;
+ qc->tf.lbam = (8 * 1024) & 0xff;
+ qc->tf.lbah = (8 * 1024) >> 8;
+ }
qc->nbytes = SCSI_SENSE_BUFFERSIZE;
- qc->waiting = &wait;
- qc->complete_fn = ata_qc_complete_noop;
+ qc->complete_fn = atapi_sense_complete;
- spin_lock_irqsave(&ap->host_set->lock, flags);
- rc = ata_qc_issue(qc);
- spin_unlock_irqrestore(&ap->host_set->lock, flags);
-
- if (rc)
- ata_port_disable(ap);
- else
- wait_for_completion(&wait);
+ if (ata_qc_issue(qc))
+ ata_qc_complete(qc, AC_ERR_OTHER);
DPRINTK("EXIT\n");
}
@@ -2008,19 +2028,8 @@ static int atapi_qc_complete(struct ata_queued_cmd *qc, unsigned int err_mask)
VPRINTK("ENTER, err_mask 0x%X\n", err_mask);
if (unlikely(err_mask & AC_ERR_DEV)) {
- DPRINTK("request check condition\n");
-
- /* FIXME: command completion with check condition
- * but no sense causes the error handler to run,
- * which then issues REQUEST SENSE, fills in the sense
- * buffer, and completes the command (for the second
- * time). We need to issue REQUEST SENSE some other
- * way, to avoid completing the command twice.
- */
cmd->result = SAM_STAT_CHECK_CONDITION;
-
- qc->scsidone(cmd);
-
+ atapi_request_sense(qc);
return 1;
}
diff --git a/drivers/scsi/libata.h b/drivers/scsi/libata.h
index fad051ca467..8ebaa694d18 100644
--- a/drivers/scsi/libata.h
+++ b/drivers/scsi/libata.h
@@ -29,7 +29,7 @@
#define __LIBATA_H__
#define DRV_NAME "libata"
-#define DRV_VERSION "1.12" /* must be exactly four chars */
+#define DRV_VERSION "1.20" /* must be exactly four chars */
struct ata_scsi_args {
u16 *id;
@@ -54,8 +54,6 @@ extern int ata_cmd_ioctl(struct scsi_device *scsidev, void __user *arg);
/* libata-scsi.c */
-extern void atapi_request_sense(struct ata_port *ap, struct ata_device *dev,
- struct scsi_cmnd *cmd);
extern void ata_scsi_scan_host(struct ata_port *ap);
extern int ata_scsi_error(struct Scsi_Host *host);
extern unsigned int ata_scsiop_inq_std(struct ata_scsi_args *args, u8 *rbuf,
diff --git a/drivers/scsi/sata_promise.c b/drivers/scsi/sata_promise.c
index 242d906987a..8a8e3e3ef0e 100644
--- a/drivers/scsi/sata_promise.c
+++ b/drivers/scsi/sata_promise.c
@@ -46,7 +46,7 @@
#include "sata_promise.h"
#define DRV_NAME "sata_promise"
-#define DRV_VERSION "1.02"
+#define DRV_VERSION "1.03"
enum {
diff --git a/drivers/scsi/sata_qstor.c b/drivers/scsi/sata_qstor.c
index 4a6d3067d23..a8987f5ff5c 100644
--- a/drivers/scsi/sata_qstor.c
+++ b/drivers/scsi/sata_qstor.c
@@ -41,7 +41,7 @@
#include <linux/libata.h>
#define DRV_NAME "sata_qstor"
-#define DRV_VERSION "0.04"
+#define DRV_VERSION "0.05"
enum {
QS_PORTS = 4,
diff --git a/drivers/scsi/sata_sil24.c b/drivers/scsi/sata_sil24.c
index 55e744d6db8..cb1933a3bd5 100644
--- a/drivers/scsi/sata_sil24.c
+++ b/drivers/scsi/sata_sil24.c
@@ -139,6 +139,7 @@ enum {
PORT_CS_DEV_RST = (1 << 1), /* device reset */
PORT_CS_INIT = (1 << 2), /* port initialize */
PORT_CS_IRQ_WOC = (1 << 3), /* interrupt write one to clear */
+ PORT_CS_CDB16 = (1 << 5), /* 0=12b cdb, 1=16b cdb */
PORT_CS_RESUME = (1 << 6), /* port resume */
PORT_CS_32BIT_ACTV = (1 << 10), /* 32-bit activation */
PORT_CS_PM_EN = (1 << 13), /* port multiplier enable */
@@ -188,11 +189,29 @@ enum {
PORT_CERR_XFR_PCIPERR = 35, /* PSD ecode 11 - PCI prity err during transfer */
PORT_CERR_SENDSERVICE = 36, /* FIS received while sending service */
+ /* bits of PRB control field */
+ PRB_CTRL_PROTOCOL = (1 << 0), /* override def. ATA protocol */
+ PRB_CTRL_PACKET_READ = (1 << 4), /* PACKET cmd read */
+ PRB_CTRL_PACKET_WRITE = (1 << 5), /* PACKET cmd write */
+ PRB_CTRL_NIEN = (1 << 6), /* Mask completion irq */
+ PRB_CTRL_SRST = (1 << 7), /* Soft reset request (ign BSY?) */
+
+ /* PRB protocol field */
+ PRB_PROT_PACKET = (1 << 0),
+ PRB_PROT_TCQ = (1 << 1),
+ PRB_PROT_NCQ = (1 << 2),
+ PRB_PROT_READ = (1 << 3),
+ PRB_PROT_WRITE = (1 << 4),
+ PRB_PROT_TRANSPARENT = (1 << 5),
+
/*
* Other constants
*/
SGE_TRM = (1 << 31), /* Last SGE in chain */
- PRB_SOFT_RST = (1 << 7), /* Soft reset request (ign BSY?) */
+ SGE_LNK = (1 << 30), /* linked list
+ Points to SGT, not SGE */
+ SGE_DRD = (1 << 29), /* discard data read (/dev/null)
+ data address ignored */
/* board id */
BID_SIL3124 = 0,
diff --git a/drivers/scsi/sata_svw.c b/drivers/scsi/sata_svw.c
index 57e5a9d964c..6e7f7c83a75 100644
--- a/drivers/scsi/sata_svw.c
+++ b/drivers/scsi/sata_svw.c
@@ -54,7 +54,7 @@
#endif /* CONFIG_PPC_OF */
#define DRV_NAME "sata_svw"
-#define DRV_VERSION "1.06"
+#define DRV_VERSION "1.07"
/* Taskfile registers offsets */
#define K2_SATA_TF_CMD_OFFSET 0x00
diff --git a/drivers/scsi/sata_sx4.c b/drivers/scsi/sata_sx4.c
index b4bbe48acab..dcc3ad9a9d6 100644
--- a/drivers/scsi/sata_sx4.c
+++ b/drivers/scsi/sata_sx4.c
@@ -46,7 +46,7 @@
#include "sata_promise.h"
#define DRV_NAME "sata_sx4"
-#define DRV_VERSION "0.7"
+#define DRV_VERSION "0.8"
enum {
diff --git a/drivers/scsi/sata_vsc.c b/drivers/scsi/sata_vsc.c
index 77a6e4b9262..fcfa486965b 100644
--- a/drivers/scsi/sata_vsc.c
+++ b/drivers/scsi/sata_vsc.c
@@ -47,7 +47,7 @@
#include <linux/libata.h>
#define DRV_NAME "sata_vsc"
-#define DRV_VERSION "1.0"
+#define DRV_VERSION "1.1"
/* Interrupt register offsets (from chip base address) */
#define VSC_SATA_INT_STAT_OFFSET 0x00
diff --git a/drivers/serial/68328serial.c b/drivers/serial/68328serial.c
index 2efb317153c..67e9afa000c 100644
--- a/drivers/serial/68328serial.c
+++ b/drivers/serial/68328serial.c
@@ -34,6 +34,7 @@
#include <linux/keyboard.h>
#include <linux/init.h>
#include <linux/pm.h>
+#include <linux/pm_legacy.h>
#include <linux/bitops.h>
#include <linux/delay.h>
@@ -1343,7 +1344,7 @@ static void show_serial_version(void)
printk("MC68328 serial driver version 1.00\n");
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_LEGACY
/* Serial Power management
* The console (currently fixed at line 0) is a special case for power
* management because the kernel is so chatty. The console will be
@@ -1393,7 +1394,7 @@ void startup_console(void)
struct m68k_serial *info = &m68k_soft[0];
startup(info);
}
-#endif
+#endif /* CONFIG_PM_LEGACY */
static struct tty_operations rs_ops = {
@@ -1486,7 +1487,7 @@ rs68328_init(void)
IRQ_FLG_STD,
"M68328_UART", NULL))
panic("Unable to attach 68328 serial interrupt\n");
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_LEGACY
serial_pm[i] = pm_register(PM_SYS_DEV, PM_SYS_COM, serial_pm_callback);
if (serial_pm[i])
serial_pm[i]->data = info;
diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c
index 3742753241e..e08510d09ff 100644
--- a/drivers/serial/8250.c
+++ b/drivers/serial/8250.c
@@ -999,7 +999,10 @@ static void autoconfig(struct uart_8250_port *up, unsigned int probeflags)
serial_outp(up, UART_MCR, save_mcr);
serial8250_clear_fifos(up);
(void)serial_in(up, UART_RX);
- serial_outp(up, UART_IER, 0);
+ if (up->capabilities & UART_CAP_UUE)
+ serial_outp(up, UART_IER, UART_IER_UUE);
+ else
+ serial_outp(up, UART_IER, 0);
out:
spin_unlock_irqrestore(&up->port.lock, flags);
diff --git a/drivers/serial/8250_pnp.c b/drivers/serial/8250_pnp.c
index 5d8660a42b7..b79ed0665d5 100644
--- a/drivers/serial/8250_pnp.c
+++ b/drivers/serial/8250_pnp.c
@@ -323,6 +323,8 @@ static const struct pnp_device_id pnp_dev_table[] = {
{ "USR9180", 0 },
/* U.S. Robotics 56K Voice INT PnP*/
{ "USR9190", 0 },
+ /* HP Compaq Tablet PC tc1100 Wacom tablet */
+ { "WACF005", 0 },
/* Rockwell's (PORALiNK) 33600 INT PNP */
{ "WCI0003", 0 },
/* Unkown PnP modems */
diff --git a/drivers/serial/cpm_uart/cpm_uart_core.c b/drivers/serial/cpm_uart/cpm_uart_core.c
index 25825f2aba2..987d22b53c2 100644
--- a/drivers/serial/cpm_uart/cpm_uart_core.c
+++ b/drivers/serial/cpm_uart/cpm_uart_core.c
@@ -7,7 +7,7 @@
* Based on ppc8xx.c by Thomas Gleixner
* Based on drivers/serial/amba.c by Russell King
*
- * Maintainer: Kumar Gala (kumar.gala@freescale.com) (CPM2)
+ * Maintainer: Kumar Gala (galak@kernel.crashing.org) (CPM2)
* Pantelis Antoniou (panto@intracom.gr) (CPM1)
*
* Copyright (C) 2004 Freescale Semiconductor, Inc.
diff --git a/drivers/serial/cpm_uart/cpm_uart_cpm1.c b/drivers/serial/cpm_uart/cpm_uart_cpm1.c
index 4b0786e7eb7..d789ee55cbb 100644
--- a/drivers/serial/cpm_uart/cpm_uart_cpm1.c
+++ b/drivers/serial/cpm_uart/cpm_uart_cpm1.c
@@ -3,7 +3,7 @@
*
* Driver for CPM (SCC/SMC) serial ports; CPM1 definitions
*
- * Maintainer: Kumar Gala (kumar.gala@freescale.com) (CPM2)
+ * Maintainer: Kumar Gala (galak@kernel.crashing.org) (CPM2)
* Pantelis Antoniou (panto@intracom.gr) (CPM1)
*
* Copyright (C) 2004 Freescale Semiconductor, Inc.
diff --git a/drivers/serial/cpm_uart/cpm_uart_cpm2.c b/drivers/serial/cpm_uart/cpm_uart_cpm2.c
index 15ad58d9488..fd9e53ed3fe 100644
--- a/drivers/serial/cpm_uart/cpm_uart_cpm2.c
+++ b/drivers/serial/cpm_uart/cpm_uart_cpm2.c
@@ -3,7 +3,7 @@
*
* Driver for CPM (SCC/SMC) serial ports; CPM2 definitions
*
- * Maintainer: Kumar Gala (kumar.gala@freescale.com) (CPM2)
+ * Maintainer: Kumar Gala (galak@kernel.crashing.org) (CPM2)
* Pantelis Antoniou (panto@intracom.gr) (CPM1)
*
* Copyright (C) 2004 Freescale Semiconductor, Inc.
diff --git a/drivers/serial/dz.c b/drivers/serial/dz.c
index e63b9dffc8d..4d8516d1bb7 100644
--- a/drivers/serial/dz.c
+++ b/drivers/serial/dz.c
@@ -1,9 +1,9 @@
/*
- * dz.c: Serial port driver for DECStations equiped
+ * dz.c: Serial port driver for DECStations equiped
* with the DZ chipset.
*
- * Copyright (C) 1998 Olivier A. D. Lebaillif
- *
+ * Copyright (C) 1998 Olivier A. D. Lebaillif
+ *
* Email: olivier.lebaillif@ifrsys.com
*
* [31-AUG-98] triemer
@@ -11,14 +11,14 @@
* removed base_addr code - moving address assignment to setup.c
* Changed name of dz_init to rs_init to be consistent with tc code
* [13-NOV-98] triemer fixed code to receive characters
- * after patches by harald to irq code.
+ * after patches by harald to irq code.
* [09-JAN-99] triemer minor fix for schedule - due to removal of timeout
* field from "current" - somewhere between 2.1.121 and 2.1.131
Qua Jun 27 15:02:26 BRT 2001
* [27-JUN-2001] Arnaldo Carvalho de Melo <acme@conectiva.com.br> - cleanups
- *
- * Parts (C) 1999 David Airlie, airlied@linux.ie
- * [07-SEP-99] Bugfixes
+ *
+ * Parts (C) 1999 David Airlie, airlied@linux.ie
+ * [07-SEP-99] Bugfixes
*
* [06-Jan-2002] Russell King <rmk@arm.linux.org.uk>
* Converted to new serial core
@@ -64,7 +64,7 @@ static struct dz_port dz_ports[DZ_NB_PORT];
#ifdef DEBUG_DZ
/*
- * debugging code to send out chars via prom
+ * debugging code to send out chars via prom
*/
static void debug_console(const char *s, int count)
{
@@ -82,7 +82,7 @@ static void debug_console(const char *s, int count)
* ------------------------------------------------------------
* dz_in () and dz_out ()
*
- * These routines are used to access the registers of the DZ
+ * These routines are used to access the registers of the DZ
* chip, hiding relocation differences between implementation.
* ------------------------------------------------------------
*/
@@ -106,8 +106,8 @@ static inline void dz_out(struct dz_port *dport, unsigned offset,
* ------------------------------------------------------------
* rs_stop () and rs_start ()
*
- * These routines are called before setting or resetting
- * tty->stopped. They enable or disable transmitter interrupts,
+ * These routines are called before setting or resetting
+ * tty->stopped. They enable or disable transmitter interrupts,
* as necessary.
* ------------------------------------------------------------
*/
@@ -156,17 +156,17 @@ static void dz_enable_ms(struct uart_port *port)
/*
* ------------------------------------------------------------
- * Here starts the interrupt handling routines. All of the
- * following subroutines are declared as inline and are folded
- * into dz_interrupt. They were separated out for readability's
- * sake.
+ * Here starts the interrupt handling routines. All of the
+ * following subroutines are declared as inline and are folded
+ * into dz_interrupt. They were separated out for readability's
+ * sake.
*
* Note: rs_interrupt() is a "fast" interrupt, which means that it
* runs with interrupts turned off. People who may want to modify
* rs_interrupt() should try to keep the interrupt handler as fast as
* possible. After you are done making modifications, it is not a bad
* idea to do:
- *
+ *
* make drivers/serial/dz.s
*
* and look at the resulting assemble code in dz.s.
@@ -403,7 +403,7 @@ static void dz_set_mctrl(struct uart_port *uport, unsigned int mctrl)
* startup ()
*
* various initialization tasks
- * -------------------------------------------------------------------
+ * -------------------------------------------------------------------
*/
static int dz_startup(struct uart_port *uport)
{
@@ -430,13 +430,13 @@ static int dz_startup(struct uart_port *uport)
return 0;
}
-/*
+/*
* -------------------------------------------------------------------
* shutdown ()
*
* This routine will shutdown a serial port; interrupts are disabled, and
* DTR is dropped if the hangup on close termio flag is on.
- * -------------------------------------------------------------------
+ * -------------------------------------------------------------------
*/
static void dz_shutdown(struct uart_port *uport)
{
@@ -451,7 +451,7 @@ static void dz_shutdown(struct uart_port *uport)
* release the bus after transmitting. This must be done when
* the transmit shift register is empty, not be done when the
* transmit holding register is empty. This functionality
- * allows an RS485 driver to be written in user space.
+ * allows an RS485 driver to be written in user space.
*/
static unsigned int dz_tx_empty(struct uart_port *uport)
{
@@ -645,9 +645,9 @@ static void __init dz_init_ports(void)
if (mips_machtype == MACH_DS23100 ||
mips_machtype == MACH_DS5100)
- base = (unsigned long) KN01_DZ11_BASE;
+ base = CKSEG1ADDR(KN01_SLOT_BASE + KN01_DZ11);
else
- base = (unsigned long) KN02_DZ11_BASE;
+ base = CKSEG1ADDR(KN02_SLOT_BASE + KN02_DZ11);
for (i = 0, dport = dz_ports; i < DZ_NB_PORT; i++, dport++) {
spin_lock_init(&dport->port.lock);
@@ -695,13 +695,13 @@ static void dz_console_put_char(struct dz_port *dport, unsigned char ch)
spin_unlock_irqrestore(&dport->port.lock, flags);
}
-/*
+/*
* -------------------------------------------------------------------
* dz_console_print ()
*
* dz_console_print is registered for printk.
* The console must be locked when we get here.
- * -------------------------------------------------------------------
+ * -------------------------------------------------------------------
*/
static void dz_console_print(struct console *cons,
const char *str,
diff --git a/drivers/serial/mpc52xx_uart.c b/drivers/serial/mpc52xx_uart.c
index 5d3cb848644..b8727d9bf69 100644
--- a/drivers/serial/mpc52xx_uart.c
+++ b/drivers/serial/mpc52xx_uart.c
@@ -725,7 +725,7 @@ mpc52xx_uart_probe(struct platform_device *dev)
int i, idx, ret;
/* Check validity & presence */
- idx = pdev->id;
+ idx = dev->id;
if (idx < 0 || idx >= MPC52xx_PSC_MAXNUM)
return -EINVAL;
@@ -748,7 +748,7 @@ mpc52xx_uart_probe(struct platform_device *dev)
port->ops = &mpc52xx_uart_ops;
/* Search for IRQ and mapbase */
- for (i=0 ; i<pdev->num_resources ; i++, res++) {
+ for (i=0 ; i<dev->num_resources ; i++, res++) {
if (res->flags & IORESOURCE_MEM)
port->mapbase = res->start;
else if (res->flags & IORESOURCE_IRQ)
diff --git a/drivers/serial/sa1100.c b/drivers/serial/sa1100.c
index fd9deee20e0..0e3daf6d7b5 100644
--- a/drivers/serial/sa1100.c
+++ b/drivers/serial/sa1100.c
@@ -156,7 +156,7 @@ static void sa1100_stop_tx(struct uart_port *port)
}
/*
- * interrupts may not be disabled on entry
+ * port locked and interrupts disabled
*/
static void sa1100_start_tx(struct uart_port *port)
{
@@ -164,11 +164,9 @@ static void sa1100_start_tx(struct uart_port *port)
unsigned long flags;
u32 utcr3;
- spin_lock_irqsave(&sport->port.lock, flags);
utcr3 = UART_GET_UTCR3(sport);
sport->port.read_status_mask |= UTSR0_TO_SM(UTSR0_TFS);
UART_PUT_UTCR3(sport, utcr3 | UTCR3_TIE);
- spin_unlock_irqrestore(&sport->port.lock, flags);
}
/*
diff --git a/drivers/serial/serial_core.c b/drivers/serial/serial_core.c
index 427a2385807..2331296e1e1 100644
--- a/drivers/serial/serial_core.c
+++ b/drivers/serial/serial_core.c
@@ -209,33 +209,45 @@ static void uart_shutdown(struct uart_state *state)
struct uart_info *info = state->info;
struct uart_port *port = state->port;
- if (!(info->flags & UIF_INITIALIZED))
- return;
-
/*
- * Turn off DTR and RTS early.
+ * Set the TTY IO error marker
*/
- if (!info->tty || (info->tty->termios->c_cflag & HUPCL))
- uart_clear_mctrl(port, TIOCM_DTR | TIOCM_RTS);
+ if (info->tty)
+ set_bit(TTY_IO_ERROR, &info->tty->flags);
- /*
- * clear delta_msr_wait queue to avoid mem leaks: we may free
- * the irq here so the queue might never be woken up. Note
- * that we won't end up waiting on delta_msr_wait again since
- * any outstanding file descriptors should be pointing at
- * hung_up_tty_fops now.
- */
- wake_up_interruptible(&info->delta_msr_wait);
+ if (info->flags & UIF_INITIALIZED) {
+ info->flags &= ~UIF_INITIALIZED;
- /*
- * Free the IRQ and disable the port.
- */
- port->ops->shutdown(port);
+ /*
+ * Turn off DTR and RTS early.
+ */
+ if (!info->tty || (info->tty->termios->c_cflag & HUPCL))
+ uart_clear_mctrl(port, TIOCM_DTR | TIOCM_RTS);
+
+ /*
+ * clear delta_msr_wait queue to avoid mem leaks: we may free
+ * the irq here so the queue might never be woken up. Note
+ * that we won't end up waiting on delta_msr_wait again since
+ * any outstanding file descriptors should be pointing at
+ * hung_up_tty_fops now.
+ */
+ wake_up_interruptible(&info->delta_msr_wait);
+
+ /*
+ * Free the IRQ and disable the port.
+ */
+ port->ops->shutdown(port);
+
+ /*
+ * Ensure that the IRQ handler isn't running on another CPU.
+ */
+ synchronize_irq(port->irq);
+ }
/*
- * Ensure that the IRQ handler isn't running on another CPU.
+ * kill off our tasklet
*/
- synchronize_irq(port->irq);
+ tasklet_kill(&info->tlet);
/*
* Free the transmit buffer page.
@@ -244,15 +256,6 @@ static void uart_shutdown(struct uart_state *state)
free_page((unsigned long)info->xmit.buf);
info->xmit.buf = NULL;
}
-
- /*
- * kill off our tasklet
- */
- tasklet_kill(&info->tlet);
- if (info->tty)
- set_bit(TTY_IO_ERROR, &info->tty->flags);
-
- info->flags &= ~UIF_INITIALIZED;
}
/**
@@ -1928,14 +1931,25 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *port)
if (state->info && state->info->flags & UIF_INITIALIZED) {
struct uart_ops *ops = port->ops;
+ int ret;
ops->set_mctrl(port, 0);
- ops->startup(port);
- uart_change_speed(state, NULL);
- spin_lock_irq(&port->lock);
- ops->set_mctrl(port, port->mctrl);
- ops->start_tx(port);
- spin_unlock_irq(&port->lock);
+ ret = ops->startup(port);
+ if (ret == 0) {
+ uart_change_speed(state, NULL);
+ spin_lock_irq(&port->lock);
+ ops->set_mctrl(port, port->mctrl);
+ ops->start_tx(port);
+ spin_unlock_irq(&port->lock);
+ } else {
+ /*
+ * Failed to resume - maybe hardware went away?
+ * Clear the "initialized" flag so we won't try
+ * to call the low level drivers shutdown method.
+ */
+ state->info->flags &= ~UIF_INITIALIZED;
+ uart_shutdown(state);
+ }
}
up(&state->sem);
diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c
index e7802ffe549..bcea87c3cc0 100644
--- a/drivers/video/console/fbcon.c
+++ b/drivers/video/console/fbcon.c
@@ -106,8 +106,7 @@ enum {
FBCON_LOGO_DONTSHOW = -3 /* do not show the logo */
};
-struct display fb_display[MAX_NR_CONSOLES];
-EXPORT_SYMBOL(fb_display);
+static struct display fb_display[MAX_NR_CONSOLES];
static signed char con2fb_map[MAX_NR_CONSOLES];
static signed char con2fb_map_boot[MAX_NR_CONSOLES];
@@ -653,13 +652,12 @@ static void set_blitting_type(struct vc_data *vc, struct fb_info *info,
{
struct fbcon_ops *ops = info->fbcon_par;
+ ops->p = (p) ? p : &fb_display[vc->vc_num];
+
if ((info->flags & FBINFO_MISC_TILEBLITTING))
fbcon_set_tileops(vc, info, p, ops);
else {
- struct display *disp;
-
- disp = (p) ? p : &fb_display[vc->vc_num];
- fbcon_set_rotation(info, disp);
+ fbcon_set_rotation(info, ops->p);
fbcon_set_bitops(ops);
}
}
@@ -668,11 +666,10 @@ static void set_blitting_type(struct vc_data *vc, struct fb_info *info,
struct display *p)
{
struct fbcon_ops *ops = info->fbcon_par;
- struct display *disp;
info->flags &= ~FBINFO_MISC_TILEBLITTING;
- disp = (p) ? p : &fb_display[vc->vc_num];
- fbcon_set_rotation(info, disp);
+ ops->p = (p) ? p : &fb_display[vc->vc_num];
+ fbcon_set_rotation(info, ops->p);
fbcon_set_bitops(ops);
}
#endif /* CONFIG_MISC_TILEBLITTING */
diff --git a/drivers/video/console/fbcon.h b/drivers/video/console/fbcon.h
index accfd7bd8e9..6892e7ff34d 100644
--- a/drivers/video/console/fbcon.h
+++ b/drivers/video/console/fbcon.h
@@ -52,8 +52,6 @@ struct display {
struct fb_videomode *mode;
};
-extern struct display fb_display[];
-
struct fbcon_ops {
void (*bmove)(struct vc_data *vc, struct fb_info *info, int sy,
int sx, int dy, int dx, int height, int width);
@@ -73,6 +71,7 @@ struct fbcon_ops {
struct fb_var_screeninfo var; /* copy of the current fb_var_screeninfo */
struct timer_list cursor_timer; /* Cursor timer */
struct fb_cursor cursor_state;
+ struct display *p;
int currcon; /* Current VC. */
int cursor_flash;
int cursor_reset;
diff --git a/drivers/video/console/fbcon_ccw.c b/drivers/video/console/fbcon_ccw.c
index 680aabab73c..3afd1eeb1ad 100644
--- a/drivers/video/console/fbcon_ccw.c
+++ b/drivers/video/console/fbcon_ccw.c
@@ -63,9 +63,9 @@ static inline void ccw_update_attr(u8 *dst, u8 *src, int attribute,
static void ccw_bmove(struct vc_data *vc, struct fb_info *info, int sy,
int sx, int dy, int dx, int height, int width)
{
- struct display *p = &fb_display[vc->vc_num];
+ struct fbcon_ops *ops = info->fbcon_par;
struct fb_copyarea area;
- u32 vyres = GETVYRES(p->scrollmode, info);
+ u32 vyres = GETVYRES(ops->p->scrollmode, info);
area.sx = sy * vc->vc_font.height;
area.sy = vyres - ((sx + width) * vc->vc_font.width);
@@ -80,10 +80,10 @@ static void ccw_bmove(struct vc_data *vc, struct fb_info *info, int sy,
static void ccw_clear(struct vc_data *vc, struct fb_info *info, int sy,
int sx, int height, int width)
{
- struct display *p = &fb_display[vc->vc_num];
+ struct fbcon_ops *ops = info->fbcon_par;
struct fb_fillrect region;
int bgshift = (vc->vc_hi_font_mask) ? 13 : 12;
- u32 vyres = GETVYRES(p->scrollmode, info);
+ u32 vyres = GETVYRES(ops->p->scrollmode, info);
region.color = attr_bgcol_ec(bgshift,vc);
region.dx = sy * vc->vc_font.height;
@@ -131,7 +131,6 @@ static void ccw_putcs(struct vc_data *vc, struct fb_info *info,
int fg, int bg)
{
struct fb_image image;
- struct display *p = &fb_display[vc->vc_num];
struct fbcon_ops *ops = info->fbcon_par;
u32 width = (vc->vc_font.height + 7)/8;
u32 cellsize = width * vc->vc_font.width;
@@ -141,7 +140,7 @@ static void ccw_putcs(struct vc_data *vc, struct fb_info *info,
u32 cnt, pitch, size;
u32 attribute = get_attribute(info, scr_readw(s));
u8 *dst, *buf = NULL;
- u32 vyres = GETVYRES(p->scrollmode, info);
+ u32 vyres = GETVYRES(ops->p->scrollmode, info);
if (!ops->fontbuffer)
return;
@@ -397,9 +396,8 @@ static void ccw_cursor(struct vc_data *vc, struct fb_info *info,
int ccw_update_start(struct fb_info *info)
{
struct fbcon_ops *ops = info->fbcon_par;
- struct display *p = &fb_display[ops->currcon];
u32 yoffset;
- u32 vyres = GETVYRES(p->scrollmode, info);
+ u32 vyres = GETVYRES(ops->p->scrollmode, info);
int err;
yoffset = (vyres - info->var.yres) - ops->var.xoffset;
diff --git a/drivers/video/console/fbcon_cw.c b/drivers/video/console/fbcon_cw.c
index 6c6f3b6dd17..6d92b845620 100644
--- a/drivers/video/console/fbcon_cw.c
+++ b/drivers/video/console/fbcon_cw.c
@@ -49,9 +49,9 @@ static inline void cw_update_attr(u8 *dst, u8 *src, int attribute,
static void cw_bmove(struct vc_data *vc, struct fb_info *info, int sy,
int sx, int dy, int dx, int height, int width)
{
- struct display *p = &fb_display[vc->vc_num];
+ struct fbcon_ops *ops = info->fbcon_par;
struct fb_copyarea area;
- u32 vxres = GETVXRES(p->scrollmode, info);
+ u32 vxres = GETVXRES(ops->p->scrollmode, info);
area.sx = vxres - ((sy + height) * vc->vc_font.height);
area.sy = sx * vc->vc_font.width;
@@ -66,10 +66,10 @@ static void cw_bmove(struct vc_data *vc, struct fb_info *info, int sy,
static void cw_clear(struct vc_data *vc, struct fb_info *info, int sy,
int sx, int height, int width)
{
- struct display *p = &fb_display[vc->vc_num];
+ struct fbcon_ops *ops = info->fbcon_par;
struct fb_fillrect region;
int bgshift = (vc->vc_hi_font_mask) ? 13 : 12;
- u32 vxres = GETVXRES(p->scrollmode, info);
+ u32 vxres = GETVXRES(ops->p->scrollmode, info);
region.color = attr_bgcol_ec(bgshift,vc);
region.dx = vxres - ((sy + height) * vc->vc_font.height);
@@ -117,7 +117,6 @@ static void cw_putcs(struct vc_data *vc, struct fb_info *info,
int fg, int bg)
{
struct fb_image image;
- struct display *p = &fb_display[vc->vc_num];
struct fbcon_ops *ops = info->fbcon_par;
u32 width = (vc->vc_font.height + 7)/8;
u32 cellsize = width * vc->vc_font.width;
@@ -127,7 +126,7 @@ static void cw_putcs(struct vc_data *vc, struct fb_info *info,
u32 cnt, pitch, size;
u32 attribute = get_attribute(info, scr_readw(s));
u8 *dst, *buf = NULL;
- u32 vxres = GETVXRES(p->scrollmode, info);
+ u32 vxres = GETVXRES(ops->p->scrollmode, info);
if (!ops->fontbuffer)
return;
@@ -381,8 +380,7 @@ static void cw_cursor(struct vc_data *vc, struct fb_info *info,
int cw_update_start(struct fb_info *info)
{
struct fbcon_ops *ops = info->fbcon_par;
- struct display *p = &fb_display[ops->currcon];
- u32 vxres = GETVXRES(p->scrollmode, info);
+ u32 vxres = GETVXRES(ops->p->scrollmode, info);
u32 xoffset;
int err;
diff --git a/drivers/video/console/fbcon_ud.c b/drivers/video/console/fbcon_ud.c
index 2e1d9d4249c..c4d7c89212b 100644
--- a/drivers/video/console/fbcon_ud.c
+++ b/drivers/video/console/fbcon_ud.c
@@ -48,10 +48,10 @@ static inline void ud_update_attr(u8 *dst, u8 *src, int attribute,
static void ud_bmove(struct vc_data *vc, struct fb_info *info, int sy,
int sx, int dy, int dx, int height, int width)
{
- struct display *p = &fb_display[vc->vc_num];
+ struct fbcon_ops *ops = info->fbcon_par;
struct fb_copyarea area;
- u32 vyres = GETVYRES(p->scrollmode, info);
- u32 vxres = GETVXRES(p->scrollmode, info);
+ u32 vyres = GETVYRES(ops->p->scrollmode, info);
+ u32 vxres = GETVXRES(ops->p->scrollmode, info);
area.sy = vyres - ((sy + height) * vc->vc_font.height);
area.sx = vxres - ((sx + width) * vc->vc_font.width);
@@ -66,11 +66,11 @@ static void ud_bmove(struct vc_data *vc, struct fb_info *info, int sy,
static void ud_clear(struct vc_data *vc, struct fb_info *info, int sy,
int sx, int height, int width)
{
- struct display *p = &fb_display[vc->vc_num];
+ struct fbcon_ops *ops = info->fbcon_par;
struct fb_fillrect region;
int bgshift = (vc->vc_hi_font_mask) ? 13 : 12;
- u32 vyres = GETVYRES(p->scrollmode, info);
- u32 vxres = GETVXRES(p->scrollmode, info);
+ u32 vyres = GETVYRES(ops->p->scrollmode, info);
+ u32 vxres = GETVXRES(ops->p->scrollmode, info);
region.color = attr_bgcol_ec(bgshift,vc);
region.dy = vyres - ((sy + height) * vc->vc_font.height);
@@ -153,7 +153,6 @@ static void ud_putcs(struct vc_data *vc, struct fb_info *info,
int fg, int bg)
{
struct fb_image image;
- struct display *p = &fb_display[vc->vc_num];
struct fbcon_ops *ops = info->fbcon_par;
u32 width = (vc->vc_font.width + 7)/8;
u32 cellsize = width * vc->vc_font.height;
@@ -163,8 +162,8 @@ static void ud_putcs(struct vc_data *vc, struct fb_info *info,
u32 mod = vc->vc_font.width % 8, cnt, pitch, size;
u32 attribute = get_attribute(info, scr_readw(s));
u8 *dst, *buf = NULL;
- u32 vyres = GETVYRES(p->scrollmode, info);
- u32 vxres = GETVXRES(p->scrollmode, info);
+ u32 vyres = GETVYRES(ops->p->scrollmode, info);
+ u32 vxres = GETVXRES(ops->p->scrollmode, info);
if (!ops->fontbuffer)
return;
@@ -421,10 +420,9 @@ static void ud_cursor(struct vc_data *vc, struct fb_info *info,
int ud_update_start(struct fb_info *info)
{
struct fbcon_ops *ops = info->fbcon_par;
- struct display *p = &fb_display[ops->currcon];
u32 xoffset, yoffset;
- u32 vyres = GETVYRES(p->scrollmode, info);
- u32 vxres = GETVXRES(p->scrollmode, info);
+ u32 vyres = GETVYRES(ops->p->scrollmode, info);
+ u32 vxres = GETVXRES(ops->p->scrollmode, info);
int err;
xoffset = (vxres - info->var.xres) - ops->var.xoffset;
diff --git a/drivers/video/nvidia/nv_proto.h b/drivers/video/nvidia/nv_proto.h
index f60b1f43227..3353103e8b0 100644
--- a/drivers/video/nvidia/nv_proto.h
+++ b/drivers/video/nvidia/nv_proto.h
@@ -42,7 +42,7 @@ int nvidia_probe_i2c_connector(struct fb_info *info, int conn,
#define nvidia_probe_i2c_connector(p, c, edid) (-1)
#endif
-#ifdef CONFIG_FB_OF
+#ifdef CONFIG_PPC_OF
int nvidia_probe_of_connector(struct fb_info *info, int conn,
u8 ** out_edid);
#else
diff --git a/drivers/video/nvidia/nvidia.c b/drivers/video/nvidia/nvidia.c
index 0b40a2a721c..bee09c6e48f 100644
--- a/drivers/video/nvidia/nvidia.c
+++ b/drivers/video/nvidia/nvidia.c
@@ -1301,7 +1301,7 @@ static int nvidiafb_pan_display(struct fb_var_screeninfo *var,
struct nvidia_par *par = info->par;
u32 total;
- total = info->var.yoffset * info->fix.line_length + info->var.xoffset;
+ total = var->yoffset * info->fix.line_length + var->xoffset;
NVSetStartAddress(par, total);
diff --git a/drivers/video/vesafb.c b/drivers/video/vesafb.c
index 2c3aa2fcfd9..3e58ddc2bc3 100644
--- a/drivers/video/vesafb.c
+++ b/drivers/video/vesafb.c
@@ -413,6 +413,7 @@ static int __init vesafb_probe(struct platform_device *dev)
* region already (FIXME) */
request_region(0x3c0, 32, "vesafb");
+#ifdef CONFIG_MTRR
if (mtrr) {
unsigned int temp_size = size_total;
unsigned int type = 0;
@@ -450,6 +451,7 @@ static int __init vesafb_probe(struct platform_device *dev)
} while (temp_size >= PAGE_SIZE && rc == -EINVAL);
}
}
+#endif
info->fbops = &vesafb_ops;
info->var = vesafb_defined;
diff --git a/drivers/video/w100fb.c b/drivers/video/w100fb.c
index daa46051f55..f6e24ee85f0 100644
--- a/drivers/video/w100fb.c
+++ b/drivers/video/w100fb.c
@@ -514,7 +514,7 @@ int __init w100fb_probe(struct platform_device *pdev)
if (remapped_fbuf == NULL)
goto out;
- info=framebuffer_alloc(sizeof(struct w100fb_par), dev);
+ info=framebuffer_alloc(sizeof(struct w100fb_par), &pdev->dev);
if (!info) {
err = -ENOMEM;
goto out;