diff options
author | Jeff Garzik <jgarzik@pobox.com> | 2005-11-16 15:12:58 -0500 |
---|---|---|
committer | Jeff Garzik <jgarzik@pobox.com> | 2005-11-16 15:12:58 -0500 |
commit | 3be4bb06b53df2c0a760eeaa5f53448faddedfca (patch) | |
tree | 67dc02f39e48a8a37d8f3e4a38422d86f5164820 /drivers | |
parent | 85d6162d6cea9220e483989817eac0cebc03070e (diff) | |
parent | 7bdd720869ff75700b48b132ee71852615b55808 (diff) |
Merge branch 'upstream-fixes'
Diffstat (limited to 'drivers')
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", ®len); + 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; |